UNPKG

binary-layout

Version:

Typescript-native, declarative DSL for working with binary data

296 lines (287 loc) 16.8 kB
type NumType = number | bigint; type BytesType = Uint8Array; type PrimitiveType = NumType | BytesType; type LayoutObject = { readonly [key: string]: any; }; declare const binaryLiterals: readonly ["int", "uint", "bytes", "array", "switch"]; type BinaryLiterals = typeof binaryLiterals[number]; type Endianness = "little" | "big"; declare const numberMaxSize = 6; type NumberSize = 1 | 2 | 3 | 4 | 5 | 6; type NumSizeToPrimitive<Size extends number> = Size extends NumberSize ? number : Size & NumberSize extends never ? bigint : number | bigint; type FixedConversion<FromType extends PrimitiveType | LayoutObject, ToType> = { readonly to: ToType; readonly from: FromType; }; type CustomConversion<FromType extends PrimitiveType | LayoutObject, ToType> = { readonly to: (val: FromType) => ToType; readonly from: (val: ToType) => FromType; }; interface ItemBase<BL extends BinaryLiterals> { readonly binary: BL; } interface FixedOmittableCustom<T extends PrimitiveType> { custom: T; omit?: boolean; } interface LengthPrefixed { readonly lengthSize: NumberSize; readonly lengthEndianness?: Endianness; } interface NumItemBase<T extends NumType, Signed extends Boolean> extends ItemBase<Signed extends true ? "int" : "uint"> { size: T extends bigint ? number : NumberSize; endianness?: Endianness; } interface FixedPrimitiveNum<T extends NumType, Signed extends Boolean> extends NumItemBase<T, Signed>, FixedOmittableCustom<T> { } interface OptionalToFromNum<T extends NumType, Signed extends Boolean> extends NumItemBase<T, Signed> { custom?: FixedConversion<T, any> | CustomConversion<T, any>; } interface FixedPrimitiveBytes extends ItemBase<"bytes">, FixedOmittableCustom<BytesType> { } interface FlexPureBytes extends ItemBase<"bytes"> { readonly custom?: BytesType | FixedConversion<BytesType, any> | CustomConversion<BytesType, any>; } interface FlexLayoutBytes extends ItemBase<"bytes"> { readonly custom?: FixedConversion<LayoutObject, any> | CustomConversion<LayoutObject, any>; readonly layout: Layout; } interface ManualSizePureBytes extends FlexPureBytes { readonly size: number; } interface LengthPrefixedPureBytes extends FlexPureBytes, LengthPrefixed { } interface ManualSizeLayoutBytes extends FlexLayoutBytes { readonly size: number; } interface LengthPrefixedLayoutBytes extends FlexLayoutBytes, LengthPrefixed { } interface ArrayItemBase extends ItemBase<"array"> { readonly layout: Layout; } interface FixedLengthArray extends ArrayItemBase { readonly length: number; } interface LengthPrefixedArray extends ArrayItemBase, LengthPrefixed { } interface RemainderArray extends ArrayItemBase { } type PlainId = number; type ConversionId = readonly [number, unknown]; type IdProperLayoutPair<Id extends PlainId | ConversionId, P extends ProperLayout = ProperLayout> = readonly [Id, P]; type IdProperLayoutPairs = readonly IdProperLayoutPair<PlainId>[] | readonly IdProperLayoutPair<ConversionId>[]; type DistributiveAtLeast1<T> = T extends any ? readonly [T, ...T[]] : never; interface SwitchItem extends ItemBase<"switch"> { readonly idSize: NumberSize; readonly idEndianness?: Endianness; readonly idTag?: string; readonly layouts: DistributiveAtLeast1<IdProperLayoutPair<PlainId> | IdProperLayoutPair<ConversionId>>; } type NumItem<Signed extends boolean = boolean> = Signed extends infer S extends boolean ? FixedPrimitiveNum<number, S> | OptionalToFromNum<number, S> | FixedPrimitiveNum<bigint, S> | OptionalToFromNum<bigint, S> : never; type BytesItem = FixedPrimitiveBytes | FlexPureBytes | ManualSizePureBytes | LengthPrefixedPureBytes | FlexLayoutBytes | ManualSizeLayoutBytes | LengthPrefixedLayoutBytes; type ArrayItem = FixedLengthArray | LengthPrefixedArray | RemainderArray; type Item = NumItem | BytesItem | ArrayItem | SwitchItem; type NamedItem = Item & { readonly name: string; }; type ProperLayout = readonly NamedItem[]; type Layout = Item | ProperLayout; type NameOrOmitted<T extends { name: string; }> = T extends { omit: true; } ? never : T["name"]; type DeriveType<L extends Layout> = Layout extends L ? unknown : L extends infer LI extends Item ? ItemToType<LI> : L extends infer P extends ProperLayout ? { readonly [I in P[number] as NameOrOmitted<I>]: ItemToType<I>; } : never; type ItemToType<II extends Item> = II extends infer I extends Item ? I extends NumItem ? NumItemToType<I> : I extends BytesItem ? BytesItemToType<I> : I extends ArrayItem ? ArrayItemToType<I> : I extends SwitchItem ? SwitchItemToType<I> : never : never; type NumItemToType<I extends NumItem> = I["custom"] extends CustomConversion<infer From extends NumType, infer To> ? To : I["custom"] extends FixedConversion<infer From extends NumType, infer To> ? To : I["custom"] extends undefined ? NumSizeToPrimitive<I["size"]> : I["custom"] extends NumType ? I["custom"] : NumSizeToPrimitive<I["size"]>; type BytesItemToType<I extends BytesItem> = I extends { layout: Layout; } ? I["custom"] extends CustomConversion<infer From extends LayoutObject, infer To> ? To : I["custom"] extends FixedConversion<infer From extends LayoutObject, infer To> ? To : DeriveType<I["layout"]> : I["custom"] extends CustomConversion<BytesType, infer To> ? To : I["custom"] extends FixedConversion<BytesType, infer To> ? To : BytesType; type TupleWithLength<T, L extends number, A extends T[] = []> = A["length"] extends L ? A : TupleWithLength<T, L, [...A, T]>; type ArrayItemToType<I extends ArrayItem> = DeriveType<I["layout"]> extends infer DT ? I extends { length: infer AL extends number; } ? number extends AL ? readonly DT[] : Readonly<TupleWithLength<DT, AL>> : readonly DT[] : never; type MaybeConvert<Id extends PlainId | ConversionId> = Id extends readonly [number, infer Converted] ? Converted : Id; type IdLayoutPairsToTypeUnion<A extends IdProperLayoutPairs, IdTag extends string> = A extends infer V extends IdProperLayoutPairs ? V extends readonly [infer Head, ...infer Tail extends IdProperLayoutPairs] ? Head extends IdProperLayoutPair<infer MaybeConversionId, infer P extends ProperLayout> ? MaybeConvert<MaybeConversionId> extends infer Id ? DeriveType<P> extends infer DT extends LayoutObject ? { readonly [K in IdTag | keyof DT]: K extends keyof DT ? DT[K] : Id; } | IdLayoutPairsToTypeUnion<Tail, IdTag> : never : never : never : never : never; type SwitchItemToType<I extends SwitchItem> = IdLayoutPairsToTypeUnion<I["layouts"], I["idTag"] extends infer ID extends string ? ID extends undefined ? "id" : ID : never>; declare function serialize<const L extends Layout, E extends BytesType | undefined = undefined>(layout: L, data: DeriveType<L>, encoded?: E): E extends undefined ? BytesType : number; type DeserializeReturn<L extends Layout, B extends boolean> = B extends true ? DeriveType<L> : readonly [DeriveType<L>, number]; declare function deserialize<const L extends Layout, const B extends boolean = true>(layout: L, bytes: BytesType, consumeAll?: B): DeserializeReturn<L, B>; type FixedItemsOf<L extends Layout> = StartFilterItemsOf<L, true>; type DynamicItemsOf<L extends Layout> = StartFilterItemsOf<L, false>; declare const fixedItemsOf: <const L extends Layout>(layout: L) => FilterItemsOf<L, true>; declare const dynamicItemsOf: <const L extends Layout>(layout: L) => FilterItemsOf<L, false>; declare function addFixedValues<const L extends Layout>(layout: L, dynamicValues: DeriveType<DynamicItemsOf<L>>): DeriveType<L>; type NonEmpty = readonly [unknown, ...unknown[]]; type IPLPair = readonly [any, ProperLayout]; type FilterItemsOfIPLPairs<ILA extends readonly IPLPair[], Fixed extends boolean> = ILA extends infer V extends readonly IPLPair[] ? V extends readonly [infer H extends IPLPair, ...infer T extends readonly IPLPair[]] ? FilterItemsOf<H[1], Fixed> extends infer P extends ProperLayout | void ? P extends NonEmpty ? [[H[0], P], ...FilterItemsOfIPLPairs<T, Fixed>] : FilterItemsOfIPLPairs<T, Fixed> : never : [] : never; type FilterLayoutOfItem<I extends { layout: Layout; }, Fixed extends boolean> = FilterItemsOf<I["layout"], Fixed> extends infer L extends Item | NonEmpty ? { readonly [K in keyof I]: K extends "layout" ? L : I[K]; } : void; type FilterItem<II extends Item, Fixed extends boolean> = II extends infer I extends Item ? I extends NumItem ? I["custom"] extends NumType | FixedConversion<infer From extends NumType, infer To> ? Fixed extends true ? I : void : Fixed extends true ? void : I : I extends ArrayItem ? FilterLayoutOfItem<I, Fixed> : I extends BytesItem & { layout: Layout; } ? I["custom"] extends { custom: FixedConversion<infer From extends LayoutObject, infer To>; } ? Fixed extends true ? I : void : I extends { custom: CustomConversion<infer From extends LayoutObject, infer To>; } ? Fixed extends true ? void : I : FilterLayoutOfItem<I, Fixed> : I extends BytesItem ? I["custom"] extends BytesType | FixedConversion<infer From extends BytesType, infer To> ? Fixed extends true ? I : void : Fixed extends true ? void : I : I extends SwitchItem ? { readonly [K in keyof I]: K extends "layouts" ? FilterItemsOfIPLPairs<I["layouts"], Fixed> : I[K]; } : never : never; type FilterItemsOf<L extends Layout, Fixed extends boolean> = L extends infer LI extends Item ? FilterItem<LI, Fixed> : L extends infer P extends ProperLayout ? P extends readonly [infer H extends Item, ...infer T extends ProperLayout] ? FilterItem<H, Fixed> extends infer NI ? NI extends Item ? [NI, ...FilterItemsOf<T, Fixed>] : FilterItemsOf<T, Fixed> : never : [] : never; type StartFilterItemsOf<L extends Layout, Fixed extends boolean> = FilterItemsOf<L, Fixed> extends infer V extends Layout ? V : never; type LayoutIndex = number; type Discriminator<B extends boolean = false> = (encoded: BytesType) => B extends false ? LayoutIndex | null : readonly LayoutIndex[]; declare function buildDiscriminator<B extends boolean = false>(layouts: readonly Layout[], allowAmbiguous?: B): Discriminator<B>; declare function calcSize<const L extends Layout>(layout: L, data: DeriveType<L>): number; declare function calcStaticSize(layout: Layout): number | null; type CustomizableBytes = undefined | Layout | Uint8Array | FixedConversion<Uint8Array, any> | CustomConversion<Uint8Array, any> | readonly [Layout, FixedConversion<any, any> | CustomConversion<any, any>]; type BytesBase = ({} | { readonly name: string; }) & Omit<BytesItem, "binary" | "custom" | "layout">; type CombineObjects<T, U> = { readonly [K in keyof T | keyof U]: K extends keyof T ? T[K] : K extends keyof U ? U[K] : never; }; type CustomizableBytesReturn<B extends BytesBase, P extends CustomizableBytes> = CombineObjects<B, P extends undefined ? { binary: "bytes"; } : P extends Layout ? { binary: "bytes"; layout: P; } : P extends Uint8Array | FixedConversion<Uint8Array, any> | CustomConversion<Uint8Array, any> ? { binary: "bytes"; custom: P; } : P extends readonly [Layout, FixedConversion<any, any> | CustomConversion<any, any>] ? { binary: "bytes"; layout: P[0]; custom: P[1]; } : never>; declare const customizableBytes: <const B extends BytesBase, const C extends CustomizableBytes>(base: B, spec?: C) => CustomizableBytesReturn<B, C>; declare function boolItem(permissive?: boolean): { readonly binary: "uint"; readonly size: 1; readonly custom: { readonly to: (encoded: number) => boolean; readonly from: (value: boolean) => number; }; }; declare function enumItem<const E extends readonly (readonly [string, number])[], const S extends NumberSize = 1, const EN extends Endianness = "big">(entries: E, opts?: { size?: S; endianness?: EN; }): { readonly binary: "uint"; readonly size: S; readonly endianness: EN; readonly custom: { readonly to: (encoded: number) => E[number][0]; readonly from: (name: E[number][0]) => number; }; }; declare const baseOptionItem: <const T extends CustomizableBytes>(someType: T) => { readonly binary: "switch"; readonly idSize: 1; readonly idTag: "isSome"; readonly layouts: readonly [readonly [readonly [0, false], readonly []], readonly [readonly [1, true], readonly [CustomizableBytesReturn<{ readonly name: "value"; }, T>]]]; }; type BaseOptionItem<T extends CustomizableBytes> = DeriveType<ReturnType<typeof baseOptionItem<T>>>; type BaseOptionValue<T extends CustomizableBytes> = DeriveType<CustomizableBytesReturn<{}, T>> | undefined; declare function optionItem<const T extends CustomizableBytes>(optVal: T): { readonly binary: "bytes"; readonly layout: { readonly binary: "switch"; readonly idSize: 1; readonly idTag: "isSome"; readonly layouts: readonly [readonly [readonly [0, false], readonly []], readonly [readonly [1, true], readonly [CustomizableBytesReturn<{ readonly name: "value"; }, T>]]]; }; readonly custom: { to: (obj: BaseOptionItem<T>) => BaseOptionValue<T>; from: (value: BaseOptionValue<T>) => BaseOptionItem<T>; }; }; type Bitset<B extends readonly (string | undefined)[]> = { [K in B[number] as K extends "" | undefined ? never : K]: boolean; }; type ByteSize = [ never, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6 ]; type BitsizeToBytesize<N extends number> = N extends keyof ByteSize ? ByteSize[N] : number; type BitsetItem<B extends readonly (string | undefined)[], S extends number = BitsizeToBytesize<B["length"]>> = { binary: "uint"; size: S; custom: { to: (encoded: NumSizeToPrimitive<S>) => Bitset<B>; from: (obj: Bitset<B>) => NumSizeToPrimitive<S>; }; }; declare function bitsetItem<const B extends readonly (string | undefined)[], const S extends number = BitsizeToBytesize<B["length"]>>(bitnames: B, size?: S): BitsetItem<B, S>; declare function setEndianness<const L extends Layout, E extends Endianness>(layout: L, endianness: E): SetEndianness<L, E>; type SetEndianness<L extends Layout, E extends Endianness> = Layout extends L ? Layout : ProperLayout extends L ? ProperLayout : Item extends L ? Item : L extends infer LI extends Item ? SetItemEndianness<LI, E> extends infer R extends Item ? R : never : L extends infer P extends ProperLayout ? SetProperLayoutEndianness<P, E> extends infer R extends ProperLayout ? R : never : never; type SetItemEndianness<I extends Item, E extends Endianness> = I extends NumItem ? I["size"] extends 1 ? I : SetProperty<I, "endianness", E> : I extends BytesItem | ArrayItem ? RecurseLayoutProperty<I, E> extends infer RI extends Item ? RI extends LengthPrefixed ? RI["lengthSize"] extends 1 ? RI : SetProperty<RI, "lengthEndianness", E> : RI : never : I extends SwitchItem ? RecurseSwitchItem<I, E> extends infer RI extends SwitchItem ? RI["idSize"] extends 1 ? RI : SetProperty<RI, "idEndianness", E> : never : never; type SetProperLayoutEndianness<P extends ProperLayout, E extends Endianness> = P extends readonly [infer H extends Item, ...infer T extends ProperLayout] ? readonly [SetItemEndianness<H, E>, ...SetProperLayoutEndianness<T, E>] : readonly []; type RecurseLayoutProperty<I extends Item, E extends Endianness> = "layout" extends keyof I ? I["layout"] extends Layout ? SetProperty<I, "layout", SetEndianness<I["layout"], E>> : never : I; type RecurseSwitchItemImpl<SL extends readonly unknown[], E extends Endianness> = SL extends readonly [ readonly [infer Id, infer P extends ProperLayout], ...infer T extends readonly unknown[] ] ? readonly [readonly [Id, SetProperLayoutEndianness<P, E>], ...RecurseSwitchItemImpl<T, E>] : readonly []; type RecurseSwitchItem<I extends SwitchItem, E extends Endianness> = SetProperty<I, "layouts", RecurseSwitchItemImpl<I["layouts"], E>>; type SetProperty<I extends Item, P extends string, V> = { readonly [K in keyof I | P]: K extends P ? V : K extends keyof I ? I[K] : never; } extends infer R extends Item ? R : never; export { type Bitset, type BitsetItem, type BytesBase, type CustomConversion, type CustomizableBytes, type CustomizableBytesReturn, type DeriveType, type Discriminator, type DynamicItemsOf, type Endianness, type FixedConversion, type FixedItemsOf, type Item, type Layout, type NamedItem, type NumberSize, type ProperLayout, type SetEndianness, addFixedValues, bitsetItem, boolItem, buildDiscriminator, calcSize, calcStaticSize, customizableBytes, deserialize, dynamicItemsOf, enumItem, fixedItemsOf, numberMaxSize, optionItem, serialize, setEndianness };