Great simplification by introducing DictionaryMap
This commit is contained in:
parent
eb4f456550
commit
cbbc6c50c0
|
@ -1,7 +1,7 @@
|
|||
import { Annotated } from "./annotated";
|
||||
import { DecodeError, ShortPacket } from "./codec";
|
||||
import { Tag } from "./constants";
|
||||
import { Set, Dictionary } from "./dictionary";
|
||||
import { Set, Dictionary, DictionaryMap } from "./dictionary";
|
||||
import { DoubleFloat } from "./float";
|
||||
import { Record } from "./record";
|
||||
import { Bytes, BytesLike, underlying, hexDigit } from "./bytes";
|
||||
|
@ -219,17 +219,14 @@ export class Decoder<T extends Embeddable = never> implements TypedDecoder<T> {
|
|||
}
|
||||
|
||||
static dictionaryFromArray<T extends Embeddable>(vs: Value<T>[]): Dictionary<T> {
|
||||
const entries: [Value<T>, Value<T>][] = [];
|
||||
const d = new DictionaryMap<T>();
|
||||
if (vs.length % 2) throw new DecodeError("Missing dictionary value");
|
||||
for (let i = 0; i < vs.length; i += 2) {
|
||||
entries.push([vs[i], vs[i+1]]);
|
||||
}
|
||||
const r = Dictionary.from<T>(entries, true);
|
||||
if ('duplicate' in r) {
|
||||
throw new DecodeError("Duplicate key");
|
||||
} else {
|
||||
return r.ok;
|
||||
if (d.has(vs[i])) throw new DecodeError(`Duplicate key: ${stringify(vs[i])}`);
|
||||
d.set(vs[i], vs[i+1]);
|
||||
}
|
||||
d.simplify();
|
||||
return d.value;
|
||||
}
|
||||
|
||||
next(): Value<T> {
|
||||
|
@ -260,9 +257,11 @@ export class Decoder<T extends Embeddable = never> implements TypedDecoder<T> {
|
|||
}
|
||||
case Tag.Sequence: return this.state.wrap<T>(this.nextvalues());
|
||||
case Tag.Set: {
|
||||
const vs = this.nextvalues();
|
||||
const s = new Set<T>(vs);
|
||||
if (vs.length !== s.size) throw new DecodeError("Duplicate value in set");
|
||||
const s = new Set<T>();
|
||||
for (const v of this.nextvalues()) {
|
||||
if (s.has(v)) throw new DecodeError(`Duplicate value: ${stringify(v)}`);
|
||||
s.add(v);
|
||||
}
|
||||
return this.state.wrap<T>(s);
|
||||
}
|
||||
case Tag.Dictionary: return this.state.wrap<T>(Decoder.dictionaryFromArray(this.nextvalues()));
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Encoder, canonicalString } from "./encoder";
|
||||
import { Tag } from "./constants";
|
||||
import { FlexMap, FlexSet, _iterMap, IdentitySet, Equivalence } from "./flex";
|
||||
import { FlexMap, FlexSet, _iterMap, IdentitySet, Equivalence, IsMap } from "./flex";
|
||||
import { Value } from "./values";
|
||||
import { Bytes } from './bytes';
|
||||
import { Embeddable, GenericEmbedded, isEmbedded } from "./embedded";
|
||||
|
@ -8,7 +8,8 @@ import type { Preservable } from "./encoder";
|
|||
import type { Writer, PreserveWritable } from "./writer";
|
||||
import { annotations, Annotated } from "./annotated";
|
||||
import { Float } from "./float";
|
||||
import { JsDictionary, JsDictionaryMap } from "./jsdictionary";
|
||||
import { JsDictionary } from "./jsdictionary";
|
||||
import { unannotate } from "./strip";
|
||||
|
||||
export type DictionaryType = 'Dictionary' | 'Set';
|
||||
export const DictionaryType = Symbol.for('DictionaryType');
|
||||
|
@ -76,6 +77,125 @@ export class KeyedDictionary<T extends Embeddable = GenericEmbedded, K extends C
|
|||
export type Dictionary<T extends Embeddable = GenericEmbedded, V = Value<T>> =
|
||||
JsDictionary<V> | KeyedDictionary<T, Value<T>, V>;
|
||||
|
||||
export class DictionaryMap<T extends Embeddable = GenericEmbedded, V = Value<T>> implements Map<Value<T>, V> {
|
||||
get [IsMap](): boolean { return true; }
|
||||
|
||||
j: JsDictionary<V> | undefined;
|
||||
k: KeyedDictionary<T, Value<T>, V> | undefined;
|
||||
|
||||
constructor(input?: Dictionary<T, V>) {
|
||||
if (input === void 0) {
|
||||
this.j = {};
|
||||
this.k = void 0;
|
||||
} else if (DictionaryType in input) {
|
||||
this.j = void 0;
|
||||
this.k = input;
|
||||
} else {
|
||||
this.j = input;
|
||||
this.k = void 0;
|
||||
}
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
if (this.j) {
|
||||
JsDictionary.clear(this.j);
|
||||
} else {
|
||||
this.k!.clear();
|
||||
}
|
||||
}
|
||||
|
||||
delete(key: Value<T>): boolean {
|
||||
if (this.j) {
|
||||
key = unannotate(key);
|
||||
if (typeof key !== 'symbol') return false;
|
||||
return JsDictionary.remove(this.j, key);
|
||||
} else {
|
||||
return this.k!.delete(key);
|
||||
}
|
||||
}
|
||||
|
||||
forEach(callbackfn: (value: V, key: Value<T>, map: Map<Value<T>, V>) => void, thisArg?: any): void {
|
||||
if (this.j) {
|
||||
JsDictionary.forEach(this.j, (v, k) => callbackfn.call(thisArg, v, k, this));
|
||||
} else {
|
||||
this.k!.forEach(callbackfn, thisArg);
|
||||
}
|
||||
}
|
||||
|
||||
get(key: Value<T>): V | undefined {
|
||||
if (this.j) {
|
||||
key = unannotate(key);
|
||||
if (typeof key !== 'symbol') return void 0;
|
||||
return JsDictionary.get(this.j, key);
|
||||
} else {
|
||||
return this.k!.get(key);
|
||||
}
|
||||
}
|
||||
|
||||
has(key: Value<T>): boolean {
|
||||
if (this.j) {
|
||||
key = unannotate(key);
|
||||
if (typeof key !== 'symbol') return false;
|
||||
return JsDictionary.has(this.j, key);
|
||||
} else {
|
||||
return this.k!.has(key);
|
||||
}
|
||||
}
|
||||
|
||||
set(key: Value<T>, value: V): this {
|
||||
if (this.j) {
|
||||
if (typeof key === 'symbol') {
|
||||
JsDictionary.set(this.j, key, value);
|
||||
return this;
|
||||
}
|
||||
this.k = new KeyedDictionary<T, Value<T>, V>(JsDictionary.entries(this.j));
|
||||
this.j = void 0;
|
||||
}
|
||||
this.k!.set(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
get size(): number {
|
||||
return this.j ? JsDictionary.size(this.j) : this.k!.size;
|
||||
}
|
||||
|
||||
entries(): IterableIterator<[Value<T>, V]> {
|
||||
return this.j ? JsDictionary.entries(this.j) : this.k!.entries();
|
||||
}
|
||||
|
||||
keys(): IterableIterator<Value<T>> {
|
||||
return this.j ? JsDictionary.keys(this.j) : this.k!.keys();
|
||||
}
|
||||
|
||||
values(): IterableIterator<V> {
|
||||
return this.j ? JsDictionary.values(this.j) : this.k!.values();
|
||||
}
|
||||
|
||||
[Symbol.iterator](): IterableIterator<[Value<T>, V]> {
|
||||
return this.entries();
|
||||
}
|
||||
|
||||
get [Symbol.toStringTag](): string {
|
||||
return 'DictionaryMap';
|
||||
}
|
||||
|
||||
get value(): Dictionary<T, V> {
|
||||
return this.j ?? this.k!;
|
||||
}
|
||||
|
||||
simplify(): void {
|
||||
if (!this.j) {
|
||||
const r: JsDictionary<V> = {};
|
||||
for (const [key, value] of this.k!.entries()) {
|
||||
if (typeof key !== 'symbol') return;
|
||||
r[key.description!] = value;
|
||||
}
|
||||
this.j = r;
|
||||
this.k = void 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export namespace Dictionary {
|
||||
export function isDictionary<T extends Embeddable = GenericEmbedded, V = Value<T>>(
|
||||
x: any
|
||||
|
@ -93,69 +213,16 @@ export namespace Dictionary {
|
|||
}
|
||||
}
|
||||
|
||||
export function mapInto<K, V, S, W, O extends Map<S, W>>(
|
||||
i: Map<K, V>,
|
||||
o: O,
|
||||
f: (entry: [K, V]) => [S, W],
|
||||
): O {
|
||||
for (let oldEntry of i.entries()) {
|
||||
const newEntry = f(oldEntry);
|
||||
o.set(newEntry[0], newEntry[1]);
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
export function asMap<T extends Embeddable = GenericEmbedded, V = Value<T>>(
|
||||
x: Dictionary<T>
|
||||
): Map<Value<T>, V>;
|
||||
x: Dictionary<T, V>
|
||||
): DictionaryMap<T, V>;
|
||||
export function asMap<T extends Embeddable = GenericEmbedded, V = Value<T>>(
|
||||
x: any
|
||||
): Map<Value<T>, V> | undefined;
|
||||
): DictionaryMap<T, V> | undefined;
|
||||
export function asMap<T extends Embeddable = GenericEmbedded, V = Value<T>>(
|
||||
x: any
|
||||
): Map<Value<T>, V> | undefined {
|
||||
if (!isDictionary<T, V>(x)) return void 0;
|
||||
if (DictionaryType in x) return x;
|
||||
return new JsDictionaryMap(x);
|
||||
}
|
||||
|
||||
export function from<T extends Embeddable, E extends [Value<T>, Value<T>, ... any[]] = [Value<T>, Value<T>]>(
|
||||
entries: Iterable<E>, failOnDuplicateKeys: true): {ok: Dictionary<T>} | {duplicate: E};
|
||||
export function from<T extends Embeddable, E extends [Value<T>, Value<T>, ... any[]] = [Value<T>, Value<T>]>(
|
||||
entries: Iterable<E>, failOnDuplicateKeys: false): Dictionary<T>;
|
||||
export function from<T extends Embeddable, E extends [Value<T>, Value<T>, ... any[]] = [Value<T>, Value<T>]>(
|
||||
entries: Iterable<E>): Dictionary<T>;
|
||||
export function from<T extends Embeddable, E extends [Value<T>, Value<T>, ... any[]] = [Value<T>, Value<T>]>(
|
||||
entries0: Iterable<E>,
|
||||
failOnDuplicateKeys: boolean = false,
|
||||
): Dictionary<T> | {ok: Dictionary<T>} | {duplicate: E} {
|
||||
const typeSet = new IdentitySet();
|
||||
const entries: E[] = [];
|
||||
for (const e of entries0) {
|
||||
typeSet.add(typeof e[0]);
|
||||
entries.push(e);
|
||||
}
|
||||
const types = Array.from(typeSet);
|
||||
if (types.length === 1 && types[0] === 'symbol') {
|
||||
const result: JsDictionary<Value<T>> = {};
|
||||
for (const e of entries) {
|
||||
const k = (e[0] as symbol).description!;
|
||||
if (failOnDuplicateKeys && Object.prototype.hasOwnProperty.call(result, k)) {
|
||||
return {duplicate: e};
|
||||
}
|
||||
result[k] = e[1];
|
||||
}
|
||||
return failOnDuplicateKeys ? {ok: result} : result;
|
||||
} else {
|
||||
const result = new KeyedDictionary<T>();
|
||||
for (const e of entries) {
|
||||
if (failOnDuplicateKeys && result.has(e[0])) {
|
||||
return {duplicate: e};
|
||||
}
|
||||
result.set(e[0], e[1]);
|
||||
}
|
||||
return failOnDuplicateKeys ? {ok: result} : result;
|
||||
}
|
||||
): DictionaryMap<T, V> | undefined {
|
||||
return isDictionary<T, V>(x) ? new DictionaryMap(x) : void 0;
|
||||
}
|
||||
|
||||
export function __from_preserve__<T extends Embeddable>(v: Value<T>): undefined | Dictionary<T> {
|
||||
|
|
|
@ -5,8 +5,7 @@ import { EncodeError } from "./codec";
|
|||
import { Record, Tuple } from "./record";
|
||||
import { EmbeddedTypeEncode, isEmbedded } from "./embedded";
|
||||
import type { Embeddable } from "./embedded";
|
||||
import { encodeDictionaryOn } from "./dictionary";
|
||||
import { JsDictionaryMap } from "./jsdictionary";
|
||||
import { DictionaryMap, encodeDictionaryOn } from "./dictionary";
|
||||
|
||||
export type Encodable<T extends Embeddable> =
|
||||
Value<T> | Preservable<T> | Iterable<Value<T>> | ArrayBufferView;
|
||||
|
@ -295,11 +294,10 @@ export class Encoder<T extends Embeddable> {
|
|||
this.embeddedEncode.encode(this.state, v);
|
||||
}
|
||||
else {
|
||||
encodeDictionaryOn(
|
||||
new JsDictionaryMap(v),
|
||||
this,
|
||||
(k, e) => e.push(k),
|
||||
(v, e) => e.push(v));
|
||||
encodeDictionaryOn(new DictionaryMap<T>(v),
|
||||
this,
|
||||
(k, e) => e.push(k),
|
||||
(v, e) => e.push(v));
|
||||
}
|
||||
return this; // for chaining
|
||||
}
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import { Record, Tuple } from "./record";
|
||||
import { Bytes } from "./bytes";
|
||||
import { Value } from "./values";
|
||||
import { Set, KeyedDictionary, Dictionary } from "./dictionary";
|
||||
import { JsDictionary, JsDictionaryMap } from "./jsdictionary";
|
||||
import { Set, KeyedDictionary, Dictionary, DictionaryMap } from "./dictionary";
|
||||
import { annotate, Annotated } from "./annotated";
|
||||
import { Double, Float } from "./float";
|
||||
import { Embeddable, isEmbedded } from "./embedded";
|
||||
|
@ -35,8 +34,7 @@ export interface FoldMethods<T extends Embeddable, R> {
|
|||
record(r: Record<Value<T>, Tuple<Value<T>>, T>, k: Fold<T, R>): R;
|
||||
array(a: Array<Value<T>>, k: Fold<T, R>): R;
|
||||
set(s: Set<T>, k: Fold<T, R>): R;
|
||||
jsDictionary(d: JsDictionary<Value<T>>, k: Fold<T, R>): R;
|
||||
keyedDictionary(d: KeyedDictionary<T>, k: Fold<T, R>): R;
|
||||
dictionary(d: DictionaryMap<T>, k: Fold<T, R>): R;
|
||||
|
||||
annotated(a: Annotated<T>, k: Fold<T, R>): R;
|
||||
|
||||
|
@ -56,10 +54,7 @@ export class VoidFold<T extends Embeddable> implements FoldMethods<T, void> {
|
|||
}
|
||||
array(a: Value<T>[], k: Fold<T, void>): void { a.forEach(k); }
|
||||
set(s: Set<T>, k: Fold<T, void>): void { s.forEach(k); }
|
||||
jsDictionary(d: JsDictionary<Value<T>>, k: Fold<T, void>): void {
|
||||
new JsDictionaryMap(d).forEach((value, key) => { k(key); k(value); });
|
||||
}
|
||||
keyedDictionary(d: KeyedDictionary<T>, k: Fold<T, void>): void {
|
||||
dictionary(d: DictionaryMap<T>, k: Fold<T, void>): void {
|
||||
d.forEach((value, key) => { k(key); k(value); });
|
||||
}
|
||||
annotated(a: Annotated<T>, k: Fold<T, void>): void { k(a.item); a.annotations.forEach(k); }
|
||||
|
@ -99,15 +94,11 @@ export abstract class ValueFold<T extends Embeddable, R extends Embeddable = T>
|
|||
set(s: Set<T>, k: Fold<T, Value<R>>): Value<R> {
|
||||
return s.map(k);
|
||||
}
|
||||
jsDictionary(d: JsDictionary<Value<T>>, k: Fold<T, Value<R>>): Value<R> {
|
||||
const results: [Value<R>, Value<R>][] = [];
|
||||
new JsDictionaryMap(d).forEach((value, key) => results.push([k(key), k(value)]));
|
||||
return Dictionary.from(results);
|
||||
}
|
||||
keyedDictionary(d: KeyedDictionary<T>, k: Fold<T, Value<R>>): Value<R> {
|
||||
const r = new KeyedDictionary<R>();
|
||||
Dictionary.mapInto(d, r, ([key, value]) => [k(key), k(value)]);
|
||||
return r;
|
||||
dictionary(d: DictionaryMap<T>, k: Fold<T, Value<R>>): Value<R> {
|
||||
const result = new DictionaryMap<R>();
|
||||
d.forEach((value, key) => result.set(k(key), k(value)));
|
||||
result.simplify();
|
||||
return result.value;
|
||||
}
|
||||
annotated(a: Annotated<T>, k: Fold<T, Value<R>>): Value<R> {
|
||||
return annotate(k(a.item), ...a.annotations.map(k));
|
||||
|
@ -208,10 +199,8 @@ export function fold<T extends Embeddable, R>(v: Value<T>, o: FoldMethods<T, R>)
|
|||
return o.bytes(v);
|
||||
} else if (Float.isDouble(v)) {
|
||||
return o.double(v.value);
|
||||
} else if (KeyedDictionary.isKeyedDictionary<T>(v)) {
|
||||
return o.keyedDictionary(v, walk);
|
||||
} else {
|
||||
return o.jsDictionary(v, walk);
|
||||
} else if (Dictionary.isDictionary<T>(v)) {
|
||||
return o.dictionary(new DictionaryMap(v), walk);
|
||||
}
|
||||
default:
|
||||
((_v: never): never => { throw new Error("Internal error"); })(v);
|
||||
|
|
|
@ -5,85 +5,73 @@ export interface JsDictionary<V> {
|
|||
[key: string]: V;
|
||||
}
|
||||
|
||||
export class JsDictionaryMap<V> implements Map<symbol, V> {
|
||||
_size: number | undefined = void 0;
|
||||
|
||||
constructor(public readonly j: JsDictionary<V>) {}
|
||||
|
||||
get [IsMap](): boolean { return true; }
|
||||
|
||||
clear(): void {
|
||||
for (const key in this.j) delete this.j[key];
|
||||
this._size = void 0;
|
||||
}
|
||||
|
||||
delete(key: symbol): boolean {
|
||||
const result = this.has(key);
|
||||
delete this.j[key.description!];
|
||||
if (result) this._size = void 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
forEach(
|
||||
callbackfn: (value: V, key: symbol, map: Map<symbol, V>) => void,
|
||||
thisArg?: any,
|
||||
): void {
|
||||
Object.entries(this.j).forEach(([key, val]) =>
|
||||
callbackfn.call(thisArg, val, Symbol.for(key), this));
|
||||
}
|
||||
|
||||
get(key: symbol): V | undefined {
|
||||
return this.j[key.description!];
|
||||
}
|
||||
|
||||
has(key: symbol): boolean {
|
||||
return Object.hasOwnProperty.call(this.j, key.description!);
|
||||
}
|
||||
|
||||
set(key: symbol, value: V): this {
|
||||
this.j[key.description!] = value;
|
||||
this._size = void 0;
|
||||
return this;
|
||||
}
|
||||
|
||||
get size(): number {
|
||||
if (this._size === void 0) {
|
||||
this._size = Object.keys(this.j).length;
|
||||
}
|
||||
return this._size;
|
||||
}
|
||||
|
||||
entries(): IterableIterator<[symbol, V]> {
|
||||
return _iterMap(Object.entries(this.j).values(), ([k, v]) => [Symbol.for(k), v]);
|
||||
}
|
||||
|
||||
keys(): IterableIterator<symbol> {
|
||||
return _iterMap(Object.keys(this.j).values(), k => Symbol.for(k));
|
||||
}
|
||||
|
||||
values(): IterableIterator<V> {
|
||||
return Object.values(this.j).values();
|
||||
}
|
||||
|
||||
[Symbol.iterator](): IterableIterator<[symbol, V]> {
|
||||
return this.entries();
|
||||
}
|
||||
|
||||
get [Symbol.toStringTag]() { return 'JsDictionaryMap'; }
|
||||
|
||||
equals(other: any, eqv: Equivalence<V> = (v1, v2) => v1 === v2): boolean {
|
||||
if (!('size' in other && 'has' in other && 'get' in other)) return false;
|
||||
if (this.size !== other.size) return false;
|
||||
for (let [k, v] of Object.entries(this.j)) {
|
||||
if (!other.has(k)) return false;
|
||||
if (!eqv(v, other.get(k))) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export namespace JsDictionary {
|
||||
export function isJsDictionary<V>(x: any): x is JsDictionary<V> {
|
||||
return Dictionary.isDictionary(x) && (x as any)[DictionaryType] === void 0;
|
||||
}
|
||||
|
||||
export function clear<V>(j: JsDictionary<V>): void {
|
||||
for (const key in j) delete j[key];
|
||||
}
|
||||
|
||||
export function remove<V>(j: JsDictionary<V>, key: symbol): boolean {
|
||||
const result = has(j, key);
|
||||
delete j[key.description!];
|
||||
return result;
|
||||
}
|
||||
|
||||
export function forEach<V>(
|
||||
j: JsDictionary<V>,
|
||||
callbackfn: (value: V, key: symbol) => void,
|
||||
): void {
|
||||
Object.entries(j).forEach(([key, val]) => callbackfn(val, Symbol.for(key)));
|
||||
}
|
||||
|
||||
export function get<V>(j: JsDictionary<V>, key: symbol): V | undefined {
|
||||
return j[key.description!];
|
||||
}
|
||||
|
||||
export function has<V>(j: JsDictionary<V>, key: symbol): boolean {
|
||||
return Object.hasOwnProperty.call(j, key.description!);
|
||||
}
|
||||
|
||||
export function set<V>(j: JsDictionary<V>, key: symbol, value: V): JsDictionary<V> {
|
||||
j[key.description!] = value;
|
||||
return j;
|
||||
}
|
||||
|
||||
export function size<V>(j: JsDictionary<V>): number {
|
||||
return Object.keys(j).length;
|
||||
}
|
||||
|
||||
export function entries<V>(j: JsDictionary<V>): IterableIterator<[symbol, V]> {
|
||||
return _iterMap(Object.entries(j).values(), ([k, v]) => [Symbol.for(k), v]);
|
||||
}
|
||||
|
||||
export function keys<V>(j: JsDictionary<V>): IterableIterator<symbol> {
|
||||
return _iterMap(Object.keys(j).values(), k => Symbol.for(k));
|
||||
}
|
||||
|
||||
export function values<V>(j: JsDictionary<V>): IterableIterator<V> {
|
||||
return Object.values(j).values();
|
||||
}
|
||||
|
||||
export function clone<V>(j: JsDictionary<V>): JsDictionary<V> {
|
||||
const r: JsDictionary<V> = {};
|
||||
Object.keys(j).forEach(k => r[k] = j[k]);
|
||||
return r;
|
||||
}
|
||||
|
||||
export function equals<V>(
|
||||
j1: JsDictionary<V>,
|
||||
j2: JsDictionary<V>,
|
||||
eqv: Equivalence<V> = (v1, v2) => v1 === v2,
|
||||
): boolean {
|
||||
if (size(j1) !== size(j2)) return false;
|
||||
for (let [k, v] of entries(j1)) {
|
||||
if (!has(j2, k)) return false;
|
||||
if (!eqv(v, get(j2, k)!)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,13 +3,13 @@ import { Bytes } from "./bytes";
|
|||
import { fold } from "./fold";
|
||||
import { is } from "./is";
|
||||
import { Value } from "./values";
|
||||
import { Set, Dictionary, KeyedDictionary } from "./dictionary";
|
||||
import { Set, Dictionary, KeyedDictionary, DictionaryMap } from "./dictionary";
|
||||
import { Annotated } from "./annotated";
|
||||
import { unannotate } from "./strip";
|
||||
import { isEmbedded } from "./embedded";
|
||||
import { isCompound } from "./compound";
|
||||
import type { Embeddable } from "./embedded";
|
||||
import { JsDictionary, JsDictionaryMap } from "./jsdictionary";
|
||||
import { JsDictionary } from "./jsdictionary";
|
||||
|
||||
export function merge<T extends Embeddable>(
|
||||
mergeEmbeddeds: (a: T, b: T) => T | undefined,
|
||||
|
@ -44,20 +44,28 @@ export function merge<T extends Embeddable>(
|
|||
if (!Record.isRecord<Value<T>, Tuple<Value<T>>, T>(b)) die();
|
||||
return Record(walk(r.label, b.label), walkMany(r, b));
|
||||
},
|
||||
|
||||
array(a: Array<Value<T>>) {
|
||||
if (!Array.isArray(b) || Record.isRecord(b)) die();
|
||||
return walkMany(a, b);
|
||||
},
|
||||
|
||||
set(_s: Set<T>) { die(); },
|
||||
jsDictionary(a: JsDictionary<Value<T>>) {
|
||||
|
||||
dictionary(aMap: DictionaryMap<T>) {
|
||||
const bMap = Dictionary.asMap<T>(b);
|
||||
if (bMap === void 0) die();
|
||||
return walkMaps(new JsDictionaryMap(a), bMap);
|
||||
},
|
||||
keyedDictionary(a: KeyedDictionary<T>) {
|
||||
const bMap = Dictionary.asMap<T>(b);
|
||||
if (bMap === void 0) die();
|
||||
return walkMaps(a, bMap);
|
||||
|
||||
const r = new DictionaryMap<T>();
|
||||
aMap.forEach((av,ak) => {
|
||||
const bv = bMap.get(ak);
|
||||
r.set(ak, bv === void 0 ? av : walk(av, bv));
|
||||
});
|
||||
bMap.forEach((bv, bk) => {
|
||||
if (!aMap.has(bk)) r.set(bk, bv);
|
||||
});
|
||||
r.simplify();
|
||||
return r.value;
|
||||
},
|
||||
|
||||
annotated(a: Annotated<T>) {
|
||||
|
@ -81,17 +89,5 @@ export function merge<T extends Embeddable>(
|
|||
}
|
||||
}
|
||||
|
||||
function walkMaps(a: Map<Value<T>, Value<T>>, b: Map<Value<T>, Value<T>>): Dictionary<T> {
|
||||
const r = new KeyedDictionary<T>();
|
||||
a.forEach((av,ak) => {
|
||||
const bv = b.get(ak);
|
||||
r.set(ak, bv === void 0 ? av : walk(av, bv));
|
||||
});
|
||||
b.forEach((bv, bk) => {
|
||||
if (!a.has(bk)) r.set(bk, bv);
|
||||
});
|
||||
return Dictionary.from(r);
|
||||
}
|
||||
|
||||
return items.reduce(walk, item0);
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import type { Value } from './values';
|
||||
import { DecodeError, ShortPacket } from './codec';
|
||||
import { Dictionary, Set } from './dictionary';
|
||||
import { Dictionary, DictionaryMap, Set } from './dictionary';
|
||||
import { strip } from './strip';
|
||||
import { Bytes, unhexDigit } from './bytes';
|
||||
import { Decoder, DecoderState, neverEmbeddedTypeDecode } from './decoder';
|
||||
|
@ -398,24 +398,20 @@ export class Reader<T extends Embeddable> {
|
|||
}
|
||||
|
||||
readDictionary(): Dictionary<T> {
|
||||
const r = Dictionary.from<T, [Value<T>, Value<T>, Position]>(
|
||||
this.seq(true, [] as [Value<T>, Value<T>, Position][], (k, acc) => {
|
||||
this.state.skipws();
|
||||
switch (this.state.peek()) {
|
||||
case ':':
|
||||
this.state.advance();
|
||||
acc.push([k, this.next(), this.state.copyPos()]);
|
||||
break;
|
||||
default:
|
||||
this.state.error('Missing key/value separator', this.state.pos);
|
||||
}
|
||||
}, '}'),
|
||||
true as const);
|
||||
if ('duplicate' in r) {
|
||||
this.state.error(`Duplicate key: ${stringify(r.duplicate[0])}`, r.duplicate[2]);
|
||||
} else {
|
||||
return r.ok;
|
||||
}
|
||||
const r = this.seq(true, new DictionaryMap<T>(), (k, acc) => {
|
||||
this.state.skipws();
|
||||
switch (this.state.peek()) {
|
||||
case ':':
|
||||
this.state.advance();
|
||||
if (acc.has(k)) this.state.error(`Duplicate key: ${stringify(k)}`, this.state.pos);
|
||||
acc.set(k, this.next());
|
||||
break;
|
||||
default:
|
||||
this.state.error('Missing key/value separator', this.state.pos);
|
||||
}
|
||||
}, '}');
|
||||
r.simplify();
|
||||
return r.value;
|
||||
}
|
||||
|
||||
readSet(): Set<T> {
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import { Value } from "./values";
|
||||
import { Annotated } from "./annotated";
|
||||
import { Record, Tuple } from "./record";
|
||||
import { Set, Dictionary, KeyedDictionary } from "./dictionary";
|
||||
import { JsDictionary, JsDictionaryMap } from "./jsdictionary";
|
||||
import { Set, Dictionary, DictionaryMap } from "./dictionary";
|
||||
import type { Embeddable, GenericEmbedded } from "./embedded";
|
||||
|
||||
export function unannotate<T extends Embeddable = GenericEmbedded>(v: Value<T>): Value<T> {
|
||||
|
@ -34,12 +33,11 @@ export function strip<T extends Embeddable = GenericEmbedded>(
|
|||
return (v.item as Value<T>[]).map(walk);
|
||||
} else if (Set.isSet<T>(v.item)) {
|
||||
return v.item.map(walk);
|
||||
} else if (KeyedDictionary.isKeyedDictionary<T>(v.item)) {
|
||||
const result = new KeyedDictionary<T>();
|
||||
return Dictionary.mapInto(v.item, result, (e) => [walk(e[0]), walk(e[1])]);
|
||||
} else if (JsDictionary.isJsDictionary<T>(v.item)) {
|
||||
const i = new JsDictionaryMap(v.item);
|
||||
return Dictionary.mapInto(i, new JsDictionaryMap<T>({}), (e) => [e[0], walk(e[1])]).j;
|
||||
} else if (Dictionary.isDictionary<T>(v.item)) {
|
||||
const result = new DictionaryMap<T>();
|
||||
new DictionaryMap<T>(v.item).forEach((val, key) => result.set(walk(key), walk(val)));
|
||||
result.simplify();
|
||||
return result.value;
|
||||
} else {
|
||||
return v.item;
|
||||
}
|
||||
|
|
|
@ -5,8 +5,7 @@ import { Encoder, EncoderState } from "./encoder";
|
|||
import type { Value } from "./values";
|
||||
import { NUMBER_RE } from './reader';
|
||||
import { encodeBase64 } from './base64';
|
||||
import { writeDictionaryOn } from './dictionary';
|
||||
import { JsDictionaryMap } from './jsdictionary';
|
||||
import { DictionaryMap, writeDictionaryOn } from './dictionary';
|
||||
|
||||
export type Writable<T extends Embeddable> =
|
||||
Value<T> | PreserveWritable<T> | Iterable<Value<T>> | ArrayBufferView;
|
||||
|
@ -309,7 +308,7 @@ export class Writer<T extends Embeddable> {
|
|||
.push(this.embeddedWrite.toValue(v));
|
||||
}
|
||||
} else {
|
||||
writeDictionaryOn(new JsDictionaryMap(v),
|
||||
writeDictionaryOn(new DictionaryMap<T>(v),
|
||||
this,
|
||||
(k, w) => w.push(k),
|
||||
(v, w) => w.push(v));
|
||||
|
|
Loading…
Reference in New Issue