obj-codec
Version:
Encode objects into binary and decode binary back into objects, supporting nested references, custom object encoding/decoding, unique pointers...
903 lines (883 loc) • 25.4 kB
JavaScript
//#region src/codec/base/flexible-unsigned-integer.ts
const flexUintCodec = {
encode(data) {
const bufferLength = Math.ceil(data.toString(2).length / 7);
const buffer = new Uint8Array(bufferLength);
for (let i = 0; i < bufferLength; i++) buffer[bufferLength - i - 1] = data >> i * 7 | 128;
buffer[bufferLength - 1] &= 127;
return buffer;
},
getBuffer(buffer) {
let complete = false;
let i = 0;
while (i < buffer.length) {
i++;
if (buffer[i - 1] & 128) continue;
complete = true;
break;
}
return complete ? buffer.slice(0, i) : null;
},
decode(encoded) {
let data = 0;
for (let i = 0; i < encoded.length; i++) {
data |= encoded[i] & 127;
if (i < encoded.length - 1) data <<= 7;
}
return data;
}
};
//#endregion
//#region src/codec/base/string.ts
const stringCodec = {
encode(data) {
return new TextEncoder().encode(data);
},
decode(encoded) {
return new TextDecoder().decode(encoded);
}
};
//#endregion
//#region src/codec/base/bigint.ts
const bigintCodec = {
encode(data) {
const byteLength = Math.ceil((data < 0n ? data : -data).toString(16).length / 2);
const buffer = new Uint8Array(byteLength);
for (let i = 0; i < byteLength; i++) buffer[i] = Number(data >> BigInt(i * 8) & 0xffn);
return buffer;
},
decode(encoded) {
let result = 0n;
for (let i = 0; i < encoded.byteLength; i++) result |= BigInt(encoded[i]) << BigInt(i * 8);
if (encoded[encoded.byteLength - 1] & 128) {
const mask = (1n << BigInt(encoded.byteLength * 8)) - 1n;
result |= ~mask;
}
return result;
}
};
//#endregion
//#region src/utils.ts
var Queue = class {
#head;
#tail;
#size = 0;
get size() {
return this.#size;
}
enqueue(value) {
const node = { value };
if (this.#head) {
this.#tail.next = node;
this.#tail = node;
} else {
this.#head = node;
this.#tail = node;
}
this.#size++;
return this.#size;
}
dequeue() {
if (!this.#head) return;
const removed = this.#head;
this.#head = this.#head.next;
if (!this.#head) this.#tail = void 0;
this.#size--;
return removed.value;
}
*iter() {
while (true) {
const value = this.dequeue();
if (value === void 0) return;
yield value;
}
}
};
/** 合并缓冲区 */
function concatBuffers(...buffers) {
let totalLength = 0;
for (const buffer of buffers) totalLength += buffer.length;
const result = new Uint8Array(totalLength);
let offset = 0;
for (const buffer of buffers) {
result.set(buffer, offset);
offset += buffer.length;
}
return result;
}
/** 转 Uint8Array */
function bufferToUint8Array(buffer) {
if (buffer instanceof ArrayBuffer) return new Uint8Array(buffer);
return new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
}
const BINARY_TYPES = [
globalThis.ArrayBuffer,
globalThis.Int8Array,
globalThis.Uint8Array,
globalThis.Uint8ClampedArray,
globalThis.Int16Array,
globalThis.Uint16Array,
globalThis.Int32Array,
globalThis.Uint32Array,
globalThis.Float16Array,
globalThis.Float32Array,
globalThis.Float64Array,
globalThis.BigInt64Array,
globalThis.BigUint64Array,
globalThis.DataView
];
function getBinaryTypeId(data) {
return BINARY_TYPES.findIndex((type) => typeof type === "function" && data instanceof type);
}
function isBinary(data) {
for (const type of BINARY_TYPES) {
if (typeof type !== "function") continue;
if (data instanceof type) return true;
}
return false;
}
var Pointer = class {
#id;
#pointers;
constructor(id, pointers) {
this.#id = id;
this.#pointers = pointers;
}
/** 解引用 */
deref() {
if (this.#id >= this.#pointers.length) throw new Error(`Unknown pointer: ${this.#id}`);
return this.#pointers[this.#id][1];
}
};
function registerCodec(scope, codec) {
if (typeof codec !== "object") throw new TypeError("codec must be an object");
if (typeof codec.name !== "string") throw new TypeError("codec.name must be a string");
if (typeof codec.decode !== "function") throw new TypeError("codec.decode must be a function");
if (typeof codec.encode !== "function") throw new TypeError("codec.encode must be a function");
if (typeof codec.class !== "function") throw new TypeError("codec.class must be a function");
if (codec.parentClasses) {
if (!Array.isArray(codec.parentClasses)) throw new TypeError("codec.parentClasses must be a array");
for (const p of codec.parentClasses) if (typeof p !== "function") throw new TypeError("parentClasses must be a function");
}
if (scope.has(codec.name)) console.warn(`Codec ${codec.name} already exists, overwriting`);
scope.set(codec.name, codec);
}
function mergeCodecs(...codecsList) {
const result = /* @__PURE__ */ new Map();
for (const codecs of codecsList) for (const [k, v] of codecs) result.set(k, v);
return result;
}
//#endregion
//#region src/codec/base/binary.ts
const binaryCodec = {
encode(data) {
return concatBuffers([getBinaryTypeId(data)], bufferToUint8Array(data));
},
decode(encoded) {
const binaryType = encoded[0];
const buffer = encoded.slice(1).buffer;
if (binaryType === 0) return buffer;
return new BINARY_TYPES[binaryType](buffer);
}
};
//#endregion
//#region src/codec/base/boolean.ts
const falseCodec = {
bufferLength: 0,
encode: () => new Uint8Array([]),
decode: () => false
};
const trueCodec = {
bufferLength: 0,
encode: () => new Uint8Array([]),
decode: () => true
};
//#endregion
//#region src/codec/base/null.ts
const nullCodec = {
bufferLength: 0,
encode: () => new Uint8Array([]),
decode: () => null
};
//#endregion
//#region src/codec/base/number.ts
const numberCodec = {
bufferLength: 8,
encode(data) {
const buffer = new Uint8Array(8);
const view = new DataView(buffer.buffer);
view.setFloat64(0, data);
return buffer;
},
decode(encoded) {
const view = new DataView(encoded.buffer);
return view.getFloat64(0);
}
};
//#endregion
//#region src/codec/base/undefined.ts
const undefinedCodec = {
bufferLength: 0,
encode: () => new Uint8Array([]),
decode: () => void 0
};
//#endregion
//#region src/codec/internal/array.ts
const arrayCodec = {
encode(data, serialize) {
return concatBuffers(...data.map((item) => serialize(item)));
},
decode(encoded, deserialize) {
return deserialize(encoded);
},
deref(data) {
for (let i = 0; i < data.length; i++) {
const item = data[i];
if (item instanceof Pointer) data[i] = item.deref();
}
}
};
//#endregion
//#region src/codec/internal/date.ts
const dateCodec = {
bufferLength: 8,
encode(data) {
const buffer = new Uint8Array(8);
const view = new DataView(buffer.buffer);
view.setFloat64(0, data.getTime());
return buffer;
},
decode(encoded) {
const view = new DataView(encoded.buffer);
return new Date(view.getFloat64(0));
}
};
//#endregion
//#region src/codec/internal/map.ts
const mapCodec = {
encode(data, serialize) {
return concatBuffers(...[...data].flatMap(([k, v]) => [serialize(k), serialize(v)]));
},
decode(encoded, deserialize) {
const pairs = deserialize(encoded);
const map = /* @__PURE__ */ new Map();
for (let i = 0; i < pairs.length; i += 2) map.set(pairs[i], pairs[i + 1]);
return map;
},
deref(data) {
for (let [key, value] of [...data]) {
if (key instanceof Pointer) {
data.delete(key);
key = key.deref();
}
if (value instanceof Pointer) value = value.deref();
data.set(key, value);
}
}
};
//#endregion
//#region src/codec/internal/object.ts
const objectCodec = {
encode(data, serialize) {
const buffers = [];
for (const key in data) {
if (!Object.hasOwn(data, key)) continue;
buffers.push(serialize(key), serialize(data[key]));
}
return concatBuffers(...buffers);
},
decode(encoded, deserialize) {
return { _pairs: deserialize(encoded) };
},
deref(data) {
const pairs = data._pairs;
delete data._pairs;
for (let i = 0; i < pairs.length; i += 2) {
let key = pairs[i];
let value = pairs[i + 1];
if (key instanceof Pointer) key = key.deref();
if (value instanceof Pointer) value = value.deref();
data[key] = value;
}
}
};
//#endregion
//#region src/codec/internal/regexp.ts
const regexpCodec = {
encode(data) {
return new TextEncoder().encode(data.toString().slice(1));
},
decode(encoded) {
const token = new TextDecoder().decode(encoded);
const flagsIndex = token.lastIndexOf("/");
const flags = token.slice(flagsIndex + 1);
return new RegExp(token.slice(0, flagsIndex), flags);
}
};
//#endregion
//#region src/codec/internal/set.ts
const setCodec = {
encode(data, serialize) {
return concatBuffers(...[...data].map((item) => serialize(item)));
},
decode(encoded, deserialize) {
return new Set(deserialize(encoded));
},
deref(data) {
const items = [...data];
data.clear();
for (const item of items) if (item instanceof Pointer) data.add(item.deref());
else data.add(item);
}
};
//#endregion
//#region src/codec/internal/symbol.ts
const symbolCodec = {
encode(data) {
const key = data.description;
if (key === void 0) return new Uint8Array([0]);
return new TextEncoder().encode(key);
},
decode(encoded) {
if (encoded.length === 1 && encoded[0] === 0) return Symbol();
return Symbol(new TextDecoder().decode(encoded));
}
};
//#endregion
//#region src/global.ts
/** 内置编解码器 */
const INTERNAL_CODEC_MAP = Object.freeze({
POINTER: flexUintCodec,
BINARY: binaryCodec,
NUMBER: numberCodec,
BIGINT: bigintCodec,
STRING: stringCodec,
FALSE: falseCodec,
TRUE: trueCodec,
NULL: nullCodec,
UNDEFINED: undefinedCodec,
OBJECT: objectCodec,
ARRAY: arrayCodec,
SET: setCodec,
MAP: mapCodec,
DATE: dateCodec,
REGEXP: regexpCodec,
SYMBOL: symbolCodec,
UNIQUE_POINTER: flexUintCodec
});
const INTERNAL_CODEC = Object.freeze([
flexUintCodec,
binaryCodec,
numberCodec,
bigintCodec,
stringCodec,
falseCodec,
trueCodec,
nullCodec,
undefinedCodec,
objectCodec,
arrayCodec,
setCodec,
mapCodec,
dateCodec,
regexpCodec,
symbolCodec,
flexUintCodec
]);
/** 内置类型 ID */
const INTERNAL_TYPE_ID = Object.freeze({
POINTER: 0,
BINARY: 1,
NUMBER: 2,
BIGINT: 3,
STRING: 4,
FALSE: 5,
TRUE: 6,
NULL: 7,
UNDEFINED: 8,
OBJECT: 9,
ARRAY: 10,
SET: 11,
MAP: 12,
DATE: 13,
REGEXP: 14,
SYMBOL: 15,
UNIQUE_POINTER: 16
});
/** 自定义类型起始 ID */
const CUSTOM_TYPE_ID_BEGIN = 17;
/** 数据结构版本号 */
const VERSION = 1;
const globalCodecs = /* @__PURE__ */ new Map();
var IObjCodec = class {
_codecs = /* @__PURE__ */ new Map();
/**
* 唯一值
* @description
* 该唯一值数组在该 `ObjCodec` 实例中生效,
* 调用 `encode`/`decode` 方法时可覆盖该值。
*
* 唯一指针指向该数组中指定下标的值,
* 需确保编解码时该数组内容和顺序不变。
*/
uniqueValues;
constructor(uniqueValues) {
this.uniqueValues = uniqueValues;
}
/** 注册全局编解码器 */
static register(codec) {
registerCodec(globalCodecs, codec);
}
/** 注册编解码器 */
register(codec) {
registerCodec(this._codecs, codec);
}
};
//#endregion
//#region src/decoder.ts
const REFERABLE_TYPE_ID = new Set([
INTERNAL_TYPE_ID.BINARY,
INTERNAL_TYPE_ID.STRING,
INTERNAL_TYPE_ID.OBJECT,
INTERNAL_TYPE_ID.ARRAY,
INTERNAL_TYPE_ID.SET,
INTERNAL_TYPE_ID.MAP,
INTERNAL_TYPE_ID.DATE,
INTERNAL_TYPE_ID.REGEXP,
INTERNAL_TYPE_ID.SYMBOL
]);
function isReferable(id) {
if (id >= CUSTOM_TYPE_ID_BEGIN) return true;
return REFERABLE_TYPE_ID.has(id);
}
const UNINITIALIZED = Symbol("uninitialized");
var Decoder = class {
#codec;
#uniqueValues;
#ignoreMissedCodec;
#ignoreMissedUniqueValue;
#allowIncompleteDecoding;
#pointers = [];
#buffer = new Uint8Array();
#version;
#codecMapLength;
#codecMap = [];
#types = [];
#length;
#root = UNINITIALIZED;
#complete = false;
constructor({ codec, uniqueValues, ignoreMissedCodec, ignoreMissedUniqueValue, allowIncompleteDecoding }) {
this.#codec = codec;
this.#uniqueValues = uniqueValues ?? [];
this.#ignoreMissedCodec = !!ignoreMissedCodec;
this.#ignoreMissedUniqueValue = !!ignoreMissedUniqueValue;
this.#allowIncompleteDecoding = !!allowIncompleteDecoding;
}
#consume(length) {
this.#buffer = this.#buffer.slice(length);
}
#getCodec(id) {
if (id < CUSTOM_TYPE_ID_BEGIN) return INTERNAL_CODEC[id];
return this.#codecMap[id - CUSTOM_TYPE_ID_BEGIN];
}
#deserializeType(box) {
let { buffer } = box;
const types = [];
while (!this.#isTypeResolvable(types.at(-1))) {
const typeBuffer = flexUintCodec.getBuffer(buffer);
if (!typeBuffer) throw new Error("Failed to deserialize while deserialize types");
const type = flexUintCodec.decode(typeBuffer);
this.#assertTypeExists(type);
types.push(type);
buffer = buffer.slice(typeBuffer.length);
}
box.buffer = buffer;
return types;
}
#deserializeLength(types, box) {
const innerType = types.at(-1);
if (innerType === INTERNAL_TYPE_ID.POINTER || innerType === INTERNAL_TYPE_ID.UNIQUE_POINTER) {
const lengthBuffer$1 = flexUintCodec.getBuffer(box.buffer);
if (!lengthBuffer$1) throw new Error("Failed to read pointer");
return lengthBuffer$1.length;
}
let length = INTERNAL_CODEC[innerType].bufferLength;
if (length !== void 0) return length;
const lengthBuffer = flexUintCodec.getBuffer(box.buffer);
if (!lengthBuffer) throw new Error("Failed to deserialize while deserialize length");
length = flexUintCodec.decode(lengthBuffer);
box.buffer = box.buffer.slice(lengthBuffer.length);
return length;
}
#deserializeBody(types, buffer) {
const innerType = types.pop();
const pointers = [];
let data = INTERNAL_CODEC[innerType].decode(buffer, this.#deserialize);
if (innerType === INTERNAL_TYPE_ID.POINTER) data = new Pointer(data, this.#pointers);
else if (innerType === INTERNAL_TYPE_ID.UNIQUE_POINTER) {
if (data >= this.#uniqueValues.length && !this.#ignoreMissedUniqueValue) throw new Error(`Missing unique value: ${data}`);
data = this.#uniqueValues[data];
}
if (isReferable(innerType)) pointers.push([this.#getCodec(innerType), data]);
while (true) {
const type = types.pop();
if (type === void 0) break;
const codec = this.#getCodec(type);
if (!codec) throw new Error(`Unknown codec: ${type}`);
data = codec.decode(data);
pointers.push([codec, data]);
}
this.#pointers.push(...pointers.reverse());
return data;
}
#deserialize = (buffer) => {
const data = [];
const box = { buffer };
while (box.buffer.length > 0) {
const types = this.#deserializeType(box);
const length = this.#deserializeLength(types, box);
data.push(this.#deserializeBody(types, box.buffer.slice(0, length)));
box.buffer = box.buffer.slice(length);
}
return data;
};
#isTypeResolvable(type = this.#types.at(-1)) {
return type !== void 0 && type < CUSTOM_TYPE_ID_BEGIN;
}
#assertTypeExists(id) {
if (id < CUSTOM_TYPE_ID_BEGIN) return;
if (id - CUSTOM_TYPE_ID_BEGIN >= this.#codecMap.length) throw new Error(`Unknown codec: ${id}`);
}
#decodeVersion() {
if (this.#version !== void 0) return true;
if (this.#buffer.length === 0) return false;
this.#version = this.#buffer[0];
this.#consume(1);
if (this.#version === 1) return true;
throw new Error(`Unsupported version: ${this.#version}`);
}
#decodeCodecMap() {
if (this.#codecMapLength === void 0) {
const lengthBuffer = flexUintCodec.getBuffer(this.#buffer);
if (!lengthBuffer) return false;
this.#codecMapLength = flexUintCodec.decode(lengthBuffer);
this.#consume(lengthBuffer.length);
}
while (this.#codecMap.length < this.#codecMapLength) {
const lengthBuffer = flexUintCodec.getBuffer(this.#buffer);
if (!lengthBuffer) return false;
const length = flexUintCodec.decode(lengthBuffer);
if (this.#buffer.length < lengthBuffer.length + length) return false;
this.#consume(lengthBuffer.length);
const name = stringCodec.decode(this.#buffer.slice(0, length));
const codec = this.#codec.get(name);
if (codec) {
this.#codecMap.push(codec);
this.#consume(length);
continue;
}
if (this.#ignoreMissedCodec) continue;
throw new Error(`Missing codec: ${name}`);
}
return this.#codecMap.length === this.#codecMapLength;
}
#decodeType() {
while (!this.#isTypeResolvable()) {
const typeBuffer = flexUintCodec.getBuffer(this.#buffer);
if (!typeBuffer) return false;
const type = flexUintCodec.decode(typeBuffer);
this.#assertTypeExists(type);
this.#types.push(type);
this.#consume(typeBuffer.length);
}
return true;
}
#decodeLength() {
if (this.#length !== void 0) return true;
const innerType = this.#types.at(-1);
if (innerType === INTERNAL_TYPE_ID.POINTER || innerType === INTERNAL_TYPE_ID.UNIQUE_POINTER) {
const lengthBuffer$1 = flexUintCodec.getBuffer(this.#buffer);
if (!lengthBuffer$1) return false;
this.#length = lengthBuffer$1.length;
return true;
}
this.#length = INTERNAL_CODEC[innerType].bufferLength;
if (this.#length !== void 0) return true;
const lengthBuffer = flexUintCodec.getBuffer(this.#buffer);
if (!lengthBuffer) return false;
this.#length = flexUintCodec.decode(lengthBuffer);
this.#consume(lengthBuffer.length);
return true;
}
#decodeBody() {
if (this.#buffer.length < this.#length) return false;
const innerType = this.#types.pop();
const pointers = [];
let data = INTERNAL_CODEC[innerType].decode(this.#buffer.slice(0, this.#length), this.#deserialize);
this.#consume(this.#length);
if (innerType === INTERNAL_TYPE_ID.POINTER) data = new Pointer(data, this.#pointers);
else if (innerType === INTERNAL_TYPE_ID.UNIQUE_POINTER) {
if (data >= this.#uniqueValues.length && !this.#ignoreMissedUniqueValue) throw new Error(`Missing unique value: ${data}`);
data = this.#uniqueValues[data];
}
if (isReferable(innerType)) pointers.push([this.#getCodec(innerType), data]);
while (true) {
const type = this.#types.pop();
if (type === void 0) break;
const codec = this.#getCodec(type);
if (!codec) throw new Error(`Unknown codec: ${type}`);
data = codec.decode(data);
pointers.push([codec, data]);
}
pointers.reverse();
this.#pointers.push(...pointers);
if (this.#root === UNINITIALIZED) this.#root = pointers.length === 0 ? data : pointers[0][1];
return true;
}
#decode() {
if (!this.#decodeVersion()) return;
if (!this.#decodeCodecMap()) return;
while (this.#buffer.length > 0) {
if (!this.#decodeType()) return;
if (!this.#decodeLength()) return;
if (!this.#decodeBody()) return;
this.#length = void 0;
}
}
decode(buffer) {
if (this.#complete) return;
if (!isBinary(buffer)) throw new Error(`Require a buffer but receive ${typeof buffer}`);
this.#buffer = concatBuffers(this.#buffer, bufferToUint8Array(buffer));
this.#decode();
}
getResult() {
if (this.#complete) return this.#root;
if (!this.#allowIncompleteDecoding && (this.#types.length > 0 || this.#buffer.length > 0)) throw new Error("Failure to decode in full");
const customCodecPointers = [];
for (const [codec, data] of this.#pointers) {
if (!codec.deref) continue;
if (!INTERNAL_CODEC.includes(codec)) {
customCodecPointers.push([codec, data]);
continue;
}
codec.deref(data);
}
for (const [codec, data] of customCodecPointers) codec.deref(data);
this.#complete = true;
return this.#root;
}
};
//#endregion
//#region src/encoder.ts
const INTERNAL_TYPES = [
[RegExp, "REGEXP"],
[Date, "DATE"],
[Map, "MAP"],
[Set, "SET"]
];
var Encoder = class {
#codec;
#uniqueValues;
#ignoreUnsupportedTypes;
#queue = new Queue();
#pointers = [];
constructor({ codec, uniqueValues, ignoreUnsupportedTypes, root }) {
this.#codec = [...codec.entries()];
this.#uniqueValues = uniqueValues ?? [];
this.#ignoreUnsupportedTypes = !!ignoreUnsupportedTypes;
this.#queue.enqueue(root);
}
#assertSupported(data) {
if (this.#ignoreUnsupportedTypes) return;
if (typeof data !== "function") return;
if (this.#uniqueValues.includes(data)) return;
throw new Error(`Unsupported type:\n${data}`);
}
#innerSerialize = (data) => {
let index = this.#uniqueValues.indexOf(data);
if (index !== -1) return concatBuffers([INTERNAL_TYPE_ID.UNIQUE_POINTER], flexUintCodec.encode(index));
const buffer = this.#serializeBasicValue(data);
if (buffer) return buffer;
index = this.#pointers.indexOf(data);
if (index === -1) {
index = this.#pointers.length;
this.#pointers.push(data);
this.#queue.enqueue(data);
}
return concatBuffers([INTERNAL_TYPE_ID.POINTER], flexUintCodec.encode(index));
};
#runInternalCodec(type, data) {
const head = [INTERNAL_TYPE_ID[type]];
const body = INTERNAL_CODEC_MAP[type].encode(data, this.#innerSerialize);
if (typeof INTERNAL_CODEC_MAP[type].bufferLength === "number") return concatBuffers(head, body);
return concatBuffers(head, flexUintCodec.encode(body.length), body);
}
#serializeUniqueValue(data) {
const index = this.#uniqueValues.indexOf(data);
if (index === -1) return null;
return concatBuffers([INTERNAL_TYPE_ID.UNIQUE_POINTER], flexUintCodec.encode(index));
}
#serializeBasicValue(data) {
const type = (typeof data).toUpperCase();
switch (type) {
case "NUMBER":
case "BIGINT":
case "UNDEFINED": return this.#runInternalCodec(type, data);
case "BOOLEAN": return this.#runInternalCodec(data ? "TRUE" : "FALSE", data);
}
if (data !== null) return null;
return this.#runInternalCodec("NULL", data);
}
#serializeReferableValue(data, atRoot) {
const index = this.#pointers.indexOf(data);
if (index === -1) this.#pointers.push(data);
else if (!atRoot) return concatBuffers([INTERNAL_TYPE_ID.POINTER], flexUintCodec.encode(index));
const type = (typeof data).toUpperCase();
switch (type) {
case "STRING":
case "SYMBOL": return this.#runInternalCodec(type, data);
}
return null;
}
#serializeBinaryValue(data) {
if (!isBinary(data)) return null;
return this.#runInternalCodec("BINARY", data);
}
#serializeCustomValue(data) {
for (const [id, [_, codec]] of this.#codec.entries()) {
if (!(data instanceof codec.class)) continue;
const body = this.#serialize(codec.encode(data));
return concatBuffers(flexUintCodec.encode(id + CUSTOM_TYPE_ID_BEGIN), body);
}
return null;
}
#serializeInternalValue(data) {
for (const [type, name] of INTERNAL_TYPES) {
if (!(data instanceof type)) continue;
return this.#runInternalCodec(name, data);
}
return null;
}
#serializeFallbackValue(data) {
if (Array.isArray(data)) return this.#runInternalCodec("ARRAY", data);
return this.#runInternalCodec("OBJECT", data);
}
#serialize(data, atRoot) {
this.#assertSupported(data);
let buffer = this.#serializeUniqueValue(data);
if (buffer) return buffer;
buffer = this.#serializeBasicValue(data);
if (buffer) return buffer;
buffer = this.#serializeReferableValue(data, atRoot);
if (buffer) return buffer;
buffer = this.#serializeBinaryValue(data);
if (buffer) return buffer;
buffer = this.#serializeCustomValue(data);
if (buffer) return buffer;
buffer = this.#serializeInternalValue(data);
if (buffer) return buffer;
return this.#serializeFallbackValue(data);
}
*encode() {
yield new Uint8Array([VERSION]);
this.#codec.sort(([_, a], [__, b]) => (a.parentClasses ?? []).includes(b.class) ? -1 : 1);
yield flexUintCodec.encode(this.#codec.length);
for (const [name] of this.#codec) {
const buffer = stringCodec.encode(name);
yield flexUintCodec.encode(buffer.length);
yield buffer;
}
for (const data of this.#queue.iter()) yield this.#serialize(data, true);
}
};
//#endregion
//#region src/web.ts
var ObjEncoder = class extends ReadableStream {
#encoder;
#generator;
constructor(options) {
super({
pull: (controller) => {
try {
const { value, done } = this.#generator.next();
if (done) controller.close();
else controller.enqueue(value);
} catch (error) {
controller.error(error);
}
},
cancel: () => {
this.#generator.return();
}
});
this.#encoder = new Encoder(options);
this.#generator = this.#encoder.encode();
}
/** 启动编码 */
encode() {
return this.#generator;
}
};
var ObjDecoder = class extends WritableStream {
#decoder;
constructor(options) {
super({ write: (chunk, controller) => {
try {
this.#decoder.decode(chunk);
} catch (error) {
controller.error(error);
}
} });
this.#decoder = new Decoder(options);
}
/** 解码 */
decode(buffer) {
this.#decoder.decode(buffer);
}
/**
* 获取结果
* @description
* 调用此方法将结束解码
*/
getResult() {
return this.#decoder.getResult();
}
};
var ObjCodec = class extends IObjCodec {
/**
* 编码
* @param root 编码根对象
*/
static encode(root, options) {
return new ObjEncoder({
...options,
root,
codec: globalCodecs
});
}
/** 解码 */
static decode(options) {
return new ObjDecoder({
...options,
codec: globalCodecs
});
}
encode(root, options) {
return new ObjEncoder({
uniqueValues: this.uniqueValues,
...options,
root,
codec: mergeCodecs(globalCodecs, this._codecs)
});
}
decode(options) {
return new ObjDecoder({
uniqueValues: this.uniqueValues,
...options,
codec: mergeCodecs(globalCodecs, this._codecs)
});
}
};
//#endregion
export { CUSTOM_TYPE_ID_BEGIN, INTERNAL_CODEC, INTERNAL_CODEC_MAP, INTERNAL_TYPE_ID, ObjCodec, Pointer, VERSION, bufferToUint8Array, concatBuffers, isBinary };
//# sourceMappingURL=web.esm.js.map