Files
travel-app/node_modules/msgpackr/dist/test.js
2026-08-09 21:56:00 +00:00

3882 lines
129 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
(function (chai, stream, module, fs) {
'use strict';
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
var decoder;
try {
decoder = new TextDecoder();
} catch(error) {}
var src;
var srcEnd;
var position$1 = 0;
const EMPTY_ARRAY = [];
var strings = EMPTY_ARRAY;
var stringPosition = 0;
var currentUnpackr = {};
var currentStructures;
var srcString;
var srcStringStart = 0;
var srcStringEnd = 0;
var bundledStrings$1;
var referenceMap;
var currentExtensions = [];
var dataView;
var defaultOptions = {
useRecords: false,
mapsAsObjects: true
};
class C1Type {}
const C1 = new C1Type();
C1.name = 'MessagePack 0xC1';
var sequentialMode = false;
var inlineObjectReadThreshold = 2;
let Unpackr$1 = class Unpackr {
constructor(options) {
if (options) {
if (options.useRecords === false && options.mapsAsObjects === undefined)
options.mapsAsObjects = true;
if (options.sequential && options.trusted !== false) {
options.trusted = true;
if (!options.structures && options.useRecords != false) {
options.structures = [];
if (!options.maxSharedStructures)
options.maxSharedStructures = 0;
}
}
if (options.structures)
options.structures.sharedLength = options.structures.length;
else if (options.getStructures) {
(options.structures = []).uninitialized = true; // this is what we use to denote an uninitialized structures
options.structures.sharedLength = 0;
}
if (options.int64AsNumber) {
options.int64AsType = 'number';
}
}
Object.assign(this, options);
}
unpack(source, options) {
if (src) {
// re-entrant execution, save the state and restore it after we do this unpack
return saveState(() => {
clearSource();
return this ? this.unpack(source, options) : Unpackr.prototype.unpack.call(defaultOptions, source, options);
});
}
if (!source.buffer && source.constructor === ArrayBuffer)
source = typeof Buffer !== 'undefined' ? Buffer.from(source) : new Uint8Array(source);
if (typeof options === 'object') {
srcEnd = options.end || source.length;
position$1 = options.start || 0;
} else {
position$1 = 0;
srcEnd = options > -1 ? options : source.length;
}
stringPosition = 0;
srcStringEnd = 0;
srcString = null;
strings = EMPTY_ARRAY;
bundledStrings$1 = null;
src = source;
// this provides cached access to the data view for a buffer if it is getting reused, which is a recommend
// technique for getting data from a database where it can be copied into an existing buffer instead of creating
// new ones
try {
dataView = source.dataView || (source.dataView = new DataView(source.buffer, source.byteOffset, source.byteLength));
} catch(error) {
// if it doesn't have a buffer, maybe it is the wrong type of object
src = null;
if (source instanceof Uint8Array)
throw error;
throw new Error('Source must be a Uint8Array or Buffer but was a ' + ((source && typeof source == 'object') ? source.constructor.name : typeof source));
}
if (this instanceof Unpackr) {
currentUnpackr = this;
if (this.structures) {
currentStructures = this.structures;
return checkedRead(options);
} else if (!currentStructures || currentStructures.length > 0) {
currentStructures = [];
}
} else {
currentUnpackr = defaultOptions;
if (!currentStructures || currentStructures.length > 0)
currentStructures = [];
}
return checkedRead(options);
}
unpackMultiple(source, forEach) {
let values, lastPosition = 0;
try {
sequentialMode = true;
let size = source.length;
let value = this ? this.unpack(source, size) : defaultUnpackr.unpack(source, size);
if (forEach) {
if (forEach(value, lastPosition, position$1) === false) return;
while(position$1 < size) {
lastPosition = position$1;
if (forEach(checkedRead(), lastPosition, position$1) === false) {
return;
}
}
}
else {
values = [ value ];
while(position$1 < size) {
lastPosition = position$1;
values.push(checkedRead());
}
return values;
}
} catch(error) {
error.lastPosition = lastPosition;
error.values = values;
throw error;
} finally {
sequentialMode = false;
clearSource();
}
}
_mergeStructures(loadedStructures, existingStructures) {
if (this._onLoadedStructures)
loadedStructures = this._onLoadedStructures(loadedStructures);
loadedStructures = loadedStructures || [];
if (Object.isFrozen(loadedStructures))
loadedStructures = loadedStructures.map(structure => structure.slice(0));
for (let i = 0, l = loadedStructures.length; i < l; i++) {
let structure = loadedStructures[i];
if (structure) {
structure.isShared = true;
if (i >= 32)
structure.highByte = (i - 32) >> 5;
}
}
loadedStructures.sharedLength = loadedStructures.length;
for (let id in existingStructures || []) {
if (id >= 0) {
let structure = loadedStructures[id];
let existing = existingStructures[id];
if (existing) {
if (structure)
(loadedStructures.restoreStructures || (loadedStructures.restoreStructures = []))[id] = structure;
loadedStructures[id] = existing;
}
}
}
return this.structures = loadedStructures;
}
decode(source, options) {
return this.unpack(source, options);
}
};
function checkedRead(options) {
try {
if (!currentUnpackr.trusted && !sequentialMode) {
let sharedLength = currentStructures.sharedLength || 0;
if (sharedLength < currentStructures.length)
currentStructures.length = sharedLength;
}
let result;
if (currentUnpackr._readStruct && src[position$1] < 0x40 && src[position$1] >= 0x20) {
result = currentUnpackr._readStruct(src, position$1, srcEnd);
src = null; // dispose of this so that recursive unpack calls don't save state
if (!(options && options.lazy) && result)
result = result.toJSON();
position$1 = srcEnd;
} else
result = read();
if (bundledStrings$1) { // bundled strings to skip past
position$1 = bundledStrings$1.postBundlePosition;
bundledStrings$1 = null;
}
if (sequentialMode)
// we only need to restore the structures if there was an error, but if we completed a read,
// we can clear this out and keep the structures we read
currentStructures.restoreStructures = null;
if (position$1 == srcEnd) {
// finished reading this source, cleanup references
if (currentStructures && currentStructures.restoreStructures)
restoreStructures();
currentStructures = null;
src = null;
if (referenceMap)
referenceMap = null;
} else if (position$1 > srcEnd) {
// over read
throw new Error('Unexpected end of MessagePack data');
} else if (!sequentialMode) {
let jsonView;
try {
jsonView = JSON.stringify(result, (_, value) => typeof value === "bigint" ? `${value}n` : value).slice(0, 100);
} catch(error) {
jsonView = '(JSON view not available ' + error + ')';
}
throw new Error('Data read, but end of buffer not reached ' + jsonView);
}
// else more to read, but we are reading sequentially, so don't clear source yet
return result;
} catch(error) {
if (currentStructures && currentStructures.restoreStructures)
restoreStructures();
clearSource();
if (error instanceof RangeError || error.message.startsWith('Unexpected end of buffer') || position$1 > srcEnd) {
error.incomplete = true;
}
throw error;
}
}
function restoreStructures() {
for (let id in currentStructures.restoreStructures) {
currentStructures[id] = currentStructures.restoreStructures[id];
}
currentStructures.restoreStructures = null;
}
function read() {
let token = src[position$1++];
if (token < 0xa0) {
if (token < 0x80) {
if (token < 0x40)
return token;
else {
let structure = currentStructures[token & 0x3f] ||
currentUnpackr.getStructures && loadStructures()[token & 0x3f];
if (structure) {
if (!structure.read) {
structure.read = createStructureReader(structure, token & 0x3f);
}
return structure.read();
} else
return token;
}
} else if (token < 0x90) {
// map
token -= 0x80;
if (currentUnpackr.mapsAsObjects) {
let object = {};
for (let i = 0; i < token; i++) {
let key = readKey();
if (key === '__proto__')
key = '__proto_';
object[key] = read();
}
return object;
} else {
let map = new Map();
for (let i = 0; i < token; i++) {
map.set(read(), read());
}
return map;
}
} else {
token -= 0x90;
let array = new Array(token);
for (let i = 0; i < token; i++) {
array[i] = read();
}
if (currentUnpackr.freezeData)
return Object.freeze(array);
return array;
}
} else if (token < 0xc0) {
// fixstr
let length = token - 0xa0;
if (srcStringEnd >= position$1) {
return srcString.slice(position$1 - srcStringStart, (position$1 += length) - srcStringStart);
}
if (srcStringEnd == 0 && srcEnd < 140) {
// for small blocks, avoiding the overhead of the extract call is helpful
let string = length < 16 ? shortStringInJS(length) : longStringInJS(length);
if (string != null)
return string;
}
return readFixedString(length);
} else {
let value;
switch (token) {
case 0xc0: return null;
case 0xc1:
if (bundledStrings$1) {
value = read(); // followed by the length of the string in characters (not bytes!)
if (value > 0)
return bundledStrings$1[1].slice(bundledStrings$1.position1, bundledStrings$1.position1 += value);
else
return bundledStrings$1[0].slice(bundledStrings$1.position0, bundledStrings$1.position0 -= value);
}
return C1; // "never-used", return special object to denote that
case 0xc2: return false;
case 0xc3: return true;
case 0xc4:
// bin 8
value = src[position$1++];
if (value === undefined)
throw new Error('Unexpected end of buffer');
return readBin(value);
case 0xc5:
// bin 16
value = dataView.getUint16(position$1);
position$1 += 2;
return readBin(value);
case 0xc6:
// bin 32
value = dataView.getUint32(position$1);
position$1 += 4;
return readBin(value);
case 0xc7:
// ext 8
return readExt(src[position$1++]);
case 0xc8:
// ext 16
value = dataView.getUint16(position$1);
position$1 += 2;
return readExt(value);
case 0xc9:
// ext 32
value = dataView.getUint32(position$1);
position$1 += 4;
return readExt(value);
case 0xca:
value = dataView.getFloat32(position$1);
if (currentUnpackr.useFloat32 > 2) {
// this does rounding of numbers that were encoded in 32-bit float to nearest significant decimal digit that could be preserved
let multiplier = mult10[((src[position$1] & 0x7f) << 1) | (src[position$1 + 1] >> 7)];
position$1 += 4;
return ((multiplier * value + (value > 0 ? 0.5 : -0.5)) >> 0) / multiplier;
}
position$1 += 4;
return value;
case 0xcb:
value = dataView.getFloat64(position$1);
position$1 += 8;
return value;
// uint handlers
case 0xcc:
return src[position$1++];
case 0xcd:
value = dataView.getUint16(position$1);
position$1 += 2;
return value;
case 0xce:
value = dataView.getUint32(position$1);
position$1 += 4;
return value;
case 0xcf:
if (currentUnpackr.int64AsType === 'number') {
value = dataView.getUint32(position$1) * 0x100000000;
value += dataView.getUint32(position$1 + 4);
} else if (currentUnpackr.int64AsType === 'string') {
value = dataView.getBigUint64(position$1).toString();
} else if (currentUnpackr.int64AsType === 'auto') {
value = dataView.getBigUint64(position$1);
if (value<=BigInt(2)<<BigInt(52)) value=Number(value);
} else
value = dataView.getBigUint64(position$1);
position$1 += 8;
return value;
// int handlers
case 0xd0:
return dataView.getInt8(position$1++);
case 0xd1:
value = dataView.getInt16(position$1);
position$1 += 2;
return value;
case 0xd2:
value = dataView.getInt32(position$1);
position$1 += 4;
return value;
case 0xd3:
if (currentUnpackr.int64AsType === 'number') {
value = dataView.getInt32(position$1) * 0x100000000;
value += dataView.getUint32(position$1 + 4);
} else if (currentUnpackr.int64AsType === 'string') {
value = dataView.getBigInt64(position$1).toString();
} else if (currentUnpackr.int64AsType === 'auto') {
value = dataView.getBigInt64(position$1);
if (value>=BigInt(-2)<<BigInt(52)&&value<=BigInt(2)<<BigInt(52)) value=Number(value);
} else
value = dataView.getBigInt64(position$1);
position$1 += 8;
return value;
case 0xd4:
// fixext 1
value = src[position$1++];
if (value == 0x72) {
return recordDefinition(src[position$1++] & 0x3f);
} else {
let extension = currentExtensions[value];
if (extension) {
if (extension.read) {
position$1++; // skip filler byte
return extension.read(read());
} else if (extension.noBuffer) {
position$1++; // skip filler byte
return extension();
} else
return extension(src.subarray(position$1, ++position$1));
} else
throw new Error('Unknown extension ' + value);
}
case 0xd5:
// fixext 2
value = src[position$1];
if (value == 0x72) {
position$1++;
return recordDefinition(src[position$1++] & 0x3f, src[position$1++]);
} else
return readExt(2);
case 0xd6:
// fixext 4
return readExt(4);
case 0xd7:
// fixext 8
return readExt(8);
case 0xd8:
// fixext 16
return readExt(16);
case 0xd9:
// str 8
value = src[position$1++];
if (srcStringEnd >= position$1) {
return srcString.slice(position$1 - srcStringStart, (position$1 += value) - srcStringStart);
}
return readString8(value);
case 0xda:
// str 16
value = dataView.getUint16(position$1);
position$1 += 2;
if (srcStringEnd >= position$1) {
return srcString.slice(position$1 - srcStringStart, (position$1 += value) - srcStringStart);
}
return readString16(value);
case 0xdb:
// str 32
value = dataView.getUint32(position$1);
position$1 += 4;
if (srcStringEnd >= position$1) {
return srcString.slice(position$1 - srcStringStart, (position$1 += value) - srcStringStart);
}
return readString32(value);
case 0xdc:
// array 16
value = dataView.getUint16(position$1);
position$1 += 2;
return readArray(value);
case 0xdd:
// array 32
value = dataView.getUint32(position$1);
position$1 += 4;
return readArray(value);
case 0xde:
// map 16
value = dataView.getUint16(position$1);
position$1 += 2;
return readMap(value);
case 0xdf:
// map 32
value = dataView.getUint32(position$1);
position$1 += 4;
return readMap(value);
default: // negative int
if (token >= 0xe0)
return token - 0x100;
if (token === undefined) {
let error = new Error('Unexpected end of MessagePack data');
error.incomplete = true;
throw error;
}
throw new Error('Unknown MessagePack token ' + token);
}
}
}
const validName = /^[a-zA-Z_$][a-zA-Z\d_$]*$/;
function createStructureReader(structure, firstId) {
function readObject() {
// This initial function is quick to instantiate, but runs slower. After several iterations pay the cost to build the faster function
if (readObject.count++ > inlineObjectReadThreshold) {
let optimizedReadObject;
try {
optimizedReadObject = structure.read = (new Function('r', 'return function(){return ' + (currentUnpackr.freezeData ? 'Object.freeze' : '') +
'({' + structure.map(key => key === '__proto__' ? '__proto_:r()' : validName.test(key) ? key + ':r()' : ('[' + JSON.stringify(key) + ']:r()')).join(',') + '})}'))(read);
} catch(error) {
// in CF workers, the new Function call could begin to fail at any point in time
inlineObjectReadThreshold = Infinity; // disable going forward
return readObject(); // recursively try again
}
structure.read0 = optimizedReadObject; // keep the un-wrapped body reader in sync
if (structure.highByte === 0)
structure.read = createSecondByteReader(firstId, structure.read);
return optimizedReadObject(); // second byte is already read, if there is one so immediately read object
}
let object = {};
for (let i = 0, l = structure.length; i < l; i++) {
let key = structure[i];
if (key === '__proto__')
key = '__proto_';
object[key] = read();
}
if (currentUnpackr.freezeData)
return Object.freeze(object);
return object;
}
readObject.count = 0;
// read0 is the un-wrapped body reader: it reads the record's values directly without
// consuming a leading high byte. recordDefinition uses it for the immediate read that follows
// a record definition (the high byte, if present, was already consumed). For highByte === 0
// structures the public reader is a second-byte reader (used by later references), but the
// definition read itself must not consume that byte.
structure.read0 = readObject;
if (structure.highByte === 0) {
return createSecondByteReader(firstId, readObject);
}
return readObject;
}
const createSecondByteReader = (firstId, read0) => {
return function() {
let highByte = src[position$1++];
if (highByte === 0)
return read0();
let id = firstId < 32 ? -(firstId + (highByte << 5)) : firstId + (highByte << 5);
let structure = currentStructures[id] || loadStructures()[id];
if (!structure) {
throw new Error('Record id is not defined for ' + id);
}
if (!structure.read)
structure.read = createStructureReader(structure, firstId);
return structure.read();
};
};
function loadStructures() {
let loadedStructures = saveState(() => {
// save the state in case getStructures modifies our buffer
src = null;
return currentUnpackr.getStructures();
});
return currentStructures = currentUnpackr._mergeStructures(loadedStructures, currentStructures);
}
var readFixedString = readStringJS;
var readString8 = readStringJS;
var readString16 = readStringJS;
var readString32 = readStringJS;
function setExtractor(extractStrings) {
readFixedString = readString(1);
readString8 = readString(2);
readString16 = readString(3);
readString32 = readString(5);
function readString(headerLength) {
return function readString(length) {
let string = strings[stringPosition++];
if (string == null) {
if (bundledStrings$1)
return readStringJS(length);
let byteOffset = src.byteOffset;
let extraction = extractStrings(position$1 - headerLength + byteOffset, srcEnd + byteOffset, src.buffer);
if (typeof extraction == 'string') {
string = extraction;
strings = EMPTY_ARRAY;
} else {
strings = extraction;
stringPosition = 1;
srcStringEnd = 1; // even if a utf-8 string was decoded, must indicate we are in the midst of extracted strings and can't skip strings
string = strings[0];
if (string === undefined)
throw new Error('Unexpected end of buffer');
}
}
let srcStringLength = string.length;
if (srcStringLength <= length) {
position$1 += length;
return string;
}
srcString = string;
srcStringStart = position$1;
srcStringEnd = position$1 + srcStringLength;
position$1 += length;
return string.slice(0, length); // we know we just want the beginning
};
}
}
function readStringJS(length) {
let result;
if (length < 16) {
if (result = shortStringInJS(length))
return result;
}
if (length > 64 && decoder)
return decoder.decode(src.subarray(position$1, position$1 += length));
const end = position$1 + length;
const units = [];
result = '';
while (position$1 < end) {
const byte1 = src[position$1++];
if ((byte1 & 0x80) === 0) {
// 1 byte
units.push(byte1);
} else if ((byte1 & 0xe0) === 0xc0) {
// 2 bytes
if (byte1 < 0xc2 || position$1 >= end || (src[position$1] & 0xc0) !== 0x80) {
units.push(0xFFFD);
} else {
const byte2 = src[position$1++] & 0x3f;
units.push(((byte1 & 0x1f) << 6) | byte2);
}
} else if ((byte1 & 0xf0) === 0xe0) {
// 3 bytes
const byte2 = position$1 < end ? src[position$1] : 0;
if (position$1 >= end || (byte2 & 0xc0) !== 0x80 ||
(byte1 === 0xe0 && byte2 < 0xa0) || (byte1 === 0xed && byte2 >= 0xa0)) {
units.push(0xFFFD);
} else {
position$1++;
if (position$1 >= end || (src[position$1] & 0xc0) !== 0x80) {
units.push(0xFFFD);
} else {
const byte3 = src[position$1++] & 0x3f;
units.push(((byte1 & 0x1f) << 12) | ((byte2 & 0x3f) << 6) | byte3);
}
}
} else if ((byte1 & 0xf8) === 0xf0) {
// 4 bytes
const byte2 = position$1 < end ? src[position$1] : 0;
if (byte1 > 0xf4 || position$1 >= end || (byte2 & 0xc0) !== 0x80 ||
(byte1 === 0xf0 && byte2 < 0x90) || (byte1 === 0xf4 && byte2 >= 0x90)) {
units.push(0xFFFD);
} else {
position$1++;
if (position$1 >= end || (src[position$1] & 0xc0) !== 0x80) {
units.push(0xFFFD);
} else {
const byte3 = src[position$1++] & 0x3f;
if (position$1 >= end || (src[position$1] & 0xc0) !== 0x80) {
units.push(0xFFFD);
} else {
const byte4 = src[position$1++] & 0x3f;
let unit = ((byte1 & 0x07) << 0x12) | ((byte2 & 0x3f) << 0x0c) | (byte3 << 0x06) | byte4;
unit -= 0x10000;
units.push(((unit >>> 10) & 0x3ff) | 0xd800);
units.push(0xdc00 | (unit & 0x3ff));
}
}
}
} else {
units.push(0xFFFD); // replacement character for invalid lead byte
}
if (units.length >= 0x1000) {
result += fromCharCode.apply(String, units);
units.length = 0;
}
}
if (units.length > 0) {
result += fromCharCode.apply(String, units);
}
return result;
}
function readArray(length) {
let array = new Array(length);
for (let i = 0; i < length; i++) {
array[i] = read();
}
if (currentUnpackr.freezeData)
return Object.freeze(array);
return array;
}
function readMap(length) {
if (currentUnpackr.mapsAsObjects) {
let object = {};
for (let i = 0; i < length; i++) {
let key = readKey();
if (key === '__proto__')
key = '__proto_';
object[key] = read();
}
return object;
} else {
let map = new Map();
for (let i = 0; i < length; i++) {
map.set(read(), read());
}
return map;
}
}
var fromCharCode = String.fromCharCode;
function longStringInJS(length) {
let start = position$1;
let bytes = new Array(length);
for (let i = 0; i < length; i++) {
const byte = src[position$1++];
if ((byte & 0x80) > 0) {
position$1 = start;
return;
}
bytes[i] = byte;
}
return fromCharCode.apply(String, bytes);
}
function shortStringInJS(length) {
if (length < 4) {
if (length < 2) {
if (length === 0)
return '';
else {
let a = src[position$1++];
if ((a & 0x80) > 1) {
position$1 -= 1;
return;
}
return fromCharCode(a);
}
} else {
let a = src[position$1++];
let b = src[position$1++];
if ((a & 0x80) > 0 || (b & 0x80) > 0) {
position$1 -= 2;
return;
}
if (length < 3)
return fromCharCode(a, b);
let c = src[position$1++];
if ((c & 0x80) > 0) {
position$1 -= 3;
return;
}
return fromCharCode(a, b, c);
}
} else {
let a = src[position$1++];
let b = src[position$1++];
let c = src[position$1++];
let d = src[position$1++];
if ((a & 0x80) > 0 || (b & 0x80) > 0 || (c & 0x80) > 0 || (d & 0x80) > 0) {
position$1 -= 4;
return;
}
if (length < 6) {
if (length === 4)
return fromCharCode(a, b, c, d);
else {
let e = src[position$1++];
if ((e & 0x80) > 0) {
position$1 -= 5;
return;
}
return fromCharCode(a, b, c, d, e);
}
} else if (length < 8) {
let e = src[position$1++];
let f = src[position$1++];
if ((e & 0x80) > 0 || (f & 0x80) > 0) {
position$1 -= 6;
return;
}
if (length < 7)
return fromCharCode(a, b, c, d, e, f);
let g = src[position$1++];
if ((g & 0x80) > 0) {
position$1 -= 7;
return;
}
return fromCharCode(a, b, c, d, e, f, g);
} else {
let e = src[position$1++];
let f = src[position$1++];
let g = src[position$1++];
let h = src[position$1++];
if ((e & 0x80) > 0 || (f & 0x80) > 0 || (g & 0x80) > 0 || (h & 0x80) > 0) {
position$1 -= 8;
return;
}
if (length < 10) {
if (length === 8)
return fromCharCode(a, b, c, d, e, f, g, h);
else {
let i = src[position$1++];
if ((i & 0x80) > 0) {
position$1 -= 9;
return;
}
return fromCharCode(a, b, c, d, e, f, g, h, i);
}
} else if (length < 12) {
let i = src[position$1++];
let j = src[position$1++];
if ((i & 0x80) > 0 || (j & 0x80) > 0) {
position$1 -= 10;
return;
}
if (length < 11)
return fromCharCode(a, b, c, d, e, f, g, h, i, j);
let k = src[position$1++];
if ((k & 0x80) > 0) {
position$1 -= 11;
return;
}
return fromCharCode(a, b, c, d, e, f, g, h, i, j, k);
} else {
let i = src[position$1++];
let j = src[position$1++];
let k = src[position$1++];
let l = src[position$1++];
if ((i & 0x80) > 0 || (j & 0x80) > 0 || (k & 0x80) > 0 || (l & 0x80) > 0) {
position$1 -= 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 = src[position$1++];
if ((m & 0x80) > 0) {
position$1 -= 13;
return;
}
return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l, m);
}
} else {
let m = src[position$1++];
let n = src[position$1++];
if ((m & 0x80) > 0 || (n & 0x80) > 0) {
position$1 -= 14;
return;
}
if (length < 15)
return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l, m, n);
let o = src[position$1++];
if ((o & 0x80) > 0) {
position$1 -= 15;
return;
}
return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o);
}
}
}
}
}
function readOnlyJSString() {
let token = src[position$1++];
let length;
if (token < 0xc0) {
// fixstr
length = token - 0xa0;
} else {
switch(token) {
case 0xd9:
// str 8
length = src[position$1++];
break;
case 0xda:
// str 16
length = dataView.getUint16(position$1);
position$1 += 2;
break;
case 0xdb:
// str 32
length = dataView.getUint32(position$1);
position$1 += 4;
break;
default:
throw new Error('Expected string');
}
}
return readStringJS(length);
}
function readBin(length) {
return currentUnpackr.copyBuffers ?
// specifically use the copying slice (not the node one)
Uint8Array.prototype.slice.call(src, position$1, position$1 += length) :
src.subarray(position$1, position$1 += length);
}
function readExt(length) {
let type = src[position$1++];
if (currentExtensions[type]) {
let end;
return currentExtensions[type](src.subarray(position$1, end = (position$1 += length)), (readPosition) => {
position$1 = readPosition;
try {
return read();
} finally {
position$1 = end;
}
});
}
else
throw new Error('Unknown extension type ' + type);
}
var keyCache = new Array(4096);
function readKey() {
let length = src[position$1++];
if (length >= 0xa0 && length < 0xc0) {
// fixstr, potentially use key cache
length = length - 0xa0;
if (srcStringEnd >= position$1) // if it has been extracted, must use it (and faster anyway)
return srcString.slice(position$1 - srcStringStart, (position$1 += length) - srcStringStart);
else if (!(srcStringEnd == 0 && srcEnd < 180))
return readFixedString(length);
} else { // not cacheable, go back and do a standard read
position$1--;
return asSafeString(read());
}
let key = ((length << 5) ^ (length > 1 ? dataView.getUint16(position$1) : length > 0 ? src[position$1] : 0)) & 0xfff;
let entry = keyCache[key];
let checkPosition = position$1;
let end = position$1 + length - 3;
let chunk;
let i = 0;
if (entry && entry.bytes == length) {
while (checkPosition < end) {
chunk = dataView.getUint32(checkPosition);
if (chunk != entry[i++]) {
checkPosition = 0x70000000;
break;
}
checkPosition += 4;
}
end += 3;
while (checkPosition < end) {
chunk = src[checkPosition++];
if (chunk != entry[i++]) {
checkPosition = 0x70000000;
break;
}
}
if (checkPosition === end) {
position$1 = checkPosition;
return entry.string;
}
end -= 3;
checkPosition = position$1;
}
entry = [];
keyCache[key] = entry;
entry.bytes = length;
while (checkPosition < end) {
chunk = dataView.getUint32(checkPosition);
entry.push(chunk);
checkPosition += 4;
}
end += 3;
while (checkPosition < end) {
chunk = src[checkPosition++];
entry.push(chunk);
}
// for small blocks, avoiding the overhead of the extract call is helpful
let string = length < 16 ? shortStringInJS(length) : longStringInJS(length);
if (string != null)
return entry.string = string;
return entry.string = readFixedString(length);
}
function asSafeString(property) {
// protect against expensive (DoS) string conversions
if (typeof property === 'string') return property;
if (typeof property === 'number' || typeof property === 'boolean' || typeof property === 'bigint') return property.toString();
if (property == null) return property + '';
if (currentUnpackr.allowArraysInMapKeys && Array.isArray(property) && property.flat().every(item => ['string', 'number', 'boolean', 'bigint'].includes(typeof item))) {
return property.flat().toString();
}
throw new Error(`Invalid property type for record: ${typeof property}`);
}
// the registration of the record definition extension (as "r")
const recordDefinition = (id, highByte) => {
let structure = read().map(asSafeString); // ensure that all keys are strings and
// that the array is mutable
let firstByte = id;
if (highByte !== undefined) {
id = id < 32 ? -((highByte << 5) + id) : ((highByte << 5) + id);
structure.highByte = highByte;
}
let existingStructure = currentStructures[id];
// If it is a shared structure, we need to restore any changes after reading.
// Also in sequential mode, we may get incomplete reads and thus errors, and we need to restore
// to the state prior to an incomplete read in order to properly resume.
if (existingStructure && (existingStructure.isShared || sequentialMode)) {
(currentStructures.restoreStructures || (currentStructures.restoreStructures = []))[id] = existingStructure;
}
currentStructures[id] = structure;
structure.read = createStructureReader(structure, firstByte);
// The high byte (if any) was already consumed as the `highByte` argument above, so read the
// record body directly. Going through structure.read (a second-byte reader when highByte === 0)
// would misinterpret the first value byte as a high byte — corrupting two-byte own-record
// definitions (0xd5 0x72 ...). createStructureReader stashes the un-wrapped body reader on
// structure.read0 precisely for this immediate post-definition read.
return (structure.read0 || structure.read)();
};
currentExtensions[0] = () => {}; // notepack defines extension 0 to mean undefined, so use that as the default here
currentExtensions[0].noBuffer = true;
currentExtensions[0x42] = data => {
let headLength = (data.byteLength % 8) || 8;
let head = BigInt(data[0] & 0x80 ? data[0] - 0x100 : data[0]);
for (let i = 1; i < headLength; i++) {
head <<= BigInt(8);
head += BigInt(data[i]);
}
if (data.byteLength !== headLength) {
let view = new DataView(data.buffer, data.byteOffset, data.byteLength);
let decode = (start, end) => {
let length = end - start;
if (length <= 40) {
let out = view.getBigUint64(start);
for (let i = start + 8; i < end; i += 8) {
out <<= BigInt(64);
out |= view.getBigUint64(i);
}
return out;
}
// if (length === 8) return view.getBigUint64(start)
let middle = start + (length >> 4 << 3);
let left = decode(start, middle);
let right = decode(middle, end);
return (left << BigInt((end - middle) * 8)) | right;
};
head = (head << BigInt((view.byteLength - headLength) * 8)) | decode(headLength, view.byteLength);
}
return head;
};
let errors = {
Error, EvalError, RangeError, ReferenceError, SyntaxError, TypeError, URIError, AggregateError: typeof AggregateError === 'function' ? AggregateError : null,
};
currentExtensions[0x65] = () => {
let data = read();
if (!errors[data[0]]) {
let error = Error(data[1], { cause: data[2] });
error.name = data[0];
return error;
}
return errors[data[0]](data[1], { cause: data[2] });
};
currentExtensions[0x69] = (data) => {
// id extension (for structured clones)
if (currentUnpackr.structuredClone === false) throw new Error('Structured clone extension is disabled');
let id = dataView.getUint32(position$1 - 4);
if (!referenceMap)
referenceMap = new Map();
let token = src[position$1];
let target;
// TODO: handle any other types that can cycle and make the code more robust if there are other extensions
if (token >= 0x90 && token < 0xa0 || token == 0xdc || token == 0xdd)
target = [];
else if (token >= 0x80 && token < 0x90 || token == 0xde || token == 0xdf)
target = new Map();
else if ((token >= 0xc7 && token <= 0xc9 || token >= 0xd4 && token <= 0xd8) && src[position$1 + 1] === 0x73)
target = new Set();
else
target = {};
let refEntry = { target }; // a placeholder object
referenceMap.set(id, refEntry);
let targetProperties = read(); // read the next value as the target object to id
if (!refEntry.used) {
// no cycle, can just use the returned read object
return refEntry.target = targetProperties; // replace the placeholder with the real one
} else {
// there is a cycle, so we have to assign properties to original target
Object.assign(target, targetProperties);
}
// copy over map/set entries if we're able to
if (target instanceof Map)
for (let [k, v] of targetProperties.entries()) target.set(k, v);
if (target instanceof Set)
for (let i of Array.from(targetProperties)) target.add(i);
return target;
};
currentExtensions[0x70] = (data) => {
// pointer extension (for structured clones)
if (currentUnpackr.structuredClone === false) throw new Error('Structured clone extension is disabled');
let id = dataView.getUint32(position$1 - 4);
let refEntry = referenceMap.get(id);
refEntry.used = true;
return refEntry.target;
};
currentExtensions[0x73] = () => new Set(read());
const typedArrays = ['Int8','Uint8','Uint8Clamped','Int16','Uint16','Int32','Uint32','Float32','Float64','BigInt64','BigUint64'].map(type => type + 'Array');
let glbl = typeof globalThis === 'object' ? globalThis : window;
currentExtensions[0x74] = (data) => {
let typeCode = data[0];
// we always have to slice to get a new ArrayBuffer that is aligned
let buffer = Uint8Array.prototype.slice.call(data, 1).buffer;
let typedArrayName = typedArrays[typeCode];
if (!typedArrayName) {
if (typeCode === 16) return buffer;
if (typeCode === 17) return new DataView(buffer);
throw new Error('Could not find typed array for code ' + typeCode);
}
return new glbl[typedArrayName](buffer);
};
currentExtensions[0x78] = () => {
let data = read();
return new RegExp(data[0], data[1]);
};
const TEMP_BUNDLE = [];
currentExtensions[0x62] = (data) => {
let dataSize = (data[0] << 24) + (data[1] << 16) + (data[2] << 8) + data[3];
let dataPosition = position$1;
position$1 += dataSize - data.length;
bundledStrings$1 = TEMP_BUNDLE;
bundledStrings$1 = [readOnlyJSString(), readOnlyJSString()];
bundledStrings$1.position0 = 0;
bundledStrings$1.position1 = 0;
bundledStrings$1.postBundlePosition = position$1;
position$1 = dataPosition;
return read();
};
currentExtensions[0xff] = (data) => {
// 32-bit date extension
if (data.length == 4)
return new Date((data[0] * 0x1000000 + (data[1] << 16) + (data[2] << 8) + data[3]) * 1000);
else if (data.length == 8)
return new Date(
((data[0] << 22) + (data[1] << 14) + (data[2] << 6) + (data[3] >> 2)) / 1000000 +
((data[3] & 0x3) * 0x100000000 + data[4] * 0x1000000 + (data[5] << 16) + (data[6] << 8) + data[7]) * 1000);
else if (data.length == 12)
return new Date(
((data[0] << 24) + (data[1] << 16) + (data[2] << 8) + data[3]) / 1000000 +
(((data[4] & 0x80) ? -0x1000000000000 : 0) + data[6] * 0x10000000000 + data[7] * 0x100000000 + data[8] * 0x1000000 + (data[9] << 16) + (data[10] << 8) + data[11]) * 1000);
else
return new Date('invalid');
};
// registration of bulk record definition?
// currentExtensions[0x52] = () =>
function saveState(callback) {
if (currentUnpackr && currentUnpackr._onSaveState)
currentUnpackr._onSaveState();
let savedSrcEnd = srcEnd;
let savedPosition = position$1;
let savedStringPosition = stringPosition;
let savedSrcStringStart = srcStringStart;
let savedSrcStringEnd = srcStringEnd;
let savedSrcString = srcString;
let savedStrings = strings;
let savedReferenceMap = referenceMap;
let savedBundledStrings = bundledStrings$1;
// TODO: We may need to revisit this if we do more external calls to user code (since it could be slow)
let savedSrc = new Uint8Array(src.slice(0, srcEnd)); // we copy the data in case it changes while external data is processed
let savedStructures = currentStructures;
let savedStructuresContents = currentStructures.slice(0, currentStructures.length);
let savedPackr = currentUnpackr;
let savedSequentialMode = sequentialMode;
let value = callback();
srcEnd = savedSrcEnd;
position$1 = savedPosition;
stringPosition = savedStringPosition;
srcStringStart = savedSrcStringStart;
srcStringEnd = savedSrcStringEnd;
srcString = savedSrcString;
strings = savedStrings;
referenceMap = savedReferenceMap;
bundledStrings$1 = savedBundledStrings;
src = savedSrc;
sequentialMode = savedSequentialMode;
currentStructures = savedStructures;
currentStructures.splice(0, currentStructures.length, ...savedStructuresContents);
currentUnpackr = savedPackr;
dataView = new DataView(src.buffer, src.byteOffset, src.byteLength);
return value;
}
function clearSource() {
src = null;
referenceMap = null;
currentStructures = null;
}
function addExtension$2(extension) {
if (extension.unpack)
currentExtensions[extension.type] = extension.unpack;
else
currentExtensions[extension.type] = extension;
}
const mult10 = new Array(147); // this is a table matching binary exponents to the multiplier to determine significant digit rounding
for (let i = 0; i < 256; i++) {
mult10[i] = +('1e' + Math.floor(45.15 - i * 0.30103));
}
var defaultUnpackr = new Unpackr$1({ useRecords: false });
const unpack$1 = defaultUnpackr.unpack;
const unpackMultiple$1 = defaultUnpackr.unpackMultiple;
defaultUnpackr.unpack;
const FLOAT32_OPTIONS = {
NEVER: 0,
ALWAYS: 1,
DECIMAL_ROUND: 3,
DECIMAL_FIT: 4
};
let f32Array = new Float32Array(1);
let u8Array = new Uint8Array(f32Array.buffer, 0, 4);
function roundFloat32$1(float32Number) {
f32Array[0] = float32Number;
let multiplier = mult10[((u8Array[3] & 0x7f) << 1) | (u8Array[2] >> 7)];
return ((multiplier * float32Number + (float32Number > 0 ? 0.5 : -0.5)) >> 0) / multiplier;
}
// Marker for downstream libraries (e.g. structon) to detect per-instance
// struct-decoding hooks (this._readStruct, this._onLoadedStructures,
// this._onSaveState). See `checkedRead` for the dispatch.
Unpackr$1.SUPPORTS_STRUCT_HOOKS = true;
let textEncoder;
try {
textEncoder = new TextEncoder();
} catch (error) {}
let extensions, extensionClasses;
const hasNodeBuffer = typeof Buffer !== 'undefined';
const ByteArrayAllocate = hasNodeBuffer ?
function(length) { return Buffer.allocUnsafeSlow(length); } : Uint8Array;
const ByteArray = hasNodeBuffer ? Buffer : Uint8Array;
const MAX_BUFFER_SIZE = hasNodeBuffer ? 0x100000000 : 0x7fd00000;
let target, keysTarget;
let targetView;
let position = 0;
let safeEnd;
let bundledStrings = null;
const MAX_BUNDLE_SIZE = 0x5500; // maximum characters such that the encoded bytes fits in 16 bits.
const hasNonLatin = /[\u0080-\uFFFF]/;
const RECORD_SYMBOL = Symbol('record-id');
let Packr$1 = class Packr extends Unpackr$1 {
constructor(options) {
super(options);
this.offset = 0;
let start;
let hasSharedUpdate;
let structures;
let referenceMap;
let encodeUtf8 = ByteArray.prototype.utf8Write ? function(string, position) {
return target.utf8Write(string, position, target.byteLength - position);
} : (textEncoder && textEncoder.encodeInto) ?
function(string, position) {
return textEncoder.encodeInto(string, target.subarray(position)).written;
} : false;
let packr = this;
if (!options)
options = {};
let isSequential = options && options.sequential;
let hasSharedStructures = options.structures || options.saveStructures;
let maxSharedStructures = options.maxSharedStructures;
if (maxSharedStructures == null)
maxSharedStructures = hasSharedStructures ? 32 : 0;
if (maxSharedStructures > 8160)
throw new Error('Maximum maxSharedStructure is 8160');
if (options.structuredClone && options.moreTypes == undefined) {
this.moreTypes = true;
}
let maxOwnStructures = options.maxOwnStructures;
if (maxOwnStructures == null)
maxOwnStructures = hasSharedStructures ? 32 : 64;
if (!this.structures && options.useRecords != false)
this.structures = [];
// two byte record ids for shared structures
let useTwoByteRecords = maxSharedStructures > 32 || (maxOwnStructures + maxSharedStructures > 64);
let sharedLimitId = maxSharedStructures + 0x40;
let maxStructureId = maxSharedStructures + maxOwnStructures + 0x40;
if (maxStructureId > 8256) {
throw new Error('Maximum maxSharedStructure + maxOwnStructure is 8192');
}
let recordIdsToRemove = [];
let transitionsCount = 0;
let serializationsSinceTransitionRebuild = 0;
this.pack = this.encode = function(value, encodeOptions) {
if (!target) {
target = new ByteArrayAllocate(8192);
targetView = target.dataView || (target.dataView = new DataView(target.buffer, 0, 8192));
position = 0;
}
safeEnd = target.length - 10;
if (safeEnd - position < 0x800) {
// don't start too close to the end,
target = new ByteArrayAllocate(target.length);
targetView = target.dataView || (target.dataView = new DataView(target.buffer, 0, target.length));
safeEnd = target.length - 10;
position = 0;
} else
position = (position + 7) & 0x7ffffff8; // Word align to make any future copying of this buffer faster
start = position;
if (encodeOptions & RESERVE_START_SPACE) position += (encodeOptions & 0xff);
referenceMap = packr.structuredClone ? new Map() : null;
if (packr.bundleStrings && typeof value !== 'string') {
bundledStrings = [];
bundledStrings.size = Infinity; // force a new bundle start on first string
} else
bundledStrings = null;
structures = packr.structures;
if (structures) {
if (structures.uninitialized)
structures = packr._mergeStructures(packr.getStructures());
let sharedLength = structures.sharedLength || 0;
if (sharedLength > maxSharedStructures) {
//if (maxSharedStructures <= 32 && structures.sharedLength > 32) // TODO: could support this, but would need to update the limit ids
throw new Error('Shared structures is larger than maximum shared structures, try increasing maxSharedStructures to ' + structures.sharedLength);
}
if (!structures.transitions) {
// rebuild our structure transitions
structures.transitions = Object.create(null);
for (let i = 0; i < sharedLength; i++) {
let keys = structures[i];
if (!keys)
continue;
let nextTransition, transition = structures.transitions;
for (let j = 0, l = keys.length; j < l; j++) {
let key = keys[j];
nextTransition = transition[key];
if (!nextTransition) {
nextTransition = transition[key] = Object.create(null);
}
transition = nextTransition;
}
transition[RECORD_SYMBOL] = i + 0x40;
}
this.lastNamedStructuresLength = sharedLength;
}
if (!isSequential) {
structures.nextId = sharedLength + 0x40;
}
}
if (hasSharedUpdate)
hasSharedUpdate = false;
let encodingError;
try {
if (packr._writeStruct && value && typeof value === 'object') {
if (value.constructor === Object) writeStruct(value); // simple object
else if (value.constructor !== Map && !Array.isArray(value) && !extensionClasses.some(extClass => value instanceof extClass)) {
// allow user classes, if they don't need special handling (but do use toJSON if available)
writeStruct(value.toJSON ? value.toJSON() : value);
} else pack(value);
} else
pack(value);
let lastBundle = bundledStrings;
if (bundledStrings)
writeBundles(start, pack, 0);
if (referenceMap && referenceMap.idsToInsert) {
let idsToInsert = referenceMap.idsToInsert.sort((a, b) => a.offset > b.offset ? 1 : -1);
let i = idsToInsert.length;
let incrementPosition = -1;
while (lastBundle && i > 0) {
let insertionPoint = idsToInsert[--i].offset + start;
if (insertionPoint < (lastBundle.stringsPosition + start) && incrementPosition === -1)
incrementPosition = 0;
if (insertionPoint > (lastBundle.position + start)) {
if (incrementPosition >= 0)
incrementPosition += 6;
} else {
if (incrementPosition >= 0) {
// update the bundle reference now
targetView.setUint32(lastBundle.position + start,
targetView.getUint32(lastBundle.position + start) + incrementPosition);
incrementPosition = -1; // reset
}
lastBundle = lastBundle.previous;
i++;
}
}
if (incrementPosition >= 0 && lastBundle) {
// update the bundle reference now
targetView.setUint32(lastBundle.position + start,
targetView.getUint32(lastBundle.position + start) + incrementPosition);
}
position += idsToInsert.length * 6;
if (position > safeEnd)
makeRoom(position);
packr.offset = position;
let serialized = insertIds(target.subarray(start, position), idsToInsert);
referenceMap = null;
return serialized;
}
packr.offset = position; // update the offset so next serialization doesn't write over our buffer, but can continue writing to same buffer sequentially
if (encodeOptions & REUSE_BUFFER_MODE) {
target.start = start;
target.end = position;
return target;
}
return target.subarray(start, position); // position can change if we call pack again in saveStructures, so we get the buffer now
} catch(error) {
encodingError = error;
throw error;
} finally {
if (structures) {
resetStructures();
if (hasSharedUpdate && packr.saveStructures) {
let sharedLength = structures.sharedLength || 0;
// we can't rely on start/end with REUSE_BUFFER_MODE since they will (probably) change when we save
let returnBuffer = target.subarray(start, position);
let newSharedData = (packr._prepareStructures || prepareStructures)(structures, packr);
if (!encodingError) { // TODO: If there is an encoding error, should make the structures as uninitialized so they get rebuilt next time
if (packr.saveStructures(newSharedData, newSharedData.isCompatible) === false) {
// The save was declined (a concurrent writer updated the shared structures,
// or the store transaction did not durably commit). Our in-memory
// structures + transition trie may now reference record ids that were
// never persisted; re-packing as-is would re-emit the same record pointing
// at an unpersisted structure (-> "Record id is not defined" on decode).
// Mark structures uninitialized so the re-pack reloads durable structures
// via getStructures, rebuilds the transition trie, and re-mints + re-saves.
structures.uninitialized = true;
return packr.pack(value, encodeOptions);
}
packr.lastNamedStructuresLength = sharedLength;
// don't keep large buffers around
if (target.length > 0x40000000) target = null;
return returnBuffer;
}
}
}
// don't keep large buffers around, they take too much memory and cause problems (limit at 1GB)
if (target.length > 0x40000000) target = null;
if (encodeOptions & RESET_BUFFER_MODE)
position = start;
}
};
const resetStructures = () => {
if (serializationsSinceTransitionRebuild < 10)
serializationsSinceTransitionRebuild++;
let sharedLength = structures.sharedLength || 0;
if (structures.length > sharedLength && !isSequential)
structures.length = sharedLength;
if (transitionsCount > 10000) {
// force a rebuild occasionally after a lot of transitions so it can get cleaned up
structures.transitions = null;
serializationsSinceTransitionRebuild = 0;
transitionsCount = 0;
if (recordIdsToRemove.length > 0)
recordIdsToRemove = [];
} else if (recordIdsToRemove.length > 0 && !isSequential) {
for (let i = 0, l = recordIdsToRemove.length; i < l; i++) {
recordIdsToRemove[i][RECORD_SYMBOL] = 0;
}
recordIdsToRemove = [];
}
};
const packArray = (value) => {
var length = value.length;
if (length < 0x10) {
target[position++] = 0x90 | length;
} else if (length < 0x10000) {
target[position++] = 0xdc;
target[position++] = length >> 8;
target[position++] = length & 0xff;
} else {
target[position++] = 0xdd;
targetView.setUint32(position, length);
position += 4;
}
for (let i = 0; i < length; i++) {
pack(value[i]);
}
};
const pack = (value) => {
if (position > safeEnd)
target = makeRoom(position);
var type = typeof value;
var length;
if (type === 'string') {
let strLength = value.length;
if (bundledStrings && strLength >= 4 && strLength < 0x1000) {
if ((bundledStrings.size += strLength) > MAX_BUNDLE_SIZE) {
let extStart;
let maxBytes = (bundledStrings[0] ? bundledStrings[0].length * 3 + bundledStrings[1].length : 0) + 10;
if (position + maxBytes > safeEnd)
target = makeRoom(position + maxBytes);
let lastBundle;
if (bundledStrings.position) { // here we use the 0x62 extension to write the last bundle and reserve space for the reference pointer to the next/current bundle
lastBundle = bundledStrings;
target[position] = 0xc8; // ext 16
position += 3; // reserve for the writing bundle size
target[position++] = 0x62; // 'b'
extStart = position - start;
position += 4; // reserve for writing bundle reference
writeBundles(start, pack, 0); // write the last bundles
targetView.setUint16(extStart + start - 3, position - start - extStart);
} else { // here we use the 0x62 extension just to reserve the space for the reference pointer to the bundle (will be updated once the bundle is written)
target[position++] = 0xd6; // fixext 4
target[position++] = 0x62; // 'b'
extStart = position - start;
position += 4; // reserve for writing bundle reference
}
bundledStrings = ['', '']; // create new ones
bundledStrings.previous = lastBundle;
bundledStrings.size = 0;
bundledStrings.position = extStart;
}
let twoByte = hasNonLatin.test(value);
bundledStrings[twoByte ? 0 : 1] += value;
target[position++] = 0xc1;
pack(twoByte ? -strLength : strLength);
return;
}
let headerSize;
// first we estimate the header size, so we can write to the correct location
if (strLength < 0x20) {
headerSize = 1;
} else if (strLength < 0x100) {
headerSize = 2;
} else if (strLength < 0x10000) {
headerSize = 3;
} else {
headerSize = 5;
}
let maxBytes = strLength * 3;
if (position + maxBytes > safeEnd)
target = makeRoom(position + maxBytes);
if (strLength < 0x40 || !encodeUtf8) {
let i, c1, c2, strPosition = position + headerSize;
for (i = 0; i < strLength; i++) {
c1 = value.charCodeAt(i);
if (c1 < 0x80) {
target[strPosition++] = c1;
} else if (c1 < 0x800) {
target[strPosition++] = c1 >> 6 | 0xc0;
target[strPosition++] = c1 & 0x3f | 0x80;
} else if (
(c1 & 0xfc00) === 0xd800 &&
((c2 = value.charCodeAt(i + 1)) & 0xfc00) === 0xdc00
) {
c1 = 0x10000 + ((c1 & 0x03ff) << 10) + (c2 & 0x03ff);
i++;
target[strPosition++] = c1 >> 18 | 0xf0;
target[strPosition++] = c1 >> 12 & 0x3f | 0x80;
target[strPosition++] = c1 >> 6 & 0x3f | 0x80;
target[strPosition++] = c1 & 0x3f | 0x80;
} else {
target[strPosition++] = c1 >> 12 | 0xe0;
target[strPosition++] = c1 >> 6 & 0x3f | 0x80;
target[strPosition++] = c1 & 0x3f | 0x80;
}
}
length = strPosition - position - headerSize;
} else {
length = encodeUtf8(value, position + headerSize);
}
if (length < 0x20) {
target[position++] = 0xa0 | length;
} else if (length < 0x100) {
if (headerSize < 2) {
target.copyWithin(position + 2, position + 1, position + 1 + length);
}
target[position++] = 0xd9;
target[position++] = length;
} else if (length < 0x10000) {
if (headerSize < 3) {
target.copyWithin(position + 3, position + 2, position + 2 + length);
}
target[position++] = 0xda;
target[position++] = length >> 8;
target[position++] = length & 0xff;
} else {
if (headerSize < 5) {
target.copyWithin(position + 5, position + 3, position + 3 + length);
}
target[position++] = 0xdb;
targetView.setUint32(position, length);
position += 4;
}
position += length;
} else if (type === 'number') {
if (value >>> 0 === value) {// positive integer, 32-bit or less
// positive uint
if (value < 0x20 || (value < 0x80 && this.useRecords === false) || (value < 0x40 && !this._writeStruct)) {
target[position++] = value;
} else if (value < 0x100) {
target[position++] = 0xcc;
target[position++] = value;
} else if (value < 0x10000) {
target[position++] = 0xcd;
target[position++] = value >> 8;
target[position++] = value & 0xff;
} else {
target[position++] = 0xce;
targetView.setUint32(position, value);
position += 4;
}
} else if (value >> 0 === value) { // negative integer
if (value >= -0x20) {
target[position++] = 0x100 + value;
} else if (value >= -0x80) {
target[position++] = 0xd0;
target[position++] = value + 0x100;
} else if (value >= -0x8000) {
target[position++] = 0xd1;
targetView.setInt16(position, value);
position += 2;
} else {
target[position++] = 0xd2;
targetView.setInt32(position, value);
position += 4;
}
} else {
let useFloat32;
if ((useFloat32 = this.useFloat32) > 0 && value < 0x100000000 && value >= -0x80000000) {
target[position++] = 0xca;
targetView.setFloat32(position, value);
let xShifted;
if (useFloat32 < 4 ||
// this checks for rounding of numbers that were encoded in 32-bit float to nearest significant decimal digit that could be preserved
((xShifted = value * mult10[((target[position] & 0x7f) << 1) | (target[position + 1] >> 7)]) >> 0) === xShifted) {
position += 4;
return;
} else
position--; // move back into position for writing a double
}
target[position++] = 0xcb;
targetView.setFloat64(position, value);
position += 8;
}
} else if (type === 'object' || type === 'function') {
if (!value)
target[position++] = 0xc0;
else {
if (referenceMap) {
let referee = referenceMap.get(value);
if (referee) {
if (!referee.id) {
let idsToInsert = referenceMap.idsToInsert || (referenceMap.idsToInsert = []);
referee.id = idsToInsert.push(referee);
}
target[position++] = 0xd6; // fixext 4
target[position++] = 0x70; // "p" for pointer
targetView.setUint32(position, referee.id);
position += 4;
return;
} else
referenceMap.set(value, { offset: position - start });
}
let constructor = value.constructor;
if (constructor === Object) {
writeObject(value);
} else if (constructor === Array) {
packArray(value);
} else if (constructor === Map) {
if (this.mapAsEmptyObject) target[position++] = 0x80;
else {
length = value.size;
if (length < 0x10) {
target[position++] = 0x80 | length;
} else if (length < 0x10000) {
target[position++] = 0xde;
target[position++] = length >> 8;
target[position++] = length & 0xff;
} else {
target[position++] = 0xdf;
targetView.setUint32(position, length);
position += 4;
}
for (let [key, entryValue] of value) {
pack(key);
pack(entryValue);
}
}
} else {
for (let i = 0, l = extensions.length; i < l; i++) {
let extensionClass = extensionClasses[i];
if (value instanceof extensionClass) {
let extension = extensions[i];
if (extension.write) {
if (extension.type) {
target[position++] = 0xd4; // one byte "tag" extension
target[position++] = extension.type;
target[position++] = 0;
}
let writeResult = extension.write.call(this, value);
if (writeResult === value) { // avoid infinite recursion
if (Array.isArray(value)) {
packArray(value);
} else {
writeObject(value);
}
} else {
pack(writeResult);
}
return;
}
let currentTarget = target;
let currentTargetView = targetView;
let currentPosition = position;
target = null;
let result;
try {
result = extension.pack.call(this, value, (size) => {
// restore target and use it
target = currentTarget;
currentTarget = null;
position += size;
if (position > safeEnd)
makeRoom(position);
return {
target, targetView, position: position - size
};
}, pack);
} finally {
// restore current target information (unless already restored)
if (currentTarget) {
target = currentTarget;
targetView = currentTargetView;
position = currentPosition;
safeEnd = target.length - 10;
}
}
if (result) {
if (result.length + position > safeEnd)
makeRoom(result.length + position);
position = writeExtensionData(result, target, position, extension.type);
}
return;
}
}
// check isArray after extensions, because extensions can extend Array
if (Array.isArray(value)) {
packArray(value);
} else {
// use this as an alternate mechanism for expressing how to serialize
if (value.toJSON) {
const json = value.toJSON();
// if for some reason value.toJSON returns itself it'll loop forever
if (json !== value)
return pack(json);
}
// if there is a writeFunction, use it, otherwise just encode as undefined
if (type === 'function')
return pack(this.writeFunction && this.writeFunction(value));
// no extension found, write as plain object
writeObject(value);
}
}
}
} else if (type === 'boolean') {
target[position++] = value ? 0xc3 : 0xc2;
} else if (type === 'bigint') {
if (value < 0x8000000000000000 && value >= -0x8000000000000000) {
// use a signed int as long as it fits
target[position++] = 0xd3;
targetView.setBigInt64(position, value);
} else if (value < 0x10000000000000000 && value > 0) {
// if we can fit an unsigned int, use that
target[position++] = 0xcf;
targetView.setBigUint64(position, value);
} else {
// overflow
if (this.largeBigIntToFloat) {
target[position++] = 0xcb;
targetView.setFloat64(position, Number(value));
} else if (this.largeBigIntToString) {
return pack(value.toString());
} else if (this.useBigIntExtension || this.moreTypes) {
let empty = value < 0 ? BigInt(-1) : BigInt(0);
let array;
if (value >> BigInt(0x10000) === empty) {
let mask = BigInt(0x10000000000000000) - BigInt(1); // literal would overflow
let chunks = [];
while (true) {
chunks.push(value & mask);
if ((value >> BigInt(63)) === empty) break;
value >>= BigInt(64);
}
array = new Uint8Array(new BigUint64Array(chunks).buffer);
array.reverse();
} else {
let invert = value < 0;
let string = (invert ? ~value : value).toString(16);
if (string.length % 2) {
string = '0' + string;
} else if (parseInt(string.charAt(0), 16) >= 8) {
string = '00' + string;
}
if (hasNodeBuffer) {
array = Buffer.from(string, 'hex');
} else {
array = new Uint8Array(string.length / 2);
for (let i = 0; i < array.length; i++) {
array[i] = parseInt(string.slice(i * 2, i * 2 + 2), 16);
}
}
if (invert) {
for (let i = 0; i < array.length; i++) array[i] = ~array[i];
}
}
if (array.length + position > safeEnd)
makeRoom(array.length + position);
position = writeExtensionData(array, target, position, 0x42);
return;
} else {
throw new RangeError(value + ' was too large to fit in MessagePack 64-bit integer format, use' +
' useBigIntExtension, or set largeBigIntToFloat to convert to float-64, or set' +
' largeBigIntToString to convert to string');
}
}
position += 8;
} else if (type === 'undefined') {
if (this.encodeUndefinedAsNil)
target[position++] = 0xc0;
else {
target[position++] = 0xd4; // a number of implementations use fixext1 with type 0, data 0 to denote undefined, so we follow suite
target[position++] = 0;
target[position++] = 0;
}
} else {
throw new Error('Unknown type: ' + type);
}
};
const writePlainObject = (this.variableMapSize || this.coercibleKeyAsNumber || this.skipValues) ? (object) => {
// this method is slightly slower, but generates "preferred serialization" (optimally small for smaller objects)
let keys;
if (this.skipValues) {
keys = [];
for (let key in object) {
if ((typeof object.hasOwnProperty !== 'function' || object.hasOwnProperty(key)) &&
!this.skipValues.includes(object[key]))
keys.push(key);
}
} else {
keys = Object.keys(object);
}
let length = keys.length;
if (length < 0x10) {
target[position++] = 0x80 | length;
} else if (length < 0x10000) {
target[position++] = 0xde;
target[position++] = length >> 8;
target[position++] = length & 0xff;
} else {
target[position++] = 0xdf;
targetView.setUint32(position, length);
position += 4;
}
let key;
if (this.coercibleKeyAsNumber) {
for (let i = 0; i < length; i++) {
key = keys[i];
let num = Number(key);
pack(isNaN(num) ? key : num);
pack(object[key]);
}
} else {
for (let i = 0; i < length; i++) {
pack(key = keys[i]);
pack(object[key]);
}
}
} :
(object) => {
target[position++] = 0xde; // always using map 16, so we can preallocate and set the length afterwards
let objectOffset = position - start;
position += 2;
let size = 0;
for (let key in object) {
if (typeof object.hasOwnProperty !== 'function' || object.hasOwnProperty(key)) {
pack(key);
pack(object[key]);
size++;
}
}
if (size > 0xffff) {
throw new Error('Object is too large to serialize with fast 16-bit map size,' +
' use the "variableMapSize" option to serialize this object');
}
target[objectOffset++ + start] = size >> 8;
target[objectOffset + start] = size & 0xff;
};
const writeRecord = this.useRecords === false ? writePlainObject :
(options.progressiveRecords && !useTwoByteRecords) ? // this is about 2% faster for highly stable structures, since it only requires one for-in loop (but much more expensive when new structure needs to be written)
(object) => {
let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null));
let objectOffset = position++ - start;
let wroteKeys;
for (let key in object) {
if (typeof object.hasOwnProperty !== 'function' || object.hasOwnProperty(key)) {
nextTransition = transition[key];
if (nextTransition)
transition = nextTransition;
else {
// record doesn't exist, create full new record and insert it
let keys = Object.keys(object);
let lastTransition = transition;
transition = structures.transitions;
let newTransitions = 0;
for (let i = 0, l = keys.length; i < l; i++) {
let key = keys[i];
nextTransition = transition[key];
if (!nextTransition) {
nextTransition = transition[key] = Object.create(null);
newTransitions++;
}
transition = nextTransition;
}
if (objectOffset + start + 1 == position) {
// first key, so we don't need to insert, we can just write record directly
position--;
newRecord(transition, keys, newTransitions);
} else // otherwise we need to insert the record, moving existing data after the record
insertNewRecord(transition, keys, objectOffset, newTransitions);
wroteKeys = true;
transition = lastTransition[key];
}
pack(object[key]);
}
}
if (!wroteKeys) {
let recordId = transition[RECORD_SYMBOL];
if (recordId)
target[objectOffset + start] = recordId;
else
insertNewRecord(transition, Object.keys(object), objectOffset, 0);
}
} :
(object) => {
let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null));
let newTransitions = 0;
for (let key in object) if (typeof object.hasOwnProperty !== 'function' || object.hasOwnProperty(key)) {
nextTransition = transition[key];
if (!nextTransition) {
nextTransition = transition[key] = Object.create(null);
newTransitions++;
}
transition = nextTransition;
}
let recordId = transition[RECORD_SYMBOL];
if (recordId) {
if (recordId >= 0x60 && useTwoByteRecords) {
target[position++] = ((recordId -= 0x60) & 0x1f) + 0x60;
target[position++] = recordId >> 5;
} else
target[position++] = recordId;
} else {
newRecord(transition, transition.__keys__ || Object.keys(object), newTransitions);
}
// now write the values
for (let key in object)
if (typeof object.hasOwnProperty !== 'function' || object.hasOwnProperty(key)) {
pack(object[key]);
}
};
// create reference to useRecords if useRecords is a function
const checkUseRecords = typeof this.useRecords == 'function' && this.useRecords;
const writeObject = checkUseRecords ? (object) => {
checkUseRecords(object) ? writeRecord(object) : writePlainObject(object);
} : writeRecord;
const writeStruct = (object) => {
let newPosition = packr._writeStruct(object, target, start, position, structures, makeRoom, (value, newPosition, notifySharedUpdate) => {
if (notifySharedUpdate)
return hasSharedUpdate = true;
position = newPosition;
let startTarget = target;
pack(value);
resetStructures();
if (startTarget !== target) {
return { position, targetView, target }; // indicate the buffer was re-allocated
}
return position;
});
if (newPosition === 0) // bail and go to a msgpack object
return writeObject(object);
position = newPosition;
};
const makeRoom = (end) => {
let newSize;
if (end > 0x1000000) {
// special handling for really large buffers
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 > 0x4000000 ? 1.25 : 2), 0x400000) / 0x1000) * 0x1000);
} else // faster handling for smaller buffers
newSize = ((Math.max((end - start) << 2, target.length - 1) >> 12) + 1) << 12;
let newBuffer = new ByteArrayAllocate(newSize);
targetView = newBuffer.dataView || (newBuffer.dataView = new DataView(newBuffer.buffer, 0, newSize));
end = Math.min(end, target.length);
if (target.copy)
target.copy(newBuffer, 0, start, end);
else
newBuffer.set(target.slice(start, end));
position -= start;
start = 0;
safeEnd = newBuffer.length - 10;
return target = newBuffer;
};
const newRecord = (transition, keys, newTransitions) => {
let recordId = structures.nextId;
if (!recordId)
recordId = 0x40;
if (recordId < sharedLimitId && this.shouldShareStructure && !this.shouldShareStructure(keys)) {
recordId = structures.nextOwnId;
if (!(recordId < maxStructureId))
recordId = sharedLimitId;
structures.nextOwnId = recordId + 1;
} else {
if (recordId >= maxStructureId)// cycle back around
recordId = sharedLimitId;
structures.nextId = recordId + 1;
}
let highByte = keys.highByte = recordId >= 0x60 && useTwoByteRecords ? (recordId - 0x60) >> 5 : -1;
transition[RECORD_SYMBOL] = recordId;
transition.__keys__ = keys;
structures[recordId - 0x40] = keys;
if (recordId < sharedLimitId) {
keys.isShared = true;
structures.sharedLength = recordId - 0x3f;
hasSharedUpdate = true;
if (highByte >= 0) {
target[position++] = (recordId & 0x1f) + 0x60;
target[position++] = highByte;
} else {
target[position++] = recordId;
}
} else {
if (highByte >= 0) {
target[position++] = 0xd5; // fixext 2
target[position++] = 0x72; // "r" record defintion extension type
target[position++] = (recordId & 0x1f) + 0x60;
target[position++] = highByte;
} else {
target[position++] = 0xd4; // fixext 1
target[position++] = 0x72; // "r" record defintion extension type
target[position++] = recordId;
}
if (newTransitions)
transitionsCount += serializationsSinceTransitionRebuild * newTransitions;
// record the removal of the id, we can maintain our shared structure
if (recordIdsToRemove.length >= maxOwnStructures)
recordIdsToRemove.shift()[RECORD_SYMBOL] = 0; // we are cycling back through, and have to remove old ones
recordIdsToRemove.push(transition);
pack(keys);
}
};
const insertNewRecord = (transition, keys, insertionOffset, newTransitions) => {
let mainTarget = target;
let mainPosition = position;
let mainSafeEnd = safeEnd;
let mainStart = start;
target = keysTarget;
position = 0;
start = 0;
if (!target)
keysTarget = target = new ByteArrayAllocate(8192);
safeEnd = target.length - 10;
newRecord(transition, keys, newTransitions);
keysTarget = target;
let keysPosition = position;
target = mainTarget;
position = mainPosition;
safeEnd = mainSafeEnd;
start = mainStart;
if (keysPosition > 1) {
let newEnd = position + keysPosition - 1;
if (newEnd > safeEnd)
makeRoom(newEnd);
let insertionPosition = insertionOffset + start;
target.copyWithin(insertionPosition + keysPosition, insertionPosition + 1, position);
target.set(keysTarget.slice(0, keysPosition), insertionPosition);
position = newEnd;
} else {
target[insertionOffset + start] = keysTarget[0];
}
};
}
useBuffer(buffer) {
// this means we are finished using our own buffer and we can write over it safely
target = buffer;
target.dataView || (target.dataView = new DataView(target.buffer, target.byteOffset, target.byteLength));
targetView = target.dataView;
position = 0;
}
set position (value) {
position = value;
}
get position() {
return position;
}
clearSharedData() {
if (this.structures)
this.structures = [];
if (this.typedStructs)
this.typedStructs = [];
}
};
extensionClasses = [ Date, Set, Error, RegExp, ArrayBuffer, Object.getPrototypeOf(Uint8Array.prototype).constructor /*TypedArray*/, DataView, C1Type ];
extensions = [{
pack(date, allocateForWrite, pack) {
let seconds = date.getTime() / 1000;
if ((this.useTimestamp32 || date.getMilliseconds() === 0) && seconds >= 0 && seconds < 0x100000000) {
// Timestamp 32
let { target, targetView, position} = allocateForWrite(6);
target[position++] = 0xd6;
target[position++] = 0xff;
targetView.setUint32(position, seconds);
} else if (seconds > 0 && seconds < 0x100000000) {
// Timestamp 64
let { target, targetView, position} = allocateForWrite(10);
target[position++] = 0xd7;
target[position++] = 0xff;
targetView.setUint32(position, date.getMilliseconds() * 4000000 + ((seconds / 1000 / 0x100000000) >> 0));
targetView.setUint32(position + 4, seconds);
} else if (isNaN(seconds)) {
if (this.onInvalidDate) {
allocateForWrite(0);
return pack(this.onInvalidDate());
}
// Intentionally invalid timestamp
let { target, targetView, position} = allocateForWrite(3);
target[position++] = 0xd4;
target[position++] = 0xff;
target[position++] = 0xff;
} else {
// Timestamp 96
let { target, targetView, position} = allocateForWrite(15);
target[position++] = 0xc7;
target[position++] = 12;
target[position++] = 0xff;
targetView.setUint32(position, date.getMilliseconds() * 1000000);
targetView.setBigInt64(position + 4, BigInt(Math.floor(seconds)));
}
}
}, {
pack(set, allocateForWrite, pack) {
if (this.setAsEmptyObject) {
allocateForWrite(0);
return pack({});
}
let array = Array.from(set);
let { target, position} = allocateForWrite(this.moreTypes ? 3 : 0);
if (this.moreTypes) {
target[position++] = 0xd4;
target[position++] = 0x73; // 's' for Set
target[position++] = 0;
}
pack(array);
}
}, {
pack(error, allocateForWrite, pack) {
let { target, position} = allocateForWrite(this.moreTypes ? 3 : 0);
if (this.moreTypes) {
target[position++] = 0xd4;
target[position++] = 0x65; // 'e' for error
target[position++] = 0;
}
pack([ error.name, error.message, error.cause ]);
}
}, {
pack(regex, allocateForWrite, pack) {
let { target, position} = allocateForWrite(this.moreTypes ? 3 : 0);
if (this.moreTypes) {
target[position++] = 0xd4;
target[position++] = 0x78; // 'x' for regeXp
target[position++] = 0;
}
pack([ regex.source, regex.flags ]);
}
}, {
pack(arrayBuffer, allocateForWrite) {
if (this.moreTypes)
writeExtBuffer(arrayBuffer, 0x10, allocateForWrite);
else
writeBuffer(hasNodeBuffer ? Buffer.from(arrayBuffer) : new Uint8Array(arrayBuffer), allocateForWrite);
}
}, {
pack(typedArray, allocateForWrite) {
let constructor = typedArray.constructor;
if (constructor !== ByteArray && this.moreTypes)
writeExtBuffer(typedArray, typedArrays.indexOf(constructor.name), allocateForWrite);
else
writeBuffer(typedArray, allocateForWrite);
}
}, {
pack(arrayBuffer, allocateForWrite) {
if (this.moreTypes)
writeExtBuffer(arrayBuffer, 0x11, allocateForWrite);
else
writeBuffer(hasNodeBuffer ? Buffer.from(arrayBuffer) : new Uint8Array(arrayBuffer), allocateForWrite);
}
}, {
pack(c1, allocateForWrite) { // specific 0xC1 object
let { target, position} = allocateForWrite(1);
target[position] = 0xc1;
}
}];
function writeExtBuffer(typedArray, type, allocateForWrite, encode) {
let length = typedArray.byteLength;
if (length + 1 < 0x100) {
var { target, position } = allocateForWrite(4 + length);
target[position++] = 0xc7;
target[position++] = length + 1;
} else if (length + 1 < 0x10000) {
var { target, position } = allocateForWrite(5 + length);
target[position++] = 0xc8;
target[position++] = (length + 1) >> 8;
target[position++] = (length + 1) & 0xff;
} else {
var { target, position, targetView } = allocateForWrite(7 + length);
target[position++] = 0xc9;
targetView.setUint32(position, length + 1); // plus one for the type byte
position += 4;
}
target[position++] = 0x74; // "t" for typed array
target[position++] = type;
if (!typedArray.buffer) typedArray = new Uint8Array(typedArray);
target.set(new Uint8Array(typedArray.buffer, typedArray.byteOffset, typedArray.byteLength), position);
}
function writeBuffer(buffer, allocateForWrite) {
let length = buffer.byteLength;
var target, position;
if (length < 0x100) {
var { target, position } = allocateForWrite(length + 2);
target[position++] = 0xc4;
target[position++] = length;
} else if (length < 0x10000) {
var { target, position } = allocateForWrite(length + 3);
target[position++] = 0xc5;
target[position++] = length >> 8;
target[position++] = length & 0xff;
} else {
var { target, position, targetView } = allocateForWrite(length + 5);
target[position++] = 0xc6;
targetView.setUint32(position, length);
position += 4;
}
target.set(buffer, position);
}
function writeExtensionData(result, target, position, type) {
let length = result.length;
switch (length) {
case 1:
target[position++] = 0xd4;
break;
case 2:
target[position++] = 0xd5;
break;
case 4:
target[position++] = 0xd6;
break;
case 8:
target[position++] = 0xd7;
break;
case 16:
target[position++] = 0xd8;
break;
default:
if (length < 0x100) {
target[position++] = 0xc7;
target[position++] = length;
} else if (length < 0x10000) {
target[position++] = 0xc8;
target[position++] = length >> 8;
target[position++] = length & 0xff;
} else {
target[position++] = 0xc9;
target[position++] = length >> 24;
target[position++] = (length >> 16) & 0xff;
target[position++] = (length >> 8) & 0xff;
target[position++] = length & 0xff;
}
}
target[position++] = type;
target.set(result, position);
position += length;
return position;
}
function insertIds(serialized, idsToInsert) {
// insert the ids that need to be referenced for structured clones
let nextId;
let distanceToMove = idsToInsert.length * 6;
let lastEnd = serialized.length - distanceToMove;
while (nextId = idsToInsert.pop()) {
let offset = nextId.offset;
let id = nextId.id;
serialized.copyWithin(offset + distanceToMove, offset, lastEnd);
distanceToMove -= 6;
let position = offset + distanceToMove;
serialized[position++] = 0xd6;
serialized[position++] = 0x69; // 'i'
serialized[position++] = id >> 24;
serialized[position++] = (id >> 16) & 0xff;
serialized[position++] = (id >> 8) & 0xff;
serialized[position++] = id & 0xff;
lastEnd = offset;
}
return serialized;
}
function writeBundles(start, pack, incrementPosition) {
if (bundledStrings.length > 0) {
targetView.setUint32(bundledStrings.position + start, position + incrementPosition - bundledStrings.position - start);
bundledStrings.stringsPosition = position - start;
let writeStrings = bundledStrings;
bundledStrings = null;
pack(writeStrings[0]);
pack(writeStrings[1]);
}
}
function addExtension$1(extension) {
if (extension.Class) {
if (!extension.pack && !extension.write)
throw new Error('Extension has no pack or write function');
if (extension.pack && !extension.type)
throw new Error('Extension has no type (numeric code to identify the extension)');
extensionClasses.unshift(extension.Class);
extensions.unshift(extension);
}
addExtension$2(extension);
}
function prepareStructures(structures, packr) {
structures.isCompatible = (existingStructures) => {
let compatible = !existingStructures || ((packr.lastNamedStructuresLength || 0) === existingStructures.length);
if (!compatible) // we want to merge these existing structures immediately since we already have it and we are in the right transaction
packr._mergeStructures(existingStructures);
return compatible;
};
return structures;
}
// Marker for downstream libraries (e.g. structon) to detect that this Packr
// supports per-instance struct-encoding hooks (this._writeStruct,
// this._prepareStructures). See `pack` for the dispatch.
Packr$1.SUPPORTS_STRUCT_HOOKS = true;
let defaultPackr = new Packr$1({ useRecords: false });
const pack$1 = defaultPackr.pack;
defaultPackr.pack;
const REUSE_BUFFER_MODE = 512;
const RESET_BUFFER_MODE = 1024;
const RESERVE_START_SPACE = 2048;
const nativeAccelerationDisabled = process.env.MSGPACKR_NATIVE_ACCELERATION_DISABLED !== undefined && process.env.MSGPACKR_NATIVE_ACCELERATION_DISABLED.toLowerCase() === 'true';
if (!nativeAccelerationDisabled) {
let extractor;
try {
if (typeof require == 'function')
extractor = require('msgpackr-extract');
else
extractor = module.createRequire((_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('test.js', document.baseURI).href))('msgpackr-extract');
if (extractor)
setExtractor(extractor.extractStrings);
} catch (error) {
// native module is optional
}
}
let allSampleData = [];
for (let i = 1; i < 6; i++) {
allSampleData.push(JSON.parse(fs.readFileSync(new URL(`./example${i > 1 ? i : ''}.json`, (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('test.js', document.baseURI).href)))));
}
allSampleData.push({
name: 'some other types',
date: new Date(),
empty: '',
});
const sampleData = allSampleData[3];
function tryRequire(module) {
try {
return require(module);
} catch(error) {
return {};
}
}
//if (typeof chai === 'undefined') { chai = require('chai') }
var assert = chai.assert;
//if (typeof msgpackr === 'undefined') { msgpackr = require('..') }
var Packr = Packr$1;
var Unpackr = Unpackr$1;
var unpack = unpack$1;
var unpackMultiple = unpackMultiple$1;
var roundFloat32 = roundFloat32$1;
var pack = pack$1;
var DECIMAL_FIT = FLOAT32_OPTIONS.DECIMAL_FIT;
var addExtension = addExtension$1;
var zlib = tryRequire('zlib');
zlib.deflateSync;
zlib.inflateSync;
zlib.brotliCompressSync;
zlib.brotliDecompressSync;
zlib.constants;
var ITERATIONS = 4000;
class ExtendArray extends Array {
}
class ExtendArray2 extends Array {
}
class ExtendArray3 extends Array {
}
class ExtendObject {
}
suite('msgpackr basic tests', function() {
test('pack/unpack data', function () {
var data = {
data: [
{a: 1, name: 'one', type: 'odd', isOdd: true},
{a: 2, name: 'two', type: 'even'},
{a: 3, name: 'three', type: 'odd', isOdd: true},
{a: 4, name: 'four', type: 'even'},
{a: 5, name: 'five', type: 'odd', isOdd: true},
{a: 6, name: 'six', type: 'even', isOdd: null}
],
description: 'some names',
types: ['odd', 'even'],
convertEnumToNum: [
{prop: 'test'},
{prop: 'test'},
{prop: 'test'},
{prop: 1},
{prop: 2},
{prop: [undefined]},
{prop: null}
]
};
let structures = [];
let packr = new Packr({structures});
var serialized = packr.pack(data);
serialized = packr.pack(data);
serialized = packr.pack(data);
var deserialized = packr.unpack(serialized);
assert.deepEqual(deserialized, data);
});
test('mixed structures', function () {
let data1 = {a: 1, b: 2, c: 3};
let data2 = {a: 1, b: 2, d: 4};
let data3 = {a: 1, b: 2, e: 5};
let structures = [];
let packr = new Packr({structures});
var serialized = packr.pack(data1);
var deserialized = packr.unpack(serialized);
assert.deepEqual(deserialized, data1);
var serialized = packr.pack(data2);
var deserialized = packr.unpack(serialized);
assert.deepEqual(deserialized, data2);
var serialized = packr.pack(data3);
var deserialized = packr.unpack(serialized);
assert.deepEqual(deserialized, data3);
});
test('mixed array', function () {
var data = [
'one',
'two',
'one',
10,
11,
null,
true,
'three',
'three',
'one', [
3, -5, -50, -400, 1.3, -5.3, true
]
];
let structures = [];
let packr = new Packr({structures});
var serialized = packr.pack(data);
var deserialized = packr.unpack(serialized);
assert.deepEqual(deserialized, data);
});
test('255 chars', function () {
const data = 'RRZG9A6I7xupPeOZhxcOcioFsuhszGOdyDUcbRf4Zef2kdPIfC9RaLO4jTM5JhuZvTsF09fbRHMGtqk7YAgu3vespeTe9l61ziZ6VrMnYu2CamK96wCkmz0VUXyqaiUoTPgzk414LS9yYrd5uh7w18ksJF5SlC2e91rukWvNqAZJjYN3jpkqHNOFchCwFrhbxq2Lrv1kSJPYCx9blRg2hGmYqTbElLTZHv20iNqwZeQbRMgSBPT6vnbCBPnOh1W';
var serialized = pack(data);
var deserialized = unpack(serialized);
assert.equal(deserialized, data);
});
test('overlong UTF-8 string', function () {
const payload = Buffer.concat([
Buffer.from([0xa2]), // msgpack fixstr, 2 bytes
Buffer.from([0xc0, 0xaf]), // overlong "/"
]);
const result = unpack(payload);
assert.notEqual(result, '/');
});
test('invalid UTF-8 continuation bytes', function () {
const replacement = '\uFFFD';
const strings = [
[[0xc2, 0x41], replacement + 'A'],
[[0xe1, 0x41, 0x42], replacement + 'AB'],
[[0xe1, 0x80, 0x41], replacement + 'A'],
[[0xf1, 0x41, 0x42, 0x43], replacement + 'ABC'],
[[0xf1, 0x80, 0x41, 0x42], replacement + 'AB'],
[[0xf1, 0x80, 0x80, 0x41], replacement + 'A'],
];
for (const [bytes, expected] of strings) {
const payload = Buffer.from([0xa0 + bytes.length, ...bytes]);
assert.equal(unpack(payload), expected);
}
});
test('truncated UTF-8 sequences do not consume following values', function () {
assert.deepEqual(unpack(Buffer.from([0x92, 0xa1, 0xc2, 0x01])), ['\uFFFD', 1]);
assert.deepEqual(unpack(Buffer.from([0x92, 0xa2, 0xe1, 0x80, 0x01])), ['\uFFFD', 1]);
assert.deepEqual(unpack(Buffer.from([0x92, 0xa3, 0xf1, 0x80, 0x80, 0x01])), ['\uFFFD', 1]);
assert.deepEqual(
unpack(Buffer.from([0x93, 0xa1, 0xe0, 0xa4, 0x6e, 0x65, 0x78, 0x74, 0x07])),
['\uFFFD', 'next', 7]
);
});
test('use ArrayBuffer', function () {
const data = {prop: 'a test'};
var serialized = pack(data);
let ab = new ArrayBuffer(serialized.length);
let u8 = new Uint8Array(ab);
u8.set(serialized);
var deserialized = unpack(ab);
assert.deepEqual(deserialized, data);
});
for (let sampleData of allSampleData) {
let snippet = JSON.stringify(sampleData).slice(0, 20) + '...';
test('pack/unpack sample data ' + snippet, function () {
var data = sampleData;
var serialized = pack(data);
var deserialized = unpack(serialized);
assert.deepEqual(deserialized, data);
var serialized = pack(data);
var deserialized = unpack(serialized);
assert.deepEqual(deserialized, data);
});
test('pack/unpack sample data with Uint8Array encoding' + snippet, function () {
var data = sampleData;
var serialized = pack(data);
serialized = new Uint8Array(serialized);
var deserialized = unpack(serialized);
assert.deepEqual(deserialized, data);
var serialized = pack(data);
var deserialized = unpack(serialized);
assert.deepEqual(deserialized, data);
});
test('pack/unpack sample data with bundled strings ' + snippet, function () {
var data = sampleData;
let packr = new Packr({ /*structures,*/ useRecords: false, bundleStrings: true});
var serialized = packr.pack(data);
var deserialized = packr.unpack(serialized);
assert.deepEqual(deserialized, data);
});
}
test('pack/unpack sample data with useRecords function', function () {
var data = [
{id: 1, type: 1, labels: {a: 1, b: 2}},
{id: 2, type: 1, labels: {b: 1, c: 2}},
{id: 3, type: 1, labels: {d: 1, e: 2}}
];
var alternatives = [
{useRecords: false}, // 88 bytes
{useRecords: true}, // 58 bytes
{mapsAsObjects: true, useRecords: (v)=>!!v.id}, // 55 bytes
{mapsAsObjects: true, variableMapSize: true, useRecords: (v)=>!!v.id} // 49 bytes
];
for(let o of alternatives) {
let packr = new Packr(o);
var serialized = packr.pack(data);
var deserialized = packr.unpack(serialized);
assert.deepEqual(deserialized, data);
}
});
test('mapAsEmptyObject combination', function () {
const msgpackr = new Packr({ useRecords: false, encodeUndefinedAsNil: true, variableMapSize: true, mapAsEmptyObject: true, setAsEmptyObject: true });
const map = new Map();
map.set('a', 1);
map.set('b', 2);
const set = new Set();
set.add('a');
set.add('b');
const input = { map, set };
const packed = msgpackr.pack(input);
const unpacked = msgpackr.unpack(packed);
assert.deepEqual(unpacked.map, {});
assert.deepEqual(unpacked.set, {});
});
test('pack/unpack numeric coercible keys', function () {
var data = { a: 1, 2: 'test', '-3.45': 'test2'};
let packr = new Packr({variableMapSize: true, coercibleKeyAsNumber: true, useRecords: false});
var serialized = packr.pack(data);
var deserialized = packr.unpack(serialized);
assert.deepEqual(deserialized, data);
});
test('pack/unpack empty data with bundled strings', function () {
var data = {};
let packr = new Packr({bundleStrings: true});
var serialized = packr.pack(data);
var deserialized = packr.unpack(serialized);
assert.deepEqual(deserialized, data);
});
test('pack/unpack large amount of chinese characters', function() {
const MSGPACK_OPTIONS = {bundleStrings: true};
const item = {
message: '你好你好你好你好你好你好你好你好你好', // some Chinese characters
};
testSize(100);
testSize(1000);
testSize(10000);
function testSize(size) {
const list = [];
for (let i = 0; i < size; i++) {
list.push({...item});
}
const packer = new Packr(MSGPACK_OPTIONS);
const unpacker = new Unpackr(MSGPACK_OPTIONS);
const encoded = packer.pack(list);
const decoded = unpacker.unpack(encoded);
assert.deepEqual(list, decoded);
}
});
test('pack/unpack sequential data', function () {
var data = {foo: 1, bar: 2};
let packr = new Packr({sequential: true});
let unpackr = new Unpackr({sequential: true});
var serialized = packr.pack(data);
var deserialized = unpackr.unpack(serialized);
assert.deepEqual(deserialized, data);
var serialized = packr.pack(data);
var deserialized = unpackr.unpack(serialized);
assert.deepEqual(deserialized, data);
});
test('pack/unpack with bundled strings and sequential', function () {
const options = {
bundleStrings: true,
sequential: true,
};
const packer = new Packr(options);
const unpacker = new Packr(options);
const data = {data: 42}; // key length >= 4
unpacker.unpackMultiple(Buffer.concat([
packer.pack(data),
packer.pack(data)
]));
});
if (typeof Buffer != 'undefined')
test('replace data', function(){
var data1 = {
data: [
{ a: 1, name: 'one', type: 'odd', isOdd: true, a: '13 characters' },
{ a: 2, name: 'two', type: 'even', a: '11 characte' },
{ a: 3, name: 'three', type: 'odd', isOdd: true, a: '12 character' },
{ a: 4, name: 'four', type: 'even', a: '9 charact'},
{ a: 5, name: 'five', type: 'odd', isOdd: true, a: '14 characters!' },
{ a: 6, name: 'six', type: 'even', isOdd: null }
],
};
var data2 = {
data: [
{ foo: 7, name: 'one', type: 'odd', isOdd: true },
{ foo: 8, name: 'two', type: 'even'},
{ foo: 9, name: 'three', type: 'odd', isOdd: true },
{ foo: 10, name: 'four', type: 'even'},
{ foo: 11, name: 'five', type: 'odd', isOdd: true },
{ foo: 12, name: 'six', type: 'even', isOdd: null }
],
};
var serialized1 = pack(data1);
var serialized2 = pack(data2);
var b = Buffer.alloc(8000);
serialized1.copy(b);
var deserialized1 = unpack(b, serialized1.length);
serialized2.copy(b);
var deserialized2 = unpack(b, serialized2.length);
assert.deepEqual(deserialized1, data1);
assert.deepEqual(deserialized2, data2);
});
test('compact 123', function() {
assert.equal(pack(123).length, 1);
});
test('BigInt', function() {
let packr = new Packr({ useBigIntExtension: true });
let data = {
a: 3333333333333333333333333333n,
b: 1234567890123456789012345678901234567890n,
c: -3333333333333333333333333333n,
d: -352523523642364364364264264264264264262642642n,
e: 0xffffffffffffffffffffffffffn,
f: -0xffffffffffffffffffffffffffn,
g: (1234n << 123n) ^ (5678n << 56n) ^ 890n,
h: (-1234n << 123n) ^ (5678n << 56n) ^ 890n,
i: (1234n << 1234n) ^ (5678n << 567n) ^ 890n,
j: (-1234n << 1234n) ^ (5678n << 567n) ^ 890n,
k: 0xdeadn << 0xbeefn,
l: -0xdeadn << 0xbeefn,
m: 11n << 0x11111n ^ 111n,
n: -11n << 0x11111n ^ 111n,
o: 12345678901234567890n,
p: -12345678901234567890n,
exp: [],
expexp: [],
};
for (let n = 1n; n.toString(16).length * 4 < 1500; n <<= 1n, n |= BigInt(Math.floor(Math.random() * 2))) {
data.exp.push(n, -n);
}
for (let n = 7n; n.toString(16).length * 4 < 150000; n *= n) {
data.expexp.push(n, -n);
}
let serialized = packr.pack(data);
let deserialized = packr.unpack(serialized);
assert.deepEqual(data, deserialized);
});
test('extended class pack/unpack', function(){
function Extended() {
}
Extended.prototype.getDouble = function() {
return this.value * 2;
};
var instance = new Extended();
instance.value = 4;
instance.string = 'decode this: ᾜ';
var data = {
prop1: 'has multi-byte: ᾜ',
extendedInstance: instance,
prop2: 'more string',
num: 3,
};
let packr = new Packr();
addExtension({
Class: Extended,
type: 11,
unpack: function(buffer) {
let e = new Extended();
let data = packr.unpack(buffer);
e.value = data[0];
e.string = data[1];
return e;
},
pack: function(instance) {
return packr.pack([instance.value, instance.string]);
}
});
var serialized = pack(data);
var deserialized = unpack(serialized);
assert.deepEqual(data, deserialized);
assert.equal(deserialized.extendedInstance.getDouble(), 8);
});
test('extended Array class read/write', function(){
var instance = new ExtendArray();
instance.push(0);
instance.push(1);
instance.push(2);
var data = {
prop1: 'has multi-byte: ᾜ',
extendedInstance: instance,
prop2: 'more string',
num: 3,
};
new Packr();
addExtension({
Class: ExtendArray,
type: 12,
read: function(data) {
Object.setPrototypeOf(data, ExtendArray.prototype);
return data;
},
write: function(instance) {
return [...instance];
}
});
var serialized = pack(data);
var deserialized = unpack(serialized);
assert.strictEqual(Object.getPrototypeOf(deserialized.extendedInstance), ExtendArray.prototype);
assert.deepEqual(data, deserialized);
});
test('unregistered extended Array class read/write', function(){
var instance = new ExtendArray2();
instance.push(0);
instance.push(1);
instance.push(2);
var data = {
prop1: 'has multi-byte: ᾜ',
extendedInstance: instance,
prop2: 'more string',
num: 3,
};
new Packr();
var serialized = pack(data);
var deserialized = unpack(serialized);
assert.strictEqual(Object.getPrototypeOf(deserialized.extendedInstance), Array.prototype);
assert.deepEqual(data, deserialized);
});
test('unregistered extended Object class read/write', function(){
var instance = new ExtendObject();
instance.test1 = "string";
instance.test2 = 3421321;
var data = {
prop1: 'has multi-byte: ᾜ',
extendedInstance: instance,
prop2: 'more string',
num: 3,
};
new Packr();
var serialized = pack(data);
var deserialized = unpack(serialized);
assert.strictEqual(Object.getPrototypeOf(deserialized.extendedInstance), Object.prototype);
assert.deepEqual(data, deserialized);
});
test('extended class pack/unpack custom size', function(){
function TestClass() {
}
addExtension({
Class: TestClass,
type: 0x01,
pack() {
return typeof Buffer != 'undefined' ? Buffer.alloc(256) : new Uint8Array(256);
},
unpack(data) {
return data.length;
}
});
let result = unpack(pack(new TestClass()));
assert.equal(result, 256);
});
test('extended class read/write', function(){
function Extended() {
}
Extended.prototype.getDouble = function() {
return this.value * 2;
};
var instance = new Extended();
instance.value = 4;
instance.string = 'decode this: ᾜ';
var data = {
prop1: 'has multi-byte: ᾜ',
extendedInstance: instance,
prop2: 'more string',
num: 3,
};
new Packr();
addExtension({
Class: Extended,
type: 12,
read: function(data) {
let e = new Extended();
e.value = data[0];
e.string = data[1];
return e;
},
write: function(instance) {
return [instance.value, instance.string];
}
});
var serialized = pack(data);
var deserialized = unpack(serialized);
assert.deepEqual(data, deserialized);
assert.equal(deserialized.extendedInstance.getDouble(), 8);
});
test('extended class return self', function(){
function Extended() {
}
Extended.prototype.getDouble = function() {
return this.value * 2;
};
var instance = new Extended();
instance.value = 4;
instance.string = 'decode this: ᾜ';
var data = {
prop1: 'has multi-byte: ᾜ',
extendedInstance: instance,
prop2: 'more string',
num: 3,
};
new Packr();
addExtension({
Class: Extended,
type: 13,
read: function(data) {
Object.setPrototypeOf(data, Extended.prototype);
return data;
},
write: function(data) {
return data;
}
});
var serialized = pack(data);
var deserialized = unpack(serialized);
assert.deepEqual(data, deserialized);
assert.strictEqual(Object.getPrototypeOf(deserialized.extendedInstance), Extended.prototype);
assert.equal(deserialized.extendedInstance.getDouble(), 8);
});
test('extended Array class return self', function(){
var instance = new ExtendArray3();
instance.push(0);
instance.push('has multi-byte: ᾜ');
var data = {
prop1: 'has multi-byte: ᾜ',
extendedInstance: instance,
prop2: 'more string',
num: 3,
};
new Packr();
addExtension({
Class: ExtendArray3,
type: 14,
read: function(data) {
Object.setPrototypeOf(data, ExtendArray3.prototype);
return data;
},
write: function(data) {
return data;
}
});
var serialized = pack(data);
var deserialized = unpack(serialized);
assert.deepEqual(data, deserialized);
assert.strictEqual(Object.getPrototypeOf(deserialized.extendedInstance), ExtendArray3.prototype);
assert.equal(deserialized.extendedInstance[0], 0);
});
test('extended class pack/unpack proxied', function(){
function Extended() {
}
Extended.prototype.__call__ = function(){
return this.value * 4;
};
Extended.prototype.getDouble = function() {
return this.value * 2;
};
var instance = function() { instance.__call__();/* callable stuff */ };
Object.setPrototypeOf(instance,Extended.prototype);
instance.value = 4;
var data = instance;
let packr = new Packr();
addExtension({
Class: Extended,
type: 15,
unpack: function(buffer) {
var e = function() { e.__call__(); };
Object.setPrototypeOf(e,Extended.prototype);
e.value = packr.unpack(buffer);
return e;
},
pack: function(instance) {
return packr.pack(instance.value);
}
});
var serialized = pack(data);
var deserialized = unpack(serialized);
assert.equal(deserialized.getDouble(), 8);
});
test.skip('convert Date to string', function(){
var data = {
aDate: new Date(),
};
new Packr();
addExtension({
Class: Date,
write(date) {
return date.toString();
}
});
var serialized = pack(data);
var deserialized = unpack(serialized);
assert.equal(deserialized.aDate, data.aDate.toString());
});
test('standard pack fails on circular reference with shared structures', function () {
var data = {};
data.self = data;
let structures = [];
let packr = new Packr({
structures,
saveStructures(structures) {
}
});
assert.throws(function () {
packr.pack(data);
});
});
test('proto handling', function() {
var objectWithProto = JSON.parse('{"__proto__":{"foo":3}}');
var decoded = unpack(pack(objectWithProto));
assert(!decoded.foo);
var objectsWithProto = [objectWithProto, objectWithProto, objectWithProto, objectWithProto, objectWithProto, objectWithProto];
let packr = new Packr();
var decoded = packr.unpack(packr.pack(objectsWithProto));
for (let object of decoded) {
assert(!decoded.foo);
}
});
test.skip('text decoder', function() {
let td = new TextDecoder('ISO-8859-15');
let b = Buffer.alloc(3);
for (var i = 0; i < 256; i++) {
b[0] = i;
b[1] = 0;
b[2] = 0;
let s = td.decode(b);
if (!require('msgpackr-extract').isOneByte(s)) {
console.log(i.toString(16), s.length);
}
}
});
test('moreTypes: Error with causes', function() {
const object = {
error: new Error('test'),
errorWithCause: new Error('test-1', { cause: new Error('test-2') }),
type: new TypeError(),
range: new RangeError('test', { cause: [1, 2] }),
};
const packr = new Packr({
moreTypes: true,
});
const serialized = packr.pack(object);
const deserialized = packr.unpack(serialized);
assert.equal(deserialized.error.message, object.error.message);
assert.equal(deserialized.error.cause, object.error.cause);
assert.equal(deserialized.errorWithCause.message, object.errorWithCause.message);
assert.equal(deserialized.errorWithCause.cause.message, object.errorWithCause.cause.message);
assert.equal(deserialized.errorWithCause.cause.cause, object.errorWithCause.cause.cause);
assert.equal(deserialized.type.message, object.type.message);
assert.equal(deserialized.range.message, object.range.message);
assert.deepEqual(deserialized.range.cause, object.range.cause);
assert(deserialized.error instanceof Error);
assert(deserialized.type instanceof TypeError);
assert(deserialized.range instanceof RangeError);
});
test('structured cloning: self reference', function() {
let object = {
test: 'string',
children: [
{ name: 'child' }
],
value: new ArrayBuffer(10)
};
let u8 = new Uint8Array(object.value);
u8[0] = 1;
u8[1] = 2;
object.self = object;
object.children[1] = object;
object.children[2] = object.children[0];
object.childrenAgain = object.children;
let packr = new Packr({
moreTypes: true,
structuredClone: true,
});
var serialized = packr.pack(object);
var deserialized = packr.unpack(serialized);
assert.equal(deserialized.self, deserialized);
assert.equal(deserialized.children[0].name, 'child');
assert.equal(deserialized.children[1], deserialized);
assert.equal(deserialized.children[0], deserialized.children[2]);
assert.equal(deserialized.children, deserialized.childrenAgain);
assert.equal(deserialized.value.constructor.name, 'ArrayBuffer');
u8 = new Uint8Array(deserialized.value);
assert.equal(u8[0], 1);
assert.equal(u8[1], 2);
});
test('structured cloning: self reference with more types', function() {
let set = new Set();
set.add(['hello', 1, 2, { map: new Map([[set, set], ['a', 'b']]) }]);
let packr = new Packr({
moreTypes: true,
structuredClone: true,
});
let serialized = packr.pack(set);
let deserialized = packr.unpack(serialized);
assert.equal(deserialized.constructor.name, 'Set');
let map = Array.from(deserialized)[0][3].map;
assert.equal(map.get(deserialized), deserialized);
let sizeTestMap = new Map();
for (let i = 0; i < 50; i++) {
sizeTestMap.set(i || sizeTestMap, sizeTestMap);
let deserialized = packr.unpack(packr.pack(sizeTestMap));
assert.equal(deserialized.size, i + 1);
assert(deserialized.has(deserialized));
assert(deserialized.has(i || deserialized));
}
let sizeTestSet = new Set();
for (let i = 0; i < 50; i++) {
sizeTestSet.add(i || sizeTestSet);
let deserialized = packr.unpack(packr.pack(sizeTestSet));
assert.equal(deserialized.size, i + 1);
assert(deserialized.has(deserialized));
assert(deserialized.has(i || deserialized));
}
});
test('structured cloning: types', function() {
let b = typeof Buffer != 'undefined' ? Buffer.alloc(20) : new Uint8Array(20);
let fa = new Float32Array(b.buffer, 8, 2);
fa[0] = 2.25;
fa[1] = 6;
let object = {
error: new Error('test'),
set: new Set(['a', 'b']),
regexp: /test/gi,
float32Array: fa,
uint16Array: new Uint16Array([3, 4]),
arrayBuffer: new Uint8Array([0xde, 0xad]).buffer,
dataView: new DataView(new Uint8Array([0xbe, 0xef]).buffer),
};
let packr = new Packr({
moreTypes: true,
structuredClone: true,
});
var serialized = packr.pack(object);
var deserialized = packr.unpack(serialized);
assert.deepEqual(Array.from(deserialized.set), Array.from(object.set));
assert.equal(deserialized.error.message, object.error.message);
assert.equal(deserialized.regexp.test('TEST'), true);
assert.equal(deserialized.float32Array.constructor.name, 'Float32Array');
assert.equal(deserialized.float32Array[0], 2.25);
assert.equal(deserialized.float32Array[1], 6);
assert.equal(deserialized.uint16Array.constructor.name, 'Uint16Array');
assert.equal(deserialized.uint16Array[0], 3);
assert.equal(deserialized.uint16Array[1], 4);
assert.equal(deserialized.arrayBuffer.constructor.name, 'ArrayBuffer');
assert.equal(new DataView(deserialized.arrayBuffer).getUint16(), 0xdead);
assert.equal(deserialized.dataView.constructor.name, 'DataView');
assert.equal(deserialized.dataView.getUint16(), 0xbeef);
});
test('big bundledStrings', function() {
const MSGPACK_OPTIONS = {bundleStrings: true};
const packer = new Packr(MSGPACK_OPTIONS);
const unpacker = new Unpackr(MSGPACK_OPTIONS);
const payload = {
output: [
{
url: 'https://www.example.com/',
},
],
};
for (let i = 0; i < 10000; i++) {
payload.output.push(payload.output[0]);
}
let deserialized = unpacker.unpack(packer.pack(payload));
assert.equal(deserialized.output[0].url, payload.output[0].url);
});
test('structured clone with bundled strings', function() {
const packer = new Packr({
structuredClone: true, // both options must be enabled
bundleStrings: true,
});
const v = {};
let shared = {
name1: v,
name2: v,
};
let deserialized = packer.unpack(packer.pack(shared));
assert.equal(deserialized.name1, deserialized.name2);
shared = {};
shared.aaaa = shared; // key length >= 4
deserialized = packer.unpack(packer.pack(shared));
assert.equal(deserialized.aaaa, deserialized);
});
test('object without prototype', function(){
var data = Object.create(null);
data.test = 3;
var serialized = pack(data);
var deserialized = unpack(serialized);
assert.deepEqual(deserialized, data);
});
test('object with __proto__', function(){
const data = { foo: 'bar', __proto__: { isAdmin: true } };
var serialized = pack(data);
var deserialized = unpack(serialized);
assert.deepEqual(deserialized, { foo: 'bar' });
});
test('separate instances', function() {
const packr = new Packr({
structures: [['m', 'e'], ['action', 'share']]
});
const packr2 = new Packr({
structures: [['m', 'e'], ['action', 'share']]
});
let packed = packr.pack([{m: 1, e: 2}, {action: 3, share: 4}]);
// also tried directly decoding this without the first Packr instance packed = new Uint8Array([0x92, 0x40, 0x01, 0x02, 0x41, 0x03, 0x04]);
console.log(packr2.unpack(packed));
});
test('many shared structures', function() {
let data = [];
for (let i = 0; i < 200; i++) {
data.push({['a' + i]: i});
}
let structures = [];
let savedStructures;
let packr = new Packr({
structures,
saveStructures(structures) {
savedStructures = structures;
}
});
var serializedWith32 = packr.pack(data);
assert.equal(savedStructures.length, 32);
var deserialized = packr.unpack(serializedWith32);
assert.deepEqual(deserialized, data);
structures = structures.slice(0, 32);
packr = new Packr({
structures,
maxSharedStructures: 100,
saveStructures(structures) {
savedStructures = structures;
}
});
deserialized = packr.unpack(serializedWith32);
assert.deepEqual(deserialized, data);
structures = structures.slice(0, 32);
packr = new Packr({
structures,
maxSharedStructures: 100,
saveStructures(structures) {
savedStructures = structures;
}
});
let serialized = packr.pack(data);
assert.equal(savedStructures.length, 100);
deserialized = packr.unpack(serialized);
assert.deepEqual(deserialized, data);
deserialized = packr.unpack(serializedWith32);
assert.deepEqual(deserialized, data);
assert.equal(savedStructures.length, 100);
deserialized = packr.unpack(serialized);
assert.deepEqual(deserialized, data);
assert.equal(packr.structures.sharedLength, 100);
});
test('more shared structures', function() {
const structures = [];
for (let i = 0; i < 40; i++) {
structures.push(['a' + i]);
}
const structures2 = [...structures];
const packr = new Packr({
getStructures() {
return structures;
},
saveStructures(structures) {
},
maxSharedStructures: 100
});
const packr2 = new Packr({
getStructures() {
return structures2;
},
saveStructures(structures) {
},
maxSharedStructures: 100
});
const inputData = {a35: 35};
const buffer = packr.pack(inputData);
const outputData = packr2.decode(buffer);
assert.deepEqual(inputData, outputData);
});
test('declined structure save re-packs against durable structures (no dangling record)', function() {
// Regression: when saveStructures declines a save (a concurrent writer updated the shared
// structures, or the store txn did not durably commit), the in-memory structures/transition
// trie reference a record id that was never persisted. The re-pack must reload the durable
// structures and re-mint/re-save — otherwise it re-emits the same record pointing at an
// unsaved structure, and reads throw "Record id is not defined".
const meta = new Packr();
let store = null;
let declinedOnce = false;
const packr = new Packr({
useRecords: true,
getStructures() { return store ? meta.unpack(store) : undefined; },
saveStructures(structures) {
if (!declinedOnce) { declinedOnce = true; return false; } // decline the first save
store = meta.pack(structures); return true;
},
});
const a = packr.pack({ x: 9, y: 8 });
const b = packr.pack({ x: 7, y: 6 });
const reader = new Packr({ getStructures() { return store ? meta.unpack(store) : undefined; } });
assert.deepEqual(reader.unpack(a), { x: 9, y: 8 });
assert.deepEqual(reader.unpack(b), { x: 7, y: 6 });
});
test('big buffer', function() {
var size = 100000000;
var data = new Uint8Array(size).fill(1);
var packed = pack(data);
var unpacked = unpack(packed);
assert.equal(unpacked.length, size);
});
test('random strings', function(){
var data = [];
for (var i = 0; i < 2000; i++) {
var str = 'test';
while (Math.random() < 0.7 && str.length < 0x100000) {
str = str + String.fromCharCode(90/(Math.random() + 0.01)) + str;
}
data.push(str);
}
var serialized = pack(data);
var deserialized = unpack(serialized);
assert.deepEqual(deserialized, data);
});
test('map/date', function(){
var map = new Map();
map.set(4, 'four');
map.set('three', 3);
var data = {
map: map,
date: new Date(1532219539733),
farFutureDate: new Date(3532219539133),
fartherFutureDate: new Date('2106-08-05T18:48:20.323Z'),
ancient: new Date(-3532219539133),
invalidDate: new Date('invalid')
};
let packr = new Packr();
var serialized = packr.pack(data);
var deserialized = packr.unpack(serialized);
assert.equal(deserialized.map.get(4), 'four');
assert.equal(deserialized.map.get('three'), 3);
assert.equal(deserialized.date.getTime(), 1532219539733);
assert.equal(deserialized.farFutureDate.getTime(), 3532219539133);
assert.equal(deserialized.fartherFutureDate.toISOString(), '2106-08-05T18:48:20.323Z');
assert.equal(deserialized.ancient.getTime(), -3532219539133);
assert.equal(deserialized.invalidDate.toString(), 'Invalid Date');
});
test('map/date with options', function(){
var map = new Map();
map.set(4, 'four');
map.set('three', 3);
var data = {
map: map,
date: new Date(1532219539011),
invalidDate: new Date('invalid')
};
let packr = new Packr({
mapsAsObjects: true,
useTimestamp32: true,
onInvalidDate: () => 'Custom invalid date'
});
var serialized = packr.pack(data);
var deserialized = packr.unpack(serialized);
assert.equal(deserialized.map[4], 'four');
assert.equal(deserialized.map.three, 3);
assert.equal(deserialized.date.getTime(), 1532219539000);
assert.equal(deserialized.invalidDate, 'Custom invalid date');
});
test('key caching', function() {
var data = {
foo: 2,
bar: 'test',
four: 4,
seven: 7,
foz: 3,
};
var serialized = pack(data);
var deserialized = unpack(serialized);
assert.deepEqual(deserialized, data);
// do multiple times to test caching
var serialized = pack(data);
var deserialized = unpack(serialized);
assert.deepEqual(deserialized, data);
var serialized = pack(data);
var deserialized = unpack(serialized);
assert.deepEqual(deserialized, data);
});
test('strings', function() {
var data = [''];
var serialized = pack(data);
var deserialized = unpack(serialized);
assert.deepEqual(deserialized, data);
// do multiple times
var serialized = pack(data);
var deserialized = unpack(serialized);
assert.deepEqual(deserialized, data);
data = 'decode this: ᾜ';
var serialized = pack(data);
var deserialized = unpack(serialized);
assert.deepEqual(deserialized, data);
data = 'decode this that is longer but without any non-latin characters';
var serialized = pack(data);
var deserialized = unpack(serialized);
assert.deepEqual(deserialized, data);
});
test('decimal float32', function() {
var data = {
a: 2.526,
b: 0.0035235,
c: 0.00000000000352501,
d: 3252.77,
};
let packr = new Packr({
useFloat32: DECIMAL_FIT
});
var serialized = packr.pack(data);
assert.equal(serialized.length, 32);
var deserialized = packr.unpack(serialized);
assert.deepEqual(deserialized, data);
});
test('int64/uint64 should be bigints by default', function() {
var data = {
a: 325283295382932843n
};
let packr = new Packr();
var serialized = packr.pack(data);
var deserialized = packr.unpack(serialized);
assert.deepEqual(deserialized.a, 325283295382932843n);
});
test('bigint to float', function() {
var data = {
a: 325283295382932843n
};
let packr = new Packr({
int64AsType: 'number'
});
var serialized = packr.pack(data);
var deserialized = packr.unpack(serialized);
assert.deepEqual(deserialized.a, 325283295382932843);
});
test('int64AsNumber compatibility', function() {
// https://github.com/kriszyp/msgpackr/pull/85
var data = {
a: 325283295382932843n
};
let packr = new Packr({
int64AsNumber: true
});
var serialized = packr.pack(data);
var deserialized = packr.unpack(serialized);
assert.deepEqual(deserialized.a, 325283295382932843);
});
test('bigint to auto (float or bigint)', function() {
var data = {
a: -9007199254740993n,
b: -9007199254740992n,
c: 0n,
d: 9007199254740992n,
e: 9007199254740993n,
};
let packr = new Packr({
int64AsType: 'auto'
});
var serialized = packr.pack(data);
var deserialized = packr.unpack(serialized);
assert.deepEqual(deserialized.a, -9007199254740993n);
assert.deepEqual(deserialized.b, -9007199254740992);
assert.deepEqual(deserialized.c, 0);
assert.deepEqual(deserialized.d, 9007199254740992);
assert.deepEqual(deserialized.e, 9007199254740993n);
});
test('bigint to string', function() {
var data = {
a: 325283295382932843n,
};
let packr = new Packr({
int64AsType: 'string'
});
var serialized = packr.pack(data);
var deserialized = packr.unpack(serialized);
assert.deepEqual(deserialized.a, '325283295382932843');
});
test('fixint should be one byte', function(){
let encoded = pack(123);
assert.equal(encoded.length, 1);
});
test('numbers', function(){
var data = {
bigEncodable: 48978578104322,
dateEpoch: 1530886513200,
realBig: 3432235352353255323,
decimal: 32.55234,
negative: -34.11,
exponential: 0.234e123,
tiny: 3.233e-120,
zero: 0,
//negativeZero: -0,
Infinity: Infinity
};
var serialized = pack(data);
var deserialized = unpack(serialized);
assert.deepEqual(deserialized, data);
});
test('bigint', function(){
var data = {
bigintSmall: 352n,
bigintSmallNegative: -333335252n,
bigintBig: 2n**64n - 1n, // biggest possible
bigintBigNegative: -(2n**63n), // largest negative
mixedWithNormal: 44,
};
var serialized = pack(data);
var deserialized = unpack(serialized);
assert.deepEqual(deserialized, data);
var tooBigInt = {
tooBig: 2n**66n
};
assert.throws(function(){ serialized = pack(tooBigInt); });
let packr = new Packr({
largeBigIntToFloat: true
});
serialized = packr.pack(tooBigInt);
deserialized = unpack(serialized);
assert.isTrue(deserialized.tooBig > 2n**65n);
packr = new Packr({
largeBigIntToString: true
});
serialized = packr.pack(tooBigInt);
deserialized = unpack(serialized);
assert.equal(deserialized.tooBig, (2n**66n).toString());
});
test('roundFloat32', function() {
assert.equal(roundFloat32(0.00333000003), 0.00333);
assert.equal(roundFloat32(43.29999999993), 43.3);
});
test('buffers', function(){
var data = {
buffer1: new Uint8Array([2,3,4]),
buffer2: new Uint8Array(pack(sampleData))
};
var serialized = pack(data);
var deserialized = unpack(serialized);
assert.deepEqual(deserialized, data);
});
test('notepack test', function() {
const data = {
foo: 1,
bar: [1, 2, 3, 4, 'abc', 'def'],
foobar: {
foo: true,
bar: -2147483649,
foobar: {
foo: new Uint8Array([1, 2, 3, 4, 5]),
bar: 1.5,
foobar: [true, false, 'abcdefghijkmonpqrstuvwxyz']
}
}
};
var serialized = pack(data);
var deserialized = unpack(serialized);
var deserialized = unpack(serialized);
var deserialized = unpack(serialized);
assert.deepEqual(deserialized, data);
});
test('arrays in map keys', function() {
const msgpackr = new Packr({ mapsAsObjects: true, allowArraysInMapKeys: true });
const map = new Map();
map.set([1, 2, 3], 1);
map.set([1, 2, ['foo', 3.14]], 2);
const packed = msgpackr.pack(map);
const unpacked = msgpackr.unpack(packed);
assert.deepEqual(unpacked, { '1,2,3': 1, '1,2,foo,3.14': 2 });
});
test('utf16 causing expansion', function() {
this.timeout(10000);
let data = {fixstr: 'ᾐᾑᾒᾓᾔᾕᾖᾗᾘᾙᾚᾛᾜᾝ', str8:'ᾐᾑᾒᾓᾔᾕᾖᾗᾘᾙᾚᾛᾜᾝᾐᾑᾒᾓᾔᾕᾖᾗᾘᾙᾚᾛᾜᾝᾐᾑᾒᾓᾔᾕᾖᾗᾘᾙᾚᾛᾜᾝᾐᾑᾒᾓᾔᾕᾖᾗᾘᾙᾚᾛᾜᾝᾐᾑᾒᾓᾔᾕᾖᾗᾘᾙᾚᾛᾜᾝᾐᾑᾒᾓᾔᾕᾖᾗᾘᾙᾚᾛᾜᾝᾐᾑᾒᾓᾔᾕᾖᾗᾘᾙᾚᾛᾜᾝᾐᾑᾒᾓᾔᾕᾖᾗᾘᾙᾚᾛᾜᾝᾐᾑᾒᾓᾔᾕᾖᾗᾘᾙᾚᾛᾜᾝᾐᾑᾒᾓᾔᾕᾖᾗᾘᾙᾚᾛᾜᾝᾐᾑᾒᾓᾔᾕᾖᾗᾘᾙᾚᾛᾜᾝᾐᾑᾒᾓᾔᾕᾖᾗᾘᾙᾚᾛᾜᾝᾐᾑᾒᾓᾔᾕᾖᾗᾘᾙᾚᾛᾜᾝᾐᾑᾒᾓᾔᾕᾖᾗᾘᾙᾚᾛᾜᾝᾐᾑᾒᾓᾔᾕᾖᾗᾘᾙᾚᾛᾜᾝᾐᾑᾒᾓᾔᾕᾖᾗᾘᾙᾚᾛᾜᾝ'};
var serialized = pack(data);
var deserialized = unpack(serialized);
assert.deepEqual(deserialized, data);
});
test('unpackMultiple', () => {
let values = unpackMultiple(new Uint8Array([1, 2, 3, 4]));
assert.deepEqual(values, [1, 2, 3, 4]);
values = [];
unpackMultiple(new Uint8Array([1, 2, 3, 4]), value => values.push(value));
assert.deepEqual(values, [1, 2, 3, 4]);
});
test('unpackMultiple with positions', () => {
let values = unpackMultiple(new Uint8Array([1, 2, 3, 4]));
assert.deepEqual(values, [1, 2, 3, 4]);
values = [];
unpackMultiple(new Uint8Array([1, 2, 3, 4]), (value,start,end) => values.push([value,start,end]));
assert.deepEqual(values, [[1,0,1], [2,1,2], [3,2,3], [4,3,4]]);
});
test('pack toJSON returning this', () => {
class Serializable {
someData = [1, 2, 3, 4];
toJSON() {
return this;
}
}
const serialized = pack(new Serializable);
const deserialized = unpack(serialized);
assert.deepStrictEqual(deserialized, { someData: [1, 2, 3, 4] });
});
test('skip values', function () {
var data = {
data: [
{ a: 1, name: 'one', type: 'odd', isOdd: true },
{ a: 2, name: 'two', type: 'even', isOdd: undefined },
{ a: 3, name: 'three', type: 'odd', isOdd: true },
{ a: 4, name: 'four', type: 'even', isOdd: null},
{ a: 5, name: 'five', type: 'odd', isOdd: true },
{ a: 6, name: 'six', type: 'even', isOdd: null }
],
description: 'some names',
types: ['odd', 'even'],
convertEnumToNum: [
{ prop: 'test' },
{ prop: 'test' },
{ prop: 'test' },
{ prop: 1 },
{ prop: 2 },
{ prop: [undefined, null] },
{ prop: null }
]
};
var expected = {
data: [
{ a: 1, name: 'one', type: 'odd', isOdd: true },
{ a: 2, name: 'two', type: 'even' },
{ a: 3, name: 'three', type: 'odd', isOdd: true },
{ a: 4, name: 'four', type: 'even', },
{ a: 5, name: 'five', type: 'odd', isOdd: true },
{ a: 6, name: 'six', type: 'even' }
],
description: 'some names',
types: ['odd', 'even'],
convertEnumToNum: [
{ prop: 'test' },
{ prop: 'test' },
{ prop: 'test' },
{ prop: 1 },
{ prop: 2 },
{ prop: [undefined, null] },
{}
]
};
let packr = new Packr({ useRecords: false, skipValues: [undefined, null] });
var serialized = packr.pack(data);
var deserialized = packr.unpack(serialized);
assert.deepEqual(deserialized, expected);
});
});
suite('msgpackr performance tests', function(){
test('performance JSON.parse', function() {
var data = sampleData;
this.timeout(10000);
var serialized = JSON.stringify(data);
console.log('JSON size', serialized.length);
for (var i = 0; i < ITERATIONS; i++) {
JSON.parse(serialized);
}
});
test('performance JSON.stringify', function() {
var data = sampleData;
this.timeout(10000);
for (var i = 0; i < ITERATIONS; i++) {
JSON.stringify(data);
}
});
test('performance unpack', function() {
var data = sampleData;
this.timeout(10000);
let structures = [];
var serialized = pack(data);
console.log('MessagePack size', serialized.length);
let packr = new Packr({ structures, bundleStrings: false });
var serialized = packr.pack(data);
console.log('msgpackr w/ record ext size', serialized.length);
for (var i = 0; i < ITERATIONS; i++) {
packr.unpack(serialized);
}
});
test('performance pack', function() {
var data = sampleData;
this.timeout(10000);
let structures = [];
let packr = new Packr({ structures, bundleStrings: false });
let buffer = typeof Buffer != 'undefined' ? Buffer.alloc(0x10000) : new Uint8Array(0x10000);
for (var i = 0; i < ITERATIONS; i++) {
//serialized = pack(data, { shared: sharedStructure })
packr.useBuffer(buffer);
packr.pack(data);
//var serializedGzip = deflateSync(serialized)
}
//console.log('serialized', serialized.length, global.propertyComparisons)
});
});
suite('msgpackr – two-byte record definitions (over-cap own records with a shared store)', function () {
// Regression for a data-corruption bug: with a shared-structures store (getStructures/
// saveStructures) and maxOwnStructures > 32, msgpackr uses two-byte record ids
// (useTwoByteRecords). Over-cap "own" record shapes fall back to the classic record encoder,
// which emits a SELF-CONTAINED record definition (0xd5 0x72 <firstByte> <highByte> <keys>
// <values>). On read, recordDefinition() consumed the high byte itself, then invoked
// structure.read() — which, for a highByte === 0 structure, was a second-byte reader that
// consumed the FIRST VALUE BYTE as a phantom high byte, mis-resolving to a never-defined id
// ("Record id is not defined for N"). The one-byte path (maxOwnStructures <= 32, fixext 1
// 0xd4 0x72) was unaffected because no high byte / second-byte reader is involved.
// Fix: recordDefinition reads the body via the un-wrapped reader (structure.read0).
const sharedStore = () => {
let saved = [];
return { getStructures: () => saved, saveStructures: (s) => { saved = s; return true; }, peek: () => saved };
};
// Width-heterogeneous record generator: many distinct shapes, exceeding any modest cap, so
// the over-cap classic-record fallback path is exercised heavily.
const makeRecords = (count) => {
const out = [];
for (let i = 0; i < count; i++) {
const rec = { id: i };
const n = 1 + (i % 6);
for (let a = 0; a < n; a++) rec['attr_' + ((i * 7 + a) % 400)] = i % 2 ? 's' + i : i;
out.push(rec);
}
return out;
};
test('cap=256 + shared store round-trips every over-cap shape (two-byte mode)', function () {
const store = sharedStore();
const o = { randomAccessStructure: true, useRecords: true, useBigIntExtension: true, maxOwnStructures: 256, ...store };
const w = new Packr(o), r = new Packr(o);
const records = makeRecords(3000);
let sawTwoByteDef = false;
for (const rec of records) {
const buf = w.pack(rec);
if (buf[0] === 0xd5 && buf[1] === 0x72) sawTwoByteDef = true;
assert.deepEqual(r.unpack(buf), rec);
}
assert.ok(sawTwoByteDef, 'expected at least one two-byte (0xd5 0x72) record definition to be exercised');
});
test('over-cap own records do not grow the shared dictionary without bound', function () {
const store = sharedStore();
const o = { randomAccessStructure: true, useRecords: true, useBigIntExtension: true, maxOwnStructures: 256, ...store };
const w = new Packr(o);
for (const rec of makeRecords(3000)) w.pack(rec);
const saved = store.peek();
const named = saved instanceof Map ? (saved.get('named') || []) : (saved || []);
// Over-cap shapes are inlined as self-contained definitions, not minted as ever-growing
// shared ids, so the shared store stays bounded by the few genuinely shared shapes.
assert.ok(named.length < 100, 'named structures should stay bounded, got ' + named.length);
});
test('two-byte record REFERENCES (shared, maxShared=64) round-trip with stable shapes', function () {
// maxSharedStructures > 32 forces two-byte mode; stable repeated shapes get a definition
// once then are emitted as two-byte references (0x60-0x7f + high byte) — the path that
// relies on structure.read remaining a second-byte reader after the fix.
const store = sharedStore();
const o = { useRecords: true, maxSharedStructures: 64, maxOwnStructures: 64, ...store };
const w = new Packr(o), r = new Packr(o);
const shapes = [];
for (let s = 0; s < 100; s++) { const rec = {}; for (let f = 0; f <= s % 8; f++) rec['k' + s + '_' + f] = s * 10 + f; shapes.push(rec); }
let sawRef = false;
for (let round = 0; round < 4; round++) for (const shp of shapes) {
const buf = w.pack(shp);
if (buf[0] >= 0x60 && buf[0] < 0x80) sawRef = true;
assert.deepEqual(r.unpack(buf), shp);
}
assert.ok(sawRef, 'expected at least one two-byte record reference (0x60-0x7f) to be exercised');
});
test('one-byte path (maxOwn=32) keeps working with a shared store', function () {
const store = sharedStore();
const o = { randomAccessStructure: true, useRecords: true, useBigIntExtension: true, maxOwnStructures: 32, ...store };
const w = new Packr(o), r = new Packr(o);
for (const rec of makeRecords(3000)) {
assert.deepEqual(r.unpack(w.pack(rec)), rec);
}
});
});
})(chai, null, module, fs);
//# sourceMappingURL=test.js.map