forked from syndicate-lang/preserves
Fix module cycles (largely by splitting PointerType in two)
This commit is contained in:
parent
854a2bc41c
commit
8442718f96
|
@ -7,14 +7,14 @@ import { Record } from "./record";
|
||||||
import { Bytes, BytesLike, underlying } from "./bytes";
|
import { Bytes, BytesLike, underlying } from "./bytes";
|
||||||
import { Value } from "./values";
|
import { Value } from "./values";
|
||||||
import { is } from "./is";
|
import { is } from "./is";
|
||||||
import { embed, neverPointerType, Pointer, PointerType } from "./pointer";
|
import { embed, GenericPointer, Pointer, PointerTypeDecode } from "./pointer";
|
||||||
|
|
||||||
export interface DecoderOptions {
|
export interface DecoderOptions {
|
||||||
includeAnnotations?: boolean;
|
includeAnnotations?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DecoderPointerOptions<T> extends DecoderOptions {
|
export interface DecoderPointerOptions<T> extends DecoderOptions {
|
||||||
pointerType?: PointerType<T>;
|
pointerDecode?: PointerTypeDecode<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TypedDecoder<T> {
|
export interface TypedDecoder<T> {
|
||||||
|
@ -25,8 +25,8 @@ export interface TypedDecoder<T> {
|
||||||
|
|
||||||
skip(): void;
|
skip(): void;
|
||||||
next(): Value<T>;
|
next(): Value<T>;
|
||||||
withPointerType<S, R>(
|
withPointerDecode<S, R>(
|
||||||
pointerType: PointerType<S>,
|
pointerDecode: PointerTypeDecode<S>,
|
||||||
body: (d: TypedDecoder<S>) => R): R;
|
body: (d: TypedDecoder<S>) => R): R;
|
||||||
|
|
||||||
nextBoolean(): boolean | undefined;
|
nextBoolean(): boolean | undefined;
|
||||||
|
@ -161,23 +161,33 @@ export class DecoderState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const neverPointerTypeDecode: PointerTypeDecode<never> = {
|
||||||
|
decode(_s: DecoderState): never {
|
||||||
|
throw new Error("Pointers not permitted at this point in Preserves document");
|
||||||
|
},
|
||||||
|
|
||||||
|
fromValue(_v: Value<GenericPointer>): never {
|
||||||
|
throw new Error("Pointers not permitted at this point in Preserves document");
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
export class Decoder<T = never> implements TypedDecoder<T> {
|
export class Decoder<T = never> implements TypedDecoder<T> {
|
||||||
state: DecoderState;
|
state: DecoderState;
|
||||||
pointerType: PointerType<T>;
|
pointerDecode: PointerTypeDecode<T>;
|
||||||
|
|
||||||
constructor(state: DecoderState, pointerType?: PointerType<T>);
|
constructor(state: DecoderState, pointerDecode?: PointerTypeDecode<T>);
|
||||||
constructor(packet?: BytesLike, options?: DecoderPointerOptions<T>);
|
constructor(packet?: BytesLike, options?: DecoderPointerOptions<T>);
|
||||||
constructor(
|
constructor(
|
||||||
packet_or_state: (DecoderState | BytesLike) = new Uint8Array(0),
|
packet_or_state: (DecoderState | BytesLike) = new Uint8Array(0),
|
||||||
options_or_pointerType?: (DecoderPointerOptions<T> | PointerType<T>))
|
options_or_pointerDecode?: (DecoderPointerOptions<T> | PointerTypeDecode<T>))
|
||||||
{
|
{
|
||||||
if (packet_or_state instanceof DecoderState) {
|
if (packet_or_state instanceof DecoderState) {
|
||||||
this.state = packet_or_state;
|
this.state = packet_or_state;
|
||||||
this.pointerType = (options_or_pointerType as PointerType<T>) ?? neverPointerType;
|
this.pointerDecode = (options_or_pointerDecode as PointerTypeDecode<T>) ?? neverPointerTypeDecode;
|
||||||
} else {
|
} else {
|
||||||
const options = (options_or_pointerType as DecoderPointerOptions<T>) ?? {};
|
const options = (options_or_pointerDecode as DecoderPointerOptions<T>) ?? {};
|
||||||
this.state = new DecoderState(packet_or_state, options);
|
this.state = new DecoderState(packet_or_state, options);
|
||||||
this.pointerType = options.pointerType ?? neverPointerType;
|
this.pointerDecode = options.pointerDecode ?? neverPointerTypeDecode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,7 +223,7 @@ export class Decoder<T = never> implements TypedDecoder<T> {
|
||||||
const v = this.next() as Annotated<T>;
|
const v = this.next() as Annotated<T>;
|
||||||
return this.state.unshiftAnnotation(a, v);
|
return this.state.unshiftAnnotation(a, v);
|
||||||
}
|
}
|
||||||
case Tag.Pointer: return this.state.wrap<T>(embed(this.pointerType.decode(this.state)));
|
case Tag.Pointer: return this.state.wrap<T>(embed(this.pointerDecode.decode(this.state)));
|
||||||
case Tag.SignedInteger: return this.state.wrap<T>(this.state.nextint(this.state.varint()));
|
case Tag.SignedInteger: return this.state.wrap<T>(this.state.nextint(this.state.varint()));
|
||||||
case Tag.String: return this.state.wrap<T>(Bytes.from(this.state.nextbytes(this.state.varint())).fromUtf8());
|
case Tag.String: return this.state.wrap<T>(Bytes.from(this.state.nextbytes(this.state.varint())).fromUtf8());
|
||||||
case Tag.ByteString: return this.state.wrap<T>(Bytes.from(this.state.nextbytes(this.state.varint())));
|
case Tag.ByteString: return this.state.wrap<T>(Bytes.from(this.state.nextbytes(this.state.varint())));
|
||||||
|
@ -257,11 +267,11 @@ export class Decoder<T = never> implements TypedDecoder<T> {
|
||||||
this.next();
|
this.next();
|
||||||
}
|
}
|
||||||
|
|
||||||
withPointerType<S, R>(
|
withPointerDecode<S, R>(
|
||||||
pointerType: PointerType<S>,
|
pointerDecode: PointerTypeDecode<S>,
|
||||||
body: (d: TypedDecoder<S>) => R): R
|
body: (d: TypedDecoder<S>) => R): R
|
||||||
{
|
{
|
||||||
return body(new Decoder(this.state, pointerType));
|
return body(new Decoder(this.state, pointerDecode));
|
||||||
}
|
}
|
||||||
|
|
||||||
skipAnnotations(): void {
|
skipAnnotations(): void {
|
||||||
|
@ -299,7 +309,7 @@ export class Decoder<T = never> implements TypedDecoder<T> {
|
||||||
nextPointer(): Pointer<T> | undefined {
|
nextPointer(): Pointer<T> | undefined {
|
||||||
this.skipAnnotations();
|
this.skipAnnotations();
|
||||||
switch (this.state.nextbyte()) {
|
switch (this.state.nextbyte()) {
|
||||||
case Tag.Pointer: return embed(this.pointerType.decode(this.state));
|
case Tag.Pointer: return embed(this.pointerDecode.decode(this.state));
|
||||||
default: return void 0;
|
default: return void 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@ import { PreserveOn } from "./symbols";
|
||||||
import { stringify } from "./text";
|
import { stringify } from "./text";
|
||||||
import { Value } from "./values";
|
import { Value } from "./values";
|
||||||
import { Bytes } from './bytes';
|
import { Bytes } from './bytes';
|
||||||
import { fromJS } from "./fromjs";
|
|
||||||
import { GenericPointer } from "./pointer";
|
import { GenericPointer } from "./pointer";
|
||||||
|
|
||||||
export type DictionaryType = 'Dictionary' | 'Set';
|
export type DictionaryType = 'Dictionary' | 'Set';
|
||||||
|
@ -79,13 +78,6 @@ export class Dictionary<T = GenericPointer, V = Value<T>> extends KeyedDictionar
|
||||||
static isDictionary<T = GenericPointer, V = Value<T>>(x: any): x is Dictionary<T, V> {
|
static isDictionary<T = GenericPointer, V = Value<T>>(x: any): x is Dictionary<T, V> {
|
||||||
return x?.[DictionaryType] === 'Dictionary';
|
return x?.[DictionaryType] === 'Dictionary';
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJS<T = GenericPointer, V = GenericPointer>(x: object): Dictionary<T, Value<V>> {
|
|
||||||
if (Dictionary.isDictionary<T, Value<V>>(x)) return x;
|
|
||||||
const d = new Dictionary<T, Value<V>>();
|
|
||||||
Object.entries(x).forEach(([key, value]) => d.set(key, fromJS(value)));
|
|
||||||
return d;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class KeyedSet<K extends Value<T>, T = GenericPointer> extends FlexSet<K> {
|
export class KeyedSet<K extends Value<T>, T = GenericPointer> extends FlexSet<K> {
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { Value } from "./values";
|
||||||
import { PreserveOn } from "./symbols";
|
import { PreserveOn } from "./symbols";
|
||||||
import { EncodeError } from "./codec";
|
import { EncodeError } from "./codec";
|
||||||
import { Record, Tuple } from "./record";
|
import { Record, Tuple } from "./record";
|
||||||
import { identityPointerType, PointerType } from "./pointer";
|
import { GenericPointer, PointerTypeEncode } from "./pointer";
|
||||||
|
|
||||||
export type Encodable<T> =
|
export type Encodable<T> =
|
||||||
Value<T> | Preservable<T> | Iterable<Value<T>> | ArrayBufferView;
|
Value<T> | Preservable<T> | Iterable<Value<T>> | ArrayBufferView;
|
||||||
|
@ -23,7 +23,7 @@ export interface EncoderOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EncoderPointerOptions<T> extends EncoderOptions {
|
export interface EncoderPointerOptions<T> extends EncoderOptions {
|
||||||
pointerType?: PointerType<T>;
|
pointerEncode?: PointerTypeEncode<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function asLatin1(bs: Uint8Array): string {
|
export function asLatin1(bs: Uint8Array): string {
|
||||||
|
@ -34,6 +34,27 @@ function isIterable<T>(v: any): v is Iterable<T> {
|
||||||
return typeof v === 'object' && v !== null && typeof v[Symbol.iterator] === 'function';
|
return typeof v === 'object' && v !== null && typeof v[Symbol.iterator] === 'function';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let _nextId = 0;
|
||||||
|
const _registry = new WeakMap<object, number>();
|
||||||
|
export function pointerId(v: any): number {
|
||||||
|
let id = _registry.get(v);
|
||||||
|
if (id === void 0) {
|
||||||
|
id = _nextId++;
|
||||||
|
_registry.set(v, id);
|
||||||
|
}
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const identityPointerTypeEncode: PointerTypeEncode<any> = {
|
||||||
|
encode(s: EncoderState, v: any): void {
|
||||||
|
new Encoder(s, this).push(pointerId(v));
|
||||||
|
},
|
||||||
|
|
||||||
|
toValue(v: any): Value<GenericPointer> {
|
||||||
|
return pointerId(v);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export class EncoderState {
|
export class EncoderState {
|
||||||
chunks: Array<Uint8Array>;
|
chunks: Array<Uint8Array>;
|
||||||
view: DataView;
|
view: DataView;
|
||||||
|
@ -138,28 +159,28 @@ export class EncoderState {
|
||||||
|
|
||||||
export class Encoder<T = object> {
|
export class Encoder<T = object> {
|
||||||
state: EncoderState;
|
state: EncoderState;
|
||||||
pointerType: PointerType<T> | undefined;
|
pointerEncode: PointerTypeEncode<T>;
|
||||||
|
|
||||||
constructor(options: EncoderPointerOptions<T>);
|
constructor(options: EncoderPointerOptions<T>);
|
||||||
constructor(state: EncoderState, pointerType?: PointerType<T>);
|
constructor(state: EncoderState, pointerEncode?: PointerTypeEncode<T>);
|
||||||
constructor(
|
constructor(
|
||||||
state_or_options: (EncoderState | EncoderPointerOptions<T>) = {},
|
state_or_options: (EncoderState | EncoderPointerOptions<T>) = {},
|
||||||
pointerType?: PointerType<T>)
|
pointerEncode?: PointerTypeEncode<T>)
|
||||||
{
|
{
|
||||||
if (state_or_options instanceof EncoderState) {
|
if (state_or_options instanceof EncoderState) {
|
||||||
this.state = state_or_options;
|
this.state = state_or_options;
|
||||||
this.pointerType = pointerType;
|
this.pointerEncode = pointerEncode ?? identityPointerTypeEncode;
|
||||||
} else {
|
} else {
|
||||||
this.state = new EncoderState(state_or_options);
|
this.state = new EncoderState(state_or_options);
|
||||||
this.pointerType = state_or_options.pointerType;
|
this.pointerEncode = state_or_options.pointerEncode ?? identityPointerTypeEncode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
withPointerType<S>(
|
withPointerEncode<S>(
|
||||||
pointerType: PointerType<S>,
|
pointerEncode: PointerTypeEncode<S>,
|
||||||
body: (e: Encoder<S>) => void): this
|
body: (e: Encoder<S>) => void): this
|
||||||
{
|
{
|
||||||
body(new Encoder(this.state, pointerType));
|
body(new Encoder(this.state, pointerEncode));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,7 +253,7 @@ export class Encoder<T = object> {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.state.emitbyte(Tag.Pointer);
|
this.state.emitbyte(Tag.Pointer);
|
||||||
(this.pointerType ?? identityPointerType).encode(this.state, v.embeddedValue);
|
this.pointerEncode.encode(this.state, v.embeddedValue);
|
||||||
}
|
}
|
||||||
return this; // for chaining
|
return this; // for chaining
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,3 +58,16 @@ export function fromJS<T = GenericPointer>(x: any): Value<T> {
|
||||||
|
|
||||||
throw new TypeError("Cannot represent JavaScript value as Preserves: " + x);
|
throw new TypeError("Cannot represent JavaScript value as Preserves: " + x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare module "./dictionary" {
|
||||||
|
namespace Dictionary {
|
||||||
|
export function fromJS<T = GenericPointer, V = GenericPointer>(x: object): Dictionary<T, Value<V>>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Dictionary.fromJS = function <T = GenericPointer, V = GenericPointer>(x: object): Dictionary<T, Value<V>> {
|
||||||
|
if (Dictionary.isDictionary<T, Value<V>>(x)) return x;
|
||||||
|
const d = new Dictionary<T, Value<V>>();
|
||||||
|
Object.entries(x).forEach(([key, value]) => d.set(key, fromJS(value)));
|
||||||
|
return d;
|
||||||
|
};
|
||||||
|
|
|
@ -1,16 +1,19 @@
|
||||||
import { Encoder, EncoderState } from "./encoder";
|
import type { EncoderState } from "./encoder";
|
||||||
import { Decoder, DecoderState } from "./decoder";
|
import type { DecoderState } from "./decoder";
|
||||||
import type { Value } from "./values";
|
import type { Value } from "./values";
|
||||||
import { strip } from "./strip";
|
|
||||||
|
|
||||||
export type PointerType<T> = {
|
export type PointerTypeEncode<T> = {
|
||||||
decode(s: DecoderState): T;
|
|
||||||
encode(s: EncoderState, v: T): void;
|
encode(s: EncoderState, v: T): void;
|
||||||
|
|
||||||
fromValue(v: Value<GenericPointer>): T;
|
|
||||||
toValue(v: T): Value<GenericPointer>;
|
toValue(v: T): Value<GenericPointer>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type PointerTypeDecode<T> = {
|
||||||
|
decode(s: DecoderState): T;
|
||||||
|
fromValue(v: Value<GenericPointer>): T;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type PointerType<T> = PointerTypeEncode<T> & PointerTypeDecode<T>;
|
||||||
|
|
||||||
export class Pointer<T> {
|
export class Pointer<T> {
|
||||||
embeddedValue: T;
|
embeddedValue: T;
|
||||||
|
|
||||||
|
@ -50,68 +53,3 @@ export class GenericPointer {
|
||||||
return this.generic.asPreservesText();
|
return this.generic.asPreservesText();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const genericPointerType: PointerType<GenericPointer> = {
|
|
||||||
decode(s: DecoderState): GenericPointer {
|
|
||||||
return new GenericPointer(new Decoder(s, this).next());
|
|
||||||
},
|
|
||||||
|
|
||||||
encode(s: EncoderState, v: GenericPointer): void {
|
|
||||||
new Encoder(s, this).push(v.generic);
|
|
||||||
},
|
|
||||||
|
|
||||||
fromValue(v: Value<GenericPointer>): GenericPointer {
|
|
||||||
return new GenericPointer(strip(v));
|
|
||||||
},
|
|
||||||
|
|
||||||
toValue(v: GenericPointer): Value<GenericPointer> {
|
|
||||||
return v.generic;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const neverPointerType: PointerType<never> = {
|
|
||||||
decode(_s: DecoderState): never {
|
|
||||||
throw new Error("Pointers not permitted at this point in Preserves document");
|
|
||||||
},
|
|
||||||
|
|
||||||
encode(_s: EncoderState, _v: never): void {
|
|
||||||
throw new Error("Pointers not permitted encoding Preserves document");
|
|
||||||
},
|
|
||||||
|
|
||||||
fromValue(_v: Value<GenericPointer>): never {
|
|
||||||
throw new Error("Pointers not permitted at this point in Preserves document");
|
|
||||||
},
|
|
||||||
|
|
||||||
toValue(_v: never): Value<GenericPointer> {
|
|
||||||
throw new Error("Pointers not permitted encoding Preserves document");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let _nextId = 0;
|
|
||||||
const _registry = new WeakMap<object, number>();
|
|
||||||
export function pointerId(v: any): number {
|
|
||||||
let id = _registry.get(v);
|
|
||||||
if (id === void 0) {
|
|
||||||
id = _nextId++;
|
|
||||||
_registry.set(v, id);
|
|
||||||
}
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const identityPointerType: PointerType<any> = {
|
|
||||||
decode(_s: DecoderState): any {
|
|
||||||
throw new Error("Cannot decode identityPointerType");
|
|
||||||
},
|
|
||||||
|
|
||||||
encode(s: EncoderState, v: any): void {
|
|
||||||
new Encoder(s, this).push(pointerId(v));
|
|
||||||
},
|
|
||||||
|
|
||||||
fromValue(_v: Value<GenericPointer>): any {
|
|
||||||
throw new Error("Cannot decode identityPointerType");
|
|
||||||
},
|
|
||||||
|
|
||||||
toValue(v: any): Value<GenericPointer> {
|
|
||||||
return pointerId(v);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
import { GenericPointer, PointerType, PointerTypeDecode, PointerTypeEncode } from "./pointer";
|
||||||
|
import { Encoder, EncoderState, identityPointerTypeEncode } from "./encoder";
|
||||||
|
import { genericPointerTypeDecode } from "./reader";
|
||||||
|
import { Value } from "./values";
|
||||||
|
import { DecoderState, neverPointerTypeDecode } from "./decoder";
|
||||||
|
|
||||||
|
export const genericPointerTypeEncode: PointerTypeEncode<GenericPointer> = {
|
||||||
|
encode(s: EncoderState, v: GenericPointer): void {
|
||||||
|
new Encoder(s, this).push(v.generic);
|
||||||
|
},
|
||||||
|
|
||||||
|
toValue(v: GenericPointer): Value<GenericPointer> {
|
||||||
|
return v.generic;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const genericPointerType: PointerType<GenericPointer> =
|
||||||
|
Object.assign({},
|
||||||
|
genericPointerTypeDecode,
|
||||||
|
genericPointerTypeEncode);
|
||||||
|
|
||||||
|
export const neverPointerTypeEncode: PointerTypeEncode<never> = {
|
||||||
|
encode(_s: EncoderState, _v: never): void {
|
||||||
|
throw new Error("Pointers not permitted encoding Preserves document");
|
||||||
|
},
|
||||||
|
|
||||||
|
toValue(_v: never): Value<GenericPointer> {
|
||||||
|
throw new Error("Pointers not permitted encoding Preserves document");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const neverPointerType: PointerType<never> =
|
||||||
|
Object.assign({},
|
||||||
|
neverPointerTypeDecode,
|
||||||
|
neverPointerTypeEncode);
|
||||||
|
|
||||||
|
export const identityPointerTypeDecode: PointerTypeDecode<any> = {
|
||||||
|
decode(_s: DecoderState): any {
|
||||||
|
throw new Error("Cannot decode identityPointerType");
|
||||||
|
},
|
||||||
|
|
||||||
|
fromValue(_v: Value<GenericPointer>): any {
|
||||||
|
throw new Error("Cannot decode identityPointerType");
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const identityPointerType: PointerType<any> =
|
||||||
|
Object.assign({},
|
||||||
|
identityPointerTypeDecode,
|
||||||
|
identityPointerTypeEncode);
|
|
@ -3,14 +3,14 @@
|
||||||
import type { Value } from './values';
|
import type { Value } from './values';
|
||||||
import { DecodeError, ShortPacket } from './codec';
|
import { DecodeError, ShortPacket } from './codec';
|
||||||
import { Dictionary, Set } from './dictionary';
|
import { Dictionary, Set } from './dictionary';
|
||||||
import { unannotate } from './strip';
|
import { strip, unannotate } from './strip';
|
||||||
import { Bytes, unhexDigit } from './bytes';
|
import { Bytes, unhexDigit } from './bytes';
|
||||||
import { decode } from './decoder';
|
import { decode, Decoder, DecoderState, neverPointerTypeDecode } from './decoder';
|
||||||
import { Record } from './record';
|
import { Record } from './record';
|
||||||
import { Annotated, newPosition, Position, updatePosition } from './annotated';
|
import { Annotated, newPosition, Position, updatePosition } from './annotated';
|
||||||
import { Double, DoubleFloat, Single, SingleFloat } from './float';
|
import { Double, DoubleFloat, Single, SingleFloat } from './float';
|
||||||
import { stringify } from './text';
|
import { stringify } from './text';
|
||||||
import { embed, GenericPointer, genericPointerType, neverPointerType, PointerType } from './pointer';
|
import { embed, GenericPointer, PointerTypeDecode } from './pointer';
|
||||||
|
|
||||||
export interface ReaderStateOptions {
|
export interface ReaderStateOptions {
|
||||||
includeAnnotations?: boolean;
|
includeAnnotations?: boolean;
|
||||||
|
@ -18,7 +18,7 @@ export interface ReaderStateOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ReaderOptions<T> extends ReaderStateOptions {
|
export interface ReaderOptions<T> extends ReaderStateOptions {
|
||||||
pointerType?: PointerType<T>;
|
pointerDecode?: PointerTypeDecode<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
type IntOrFloat = 'int' | 'float';
|
type IntOrFloat = 'int' | 'float';
|
||||||
|
@ -275,23 +275,33 @@ export class ReaderState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const genericPointerTypeDecode: PointerTypeDecode<GenericPointer> = {
|
||||||
|
decode(s: DecoderState): GenericPointer {
|
||||||
|
return new GenericPointer(new Decoder(s, this).next());
|
||||||
|
},
|
||||||
|
|
||||||
|
fromValue(v: Value<GenericPointer>): GenericPointer {
|
||||||
|
return new GenericPointer(strip(v));
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
export class Reader<T> {
|
export class Reader<T> {
|
||||||
state: ReaderState;
|
state: ReaderState;
|
||||||
pointerType: PointerType<T>;
|
pointerType: PointerTypeDecode<T>;
|
||||||
|
|
||||||
constructor(state: ReaderState, pointerType: PointerType<T>);
|
constructor(state: ReaderState, pointerType: PointerTypeDecode<T>);
|
||||||
constructor(buffer: string, options?: ReaderOptions<T>);
|
constructor(buffer: string, options?: ReaderOptions<T>);
|
||||||
constructor(
|
constructor(
|
||||||
state_or_buffer: (ReaderState | string) = '',
|
state_or_buffer: (ReaderState | string) = '',
|
||||||
pointerType_or_options?: (PointerType<T> | ReaderOptions<T>))
|
pointerType_or_options?: (PointerTypeDecode<T> | ReaderOptions<T>))
|
||||||
{
|
{
|
||||||
if (state_or_buffer instanceof ReaderState) {
|
if (state_or_buffer instanceof ReaderState) {
|
||||||
this.state = state_or_buffer;
|
this.state = state_or_buffer;
|
||||||
this.pointerType = pointerType_or_options as PointerType<T>;
|
this.pointerType = pointerType_or_options as PointerTypeDecode<T>;
|
||||||
} else {
|
} else {
|
||||||
const options = (pointerType_or_options as ReaderOptions<T>) ?? {};
|
const options = (pointerType_or_options as ReaderOptions<T>) ?? {};
|
||||||
this.state = new ReaderState(state_or_buffer, options);
|
this.state = new ReaderState(state_or_buffer, options);
|
||||||
this.pointerType = options.pointerType ?? neverPointerType;
|
this.pointerType = options.pointerDecode ?? neverPointerTypeDecode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -378,12 +388,12 @@ export class Reader<T> {
|
||||||
if (!Bytes.isBytes(bs)) this.state.error('ByteString must follow #=',
|
if (!Bytes.isBytes(bs)) this.state.error('ByteString must follow #=',
|
||||||
startPos);
|
startPos);
|
||||||
return decode<T>(bs, {
|
return decode<T>(bs, {
|
||||||
pointerType: this.pointerType,
|
pointerDecode: this.pointerType,
|
||||||
includeAnnotations: this.state.options.includeAnnotations,
|
includeAnnotations: this.state.options.includeAnnotations,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
case '!': return embed(this.pointerType.fromValue(
|
case '!': return embed(this.pointerType.fromValue(
|
||||||
new Reader<GenericPointer>(this.state, genericPointerType).next()));
|
new Reader<GenericPointer>(this.state, genericPointerTypeDecode).next()));
|
||||||
default:
|
default:
|
||||||
this.state.error(`Invalid # syntax: ${c}`, startPos);
|
this.state.error(`Invalid # syntax: ${c}`, startPos);
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ export * from './fromjs';
|
||||||
export * from './is';
|
export * from './is';
|
||||||
export * from './merge';
|
export * from './merge';
|
||||||
export * from './pointer';
|
export * from './pointer';
|
||||||
|
export * from './pointerTypes';
|
||||||
export * from './reader';
|
export * from './reader';
|
||||||
export * from './record';
|
export * from './record';
|
||||||
export * from './strip';
|
export * from './strip';
|
||||||
|
|
|
@ -17,7 +17,8 @@ import {
|
||||||
Decoder,
|
Decoder,
|
||||||
Pointer,
|
Pointer,
|
||||||
embed,
|
embed,
|
||||||
genericPointerType,
|
genericPointerTypeDecode,
|
||||||
|
genericPointerTypeEncode,
|
||||||
} from '../src/index';
|
} from '../src/index';
|
||||||
const { Tag } = Constants;
|
const { Tag } = Constants;
|
||||||
import './test-utils';
|
import './test-utils';
|
||||||
|
@ -144,7 +145,7 @@ describe('encoding and decoding pointers', () => {
|
||||||
const pt = new LookasidePointerType(objects);
|
const pt = new LookasidePointerType(objects);
|
||||||
const A = embed({a: 1});
|
const A = embed({a: 1});
|
||||||
const B = embed({b: 2});
|
const B = embed({b: 2});
|
||||||
expect(encode([A, B], { pointerType: pt })).is(
|
expect(encode([A, B], { pointerEncode: pt })).is(
|
||||||
Bytes.from([Tag.Sequence,
|
Bytes.from([Tag.Sequence,
|
||||||
Tag.Pointer, Tag.SmallInteger_lo,
|
Tag.Pointer, Tag.SmallInteger_lo,
|
||||||
Tag.Pointer, Tag.SmallInteger_lo + 1,
|
Tag.Pointer, Tag.SmallInteger_lo + 1,
|
||||||
|
@ -163,7 +164,7 @@ describe('encoding and decoding pointers', () => {
|
||||||
Tag.Pointer, Tag.SmallInteger_lo,
|
Tag.Pointer, Tag.SmallInteger_lo,
|
||||||
Tag.Pointer, Tag.SmallInteger_lo + 1,
|
Tag.Pointer, Tag.SmallInteger_lo + 1,
|
||||||
Tag.End
|
Tag.End
|
||||||
]), { pointerType: pt })).is([X, Y]);
|
]), { pointerDecode: pt })).is([X, Y]);
|
||||||
});
|
});
|
||||||
it('should store pointers embedded in map keys correctly', () => {
|
it('should store pointers embedded in map keys correctly', () => {
|
||||||
const A1a = {a: 1};
|
const A1a = {a: 1};
|
||||||
|
@ -183,7 +184,7 @@ describe('encoding and decoding pointers', () => {
|
||||||
|
|
||||||
describe('common test suite', () => {
|
describe('common test suite', () => {
|
||||||
const samples_bin = fs.readFileSync(__dirname + '/../../../../../tests/samples.bin');
|
const samples_bin = fs.readFileSync(__dirname + '/../../../../../tests/samples.bin');
|
||||||
const samples = decodeWithAnnotations(samples_bin, { pointerType: genericPointerType });
|
const samples = decodeWithAnnotations(samples_bin, { pointerDecode: genericPointerTypeDecode });
|
||||||
|
|
||||||
const TestCases = Record.makeConstructor<{
|
const TestCases = Record.makeConstructor<{
|
||||||
cases: Dictionary<GenericPointer>
|
cases: Dictionary<GenericPointer>
|
||||||
|
@ -191,13 +192,13 @@ describe('common test suite', () => {
|
||||||
type TestCases = ReturnType<typeof TestCases>;
|
type TestCases = ReturnType<typeof TestCases>;
|
||||||
|
|
||||||
function DS(bs: Bytes) {
|
function DS(bs: Bytes) {
|
||||||
return decode(bs, { pointerType: genericPointerType });
|
return decode(bs, { pointerDecode: genericPointerTypeDecode });
|
||||||
}
|
}
|
||||||
function D(bs: Bytes) {
|
function D(bs: Bytes) {
|
||||||
return decodeWithAnnotations(bs, { pointerType: genericPointerType });
|
return decodeWithAnnotations(bs, { pointerDecode: genericPointerTypeDecode });
|
||||||
}
|
}
|
||||||
function E(v: Value<GenericPointer>) {
|
function E(v: Value<GenericPointer>) {
|
||||||
return encodeWithAnnotations(v, { pointerType: genericPointerType });
|
return encodeWithAnnotations(v, { pointerEncode: genericPointerTypeEncode });
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ExpectedValues {
|
interface ExpectedValues {
|
||||||
|
|
|
@ -8,21 +8,21 @@ describe('reading common test suite', () => {
|
||||||
const samples_pr = fs.readFileSync(__dirname + '/../../../../../tests/samples.pr', 'utf-8');
|
const samples_pr = fs.readFileSync(__dirname + '/../../../../../tests/samples.pr', 'utf-8');
|
||||||
|
|
||||||
it('should read equal to decoded binary without annotations', () => {
|
it('should read equal to decoded binary without annotations', () => {
|
||||||
const s1 = new Reader(samples_pr, { pointerType: genericPointerType, includeAnnotations: false }).next();
|
const s1 = new Reader(samples_pr, { pointerDecode: genericPointerType, includeAnnotations: false }).next();
|
||||||
const s2 = new Decoder(samples_bin, { pointerType: genericPointerType, includeAnnotations: false }).next();
|
const s2 = new Decoder(samples_bin, { pointerDecode: genericPointerType, includeAnnotations: false }).next();
|
||||||
expect(s1).is(s2);
|
expect(s1).is(s2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should read equal to decoded binary with annotations', () => {
|
it('should read equal to decoded binary with annotations', () => {
|
||||||
const s1 = new Reader(samples_pr, { pointerType: genericPointerType, includeAnnotations: true }).next();
|
const s1 = new Reader(samples_pr, { pointerDecode: genericPointerType, includeAnnotations: true }).next();
|
||||||
const s2 = new Decoder(samples_bin, { pointerType: genericPointerType, includeAnnotations: true }).next();
|
const s2 = new Decoder(samples_bin, { pointerDecode: genericPointerType, includeAnnotations: true }).next();
|
||||||
expect(s1).is(s2);
|
expect(s1).is(s2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should read and encode back to binary with annotations', () => {
|
it('should read and encode back to binary with annotations', () => {
|
||||||
const s = new Reader(samples_pr, { pointerType: genericPointerType, includeAnnotations: true }).next();
|
const s = new Reader(samples_pr, { pointerDecode: genericPointerType, includeAnnotations: true }).next();
|
||||||
const bs = Bytes.toIO(encode(s, {
|
const bs = Bytes.toIO(encode(s, {
|
||||||
pointerType: genericPointerType,
|
pointerEncode: genericPointerType,
|
||||||
includeAnnotations: true,
|
includeAnnotations: true,
|
||||||
canonical: true,
|
canonical: true,
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -36,3 +36,9 @@ describe('fold', () => {
|
||||||
expect(mapPointers(v, _t => embed(w1))).is(v1);
|
expect(mapPointers(v, _t => embed(w1))).is(v1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('fromJS', () => {
|
||||||
|
it('should map integers to themselves', () => {
|
||||||
|
expect(fromJS(1)).toBe(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
Loading…
Reference in New Issue