@andrew_l/tl-pack
Version:
Another implementation of binary serialization.
1,664 lines (1,653 loc) • 46.3 kB
JavaScript
'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