UNPKG

@andrew_l/tl-pack

Version:

Another implementation of binary serialization.

483 lines (475 loc) 15.2 kB
import { Data } from '@andrew_l/toolkit'; declare function createDictionary(values?: string[]): Dictionary; declare class Dictionary { private _count; private _wordToIndex; private _words; private _offset; constructor(values?: string[], offset?: number); clear(): void; get size(): number; /** * Returns inserted index or nothing */ maybeInsert(word: string): number | null; getValue(index: number): string | null; getIndex(value: string): number | null; hasValue(value: string): boolean; hasIndex(index: number): boolean; } type EncodeHandler<T = any> = (this: BinaryWriter, value: T) => void; type DecodeHandler<T = any> = (this: BinaryReader) => T; interface TLExtension<T = any> { token: number; encode: EncodeHandler<T>; decode: DecodeHandler<T>; } declare function createExtension(token: number, { encode, decode }: { encode: EncodeHandler; decode: DecodeHandler; }): TLExtension; interface BinaryWriterOptions { gzip?: boolean; dictionary?: string[] | Dictionary; extensions?: TLExtension[]; structures?: Structure.Constructor[]; } declare class BinaryWriter { private withGzip; private target; private dictionary; private dictionaryExtended; private extensions; private structures; private _last; private offsetChecksum; private _repeat?; offset: number; constructor({ gzip, dictionary, extensions, structures, }?: BinaryWriterOptions); /** * Reset internal state */ reset(): this; allocate(size: number): this; private makeRoom; get safeEnd(): number; getBuffer(): Uint8Array; writeByte(value: number): this; writeBool(value: boolean): this; writeNull(): this; writeInt64(value: number | bigint, signed?: boolean): this; writeInt32(value: number, signed?: boolean): this; writeInt16(value: number, signed?: boolean): this; writeInt8(value: number, signed?: boolean): this; writeFloat(value: number): this; writeDouble(value: number): this; writeDate(value: number | Date): this; writeString(value: string): this; writeChecksum(withConstructor?: boolean): this; writeBytes(value: Uint8Array): this; writeLength(value: number): this; writeVector(value: Array<any>): this; writeMap(object: Record<string, any>): this; wireDictionary(value: string): this; writeStructure(value: Structure): this; writeGzip(value: Uint8Array | ArrayBuffer): this; encode(value: any): Uint8Array; startDynamicVector(): this; endDynamicVector(): this; private _writeCustom; writeObject(value: any): this; writeObjectGzip(value: any): this; private writeCore; private writeRepeat; } declare enum CORE_TYPES { None = 0, Binary = 1, BoolFalse = 2, BoolTrue = 3, Null = 4, Date = 5, Vector = 6, VectorDynamic = 7, Int64 = 22, Int32 = 8, Int16 = 9, Int8 = 10, UInt64 = 23, UInt32 = 11, UInt16 = 12, UInt8 = 13, Float = 14, Double = 15, Map = 16, DictValue = 17, DictIndex = 18, String = 19, Repeat = 20, Checksum = 21, GZIP = 25, Structure = 26 } declare const MAX_BUFFER_SIZE = 2144337920; interface DefineStructureOptions<Props extends Structure.ObjectPropsOptions> { /** * Unique name of binary structure */ readonly name: string; /** * Version of binary structure */ readonly version: number; /** * Binary structure properties */ readonly properties: Props; /** * Write checksum to verify decoded data * @default false */ readonly checksum?: boolean; } /** * Create binary structure definition with type safety and performance optimization * * @example * // Define a user structure * const User = defineStructure({ * name: 'User', * version: 1, * checksum: true, * properties: { * id: { type: Number, required: true }, * name: { type: String, required: true }, * email: { type: String, required: false }, * isActive: { type: Boolean, required: true }, * createdAt: { type: Date, required: true }, * tags: { type: Array, required: false } * } * }); * * // Create and serialize a user * const user = new User({ * id: 123, * name: 'John Doe', * email: 'john@example.com', * isActive: true, * createdAt: new Date(), * tags: ['admin', 'verified'] * }); * * const buffer = user.toBuffer(); * const restored = User.fromBuffer(buffer); * * @example * // Define nested structures * const Address = defineStructure({ * name: 'Address', * version: 1, * properties: { * street: { type: String, required: true }, * city: { type: String, required: true }, * zipCode: { type: String, required: true } * } * }); * * const Person = defineStructure({ * name: 'Person', * version: 1, * properties: { * name: { type: String, required: true }, * address: { type: Address, required: false } * } * }); * * @example * // Using with complex data types * const GameState = defineStructure({ * name: 'GameState', * version: 2, * checksum: true, * properties: { * playerData: { type: Object, required: true }, // Untrusted data * screenshot: { type: Uint8Array, required: false }, // Binary data * timestamp: { type: Date, required: true }, * metadata: { type: Object, required: false } * } * }); * * @group Main */ declare function defineStructure<PropsOptions extends Structure.ObjectPropsOptions, T extends Data = Structure.ExtractPropTypes<PropsOptions>>({ name, properties, version, checksum, }: DefineStructureOptions<PropsOptions>): Structure.Constructor<T>; declare class Structure<T extends Data = Data> implements Structure.Structure<T> { readonly value: T; constructor(value: T); toBuffer(options?: BinaryWriterOptions): Uint8Array; static fromBuffer(buffer: Uint8Array, options?: BinaryReaderOptions): Data; static readonly estimatedSizeBytes: number; static readonly structures: Structure.Constructor[]; static readonly extension: TLExtension; } declare namespace Structure { export interface Options<T extends Data = Data> { readonly encode: EncodeHandler<T>; readonly decode: DecodeHandler<T>; } export interface Structure<T extends Data = Data> { /** * The structured data value * @type {T} * @readonly * * @example * const user = new UserStruct({ id: 1, name: 'Alice' }); * console.log(user.value.name); // 'Alice' */ readonly value: T; /** * Serializes the structure to a binary buffer * * @param {BinaryWriterOptions} [options] - Writer configuration options * @returns {Uint8Array} Serialized binary data * * @example * const user = new User({ id: 1, name: 'Alice' }); * const buffer = user.toBuffer(); * console.log(buffer.length); // Size in bytes * * @example * // With custom writer options * const buffer = user.toBuffer({ * initialSize: 1024, * growthFactor: 2 * }); */ toBuffer(options?: BinaryWriterOptions): Uint8Array; } /** * Base Structure class for binary serialization with type safety * * @template T - Data type extending Data interface * * @example * // Direct usage (not recommended, use defineStructure instead) * class CustomStruct extends Structure<{id: number, name: string}> { * // Custom implementation * } * * @example * // Typical usage through defineStructure * const MyStruct = defineStructure({ * name: 'MyStruct', * version: 1, * properties: { id: { type: Number, required: true } } * }); * * const instance = new MyStruct({ id: 42 }); * console.log(instance.value.id); // 42 */ export interface Constructor<T extends Data = any> { /** * Creates a new Structure instance * * @param {T} value - The data to structure * * @example * const data = { id: 123, name: 'Test' }; * const struct = new MyStructure(data); */ new (value: T): Structure<T>; /** * Deserializes a structure from a binary buffer * * @param {Uint8Array} buffer - Binary data to deserialize * @param {BinaryReaderOptions} [options] - Reader configuration options * @returns {Data} Deserialized structure data * * @example * const buffer = user.toBuffer(); * const restored = User.fromBuffer(buffer); * console.log(restored.id); // Original user ID */ fromBuffer(buffer: Uint8Array, options?: BinaryReaderOptions): T; /** * Estimated size of bytes */ readonly estimatedSizeBytes: number; /** * Nested structures defining encoding/decoding behavior */ readonly structures: Constructor[]; /** * Structure extension defining encoding/decoding behavior */ readonly extension: TLExtension; } type PropConstructor<T = any> = { new (...args: any[]): T & {}; } | { (): T; } | PropMethod<T>; type PropMethod<T, TConstructor = any> = [T] extends [ ((...args: any) => any) | undefined ] ? { new (): TConstructor; (): T; readonly prototype: TConstructor; } : never; type OptionalKeys<T> = Exclude<keyof T, RequiredKeys<T>>; type RequiredKeys<T> = { [K in keyof T]: T[K] extends { required: true; } ? K : never; }[keyof T]; type Prop<T> = PropOptions<T> | PropType<T>; type CoreInt = CORE_TYPES.Int8 | CORE_TYPES.Int16 | CORE_TYPES.Int32 | CORE_TYPES.UInt8 | CORE_TYPES.UInt16 | CORE_TYPES.UInt32 | CORE_TYPES.Float | CORE_TYPES.Double; type InferPropType<T, NullAsAny = true> = [T] extends [null] ? NullAsAny extends true ? any : null : [T] extends [{ type: null; }] ? any : [T] extends [{ type: [null]; }] ? any[] : [T] extends [{ type: ObjectConstructor | CORE_TYPES.Map; }] ? Record<string, any> : [T] extends [{ type: CoreInt; }] ? number : [T] extends [{ type: CORE_TYPES.String; }] ? string : [T] extends [{ type: CORE_TYPES.Vector; }] ? unknown[] : [T] extends [{ type: CORE_TYPES.Binary; }] ? Uint8Array : [T] extends [{ type: BooleanConstructor; }] ? boolean : [T] extends [{ type: DateConstructor | CORE_TYPES.Date; }] ? Date : [T] extends [ { type: CORE_TYPES.UInt64 | CORE_TYPES.Int64; } ] ? bigint : [T] extends [{ type: Constructor<infer U>; }] ? U : [T] extends [{ type: [infer U]; }] ? U extends DateConstructor ? Date[] : U extends Constructor<infer V> ? V[] : InferPropType<U, false>[] : [T] extends [{ type: (infer U)[]; }] ? U extends DateConstructor ? Date | InferPropType<U, false> : InferPropType<U, false> : [T] extends [Prop<infer V>] ? V : T; export type ObjectPropsOptions<P = Data> = { readonly [K in keyof P]: PropOptions<P[K]>; }; export interface PropOptions<T = any> { type: PropType<T> | null | [null]; required?: boolean; } export type PropType<T> = PropConstructor<T> | [PropConstructor<T>] | CORE_TYPES | [CORE_TYPES]; export type ExtractType<T extends Constructor> = T extends Constructor<infer U> ? U : never; export type ExtractPropTypes<O> = { [K in keyof Pick<O, RequiredKeys<O>>]: O[K] extends { default: any; } ? Exclude<InferPropType<O[K]>, undefined> : InferPropType<O[K]>; } & { [K in keyof Pick<O, OptionalKeys<O>>]?: InferPropType<O[K]> | null; }; export { }; } interface BinaryReaderOptions { dictionary?: string[] | Dictionary; extensions?: TLExtension[]; structures?: Structure.Constructor[]; } declare class BinaryReader { private target; private _last?; private _lastObject?; private dictionary?; private dictionaryExtended; private extensions; private structures; private _repeat?; private _checksumOffset; offset: number; length: number; /** * Small utility class to read binary data. */ constructor(data: Uint8Array, { dictionary, extensions, structures, }?: BinaryReaderOptions); readByte(): number; readInt64(signed?: boolean): bigint; readInt32(signed?: boolean): number; readInt16(signed?: boolean): number; readInt8(signed?: boolean): number; /** * Reads a real floating point (4 bytes) value. * @returns {number} */ readFloat(): number; /** * Reads a real floating point (8 bytes) value. */ readDouble(): number; /** * Throws error if provided length cannot be read from buffer * @param length {number} */ assertRead(length: number): void; assertConstructor(constructorId: CORE_TYPES): void; /** * Gets the byte array representing the current buffer as a whole. */ getBuffer(): Uint8Array; readNull(): null; readLength(): number; readAll(): any[]; readBytes(): Uint8Array; /** * Reads encoded string. */ readString(): string; /** * Reads a boolean value. */ readBool(): boolean; /** * Reads and converts Unix time * into a Javascript {Date} object. */ readDate(): Date; readStructure<T extends Data = Data>(checkConstructor?: boolean): T; /** * Reads a object. */ readObject(): any; readObjectGzip(): any; readGzip(): any; private readCore; getDictionaryValue(index: number): string | null; readDictionary(): null | string; readMap(checkConstructor?: boolean): Record<string, any>; decode<T = any>(value: Uint8Array): T; /** * Reads a vector (a list) of objects. */ readVector<T = any>(checkConstructor?: boolean): T[]; /** * Reads a vector (a list) of objects. */ readVectorDynamic<T>(checkConstructor?: boolean): T[]; readChecksum(checkConstructor?: boolean): void; /** * Tells the current position on the stream. */ tellPosition(): number; /** * Sets the current position on the stream. */ setPosition(position: number): void; /** * Seeks the stream position given an offset from the current position. * The offset may be negative. */ seek(offset: number): void; /** * Sets the current buffer and reset initial state. */ reset(data?: Uint8Array): void; } export { type BinaryWriterOptions as B, CORE_TYPES as C, type DecodeHandler as D, type EncodeHandler as E, MAX_BUFFER_SIZE as M, Structure as S, type TLExtension as T, type BinaryReaderOptions as a, BinaryReader as b, BinaryWriter as c, createDictionary as d, createExtension as e, type DefineStructureOptions as f, defineStructure as g };