@andrew_l/tl-pack
Version:
Another implementation of binary serialization.
1 lines • 120 kB
Source Map (JSON)
{"version":3,"file":"tl-pack.5CF-VZgL.cjs","sources":["../../src/constants.ts","../../src/dictionary.ts","../../src/BinaryWriter.ts","../../src/Structure.ts","../../src/helpers.ts","../../src/BinaryReader.ts"],"sourcesContent":["export enum CORE_TYPES {\n None = 0,\n Binary = 1,\n BoolFalse = 2,\n BoolTrue = 3,\n Null = 4,\n Date = 5,\n Vector = 6,\n VectorDynamic = 7,\n Int64 = 22,\n Int32 = 8,\n Int16 = 9,\n Int8 = 10,\n UInt64 = 23,\n UInt32 = 11,\n UInt16 = 12,\n UInt8 = 13,\n Float = 14,\n Double = 15,\n Map = 16,\n DictValue = 17,\n DictIndex = 18,\n String = 19,\n Repeat = 20,\n Checksum = 21,\n GZIP = 25,\n Structure = 26,\n}\n\nexport const MAX_BUFFER_SIZE = 0x7fd00000;\n","export function createDictionary(values?: string[]): Dictionary {\n return new Dictionary(values);\n}\n\nexport class Dictionary {\n private _count;\n private _wordToIndex: Map<string, number>;\n private _words: string[];\n private _offset: number;\n\n constructor(values?: string[], offset = 0) {\n this._count = 0;\n this._words = [];\n this._wordToIndex = new Map();\n this._offset = offset;\n\n if (Array.isArray(values) && values.length) {\n values.forEach(word => {\n if (this._wordToIndex!.has(word)) return;\n\n this._wordToIndex.set(word, this._count++);\n this._words.push(word);\n });\n }\n }\n\n clear(): void {\n this._count = 0;\n this._words.length = 0;\n this._wordToIndex.clear();\n }\n\n get size(): number {\n return this._count;\n }\n\n /**\n * Returns inserted index or nothing\n */\n maybeInsert(word: string): number | null {\n if (this._wordToIndex.has(word)) return null;\n\n this._wordToIndex.set(word, this._count++);\n this._words.push(word);\n\n return this._count + this._offset;\n }\n\n getValue(index: number): string | null {\n return this._words[index - this._offset] ?? null;\n }\n\n getIndex(value: string): number | null {\n const idx = this._wordToIndex.get(value);\n\n if (idx === undefined) {\n return null;\n }\n\n return idx + this._offset;\n }\n\n hasValue(value: string): boolean {\n return this._wordToIndex.has(value);\n }\n\n hasIndex(index: number): boolean {\n return this._words[index - this._offset] !== undefined;\n }\n}\n","import pako from 'pako';\nimport type { Structure } from './Structure.js';\nimport { CORE_TYPES, MAX_BUFFER_SIZE } from './constants';\nimport { Dictionary } from './dictionary';\nimport type { TLExtension } from './extension';\nimport {\n byteArrayAllocate,\n coreType,\n float32,\n float64,\n int32,\n utf8Write,\n} from './helpers.js';\n\nconst noop = Symbol();\n\nexport interface BinaryWriterOptions {\n gzip?: boolean;\n dictionary?: string[] | Dictionary;\n extensions?: TLExtension[];\n structures?: Structure.Constructor[];\n}\n\nconst NO_CONSTRUCTOR = new Set([\n CORE_TYPES.BoolFalse,\n CORE_TYPES.BoolTrue,\n CORE_TYPES.Null,\n]);\n\nconst SUPPORT_COMPRESSION = new Set([CORE_TYPES.String]);\n\nconst NOOP_DICTIONARY = new Dictionary();\n\nexport class BinaryWriter {\n private withGzip: boolean;\n private target: Uint8Array;\n private dictionary: Dictionary;\n private dictionaryExtended: Dictionary;\n private extensions: Map<number, TLExtension>;\n private structures: Map<number, Structure.Constructor>;\n private _last: any;\n private offsetChecksum: number;\n private _repeat?: { offset: number; count: number };\n offset: number;\n\n constructor({\n gzip = false,\n dictionary = NOOP_DICTIONARY,\n extensions,\n structures,\n }: BinaryWriterOptions = {}) {\n this.offset = 0;\n this.offsetChecksum = 0;\n this.extensions = new Map();\n this.structures = new Map();\n this.withGzip = gzip;\n\n this.target = byteArrayAllocate(8192);\n this._last = noop;\n\n if (extensions) {\n for (const ext of extensions) {\n this.extensions.set(ext.token, ext);\n }\n }\n\n if (structures) {\n for (const struct of structures) {\n this.structures.set(struct.extension.token, struct);\n }\n }\n\n if (Array.isArray(dictionary)) {\n this.dictionary = new Dictionary(dictionary);\n } else {\n this.dictionary = dictionary;\n }\n\n this.dictionaryExtended = new Dictionary(undefined, this.dictionary.size);\n }\n\n /**\n * Reset internal state\n */\n reset(): this {\n this.offset = 0;\n this.offsetChecksum = 0;\n this._last = noop;\n this._repeat = undefined;\n this.dictionaryExtended.clear();\n return this;\n }\n\n allocate(size: number): this {\n const position = this.offset + size;\n\n if (this.safeEnd < position) {\n this.makeRoom(position);\n }\n\n return this;\n }\n\n private makeRoom(end: number): void {\n let start = 0;\n let newSize = 0;\n let target = this.target;\n\n if (end > 0x1000000) {\n // special handling for really large buffers\n if (end - start > MAX_BUFFER_SIZE)\n throw new Error(\n 'Packed buffer would be larger than maximum buffer size',\n );\n newSize = Math.min(\n MAX_BUFFER_SIZE,\n Math.round(\n Math.max((end - start) * (end > 0x4000000 ? 1.25 : 2), 0x400000) /\n 0x1000,\n ) * 0x1000,\n );\n } else {\n // faster handling for smaller buffers\n newSize =\n ((Math.max((end - start) << 2, target.length - 1) >> 12) + 1) << 12;\n }\n\n const newBuffer = byteArrayAllocate(newSize);\n\n end = Math.min(end, target.length);\n\n newBuffer.set(target.slice(start, end));\n\n this.target = newBuffer;\n }\n\n get safeEnd(): number {\n return this.target.length - 10;\n }\n\n getBuffer(): Uint8Array {\n return this.target.subarray(0, this.offset);\n }\n\n writeByte(value: number): this {\n this.allocate(1);\n this.target[this.offset++] = value;\n return this;\n }\n\n writeBool(value: boolean): this {\n if (value) {\n this.writeByte(CORE_TYPES.BoolTrue);\n } else {\n this.writeByte(CORE_TYPES.BoolFalse);\n }\n\n return this;\n }\n\n writeNull(): this {\n this.writeByte(CORE_TYPES.Null);\n return this;\n }\n\n writeInt64(value: number | bigint, signed = true): this {\n this.allocate(8);\n\n if (typeof value === 'number') value = BigInt(value);\n\n const low32 = Number(value & 0xffffffffn);\n const high32 = Number(value >> 32n);\n\n this.writeInt32(low32, signed);\n this.writeInt32(high32, signed);\n\n return this;\n }\n\n writeInt32(value: number, signed = true): this {\n this.allocate(4);\n\n if (signed) {\n this.target[this.offset++] = value;\n this.target[this.offset++] = value >> 8;\n this.target[this.offset++] = value >> 16;\n this.target[this.offset++] = value >> 24;\n } else {\n this.target[this.offset++] = value;\n this.target[this.offset++] = value >> 8;\n this.target[this.offset++] = value >> 16;\n this.target[this.offset++] = value >> 24;\n }\n\n return this;\n }\n\n writeInt16(value: number, signed = true): this {\n this.allocate(2);\n\n if (signed) {\n this.target[this.offset++] = value;\n this.target[this.offset++] = value >> 8;\n } else {\n this.target[this.offset++] = value;\n this.target[this.offset++] = value >> 8;\n }\n\n return this;\n }\n\n writeInt8(value: number, signed = true): this {\n this.allocate(1);\n this.target[this.offset++] = value;\n return this;\n }\n\n writeFloat(value: number): this {\n this.allocate(4);\n float32[0] = value;\n this.writeInt32(int32[0]);\n return this;\n }\n\n writeDouble(value: number): this {\n this.allocate(8);\n\n float64[0] = value;\n this.writeInt32(int32[0], false);\n this.writeInt32(int32[1], false);\n\n return this;\n }\n\n writeDate(value: number | Date): this {\n let timestamp = 0;\n\n if (value instanceof Date) {\n timestamp = value.getTime();\n } else if (typeof value === 'number') {\n timestamp = value;\n }\n\n this.writeDouble(timestamp);\n return this;\n }\n\n writeString(value: string): this {\n const strLength = value.length;\n\n let start = this.offset;\n let require = strLength << 2;\n\n if (require < 254) {\n require += 1;\n this.offset += 1;\n } else {\n require += 4;\n this.offset += 4;\n }\n\n this.allocate(require);\n\n const bytes = utf8Write(this.target, value, this.offset);\n\n if (require < 254) {\n this.target[start++] = bytes;\n } else {\n this.target[start++] = 254;\n this.target[start++] = bytes % 256;\n this.target[start++] = (bytes >> 8) % 256;\n this.target[start++] = (bytes >> 16) % 256;\n }\n\n this.offset += bytes;\n\n return this;\n }\n\n writeChecksum(withConstructor: boolean = true): this {\n const bytes = this.target.subarray(this.offsetChecksum, this.offset);\n let sum = 0;\n\n for (const val of bytes) {\n sum += val;\n }\n\n if (withConstructor) {\n this.writeByte(CORE_TYPES.Checksum);\n }\n\n this.writeInt32(sum);\n this.offsetChecksum = this.offset;\n\n return this;\n }\n\n writeBytes(value: Uint8Array): this {\n const length = value.length;\n\n this.writeLength(length);\n this.allocate(length);\n this.target.set(value, this.offset);\n\n this.offset += length;\n\n return this;\n }\n\n writeLength(value: number): this {\n if (value < 254) {\n this.allocate(1);\n this.target[this.offset++] = value;\n } else {\n this.allocate(4);\n this.target[this.offset++] = 254;\n this.target[this.offset++] = value % 256;\n this.target[this.offset++] = (value >> 8) % 256;\n this.target[this.offset++] = (value >> 16) % 256;\n }\n\n return this;\n }\n\n writeVector(value: Array<any>): this {\n const length = value.length;\n this.writeLength(length);\n\n for (let i = 0; i < length; i++) {\n if (value[i] === undefined) {\n this.writeNull();\n } else {\n this.writeObject(value[i]);\n }\n }\n\n return this;\n }\n\n writeMap(object: Record<string, any>): this {\n for (const key in object) {\n if (object[key] === undefined) continue;\n\n this._last = noop;\n this.wireDictionary(key);\n this.writeObject(object[key]);\n }\n\n this.writeByte(CORE_TYPES.None);\n\n return this;\n }\n\n wireDictionary(value: string): this {\n let idx: number | null = null;\n\n idx = this.dictionary.getIndex(value);\n\n if (idx === null) {\n idx = this.dictionaryExtended.getIndex(value);\n }\n\n if (idx === null) {\n this.dictionaryExtended.maybeInsert(value);\n this.writeCore(CORE_TYPES.DictValue, value);\n } else {\n this.writeCore(CORE_TYPES.DictIndex, idx);\n }\n\n return this;\n }\n\n writeStructure(value: Structure): this {\n const ctor = value.constructor as typeof Structure;\n\n this.writeInt32(ctor.extension.token, false);\n ctor.extension.encode.call(this, value.value);\n\n return this;\n }\n\n writeGzip(value: Uint8Array | ArrayBuffer): this {\n const compressed = pako.deflateRaw(value, { level: 9 });\n this.writeBytes(compressed);\n return this;\n }\n\n encode(value: any): Uint8Array {\n this.offset = 0;\n this.offsetChecksum = 0;\n this._last = noop;\n this._repeat = undefined;\n this.target = byteArrayAllocate(256);\n\n this.writeObject(value);\n\n return this.getBuffer();\n }\n\n startDynamicVector(): this {\n this.writeByte(CORE_TYPES.VectorDynamic);\n return this;\n }\n\n endDynamicVector(): this {\n this.writeByte(CORE_TYPES.None);\n return this;\n }\n\n private _writeCustom(value: any): boolean {\n const start = this.offset;\n\n this.allocate(1);\n\n this.offset++;\n\n let edgeExt;\n\n for (const ext of this.extensions.values()) {\n if (ext.token === -1) {\n edgeExt = ext;\n continue;\n }\n\n ext.encode.call(this, value);\n\n const processed = start + 1 < this.offset;\n\n if (processed) {\n const end = this.offset;\n this.offset = start;\n this.writeByte(ext.token);\n this.offset = end;\n\n return true;\n }\n }\n\n this.offset = start;\n\n if (edgeExt) {\n edgeExt.encode.call(this, value);\n return start < this.offset;\n }\n\n return false;\n }\n\n writeObject(value: any): this {\n if (value === undefined) return this;\n\n const constructorId = coreType(value);\n\n // console.log('write', {\n // \toffset: this.offset,\n // \tconstructorId: CORE_TYPES[constructorId],\n // \tvalue: String(value),\n // });\n\n if (constructorId === CORE_TYPES.None) {\n if (this._writeCustom(value)) {\n return this;\n }\n\n throw new TypeError(`Invalid core type of ${value}`);\n }\n\n if (this._last === value) {\n this.writeRepeat();\n } else {\n this._last = value;\n this._repeat = undefined;\n this.writeCore(constructorId, value);\n }\n\n return this;\n }\n\n writeObjectGzip(value: any): this {\n const writer = new BinaryWriter();\n\n writer.extensions = this.extensions;\n writer.dictionary = this.dictionary;\n writer.structures = this.structures;\n writer.dictionaryExtended = this.dictionaryExtended;\n\n writer.writeObject(value);\n this.writeCore(CORE_TYPES.GZIP, writer.getBuffer());\n\n return this;\n }\n\n private writeCore(constructorId: CORE_TYPES, value: any): this {\n if (this.withGzip && SUPPORT_COMPRESSION.has(constructorId)) {\n this.writeObjectGzip(value);\n return this;\n } else if (!NO_CONSTRUCTOR.has(constructorId)) {\n this.writeByte(constructorId);\n }\n\n switch (constructorId) {\n case CORE_TYPES.Structure: {\n return this.writeStructure(value);\n }\n\n case CORE_TYPES.Binary: {\n return this.writeBytes(value);\n }\n\n case CORE_TYPES.GZIP: {\n return this.writeGzip(value);\n }\n\n case CORE_TYPES.DictIndex: {\n return this.writeLength(value);\n }\n\n case CORE_TYPES.DictValue: {\n return this.writeString(value);\n }\n\n case CORE_TYPES.BoolFalse: {\n return this.writeBool(value);\n }\n\n case CORE_TYPES.BoolTrue: {\n return this.writeBool(value);\n }\n\n case CORE_TYPES.Date: {\n return this.writeDate(value);\n }\n\n case CORE_TYPES.Int64: {\n return this.writeInt64(value);\n }\n\n case CORE_TYPES.Int32: {\n return this.writeInt32(value);\n }\n\n case CORE_TYPES.Int16: {\n return this.writeInt16(value);\n }\n\n case CORE_TYPES.Int8: {\n return this.writeInt8(value);\n }\n\n case CORE_TYPES.UInt64: {\n return this.writeInt64(value, false);\n }\n\n case CORE_TYPES.UInt32: {\n return this.writeInt32(value, false);\n }\n\n case CORE_TYPES.UInt16: {\n return this.writeInt16(value, false);\n }\n\n case CORE_TYPES.UInt8: {\n return this.writeInt8(value, false);\n }\n\n case CORE_TYPES.Double: {\n return this.writeDouble(value);\n }\n\n case CORE_TYPES.Float: {\n return this.writeFloat(value);\n }\n\n case CORE_TYPES.Null: {\n return this.writeNull();\n }\n\n case CORE_TYPES.String: {\n // write short strings into dictionary\n if (value.length <= 0x10) {\n this.offset--;\n return this.wireDictionary(value);\n }\n\n return this.writeString(value);\n }\n\n case CORE_TYPES.Vector: {\n return this.writeVector(value);\n }\n\n case CORE_TYPES.Map: {\n return this.writeMap(value);\n }\n }\n\n return this;\n }\n\n private writeRepeat(): this {\n if (!this._repeat) {\n this.writeByte(CORE_TYPES.Repeat);\n this._repeat = { count: 0, offset: this.offset };\n }\n\n this.offset = this._repeat.offset;\n this._repeat.count++;\n\n this.writeLength(this._repeat.count);\n return this;\n }\n}\n","import { type Data, assert, crc32, noop } from '@andrew_l/toolkit';\nimport { BinaryReader, type BinaryReaderOptions } from './BinaryReader';\nimport { BinaryWriter, type BinaryWriterOptions } from './BinaryWriter';\nimport { CORE_TYPES } from './constants';\nimport type { DecodeHandler, EncodeHandler, TLExtension } from './extension';\n\ninterface CompiledStructure {\n id: number;\n encodeFns: EncodeFn[];\n decodeFns: DecodeFn[];\n structures: Structure.Constructor[];\n estimatedSizeBytes: number;\n}\n\ntype EncodeFn = (this: BinaryWriter, value: any) => void;\ntype DecodeFn = (this: BinaryReader, result: any) => void;\n\ntype TypeHandler = {\n encode: (this: BinaryWriter, value: any, key: string) => void;\n decode: (this: BinaryReader, result: any, key: string) => void;\n estimatedSizeBytes: number;\n};\n\nexport interface DefineStructureOptions<\n Props extends Structure.ObjectPropsOptions,\n> {\n /**\n * Unique name of binary structure\n */\n readonly name: string;\n\n /**\n * Version of binary structure\n */\n readonly version: number;\n\n /**\n * Binary structure properties\n */\n readonly properties: Props;\n\n /**\n * Write checksum to verify decoded data\n * @default false\n */\n readonly checksum?: boolean;\n}\n\nconst CONSTRUCTOR_OPTIONAL = 0x62016eac;\nconst CONSTRUCTOR_OPTIONAL_NULL = 0x22016eac;\n\nconst TYPE_HANDLERS: Record<string | number, TypeHandler> = {\n ['unknown']: {\n encode: function (this: BinaryWriter, value: any, key: string): void {\n this.writeObject(value[key]);\n },\n decode: function (this: BinaryReader, result: any, key: string): void {\n result[key] = this.readObject();\n },\n estimatedSizeBytes: 0,\n },\n [CORE_TYPES.Map]: {\n encode: function (this: BinaryWriter, value: any, key: string): void {\n this.writeMap(value[key]);\n },\n decode: function (this: BinaryReader, result: any, key: string): void {\n result[key] = this.readMap(false);\n },\n estimatedSizeBytes: 0,\n },\n [CORE_TYPES.Binary]: {\n encode: function (this: BinaryWriter, value: any, key: string): void {\n this.writeBytes(value[key]);\n },\n decode: function (this: BinaryReader, result: any, key: string): void {\n result[key] = this.readBytes();\n },\n estimatedSizeBytes: 0,\n },\n [CORE_TYPES.Vector]: {\n encode: function (this: BinaryWriter, value: any, key: string): void {\n this.writeVector(value[key]);\n },\n decode: function (this: BinaryReader, result: any, key: string): void {\n result[key] = this.readVector(false);\n },\n estimatedSizeBytes: 0,\n },\n [Boolean.name]: {\n encode: function (this: BinaryWriter, value: any, key: string): void {\n this.writeBool(value[key]);\n },\n decode: function (this: BinaryReader, result: any, key: string): void {\n result[key] = this.readBool();\n },\n estimatedSizeBytes: 1,\n },\n [CORE_TYPES.Int8]: {\n encode: function (this: BinaryWriter, value: any, key: string): void {\n this.writeInt8(value[key], true);\n },\n decode: function (this: BinaryReader, result: any, key: string): void {\n result[key] = this.readInt8(true);\n },\n estimatedSizeBytes: 1,\n },\n [CORE_TYPES.Int16]: {\n encode: function (this: BinaryWriter, value: any, key: string): void {\n this.writeInt16(value[key], true);\n },\n decode: function (this: BinaryReader, result: any, key: string): void {\n result[key] = this.readInt16(true);\n },\n estimatedSizeBytes: 2,\n },\n [CORE_TYPES.Int32]: {\n encode: function (this: BinaryWriter, value: any, key: string): void {\n this.writeInt32(value[key], true);\n },\n decode: function (this: BinaryReader, result: any, key: string): void {\n result[key] = this.readInt32(true);\n },\n estimatedSizeBytes: 4,\n },\n [CORE_TYPES.Int64]: {\n encode: function (this: BinaryWriter, value: any, key: string): void {\n this.writeInt64(value[key], true);\n },\n decode: function (this: BinaryReader, result: any, key: string): void {\n result[key] = this.readInt64(true);\n },\n estimatedSizeBytes: 8,\n },\n [CORE_TYPES.UInt8]: {\n encode: function (this: BinaryWriter, value: any, key: string): void {\n this.writeInt8(value[key], false);\n },\n decode: function (this: BinaryReader, result: any, key: string): void {\n result[key] = this.readInt8(false);\n },\n estimatedSizeBytes: 1,\n },\n [CORE_TYPES.UInt16]: {\n encode: function (this: BinaryWriter, value: any, key: string): void {\n this.writeInt16(value[key], false);\n },\n decode: function (this: BinaryReader, result: any, key: string): void {\n result[key] = this.readInt16(false);\n },\n estimatedSizeBytes: 2,\n },\n [CORE_TYPES.UInt32]: {\n encode: function (this: BinaryWriter, value: any, key: string): void {\n this.writeInt32(value[key], false);\n },\n decode: function (this: BinaryReader, result: any, key: string): void {\n result[key] = this.readInt32(false);\n },\n estimatedSizeBytes: 4,\n },\n [CORE_TYPES.UInt64]: {\n encode: function (this: BinaryWriter, value: any, key: string): void {\n this.writeInt64(value[key], false);\n },\n decode: function (this: BinaryReader, result: any, key: string): void {\n result[key] = this.readInt64(false);\n },\n estimatedSizeBytes: 8,\n },\n [CORE_TYPES.Double]: {\n encode: function (this: BinaryWriter, value: any, key: string): void {\n this.writeDouble(value[key]);\n },\n decode: function (this: BinaryReader, result: any, key: string): void {\n result[key] = this.readDouble();\n },\n estimatedSizeBytes: 8,\n },\n [CORE_TYPES.Date]: {\n encode: function (this: BinaryWriter, value: any, key: string): void {\n this.writeDate(value[key]);\n },\n decode: function (this: BinaryReader, result: any, key: string): void {\n result[key] = this.readDate();\n },\n estimatedSizeBytes: 8,\n },\n [CORE_TYPES.String]: {\n encode: function (this: BinaryWriter, value: any, key: string): void {\n this.writeString(value[key]);\n },\n decode: function (this: BinaryReader, result: any, key: string): void {\n result[key] = this.readString();\n },\n estimatedSizeBytes: 0,\n },\n};\n\nTYPE_HANDLERS[Number.name] = TYPE_HANDLERS[CORE_TYPES.Double];\nTYPE_HANDLERS[String.name] = TYPE_HANDLERS[CORE_TYPES.String];\nTYPE_HANDLERS[Object.name] = TYPE_HANDLERS[CORE_TYPES.Map];\nTYPE_HANDLERS[Uint8Array.name] = TYPE_HANDLERS[CORE_TYPES.Binary];\nTYPE_HANDLERS[Array.name] = TYPE_HANDLERS[CORE_TYPES.Vector];\nTYPE_HANDLERS[Date.name] = TYPE_HANDLERS[CORE_TYPES.Date];\n\nfunction compileStructure(\n name: string,\n version: number,\n properties: Structure.ObjectPropsOptions,\n checksum: boolean,\n): CompiledStructure {\n const encodeFns: EncodeFn[] = [];\n const decodeFns: DecodeFn[] = [];\n const structures: Structure.Constructor[] = [];\n const structureId = crc32(name) >>> 0;\n\n let estimatedSizeBytes = 1;\n\n // Version handling\n encodeFns.push(function (this: BinaryWriter): void {\n this.writeByte(version);\n });\n\n decodeFns.push(function (this: BinaryReader): void {\n const ver = this.readByte();\n assert.ok(\n version === ver,\n `Structure ${structureId} version mismatch: expected ${version}, got ${ver}`,\n );\n });\n\n // Property handling - optimized loop\n const entries = Object.entries(properties);\n const entriesLength = entries.length;\n\n for (let i = 0; i < entriesLength; i++) {\n const [key, prop] = entries[i];\n const isRequired = prop.required === true;\n const isArray = Array.isArray(prop.type);\n const propType = Array.isArray(prop.type) ? prop.type[0] : prop.type;\n\n if (isStructureType(propType)) {\n encodeFns.push(\n createStructureEncoder(key, propType, isRequired, isArray),\n );\n decodeFns.push(createStructureDecoder(key, isRequired, isArray));\n estimatedSizeBytes += propType.estimatedSizeBytes;\n structures.push(propType);\n continue;\n }\n\n // Handle primitive types\n const typeName =\n (propType as Function)?.name ||\n (propType === null ? 'unknown' : propType);\n const handler = TYPE_HANDLERS[typeName as keyof typeof TYPE_HANDLERS];\n\n if (handler) {\n encodeFns.push(\n createBoundEncoder(handler.encode, key, isRequired, isArray),\n );\n decodeFns.push(\n createBoundDecoder(handler.decode, key, isRequired, isArray),\n );\n estimatedSizeBytes += handler.estimatedSizeBytes;\n } else {\n throw new Error(`Unsupported property type: ${typeName || 'unknown'}`);\n }\n }\n\n if (checksum) {\n estimatedSizeBytes += 4;\n\n encodeFns.push(function (this: BinaryWriter): void {\n this.writeChecksum(false);\n });\n\n decodeFns.push(function (this: BinaryReader): void {\n this.readChecksum(false);\n });\n }\n\n const compiled: CompiledStructure = {\n id: structureId,\n encodeFns,\n decodeFns,\n structures,\n estimatedSizeBytes,\n };\n\n return compiled;\n}\n\nfunction isStructureType(type: any): type is Structure.Constructor {\n return type?.prototype && Structure.prototype.isPrototypeOf(type.prototype);\n}\n\nfunction createStructureEncoder(\n key: string,\n StructureCtor: Structure.Constructor,\n isRequired: boolean,\n isArray: boolean,\n): EncodeFn {\n return function (this: BinaryWriter, value: any): void {\n const hasValue = value[key] !== undefined && value[key] !== null;\n\n if (!hasValue) {\n assert.ok(!isRequired, `Required property \"${key}\" is missing or null`);\n\n if (value[key] === null) {\n this.writeInt32(CONSTRUCTOR_OPTIONAL_NULL);\n } else {\n this.writeInt32(CONSTRUCTOR_OPTIONAL);\n }\n } else if (isArray) {\n const arr = value[key];\n\n assert.array(arr, `Expected property \"${key}\" to be array.`);\n\n this.writeLength(arr.length);\n\n for (let idx = 0; idx < arr.length; idx++) {\n this.writeStructure(\n (arr as any[])[idx] instanceof Structure\n ? (arr as any[])[idx]\n : new StructureCtor(arr[idx]),\n );\n }\n } else {\n this.writeStructure(\n value[key] instanceof Structure\n ? value[key]\n : new StructureCtor(value[key]),\n );\n }\n };\n}\n\nfunction createStructureDecoder(\n key: string,\n isRequired: boolean,\n isArray: boolean,\n): DecodeFn {\n return function (this: BinaryReader, result: any): void {\n let shouldRead = true;\n\n if (!isRequired) {\n shouldRead = !readMaybeInt32(this, CONSTRUCTOR_OPTIONAL);\n\n if (shouldRead && readMaybeInt32(this, CONSTRUCTOR_OPTIONAL_NULL)) {\n result[key] = null;\n return;\n }\n }\n\n if (shouldRead) {\n if (isArray) {\n const length = this.readLength();\n const arrResult = Array.from({ length });\n\n for (let idx = 0; idx < length; idx++) {\n arrResult[idx] = this.readStructure(false);\n }\n\n result[key] = arrResult;\n } else {\n result[key] = this.readStructure(false);\n }\n }\n };\n}\n\nfunction readMaybeInt32(reader: BinaryReader, expectedValue: number): boolean {\n let result = false;\n\n if (reader.length >= reader.offset + 4) {\n result = reader.readInt32() === expectedValue;\n\n // Make offset back when null constructor not detected\n if (!result) {\n reader.offset -= 4;\n }\n }\n\n return result;\n}\n\nfunction createBoundEncoder(\n encodeFn: Function,\n key: string,\n isRequired: boolean,\n isArray: boolean,\n): EncodeFn {\n return function (this: BinaryWriter, value: any): void {\n const hasValue = value[key] !== undefined && value[key] !== null;\n\n if (!hasValue) {\n assert.ok(!isRequired, `Required property \"${key}\" is missing or null`);\n\n if (value[key] === null) {\n this.writeInt32(CONSTRUCTOR_OPTIONAL_NULL);\n } else {\n this.writeInt32(CONSTRUCTOR_OPTIONAL);\n }\n } else if (isArray) {\n const arr = value[key];\n\n assert.array(arr, `Expected property \"${key}\" to be array.`);\n\n this.writeLength(arr.length);\n\n for (let idx = 0; idx < arr.length; idx++) {\n encodeFn.call(this, arr, idx);\n }\n } else {\n encodeFn.call(this, value, key);\n }\n };\n}\n\nfunction createBoundDecoder(\n decodeFn: Function,\n key: string,\n isRequired: boolean,\n isArray: boolean,\n): DecodeFn {\n return function (this: BinaryReader, result: any): void {\n let shouldRead = true;\n\n if (!isRequired) {\n shouldRead = !readMaybeInt32(this, CONSTRUCTOR_OPTIONAL);\n\n if (shouldRead && readMaybeInt32(this, CONSTRUCTOR_OPTIONAL_NULL)) {\n result[key] = null;\n return;\n }\n }\n\n if (shouldRead) {\n if (isArray) {\n const length = this.readLength();\n const arrResult = Array.from({ length });\n\n for (let idx = 0; idx < length; idx++) {\n decodeFn.call(this, arrResult, idx);\n }\n\n result[key] = arrResult;\n } else {\n decodeFn.call(this, result, key);\n }\n }\n };\n}\n\n/**\n * Create binary structure definition with type safety and performance optimization\n *\n * @example\n * // Define a user structure\n * const User = defineStructure({\n * name: 'User',\n * version: 1,\n * checksum: true,\n * properties: {\n * id: { type: Number, required: true },\n * name: { type: String, required: true },\n * email: { type: String, required: false },\n * isActive: { type: Boolean, required: true },\n * createdAt: { type: Date, required: true },\n * tags: { type: Array, required: false }\n * }\n * });\n *\n * // Create and serialize a user\n * const user = new User({\n * id: 123,\n * name: 'John Doe',\n * email: 'john@example.com',\n * isActive: true,\n * createdAt: new Date(),\n * tags: ['admin', 'verified']\n * });\n *\n * const buffer = user.toBuffer();\n * const restored = User.fromBuffer(buffer);\n *\n * @example\n * // Define nested structures\n * const Address = defineStructure({\n * name: 'Address',\n * version: 1,\n * properties: {\n * street: { type: String, required: true },\n * city: { type: String, required: true },\n * zipCode: { type: String, required: true }\n * }\n * });\n *\n * const Person = defineStructure({\n * name: 'Person',\n * version: 1,\n * properties: {\n * name: { type: String, required: true },\n * address: { type: Address, required: false }\n * }\n * });\n *\n * @example\n * // Using with complex data types\n * const GameState = defineStructure({\n * name: 'GameState',\n * version: 2,\n * checksum: true,\n * properties: {\n * playerData: { type: Object, required: true }, // Untrusted data\n * screenshot: { type: Uint8Array, required: false }, // Binary data\n * timestamp: { type: Date, required: true },\n * metadata: { type: Object, required: false }\n * }\n * });\n *\n * @group Main\n */\nexport function defineStructure<\n PropsOptions extends Structure.ObjectPropsOptions,\n T extends Data = Structure.ExtractPropTypes<PropsOptions>,\n>({\n name,\n properties,\n version,\n checksum = false,\n}: DefineStructureOptions<PropsOptions>): Structure.Constructor<T> {\n const compiled = compileStructure(name, version, properties, checksum);\n\n return class DefinedStructure extends Structure<T> {\n static readonly estimatedSizeBytes = compiled.estimatedSizeBytes;\n static readonly structures = compiled.structures;\n static readonly extension: TLExtension<T> = {\n token: compiled.id,\n\n encode(this: BinaryWriter, value: T): void {\n const fns = compiled.encodeFns;\n const length = fns.length;\n\n for (let i = 0; i < length; i++) {\n fns[i].call(this, value);\n }\n },\n\n decode(this: BinaryReader): T {\n const result: Record<string, any> = {};\n const fns = compiled.decodeFns;\n const length = fns.length;\n\n for (let i = 0; i < length; i++) {\n fns[i].call(this, result);\n }\n\n return result as T;\n },\n };\n } as Structure.Constructor<T>;\n}\n\nexport class Structure<T extends Data = Data>\n implements Structure.Structure<T>\n{\n public readonly value: T;\n\n constructor(value: T) {\n this.value = value;\n }\n\n toBuffer(options?: BinaryWriterOptions): Uint8Array {\n const ctor = this.constructor as Structure.Constructor;\n const writer = new BinaryWriter(options);\n\n ctor.extension.encode.call(writer, this.value);\n return writer.getBuffer();\n }\n\n static fromBuffer(\n buffer: Uint8Array,\n options: BinaryReaderOptions = {},\n ): Data {\n const reader = new BinaryReader(buffer, {\n ...options,\n structures: options.structures\n ? this.structures.concat(options.structures)\n : this.structures,\n });\n\n return this.extension.decode.call(reader);\n }\n\n static readonly estimatedSizeBytes: number = -1;\n\n static readonly structures: Structure.Constructor[] = [];\n\n static readonly extension: TLExtension = {\n token: CORE_TYPES.Binary,\n encode: noop,\n decode: noop,\n };\n}\n\nexport namespace Structure {\n export interface Options<T extends Data = Data> {\n readonly encode: EncodeHandler<T>;\n readonly decode: DecodeHandler<T>;\n }\n\n export interface Structure<T extends Data = Data> {\n /**\n * The structured data value\n * @type {T}\n * @readonly\n *\n * @example\n * const user = new UserStruct({ id: 1, name: 'Alice' });\n * console.log(user.value.name); // 'Alice'\n */\n readonly value: T;\n\n /**\n * Serializes the structure to a binary buffer\n *\n * @param {BinaryWriterOptions} [options] - Writer configuration options\n * @returns {Uint8Array} Serialized binary data\n *\n * @example\n * const user = new User({ id: 1, name: 'Alice' });\n * const buffer = user.toBuffer();\n * console.log(buffer.length); // Size in bytes\n *\n * @example\n * // With custom writer options\n * const buffer = user.toBuffer({\n * initialSize: 1024,\n * growthFactor: 2\n * });\n */\n toBuffer(options?: BinaryWriterOptions): Uint8Array;\n }\n\n /**\n * Base Structure class for binary serialization with type safety\n *\n * @template T - Data type extending Data interface\n *\n * @example\n * // Direct usage (not recommended, use defineStructure instead)\n * class CustomStruct extends Structure<{id: number, name: string}> {\n * // Custom implementation\n * }\n *\n * @example\n * // Typical usage through defineStructure\n * const MyStruct = defineStructure({\n * name: 'MyStruct',\n * version: 1,\n * properties: { id: { type: Number, required: true } }\n * });\n *\n * const instance = new MyStruct({ id: 42 });\n * console.log(instance.value.id); // 42\n */\n export interface Constructor<T extends Data = any> {\n /**\n * Creates a new Structure instance\n *\n * @param {T} value - The data to structure\n *\n * @example\n * const data = { id: 123, name: 'Test' };\n * const struct = new MyStructure(data);\n */\n new (value: T): Structure<T>;\n\n /**\n * Deserializes a structure from a binary buffer\n *\n * @param {Uint8Array} buffer - Binary data to deserialize\n * @param {BinaryReaderOptions} [options] - Reader configuration options\n * @returns {Data} Deserialized structure data\n *\n * @example\n * const buffer = user.toBuffer();\n * const restored = User.fromBuffer(buffer);\n * console.log(restored.id); // Original user ID\n */\n fromBuffer(buffer: Uint8Array, options?: BinaryReaderOptions): T;\n\n /**\n * Estimated size of bytes\n */\n readonly estimatedSizeBytes: number;\n\n /**\n * Nested structures defining encoding/decoding behavior\n */\n readonly structures: Constructor[];\n\n /**\n * Structure extension defining encoding/decoding behavior\n */\n readonly extension: TLExtension;\n }\n\n type PropConstructor<T = any> =\n | { new (...args: any[]): T & {} }\n | { (): T }\n | PropMethod<T>;\n\n type PropMethod<T, TConstructor = any> = [T] extends [\n ((...args: any) => any) | undefined,\n ] // if is function with args, allowing non-required functions\n ? { new (): TConstructor; (): T; readonly prototype: TConstructor } // Create Function like constructor\n : never;\n\n // Fixed OptionalKeys to properly detect optional properties\n type OptionalKeys<T> = Exclude<keyof T, RequiredKeys<T>>;\n\n type RequiredKeys<T> = {\n [K in keyof T]: T[K] extends { required: true } ? K : never;\n }[keyof T];\n\n type Prop<T> = PropOptions<T> | PropType<T>;\n\n type CoreInt =\n | CORE_TYPES.Int8\n | CORE_TYPES.Int16\n | CORE_TYPES.Int32\n | CORE_TYPES.UInt8\n | CORE_TYPES.UInt16\n | CORE_TYPES.UInt32\n | CORE_TYPES.Float\n | CORE_TYPES.Double;\n\n type InferPropType<T, NullAsAny = true> = [T] extends [null]\n ? NullAsAny extends true\n ? any\n : null\n : [T] extends [{ type: null }]\n ? any // As TS issue https://github.com/Microsoft/TypeScript/issues/14829 // somehow `ObjectConstructor` when inferred from { (): T } becomes `any` // `BooleanConstructor` when inferred from PropConstructor(with PropMethod) becomes `Boolean`\n : [T] extends [{ type: [null] }]\n ? any[]\n : [T] extends [{ type: ObjectConstructor | CORE_TYPES.Map }]\n ? Record<string, any>\n : [T] extends [{ type: CoreInt }]\n ? number\n : [T] extends [{ type: CORE_TYPES.String }]\n ? string\n : [T] extends [{ type: CORE_TYPES.Vector }]\n ? unknown[]\n : [T] extends [{ type: CORE_TYPES.Binary }]\n ? Uint8Array\n : [T] extends [{ type: BooleanConstructor }]\n ? boolean\n : [T] extends [{ type: DateConstructor | CORE_TYPES.Date }]\n ? Date\n : [T] extends [\n {\n type: CORE_TYPES.UInt64 | CORE_TYPES.Int64;\n },\n ]\n ? bigint\n : [T] extends [{ type: Constructor<infer U> }]\n ? U\n : [T] extends [{ type: [infer U] }]\n ? U extends DateConstructor\n ? Date[]\n : U extends Constructor<infer V>\n ? V[]\n : InferPropType<U, false>[]\n : [T] extends [{ type: (infer U)[] }]\n ? U extends DateConstructor\n ? Date | InferPropType<U, false>\n : InferPropType<U, false>\n : [T] extends [Prop<infer V>]\n ? V\n : T;\n\n export type ObjectPropsOptions<P = Data> = {\n readonly [K in keyof P]: PropOptions<P[K]>;\n };\n\n export interface PropOptions<T = any> {\n type: PropType<T> | null | [null];\n required?: boolean;\n }\n\n export type PropType<T> =\n | PropConstructor<T>\n | [PropConstructor<T>]\n | CORE_TYPES\n | [CORE_TYPES];\n\n export type ExtractType<T extends Constructor> =\n T extends Constructor<infer U> ? U : never;\n\n export type ExtractPropTypes<O> = {\n // use `keyof Pick<O, RequiredKeys<O>>` instead of `RequiredKeys<O>` to\n // support IDE features\n [K in keyof Pick<O, RequiredKeys<O>>]: O[K] extends { default: any }\n ? Exclude<InferPropType<O[K]>, undefined>\n : InferPropType<O[K]>;\n } & {\n // use `keyof Pick<O, OptionalKeys<O>>` instead of `OptionalKeys<O>` to\n // support IDE features\n [K in keyof Pick<O, OptionalKeys<O>>]?: InferPropType<O[K]> | null;\n };\n}\n","import { isPlainObject, textDecoder, textEncoder } from '@andrew_l/toolkit';\nimport { Structure } from './Structure';\nimport { CORE_TYPES } from './constants';\n\nconst fromCharCode = String.fromCharCode;\n\nexport const int32 = new Int32Array(2);\nexport const float32 = new Float32Array(int32.buffer);\nexport const float64 = new Float64Array(int32.buffer);\n\nexport function byteArrayAllocate(length: number): Uint8Array {\n return new Uint8Array(length);\n}\n\nexport function coreType(value: any): CORE_TYPES {\n if (value instanceof Structure) {\n return CORE_TYPES.Structure;\n } else if (value instanceof Uint8Array) {\n return CORE_TYPES.Binary;\n }\n\n switch (typeof value) {\n case 'string': {\n return CORE_TYPES.String;\n }\n\n case 'boolean': {\n return value ? CORE_TYPES.BoolTrue : CORE_TYPES.BoolFalse;\n }\n\n case 'bigint': {\n if (value >= 0n && value <= 0xffffffffffffffffn) {\n return CORE_TYPES.UInt64;\n } else if (\n value >= -0x8000000000000000n &&\n value <= 0x7fffffffffffffffn\n ) {\n return CORE_TYPES.Int64;\n }\n\n return CORE_TYPES.None;\n }\n\n case 'number': {\n if (Math.trunc(value) === value) {\n if (value >= 0 && value <= 0xff) {\n return CORE_TYPES.UInt8;\n } else if (value >= 0 && value <= 0xffff) {\n return CORE_TYPES.UInt16;\n } else if (value >= 0 && value <= 0xffffffff) {\n return CORE_TYPES.UInt32;\n } else if (value >= -0x80 && value <= 0x7f) {\n return CORE_TYPES.Int8;\n } else if (value >= -0x8000 && value <= 0x7fff) {\n return CORE_TYPES.Int16;\n } else if (value >= -0x80000000 && value <= 0x7fffffff) {\n return CORE_TYPES.Int32;\n }\n }\n\n return CORE_TYPES.Double;\n }\n\n case 'object': {\n if (value === null) return CORE_TYPES.Null;\n\n if (value instanceof Date) {\n return CORE_TYPES.Date;\n }\n\n if (Array.isArray(value)) {\n return CORE_TYPES.Vector;\n }\n\n if (isPlainObject(value)) {\n return CORE_TYPES.Map;\n }\n }\n }\n\n return CORE_TYPES.None;\n}\n\nexport function utf8Read(target: Uint8Array, length: number, offset: number) {\n let result;\n if (length < 16) {\n if ((result = utf8ReadShort(target, length, offset))) return result;\n }\n if (length > 64)\n return textDecoder.decode(target.subarray(offset, (offset += length)));\n const end = offset + length;\n const units = [];\n result = '';\n while (offset < end) {\n const byte1 = target[offset++];\n if ((byte1 & 0x80) === 0) {\n // 1 byte\n units.push(byte1);\n } else if ((byte1 & 0xe0) === 0xc0) {\n // 2 bytes\n const byte2 = target[offset++] & 0x3f;\n units.push(((byte1 & 0x1f) << 6) | byte2);\n } else if ((byte1 & 0xf0) === 0xe0) {\n // 3 bytes\n const byte2 = target[offset++] & 0x3f;\n const byte3 = target[offset++] & 0x3f;\n units.push(((byte1 & 0x1f) << 12) | (byte2 << 6) | byte3);\n } else if ((byte1 & 0xf8) === 0xf0) {\n // 4 bytes\n const byte2 = target[offset++] & 0x3f;\n const byte3 = target[offset++] & 0x3f;\n const byte4 = target[offset++] & 0x3f;\n let unit =\n ((byte1 & 0x07) << 0x12) | (byte2 << 0x0c) | (byte3 << 0x06) | byte4;\n if (unit > 0xffff) {\n unit -= 0x10000;\n units.push(((unit >>> 10) & 0x3ff) | 0xd800);\n unit = 0xdc00 | (unit & 0x3ff);\n }\n units.push(unit);\n } else {\n units.push(byte1);\n }\n\n if (units.length >= 0x1000) {\n result += fromCharCode.apply(String, units);\n units.length = 0;\n }\n }\n\n if (units.length > 0) {\n result += fromCharCode.apply(String, units);\n }\n\n return result;\n}\n\nexport function utf8ReadShort(\n target: Uint8Array,\n length: number,\n offset: number,\n) {\n if (length < 4) {\n if (length < 2) {\n if (length === 0) return '';\n else {\n let a = target[offset++];\n if ((a & 0x80) > 1) {\n offset -= 1;\n return;\n }\n return fromCharCode(a);\n }\n } else {\n let a = target[offset++];\n let b = target[offset++];\n if ((a & 0x80) > 0 || (b & 0x80) > 0) {\n offset -= 2;\n return;\n }\n if (length < 3) return fromCharCode(a, b);\n let c = target[offset++];\n if ((c & 0x80) > 0) {\n offset -= 3;\n return;\n }\n return fromCharCode(a, b, c);\n }\n } else {\n let a = target[offset++];\n let b = target[offset++];\n let c = target[offset++];\n let d = target[offset++];\n if ((a & 0x80) > 0 || (b & 0x80) > 0 || (c & 0x80) > 0 || (d & 0x80) > 0) {\n offset -= 4;\n return;\n }\n if (length < 6) {\n if (length === 4) return fromCharCode(a, b, c, d);\n else {\n let e = target[offset++];\n if ((e & 0x80) > 0) {\n offset -= 5;\n return;\n }\n return fromCharCode(a, b, c, d, e);\n }\n } else if (length < 8) {\n let e = target[offset++];\n let f = target[offset++];\n if ((e & 0x80) > 0 || (f & 0x80) > 0) {\n offset -= 6;\n return;\n }\n if (length < 7) return fromCharCode(a, b, c, d, e, f);\n let g = target[offset++];\n if ((g & 0x80) > 0) {\n offset -= 7;\n return;\n }\n return fromCharCode(a, b, c, d, e, f, g);\n } else {\n let e = target[offset++];\n let f = target[offset++];\n let g = target[offset++];\n let h = target[offset++];\n if (\n (e & 0x80) > 0 ||\n (f & 0x80) > 0 ||\n (g & 0x80) > 0 ||\n (h & 0x80) > 0\n ) {\n offset -= 8;\n return;\n }\n if (length < 10) {\n if (length === 8) return fromCharCode(a, b, c, d, e, f, g, h);\n else {\n let i = target[offset++];\n if ((i & 0x80) > 0) {\n offset -= 9;\n return;\n }\n return fromCharCode(a, b, c, d, e, f, g, h, i);\n }\n } else if (length < 12) {\n let i = target[offset++];\n let j = target[offset++];\n if ((i & 0x80) > 0 || (j & 0x80) > 0) {\n offset -= 10;\n return;\n }\n if (length < 11) return fromCharCode(a, b, c, d, e, f, g, h, i, j);\n let k = target[offset++];\n if ((k & 0x80) > 0) {\n offset -= 11;\n return;\n }\n return fromCharCode(a, b, c, d, e, f, g, h, i, j, k);\n } else {\n let i = target[offset++];\n let j = target[offset++];\n let k = target[offset++];\n let l = target[offset++];\n if (\n (i & 0x80) > 0 ||\n (j & 0x80) > 0 ||\n (k & 0x80) > 0 ||\n (l & 0x80) > 0\n ) {\n offset -= 12;\n return;\n }\n if (length < 14) {\n if (length === 12)\n return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l);\n else {\n let m = target[offset++];\n if ((m & 0x80) > 0) {\n offset -= 13;\n return;\n }\n return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l, m);\n }\n } else {\n let m = target[offset++];\n let n = target[offset++];\n if ((m & 0x80) > 0 || (n & 0x80) > 0) {\n offset -= 14;\n return;\n }\n if (length < 15)\n return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l, m, n);\n let o = target[offset++];\n if ((o & 0x80) > 0) {\n offset -= 15;\n return;\n }\n return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o);\n }\n }\n }\n }\n}\n\nexport const utf8Write = function (target: any, value: string, offset: number) {\n return value.length < 0x40\n ? utf8WriteShort(target, value, offset)\n : textEncoder.encodeInto(value, target.subarray(offset)).written;\n};\n\nexport const utf8WriteShort = (target: any, value: string, offset: number) => {\n let i,\n c1,\n c2,\n strPosition = offset;\n\n const strLength = value.length;\n\n for (i = 0; i < strLength; i++) {\n c1 = value.charCodeAt(i);\n if (c1 < 0x80) {\n target[strPosition++] = c1;\n } else if (c1 < 0x800) {\n target[strPosition++] = (c1 >> 6) | 0xc0;\n target[strPosition++] = (c1 & 0x3f) | 0x80;\n } else if (\n (c1 & 0xfc00) === 0xd800 &&\n ((c2 = value.charCodeAt(i + 1)) & 0xfc00) === 0xdc00\n ) {\n c1 = 0x10000 + ((c1 & 0x03ff) << 10) + (c2 & 0x03ff);\n i++;\n target[strPosition++] = (c1 >> 18) | 0xf0;\n target[strPosition++] = ((c1 >> 12) & 0x3f) | 0x80;\n target[strPosition++] = ((c1 >> 6) & 0x3f) | 0x80;\n target[strPosition++] = (c1 & 0x3f) | 0x80;\n } else {\n target[strPosition++] = (c1 >> 12) | 0xe0;\n target[strPosition++] = ((c1 >> 6) & 0x3f) | 0x80;\n target[strPosition++] = (c1 & 0x3f) | 0x80;\n }\n }\n\n return strPosition - offset;\n};\n","import type { Data