UNPKG

@cloudpss/ubjson

Version:

Opinionated UBJSON encoder/decoder for CloudPSS.

181 lines 7.52 kB
import { I8_MASK, writeNumber, writeTypedArray, writeTypedArrayData, writeTypedArrayHeader, } from '../helper/encode.js'; import { unsupportedType } from '../helper/errors.js'; import { stringByteLength, encodeInto } from '../helper/string-encoder.js'; const LARGE_DATA_LENGTH = 65536; const { isArray } = Array; // eslint-disable-next-line @typescript-eslint/unbound-method const { isView } = ArrayBuffer; const { keys: objectKeys } = Object; /** 编码至 ubjson */ export class EncoderBase { /** 序列化对象时排序属性 */ sortObjectKeys = false; /** 当前写指针位置 */ length = 0; /** 数据 */ data; /** buffer 的 DataView */ view; /** 编码至 ubjson,对于 `undefined` 写入 NOOP */ writeValue(value) { if (value === undefined) { this.ensureCapacity(1); this.data[this.length++] = 78 /* constants.NO_OP */; return; } this.write(value); } /** 写入一个对象 */ write(value) { switch (typeof value) { case 'string': if (value.length === 1 && value.charCodeAt(0) < 0x80) { // 1 byte ascii char this.ensureCapacity(2); this.data[this.length++] = 67 /* constants.CHAR */; this.data[this.length++] = value.charCodeAt(0); } else { this.ensureCapacity(2); this.data[this.length++] = 83 /* constants.STRING */; this.writeStringData(value); } return; case 'number': return writeNumber(this, value); case 'object': { if (value === null) { this.ensureCapacity(1); this.data[this.length++] = 90 /* constants.NULL */; return; } if (isArray(value)) { this.ensureCapacity(1); this.data[this.length++] = 91 /* constants.ARRAY */; const size = value.length; for (let index = 0; index < size; index++) { const element = value[index]; // 在数组中 undefined 和 function 也被视作 null 进行序列化 if (element == null || typeof element == 'function') { this.ensureCapacity(1); this.data[this.length++] = 90 /* constants.NULL */; } else { this.write(element); } } this.ensureCapacity(1); this.data[this.length++] = 93 /* constants.ARRAY_END */; return; } if (isView(value)) { if (value.byteLength <= LARGE_DATA_LENGTH) { writeTypedArray(this, value); return; } const type = writeTypedArrayHeader(this, value); this.writeLargeTypedArrayData(type, value); return; } const { toJSON } = value; if (typeof toJSON == 'function') { this.write(toJSON.call(value)); return; } // 生成稳定的结果以便 hash 计算 const keys = objectKeys(value); const size = keys.length; if (this.sortObjectKeys && size > 1) { keys.sort(); } this.ensureCapacity(2 + size); this.data[this.length++] = 123 /* constants.OBJECT */; for (let index = 0; index < size; index++) { const key = keys[index]; const element = value[key]; if (element === undefined || typeof element == 'function') continue; this.writeStringData(key); this.write(element); } this.ensureCapacity(1); this.data[this.length++] = 125 /* constants.OBJECT_END */; return; } case 'boolean': this.ensureCapacity(1); this.data[this.length++] = value ? 84 /* constants.TRUE */ : 70 /* constants.FALSE */; return; case 'bigint': // int32 range if (value >= -2147483648n && value <= 2147483647n) { this.write(Number(value)); } // int64 range else if (value >= -9223372036854775808n && value <= 9223372036854775807n) { this.ensureCapacity(9); this.data[this.length++] = 76 /* constants.INT64 */; this.view.setBigInt64(this.length, value); this.length += 8; } else { throw new RangeError(`BigInt value out of range: ${value}`); } return; default: unsupportedType(value); } } /** writeStringData */ writeStringData(value) { const strLength = value.length; if (strLength > LARGE_DATA_LENGTH) { return this.writeLargeStringData(value); } // 对于短字符串,直接计算最大使用空间 const maxUsage = strLength * 3; // 一次性分配 setLength 和 encodeInto 的空间,避免无法回溯 // 额外分配 3 字节,避免 encodeInto 无法写入最后一个字符 this.ensureCapacity(maxUsage + 5 + 3); // 预估头部大小 const headerSize = strLength < 0x80 ? 2 : strLength < 0x8000 ? 3 : 5; const { length: headerPos, data, view } = this; const bufLength = encodeInto(value, data, headerPos + headerSize); if (bufLength < 0x80) { view.setInt16(headerPos, I8_MASK | bufLength); this.length = headerPos + 2 + bufLength; } else if (bufLength < 0x8000) { if (headerSize < 3) { data.copyWithin(headerPos + 3, headerPos + headerSize, headerPos + headerSize + bufLength); } data[headerPos] = 73 /* constants.INT16 */; view.setInt16(headerPos + 1, bufLength); this.length = headerPos + 3 + bufLength; } else { if (headerSize < 5) { data.copyWithin(headerPos + 5, headerPos + headerSize, headerPos + headerSize + bufLength); } data[headerPos] = 108 /* constants.INT32 */; view.setInt32(headerPos + 1, bufLength); this.length = headerPos + 5 + bufLength; } } /** 写入大字符串 */ writeLargeStringData(value) { const binLen = stringByteLength(value); this.ensureCapacity(5); this.data[this.length++] = 108 /* constants.INT32 */; this.view.setInt32(this.length, binLen); this.length += 4; this.ensureCapacity(binLen); encodeInto(value, this.data, this.length); this.length += binLen; } /** 写入数组 */ writeLargeTypedArrayData(type, value) { writeTypedArrayData(this, type, value); } } //# sourceMappingURL=encoder.js.map