UNPKG

@andrew_l/tl-pack

Version:

Another implementation of binary serialization.

1,664 lines (1,653 loc) 46.3 kB
'use strict'; const pako = require('pako'); const toolkit = require('@andrew_l/toolkit'); function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; } const pako__default = /*#__PURE__*/_interopDefaultCompat(pako); var CORE_TYPES = /* @__PURE__ */ ((CORE_TYPES2) => { CORE_TYPES2[CORE_TYPES2["None"] = 0] = "None"; CORE_TYPES2[CORE_TYPES2["Binary"] = 1] = "Binary"; CORE_TYPES2[CORE_TYPES2["BoolFalse"] = 2] = "BoolFalse"; CORE_TYPES2[CORE_TYPES2["BoolTrue"] = 3] = "BoolTrue"; CORE_TYPES2[CORE_TYPES2["Null"] = 4] = "Null"; CORE_TYPES2[CORE_TYPES2["Date"] = 5] = "Date"; CORE_TYPES2[CORE_TYPES2["Vector"] = 6] = "Vector"; CORE_TYPES2[CORE_TYPES2["VectorDynamic"] = 7] = "VectorDynamic"; CORE_TYPES2[CORE_TYPES2["Int64"] = 22] = "Int64"; CORE_TYPES2[CORE_TYPES2["Int32"] = 8] = "Int32"; CORE_TYPES2[CORE_TYPES2["Int16"] = 9] = "Int16"; CORE_TYPES2[CORE_TYPES2["Int8"] = 10] = "Int8"; CORE_TYPES2[CORE_TYPES2["UInt64"] = 23] = "UInt64"; CORE_TYPES2[CORE_TYPES2["UInt32"] = 11] = "UInt32"; CORE_TYPES2[CORE_TYPES2["UInt16"] = 12] = "UInt16"; CORE_TYPES2[CORE_TYPES2["UInt8"] = 13] = "UInt8"; CORE_TYPES2[CORE_TYPES2["Float"] = 14] = "Float"; CORE_TYPES2[CORE_TYPES2["Double"] = 15] = "Double"; CORE_TYPES2[CORE_TYPES2["Map"] = 16] = "Map"; CORE_TYPES2[CORE_TYPES2["DictValue"] = 17] = "DictValue"; CORE_TYPES2[CORE_TYPES2["DictIndex"] = 18] = "DictIndex"; CORE_TYPES2[CORE_TYPES2["String"] = 19] = "String"; CORE_TYPES2[CORE_TYPES2["Repeat"] = 20] = "Repeat"; CORE_TYPES2[CORE_TYPES2["Checksum"] = 21] = "Checksum"; CORE_TYPES2[CORE_TYPES2["GZIP"] = 25] = "GZIP"; CORE_TYPES2[CORE_TYPES2["Structure"] = 26] = "Structure"; return CORE_TYPES2; })(CORE_TYPES || {}); const MAX_BUFFER_SIZE = 2144337920; function createDictionary(values) { return new Dictionary(values); } class Dictionary { _count; _wordToIndex; _words; _offset; constructor(values, offset = 0) { this._count = 0; this._words = []; this._wordToIndex = /* @__PURE__ */ new Map(); this._offset = offset; if (Array.isArray(values) && values.length) { values.forEach((word) => { if (this._wordToIndex.has(word)) return; this._wordToIndex.set(word, this._count++); this._words.push(word); }); } } clear() { this._count = 0; this._words.length = 0; this._wordToIndex.clear(); } get size() { return this._count; } /** * Returns inserted index or nothing */ maybeInsert(word) { if (this._wordToIndex.has(word)) return null; this._wordToIndex.set(word, this._count++); this._words.push(word); return this._count + this._offset; } getValue(index) { return this._words[index - this._offset] ?? null; } getIndex(value) { const idx = this._wordToIndex.get(value); if (idx === void 0) { return null; } return idx + this._offset; } hasValue(value) { return this._wordToIndex.has(value); } hasIndex(index) { return this._words[index - this._offset] !== void 0; } } const noop = Symbol(); const NO_CONSTRUCTOR = /* @__PURE__ */ new Set([ CORE_TYPES.BoolFalse, CORE_TYPES.BoolTrue, CORE_TYPES.Null ]); const SUPPORT_COMPRESSION = /* @__PURE__ */ new Set([CORE_TYPES.String]); const NOOP_DICTIONARY$1 = new Dictionary(); class BinaryWriter { withGzip; target; dictionary; dictionaryExtended; extensions; structures; _last; offsetChecksum; _repeat; offset; constructor({ gzip = false, dictionary = NOOP_DICTIONARY$1, extensions, structures } = {}) { this.offset = 0; this.offsetChecksum = 0; this.extensions = /* @__PURE__ */ new Map(); this.structures = /* @__PURE__ */ new Map(); this.withGzip = gzip; this.target = byteArrayAllocate(8192); this._last = noop; if (extensions) { for (const ext of extensions) { this.extensions.set(ext.token, ext); } } if (structures) { for (const struct of structures) { this.structures.set(struct.extension.token, struct); } } if (Array.isArray(dictionary)) { this.dictionary = new Dictionary(dictionary); } else { this.dictionary = dictionary; } this.dictionaryExtended = new Dictionary(void 0, this.dictionary.size); } /** * Reset internal state */ reset() { this.offset = 0; this.offsetChecksum = 0; this._last = noop; this._repeat = void 0; this.dictionaryExtended.clear(); return this; } allocate(size) { const position = this.offset + size; if (this.safeEnd < position) { this.makeRoom(position); } return this; } makeRoom(end) { let start = 0; let newSize = 0; let target = this.target; if (end > 16777216) { if (end - start > MAX_BUFFER_SIZE) throw new Error( "Packed buffer would be larger than maximum buffer size" ); newSize = Math.min( MAX_BUFFER_SIZE, Math.round( Math.max((end - start) * (end > 67108864 ? 1.25 : 2), 4194304) / 4096 ) * 4096 ); } else { newSize = (Math.max(end - start << 2, target.length - 1) >> 12) + 1 << 12; } const newBuffer = byteArrayAllocate(newSize); end = Math.min(end, target.length); newBuffer.set(target.slice(start, end)); this.target = newBuffer; } get safeEnd() { return this.target.length - 10; } getBuffer() { return this.target.subarray(0, this.offset); } writeByte(value) { this.allocate(1); this.target[this.offset++] = value; return this; } writeBool(value) { if (value) { this.writeByte(CORE_TYPES.BoolTrue); } else { this.writeByte(CORE_TYPES.BoolFalse); } return this; } writeNull() { this.writeByte(CORE_TYPES.Null); return this; } writeInt64(value, signed = true) { this.allocate(8); if (typeof value === "number") value = BigInt(value); const low32 = Number(value & 0xffffffffn); const high32 = Number(value >> 32n); this.writeInt32(low32, signed); this.writeInt32(high32, signed); return this; } writeInt32(value, signed = true) { this.allocate(4); if (signed) { this.target[this.offset++] = value; this.target[this.offset++] = value >> 8; this.target[this.offset++] = value >> 16; this.target[this.offset++] = value >> 24; } else { this.target[this.offset++] = value; this.target[this.offset++] = value >> 8; this.target[this.offset++] = value >> 16; this.target[this.offset++] = value >> 24; } return this; } writeInt16(value, signed = true) { this.allocate(2); if (signed) { this.target[this.offset++] = value; this.target[this.offset++] = value >> 8; } else { this.target[this.offset++] = value; this.target[this.offset++] = value >> 8; } return this; } writeInt8(value, signed = true) { this.allocate(1); this.target[this.offset++] = value; return this; } writeFloat(value) { this.allocate(4); float32[0] = value; this.writeInt32(int32[0]); return this; } writeDouble(value) { this.allocate(8); float64[0] = value; this.writeInt32(int32[0], false); this.writeInt32(int32[1], false); return this; } writeDate(value) { let timestamp = 0; if (value instanceof Date) { timestamp = value.getTime(); } else if (typeof value === "number") { timestamp = value; } this.writeDouble(timestamp); return this; } writeString(value) { const strLength = value.length; let start = this.offset; let require = strLength << 2; if (require < 254) { require += 1; this.offset += 1; } else { require += 4; this.offset += 4; } this.allocate(require); const bytes = utf8Write(this.target, value, this.offset); if (require < 254) { this.target[start++] = bytes; } else { this.target[start++] = 254; this.target[start++] = bytes % 256; this.target[start++] = (bytes >> 8) % 256; this.target[start++] = (bytes >> 16) % 256; } this.offset += bytes; return this; } writeChecksum(withConstructor = true) { const bytes = this.target.subarray(this.offsetChecksum, this.offset); let sum = 0; for (const val of bytes) { sum += val; } if (withConstructor) { this.writeByte(CORE_TYPES.Checksum); } this.writeInt32(sum); this.offsetChecksum = this.offset; return this; } writeBytes(value) { const length = value.length; this.writeLength(length); this.allocate(length); this.target.set(value, this.offset); this.offset += length; return this; } writeLength(value) { if (value < 254) { this.allocate(1); this.target[this.offset++] = value; } else { this.allocate(4); this.target[this.offset++] = 254; this.target[this.offset++] = value % 256; this.target[this.offset++] = (value >> 8) % 256; this.target[this.offset++] = (value >> 16) % 256; } return this; } writeVector(value) { const length = value.length; this.writeLength(length); for (let i = 0; i < length; i++) { if (value[i] === void 0) { this.writeNull(); } else { this.writeObject(value[i]); } } return this; } writeMap(object) { for (const key in object) { if (object[key] === void 0) continue; this._last = noop; this.wireDictionary(key); this.writeObject(object[key]); } this.writeByte(CORE_TYPES.None); return this; } wireDictionary(value) { let idx = null; idx = this.dictionary.getIndex(value); if (idx === null) { idx = this.dictionaryExtended.getIndex(value); } if (idx === null) { this.dictionaryExtended.maybeInsert(value); this.writeCore(CORE_TYPES.DictValue, value); } else { this.writeCore(CORE_TYPES.DictIndex, idx); } return this; } writeStructure(value) { const ctor = value.constructor; this.writeInt32(ctor.extension.token, false); ctor.extension.encode.call(this, value.value); return this; } writeGzip(value) { const compressed = pako__default.deflateRaw(value, { level: 9 }); this.writeBytes(compressed); return this; } encode(value) { this.offset = 0; this.offsetChecksum = 0; this._last = noop; this._repeat = void 0; this.target = byteArrayAllocate(256); this.writeObject(value); return this.getBuffer(); } startDynamicVector() { this.writeByte(CORE_TYPES.VectorDynamic); return this; } endDynamicVector() { this.writeByte(CORE_TYPES.None); return this; } _writeCustom(value) { const start = this.offset; this.allocate(1); this.offset++; let edgeExt; for (const ext of this.extensions.values()) { if (ext.token === -1) { edgeExt = ext; continue; } ext.encode.call(this, value); const processed = start + 1 < this.offset; if (processed) { const end = this.offset; this.offset = start; this.writeByte(ext.token); this.offset = end; return true; } } this.offset = start; if (edgeExt) { edgeExt.encode.call(this, value); return start < this.offset; } return false; } writeObject(value) { if (value === void 0) return this; const constructorId = coreType(value); if (constructorId === CORE_TYPES.None) { if (this._writeCustom(value)) { return this; } throw new TypeError(`Invalid core type of ${value}`); } if (this._last === value) { this.writeRepeat(); } else { this._last = value; this._repeat = void 0; this.writeCore(constructorId, value); } return this; } writeObjectGzip(value) { const writer = new BinaryWriter(); writer.extensions = this.extensions; writer.dictionary = this.dictionary; writer.structures = this.structures; writer.dictionaryExtended = this.dictionaryExtended; writer.writeObject(value); this.writeCore(CORE_TYPES.GZIP, writer.getBuffer()); return this; } writeCore(constructorId, value) { if (this.withGzip && SUPPORT_COMPRESSION.has(constructorId)) { this.writeObjectGzip(value); return this; } else if (!NO_CONSTRUCTOR.has(constructorId)) { this.writeByte(constructorId); } switch (constructorId) { case CORE_TYPES.Structure: { return this.writeStructure(value); } case CORE_TYPES.Binary: { return this.writeBytes(value); } case CORE_TYPES.GZIP: { return this.writeGzip(value); } case CORE_TYPES.DictIndex: { return this.writeLength(value); } case CORE_TYPES.DictValue: { return this.writeString(value); } case CORE_TYPES.BoolFalse: { return this.writeBool(value); } case CORE_TYPES.BoolTrue: { return this.writeBool(value); } case CORE_TYPES.Date: { return this.writeDate(value); } case CORE_TYPES.Int64: { return this.writeInt64(value); } case CORE_TYPES.Int32: { return this.writeInt32(value); } case CORE_TYPES.Int16: { return this.writeInt16(value); } case CORE_TYPES.Int8: { return this.writeInt8(value); } case CORE_TYPES.UInt64: { return this.writeInt64(value, false); } case CORE_TYPES.UInt32: { return this.writeInt32(value, false); } case CORE_TYPES.UInt16: { return this.writeInt16(value, false); } case CORE_TYPES.UInt8: { return this.writeInt8(value, false); } case CORE_TYPES.Double: { return this.writeDouble(value); } case CORE_TYPES.Float: { return this.writeFloat(value); } case CORE_TYPES.Null: { return this.writeNull(); } case CORE_TYPES.String: { if (value.length <= 16) { this.offset--; return this.wireDictionary(value); } return this.writeString(value); } case CORE_TYPES.Vector: { return this.writeVector(value); } case CORE_TYPES.Map: { return this.writeMap(value); } } return this; } writeRepeat() { if (!this._repeat) { this.writeByte(CORE_TYPES.Repeat); this._repeat = { count: 0, offset: this.offset }; } this.offset = this._repeat.offset; this._repeat.count++; this.writeLength(this._repeat.count); return this; } } const CONSTRUCTOR_OPTIONAL = 1644261036; const CONSTRUCTOR_OPTIONAL_NULL = 570519212; const TYPE_HANDLERS = { ["unknown"]: { encode: function(value, key) { this.writeObject(value[key]); }, decode: function(result, key) { result[key] = this.readObject(); }, estimatedSizeBytes: 0 }, [CORE_TYPES.Map]: { encode: function(value, key) { this.writeMap(value[key]); }, decode: function(result, key) { result[key] = this.readMap(false); }, estimatedSizeBytes: 0 }, [CORE_TYPES.Binary]: { encode: function(value, key) { this.writeBytes(value[key]); }, decode: function(result, key) { result[key] = this.readBytes(); }, estimatedSizeBytes: 0 }, [CORE_TYPES.Vector]: { encode: function(value, key) { this.writeVector(value[key]); }, decode: function(result, key) { result[key] = this.readVector(false); }, estimatedSizeBytes: 0 }, [Boolean.name]: { encode: function(value, key) { this.writeBool(value[key]); }, decode: function(result, key) { result[key] = this.readBool(); }, estimatedSizeBytes: 1 }, [CORE_TYPES.Int8]: { encode: function(value, key) { this.writeInt8(value[key], true); }, decode: function(result, key) { result[key] = this.readInt8(true); }, estimatedSizeBytes: 1 }, [CORE_TYPES.Int16]: { encode: function(value, key) { this.writeInt16(value[key], true); }, decode: function(result, key) { result[key] = this.readInt16(true); }, estimatedSizeBytes: 2 }, [CORE_TYPES.Int32]: { encode: function(value, key) { this.writeInt32(value[key], true); }, decode: function(result, key) { result[key] = this.readInt32(true); }, estimatedSizeBytes: 4 }, [CORE_TYPES.Int64]: { encode: function(value, key) { this.writeInt64(value[key], true); }, decode: function(result, key) { result[key] = this.readInt64(true); }, estimatedSizeBytes: 8 }, [CORE_TYPES.UInt8]: { encode: function(value, key) { this.writeInt8(value[key], false); }, decode: function(result, key) { result[key] = this.readInt8(false); }, estimatedSizeBytes: 1 }, [CORE_TYPES.UInt16]: { encode: function(value, key) { this.writeInt16(value[key], false); }, decode: function(result, key) { result[key] = this.readInt16(false); }, estimatedSizeBytes: 2 }, [CORE_TYPES.UInt32]: { encode: function(value, key) { this.writeInt32(value[key], false); }, decode: function(result, key) { result[key] = this.readInt32(false); }, estimatedSizeBytes: 4 }, [CORE_TYPES.UInt64]: { encode: function(value, key) { this.writeInt64(value[key], false); }, decode: function(result, key) { result[key] = this.readInt64(false); }, estimatedSizeBytes: 8 }, [CORE_TYPES.Double]: { encode: function(value, key) { this.writeDouble(value[key]); }, decode: function(result, key) { result[key] = this.readDouble(); }, estimatedSizeBytes: 8 }, [CORE_TYPES.Date]: { encode: function(value, key) { this.writeDate(value[key]); }, decode: function(result, key) { result[key] = this.readDate(); }, estimatedSizeBytes: 8 }, [CORE_TYPES.String]: { encode: function(value, key) { this.writeString(value[key]); }, decode: function(result, key) { result[key] = this.readString(); }, estimatedSizeBytes: 0 } }; TYPE_HANDLERS[Number.name] = TYPE_HANDLERS[CORE_TYPES.Double]; TYPE_HANDLERS[String.name] = TYPE_HANDLERS[CORE_TYPES.String]; TYPE_HANDLERS[Object.name] = TYPE_HANDLERS[CORE_TYPES.Map]; TYPE_HANDLERS[Uint8Array.name] = TYPE_HANDLERS[CORE_TYPES.Binary]; TYPE_HANDLERS[Array.name] = TYPE_HANDLERS[CORE_TYPES.Vector]; TYPE_HANDLERS[Date.name] = TYPE_HANDLERS[CORE_TYPES.Date]; function compileStructure(name, version, properties, checksum) { const encodeFns = []; const decodeFns = []; const structures = []; const structureId = toolkit.crc32(name) >>> 0; let estimatedSizeBytes = 1; encodeFns.push(function() { this.writeByte(version); }); decodeFns.push(function() { const ver = this.readByte(); toolkit.assert.ok( version === ver, `Structure ${structureId} version mismatch: expected ${version}, got ${ver}` ); }); const entries = Object.entries(properties); const entriesLength = entries.length; for (let i = 0; i < entriesLength; i++) { const [key, prop] = entries[i]; const isRequired = prop.required === true; const isArray = Array.isArray(prop.type); const propType = Array.isArray(prop.type) ? prop.type[0] : prop.type; if (isStructureType(propType)) { encodeFns.push( createStructureEncoder(key, propType, isRequired, isArray) ); decodeFns.push(createStructureDecoder(key, isRequired, isArray)); estimatedSizeBytes += propType.estimatedSizeBytes; structures.push(propType); continue; } const typeName = propType?.name || (propType === null ? "unknown" : propType); const handler = TYPE_HANDLERS[typeName]; if (handler) { encodeFns.push( createBoundEncoder(handler.encode, key, isRequired, isArray) ); decodeFns.push( createBoundDecoder(handler.decode, key, isRequired, isArray) ); estimatedSizeBytes += handler.estimatedSizeBytes; } else { throw new Error(`Unsupported property type: ${typeName || "unknown"}`); } } if (checksum) { estimatedSizeBytes += 4; encodeFns.push(function() { this.writeChecksum(false); }); decodeFns.push(function() { this.readChecksum(false); }); } const compiled = { id: structureId, encodeFns, decodeFns, structures, estimatedSizeBytes }; return compiled; } function isStructureType(type) { return type?.prototype && Structure.prototype.isPrototypeOf(type.prototype); } function createStructureEncoder(key, StructureCtor, isRequired, isArray) { return function(value) { const hasValue = value[key] !== void 0 && value[key] !== null; if (!hasValue) { toolkit.assert.ok(!isRequired, `Required property "${key}" is missing or null`); if (value[key] === null) { this.writeInt32(CONSTRUCTOR_OPTIONAL_NULL); } else { this.writeInt32(CONSTRUCTOR_OPTIONAL); } } else if (isArray) { const arr = value[key]; toolkit.assert.array(arr, `Expected property "${key}" to be array.`); this.writeLength(arr.length); for (let idx = 0; idx < arr.length; idx++) { this.writeStructure( arr[idx] instanceof Structure ? arr[idx] : new StructureCtor(arr[idx]) ); } } else { this.writeStructure( value[key] instanceof Structure ? value[key] : new StructureCtor(value[key]) ); } }; } function createStructureDecoder(key, isRequired, isArray) { return function(result) { let shouldRead = true; if (!isRequired) { shouldRead = !readMaybeInt32(this, CONSTRUCTOR_OPTIONAL); if (shouldRead && readMaybeInt32(this, CONSTRUCTOR_OPTIONAL_NULL)) { result[key] = null; return; } } if (shouldRead) { if (isArray) { const length = this.readLength(); const arrResult = Array.from({ length }); for (let idx = 0; idx < length; idx++) { arrResult[idx] = this.readStructure(false); } result[key] = arrResult; } else { result[key] = this.readStructure(false); } } }; } function readMaybeInt32(reader, expectedValue) { let result = false; if (reader.length >= reader.offset + 4) { result = reader.readInt32() === expectedValue; if (!result) { reader.offset -= 4; } } return result; } function createBoundEncoder(encodeFn, key, isRequired, isArray) { return function(value) { const hasValue = value[key] !== void 0 && value[key] !== null; if (!hasValue) { toolkit.assert.ok(!isRequired, `Required property "${key}" is missing or null`); if (value[key] === null) { this.writeInt32(CONSTRUCTOR_OPTIONAL_NULL); } else { this.writeInt32(CONSTRUCTOR_OPTIONAL); } } else if (isArray) { const arr = value[key]; toolkit.assert.array(arr, `Expected property "${key}" to be array.`); this.writeLength(arr.length); for (let idx = 0; idx < arr.length; idx++) { encodeFn.call(this, arr, idx); } } else { encodeFn.call(this, value, key); } }; } function createBoundDecoder(decodeFn, key, isRequired, isArray) { return function(result) { let shouldRead = true; if (!isRequired) { shouldRead = !readMaybeInt32(this, CONSTRUCTOR_OPTIONAL); if (shouldRead && readMaybeInt32(this, CONSTRUCTOR_OPTIONAL_NULL)) { result[key] = null; return; } } if (shouldRead) { if (isArray) { const length = this.readLength(); const arrResult = Array.from({ length }); for (let idx = 0; idx < length; idx++) { decodeFn.call(this, arrResult, idx); } result[key] = arrResult; } else { decodeFn.call(this, result, key); } } }; } function defineStructure({ name, properties, version, checksum = false }) { const compiled = compileStructure(name, version, properties, checksum); return class DefinedStructure extends Structure { static estimatedSizeBytes = compiled.estimatedSizeBytes; static structures = compiled.structures; static extension = { token: compiled.id, encode(value) { const fns = compiled.encodeFns; const length = fns.length; for (let i = 0; i < length; i++) { fns[i].call(this, value); } }, decode() { const result = {}; const fns = compiled.decodeFns; const length = fns.length; for (let i = 0; i < length; i++) { fns[i].call(this, result); } return result; } }; }; } class Structure { value; constructor(value) { this.value = value; } toBuffer(options) { const ctor = this.constructor; const writer = new BinaryWriter(options); ctor.extension.encode.call(writer, this.value); return writer.getBuffer(); } static fromBuffer(buffer, options = {}) { const reader = new BinaryReader(buffer, { ...options, structures: options.structures ? this.structures.concat(options.structures) : this.structures }); return this.extension.decode.call(reader); } static estimatedSizeBytes = -1; static structures = []; static extension = { token: CORE_TYPES.Binary, encode: toolkit.noop, decode: toolkit.noop }; } const fromCharCode = String.fromCharCode; const int32 = new Int32Array(2); const float32 = new Float32Array(int32.buffer); const float64 = new Float64Array(int32.buffer); function byteArrayAllocate(length) { return new Uint8Array(length); } function coreType(value) { if (value instanceof Structure) { return CORE_TYPES.Structure; } else if (value instanceof Uint8Array) { return CORE_TYPES.Binary; } switch (typeof value) { case "string": { return CORE_TYPES.String; } case "boolean": { return value ? CORE_TYPES.BoolTrue : CORE_TYPES.BoolFalse; } case "bigint": { if (value >= 0n && value <= 0xffffffffffffffffn) { return CORE_TYPES.UInt64; } else if (value >= -0x8000000000000000n && value <= 0x7fffffffffffffffn) { return CORE_TYPES.Int64; } return CORE_TYPES.None; } case "number": { if (Math.trunc(value) === value) { if (value >= 0 && value <= 255) { return CORE_TYPES.UInt8; } else if (value >= 0 && value <= 65535) { return CORE_TYPES.UInt16; } else if (value >= 0 && value <= 4294967295) { return CORE_TYPES.UInt32; } else if (value >= -128 && value <= 127) { return CORE_TYPES.Int8; } else if (value >= -32768 && value <= 32767) { return CORE_TYPES.Int16; } else if (value >= -2147483648 && value <= 2147483647) { return CORE_TYPES.Int32; } } return CORE_TYPES.Double; } case "object": { if (value === null) return CORE_TYPES.Null; if (value instanceof Date) { return CORE_TYPES.Date; } if (Array.isArray(value)) { return CORE_TYPES.Vector; } if (toolkit.isPlainObject(value)) { return CORE_TYPES.Map; } } } return CORE_TYPES.None; } function utf8Read(target, length, offset) { let result; if (length < 16) { if (result = utf8ReadShort(target, length, offset)) return result; } if (length > 64) return toolkit.textDecoder.decode(target.subarray(offset, offset += length)); const end = offset + length; const units = []; result = ""; while (offset < end) { const byte1 = target[offset++]; if ((byte1 & 128) === 0) { units.push(byte1); } else if ((byte1 & 224) === 192) { const byte2 = target[offset++] & 63; units.push((byte1 & 31) << 6 | byte2); } else if ((byte1 & 240) === 224) { const byte2 = target[offset++] & 63; const byte3 = target[offset++] & 63; units.push((byte1 & 31) << 12 | byte2 << 6 | byte3); } else if ((byte1 & 248) === 240) { const byte2 = target[offset++] & 63; const byte3 = target[offset++] & 63; const byte4 = target[offset++] & 63; let unit = (byte1 & 7) << 18 | byte2 << 12 | byte3 << 6 | byte4; if (unit > 65535) { unit -= 65536; units.push(unit >>> 10 & 1023 | 55296); unit = 56320 | unit & 1023; } units.push(unit); } else { units.push(byte1); } if (units.length >= 4096) { result += fromCharCode.apply(String, units); units.length = 0; } } if (units.length > 0) { result += fromCharCode.apply(String, units); } return result; } function utf8ReadShort(target, length, offset) { if (length < 4) { if (length < 2) { if (length === 0) return ""; else { let a = target[offset++]; if ((a & 128) > 1) { offset -= 1; return; } return fromCharCode(a); } } else { let a = target[offset++]; let b = target[offset++]; if ((a & 128) > 0 || (b & 128) > 0) { offset -= 2; return; } if (length < 3) return fromCharCode(a, b); let c = target[offset++]; if ((c & 128) > 0) { offset -= 3; return; } return fromCharCode(a, b, c); } } else { let a = target[offset++]; let b = target[offset++]; let c = target[offset++]; let d = target[offset++]; if ((a & 128) > 0 || (b & 128) > 0 || (c & 128) > 0 || (d & 128) > 0) { offset -= 4; return; } if (length < 6) { if (length === 4) return fromCharCode(a, b, c, d); else { let e = target[offset++]; if ((e & 128) > 0) { offset -= 5; return; } return fromCharCode(a, b, c, d, e); } } else if (length < 8) { let e = target[offset++]; let f = target[offset++]; if ((e & 128) > 0 || (f & 128) > 0) { offset -= 6; return; } if (length < 7) return fromCharCode(a, b, c, d, e, f); let g = target[offset++]; if ((g & 128) > 0) { offset -= 7; return; } return fromCharCode(a, b, c, d, e, f, g); } else { let e = target[offset++]; let f = target[offset++]; let g = target[offset++]; let h = target[offset++]; if ((e & 128) > 0 || (f & 128) > 0 || (g & 128) > 0 || (h & 128) > 0) { offset -= 8; return; } if (length < 10) { if (length === 8) return fromCharCode(a, b, c, d, e, f, g, h); else { let i = target[offset++]; if ((i & 128) > 0) { offset -= 9; return; } return fromCharCode(a, b, c, d, e, f, g, h, i); } } else if (length < 12) { let i = target[offset++]; let j = target[offset++]; if ((i & 128) > 0 || (j & 128) > 0) { offset -= 10; return; } if (length < 11) return fromCharCode(a, b, c, d, e, f, g, h, i, j); let k = target[offset++]; if ((k & 128) > 0) { offset -= 11; return; } return fromCharCode(a, b, c, d, e, f, g, h, i, j, k); } else { let i = target[offset++]; let j = target[offset++]; let k = target[offset++]; let l = target[offset++]; if ((i & 128) > 0 || (j & 128) > 0 || (k & 128) > 0 || (l & 128) > 0) { offset -= 12; return; } if (length < 14) { if (length === 12) return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l); else { let m = target[offset++]; if ((m & 128) > 0) { offset -= 13; return; } return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l, m); } } else { let m = target[offset++]; let n = target[offset++]; if ((m & 128) > 0 || (n & 128) > 0) { offset -= 14; return; } if (length < 15) return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l, m, n); let o = target[offset++]; if ((o & 128) > 0) { offset -= 15; return; } return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o); } } } } } const utf8Write = function(target, value, offset) { return value.length < 64 ? utf8WriteShort(target, value, offset) : toolkit.textEncoder.encodeInto(value, target.subarray(offset)).written; }; const utf8WriteShort = (target, value, offset) => { let i, c1, c2, strPosition = offset; const strLength = value.length; for (i = 0; i < strLength; i++) { c1 = value.charCodeAt(i); if (c1 < 128) { target[strPosition++] = c1; } else if (c1 < 2048) { target[strPosition++] = c1 >> 6 | 192; target[strPosition++] = c1 & 63 | 128; } else if ((c1 & 64512) === 55296 && ((c2 = value.charCodeAt(i + 1)) & 64512) === 56320) { c1 = 65536 + ((c1 & 1023) << 10) + (c2 & 1023); i++; target[strPosition++] = c1 >> 18 | 240; target[strPosition++] = c1 >> 12 & 63 | 128; target[strPosition++] = c1 >> 6 & 63 | 128; target[strPosition++] = c1 & 63 | 128; } else { target[strPosition++] = c1 >> 12 | 224; target[strPosition++] = c1 >> 6 & 63 | 128; target[strPosition++] = c1 & 63 | 128; } } return strPosition - offset; }; const NOOP_DICTIONARY = new Dictionary(); class BinaryReader { target; _last; _lastObject; dictionary; dictionaryExtended; extensions; structures; _repeat; _checksumOffset; offset; length; /** * Small utility class to read binary data. */ constructor(data, { dictionary = NOOP_DICTIONARY, extensions, structures } = {}) { this.target = data; this.offset = 0; this._checksumOffset = 0; this.length = data.length; this.extensions = /* @__PURE__ */ new Map(); this.structures = /* @__PURE__ */ new Map(); if (extensions) { for (const ext of extensions) { this.extensions.set(ext.token, ext); } } if (structures) { for (const struct of structures) { this.structures.set(struct.extension.token, struct); } } if (Array.isArray(dictionary)) { this.dictionary = new Dictionary(dictionary); } else { this.dictionary = dictionary; } this.dictionaryExtended = new Dictionary(void 0, this.dictionary.size); } readByte() { this.assertRead(1); this._last = this.target[this.offset++]; return this._last; } readInt64(signed = true) { const low32 = this.readInt32(signed); const high32 = this.readInt32(signed); this._last = BigInt(high32) << 32n | BigInt(low32); return this._last; } readInt32(signed = true) { this.assertRead(4); this._last = this.target[this.offset++] | this.target[this.offset++] << 8 | this.target[this.offset++] << 16 | this.target[this.offset++] << 24; if (!signed) { this._last = this._last >>> 0; } return this._last; } readInt16(signed = true) { this.assertRead(2); this._last = this.target[this.offset++] | this.target[this.offset++] << 8; if (signed) { this._last = this._last << 16 >> 16; } return this._last; } readInt8(signed = true) { this.assertRead(1); this._last = this.target[this.offset++]; if (signed) { this._last = this._last << 24 >> 24; } return this._last; } /** * Reads a real floating point (4 bytes) value. * @returns {number} */ readFloat() { this.assertRead(4); int32[0] = this.readInt32(); this._last = float32[0]; return this._last; } /** * Reads a real floating point (8 bytes) value. */ readDouble() { this.assertRead(8); int32[0] = this.readInt32(); int32[1] = this.readInt32(); this._last = float64[0]; return this._last; } /** * Throws error if provided length cannot be read from buffer * @param length {number} */ assertRead(length) { if (this.length < this.offset + +length) { const left = this.target.length - this.offset; const result = this.target.subarray(this.offset, this.offset + left); const err = new Error( `No more data left to read (need ${length}, got ${left}: ${result}); last read ${this._last}` ); err.incomplete = true; Error.captureStackTrace(err, this.assertRead); throw err; } } assertConstructor(constructorId) { const byte = this.readByte(); if (byte !== constructorId) { throw new Error( `Invalid constructor code, expected = ${CORE_TYPES[constructorId]}, got = ${CORE_TYPES[byte] || byte}, offset = ${this.offset - 1}` ); } } /** * Gets the byte array representing the current buffer as a whole. */ getBuffer() { return this.target; } readNull() { const value = this.readByte(); if (value === CORE_TYPES.Null) { return null; } throw new Error(`Invalid boolean code ${value.toString(16)}`); } readLength() { const firstByte = this.readByte(); if (firstByte === 254) { return this.readByte() | this.readByte() << 8 | this.readByte() << 16; } return firstByte; } readAll() { const result = []; while (this.length > this.offset) { result.push(this.readObject()); } return result; } readBytes() { const length = this.readLength(); this.assertRead(length); const bytes = this.target.subarray(this.offset, this.offset + length); this.offset += bytes.length; this._last = bytes; return bytes; } /** * Reads encoded string. */ readString() { const length = this.readLength(); this.assertRead(length); const result = utf8Read(this.target, length, this.offset); this.offset += length; this._last = result; return result; } /** * Reads a boolean value. */ readBool() { const value = this.readByte(); if (value === CORE_TYPES.BoolTrue) { return true; } else if (value === CORE_TYPES.BoolFalse) { return false; } else { throw new Error(`Invalid boolean code ${value.toString(16)}`); } } /** * Reads and converts Unix time * into a Javascript {Date} object. */ readDate() { const value = this.readDouble(); return new Date(value); } readStructure(checkConstructor = true) { if (checkConstructor) { this.assertConstructor(CORE_TYPES.Structure); } const structureId = this.readInt32(false); const struct = this.structures.get(structureId); if (!struct) { throw new Error( `Unknown structure id = ${structureId}, offset = ${this.offset - 1}` ); } return struct.extension.decode.call(this); } /** * Reads a object. */ readObject() { if (this._repeat) { if (this._repeat.pool > 0) { --this._repeat.pool; return this._repeat.value; } else { this._repeat = void 0; } } const constructorId = this.readByte(); const ext = this.extensions.get(constructorId); let value; if (ext) { value = ext.decode.call(this); } else { value = this._lastObject = this.readCore(constructorId); } return value; } readObjectGzip() { const bytes = this.readGzip(); const reader = new BinaryReader(bytes); reader.extensions = this.extensions; reader.dictionary = this.dictionary; reader.dictionaryExtended = this.dictionaryExtended; return reader.readObject(); } readGzip() { return pako__default.inflateRaw(this.readBytes()); } readCore(constructorId) { switch (constructorId) { case CORE_TYPES.None: return this.readObject(); case CORE_TYPES.GZIP: return this.readObjectGzip(); case CORE_TYPES.BoolTrue: return true; case CORE_TYPES.BoolFalse: return false; case CORE_TYPES.Vector: return this.readVector(false); case CORE_TYPES.VectorDynamic: return this.readVectorDynamic(false); case CORE_TYPES.Null: return null; case CORE_TYPES.Binary: return this.readBytes(); case CORE_TYPES.String: return this.readString(); case CORE_TYPES.Date: return this.readDate(); case CORE_TYPES.Int64: return this.readInt64(); case CORE_TYPES.Int32: return this.readInt32(); case CORE_TYPES.Int16: return this.readInt16(); case CORE_TYPES.Int8: return this.readInt8(); case CORE_TYPES.UInt64: return this.readInt64(false); case CORE_TYPES.UInt32: return this.readInt32(false); case CORE_TYPES.UInt16: return this.readInt16(false); case CORE_TYPES.UInt8: return this.readInt8(false); case CORE_TYPES.Float: return this.readFloat(); case CORE_TYPES.Double: return this.readDouble(); case CORE_TYPES.Map: return this.readMap(false); case CORE_TYPES.Checksum: return void this.readChecksum(false); case CORE_TYPES.Structure: return this.readStructure(false); case CORE_TYPES.DictIndex: { const idx = this.readLength(); return this.getDictionaryValue(idx); } case CORE_TYPES.DictValue: { const value = this.readString(); this.dictionaryExtended.maybeInsert(value); return value; } case CORE_TYPES.Repeat: { const size = this.readLength(); this._repeat = { pool: size - 1, value: this._lastObject }; return this._lastObject; } } throw new Error( `Invalid constructor = ${CORE_TYPES[constructorId] || constructorId}, offset = ${this.offset - 1}` ); } getDictionaryValue(index) { let value = null; if (this.dictionary) { value = this.dictionary.getValue(index); } if (value === null) { value = this.dictionaryExtended.getValue(index); } return value; } readDictionary() { const constructorId = this.readByte(); let key = null; switch (constructorId) { case CORE_TYPES.DictIndex: { const idx = this.readLength(); key = this.getDictionaryValue(idx); break; } case CORE_TYPES.DictValue: { key = this.readString(); this.dictionaryExtended.maybeInsert(key); break; } case CORE_TYPES.None: { key = null; break; } default: { this.seek(-1); } } return key; } readMap(checkConstructor = true) { if (checkConstructor) { this.assertConstructor(CORE_TYPES.Map); } const temp = {}; let key = this.readDictionary(); while (key !== null) { temp[key] = this.readObject(); key = this.readDictionary(); } return temp; } decode(value) { this.target = value; this._last = void 0; this._lastObject = void 0; this._repeat = void 0; this.offset = 0; this._checksumOffset = 0; this.length = value.length; return this.readObject(); } /** * Reads a vector (a list) of objects. */ readVector(checkConstructor = true) { if (checkConstructor) { this.assertConstructor(CORE_TYPES.Vector); } const count = this.readLength(); const temp = []; for (let i = 0; i < count; i++) { temp.push(this.readObject()); } return temp; } /** * Reads a vector (a list) of objects. */ readVectorDynamic(checkConstructor = true) { if (checkConstructor) { this.assertConstructor(CORE_TYPES.VectorDynamic); } const temp = []; let complete = false; while (this.length > this.offset) { const constructorId = this.readByte(); if (constructorId === CORE_TYPES.None) { complete = true; break; } const ext = this.extensions.get(constructorId); let value; if (ext) { value = ext.decode.call(this); } else { value = this.readCore(constructorId); } temp.push(value); } if (!complete) { const err = new Error(`DynamicVector incomplete.`); err.incomplete = true; Error.captureStackTrace(err, this.readDictionary); throw err; } this._last = temp; return temp; } readChecksum(checkConstructor = true) { const offset = this.offset; if (checkConstructor) { this.assertConstructor(CORE_TYPES.Checksum); } const bytes = this.target.subarray(this._checksumOffset, offset); const checksum = this.readInt32(); let sum = 0; for (const val of bytes) { sum += val; } if (checksum - sum !== 0) { throw new Error( `Invalid checksum = ${checksum - sum}, offset = ${offset}` ); } this._checksumOffset = this.offset; } /** * Tells the current position on the stream. */ tellPosition() { return this.offset; } /** * Sets the current position on the stream. */ setPosition(position) { this.offset = position; } /** * Seeks the stream position given an offset from the current position. * The offset may be negative. */ seek(offset) { this.offset += offset; } /** * Sets the current buffer and reset initial state. */ reset(data) { this.offset = 0; this._checksumOffset = 0; this._lastObject = void 0; this._last = void 0; this._repeat = void 0; this.dictionaryExtended.clear(); if (data) { this.length = data.length; this.target = data; } } } exports.BinaryReader = BinaryReader; exports.BinaryWriter = BinaryWriter; exports.CORE_TYPES = CORE_TYPES; exports.MAX_BUFFER_SIZE = MAX_BUFFER_SIZE; exports.Structure = Structure; exports.createDictionary = createDictionary; exports.defineStructure = defineStructure; //# sourceMappingURL=tl-pack.5CF-VZgL.cjs.map