(function (chai, stream, module, fs) { 'use strict'; 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; var readStruct$1, onLoadedStructures$1, onSaveState; // no-eval build try { new Function(''); } catch(error) { // if eval variants are not supported, do not create inline object readers ever inlineObjectReadThreshold = Infinity; } 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$1(() => { clearSource(); return this ? this.unpack(source, options) : Unpackr$1.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$1) { 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 (onLoadedStructures$1) loadedStructures = onLoadedStructures$1.call(this, 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.randomAccessStructure && src[position$1] < 0x40 && src[position$1] >= 0x20 && readStruct$1) { result = readStruct$1(src, position$1, srcEnd, currentUnpackr); 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(-2)<= 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 readObject = 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); if (structure.highByte === 0) structure.read = createSecondByteReader(firstId, structure.read); return readObject() // 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; 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$1(() => { // 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 const byte2 = src[position$1++] & 0x3f; units.push(((byte1 & 0x1f) << 6) | byte2); } else if ((byte1 & 0xf0) === 0xe0) { // 3 bytes const byte2 = src[position$1++] & 0x3f; const byte3 = src[position$1++] & 0x3f; units.push(((byte1 & 0x1f) << 12) | (byte2 << 6) | byte3); } else if ((byte1 & 0xf8) === 0xf0) { // 4 bytes const byte2 = src[position$1++] & 0x3f; const byte3 = src[position$1++] & 0x3f; const byte4 = src[position$1++] & 0x3f; let unit = ((byte1 & 0x07) << 0x12) | (byte2 << 0x0c) | (byte3 << 0x06) | byte4; if (unit > 0xffff) { unit -= 0x10000; units.push(((unit >>> 10) & 0x3ff) | 0xd800); unit = 0xdc00 | (unit & 0x3ff); } units.push(unit); } else { units.push(byte1); } if (units.length >= 0x1000) { result += fromCharCode.apply(String, units); units.length = 0; } } if (units.length > 0) { result += fromCharCode.apply(String, units); } return result } function readString(source, start, length) { let existingSrc = src; src = source; position$1 = start; try { return readStringJS(length); } finally { src = existingSrc; } } 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 + ''; 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); return 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) => { // decode bigint let length = data.length; let value = BigInt(data[0] & 0x80 ? data[0] - 0x100 : data[0]); for (let i = 1; i < length; i++) { value <<= 8n; value += BigInt(data[i]); } return value; }; let errors = { Error, TypeError, ReferenceError }; currentExtensions[0x65] = () => { let data = read(); return (errors[data[0]] || Error)(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 Maps, Sets, and other types that can cycle; this is complicated, because you potentially need to read // ahead past references to record structure definitions if (token >= 0x90 && token < 0xa0 || token == 0xdc || token == 0xdd) target = []; 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) // there is a cycle, so we have to assign properties to original target return Object.assign(target, targetProperties) refEntry.target = targetProperties; // the placeholder wasn't used, replace with the deserialized one return targetProperties // no cycle, can just use the returned read object }; 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]; let typedArrayName = typedArrays[typeCode]; if (!typedArrayName) { if (typeCode === 16) { let ab = new ArrayBuffer(data.length - 1); let u8 = new Uint8Array(ab); u8.set(data.subarray(1)); return ab; } throw new Error('Could not find typed array for code ' + typeCode) } // we have to always slice/copy here to get a new ArrayBuffer that is word/byte aligned return new glbl[typedArrayName](Uint8Array.prototype.slice.call(data, 1).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)// TODO: Implement support for negative 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') }; // notepack defines extension 0 to mean undefined, so use that as the default here // registration of bulk record definition? // currentExtensions[0x52] = () => function saveState$1(callback) { if (onSaveState) 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 } function setReadStruct(updatedReadStruct, loadedStructs, saveState) { readStruct$1 = updatedReadStruct; onLoadedStructures$1 = loadedStructs; onSaveState = saveState; } let textEncoder$1; try { textEncoder$1 = new TextEncoder(); } catch (error) {} let extensions, extensionClasses; const hasNodeBuffer$1 = typeof Buffer !== 'undefined'; const ByteArrayAllocate = hasNodeBuffer$1 ? function(length) { return Buffer.allocUnsafeSlow(length) } : Uint8Array; const ByteArray = hasNodeBuffer$1 ? Buffer : Uint8Array; const MAX_BUFFER_SIZE = hasNodeBuffer$1 ? 0x100000000 : 0x7fd00000; let target, keysTarget; let targetView; let position = 0; let safeEnd; let bundledStrings = null; let writeStructSlots; 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, 0xffffffff) } : (textEncoder$1 && textEncoder$1.encodeInto) ? function(string, position) { return textEncoder$1.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.randomAccessStructure && value && value.constructor && value.constructor === Object) writeStruct(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 = prepareStructures$1(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) { // get updated structures and try again if the update failed 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.randomAccessStructure)) { 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 < (BigInt(1)<= -(BigInt(1)< 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.useBigIntExtension && value < 2n**(1023n) && value > -(2n**(1023n))) { target[position++] = 0xc7; position++; target[position++] = 0x42; // "B" for BigInt let bytes = []; let alignedSign; do { let byte = value & 0xffn; alignedSign = (byte & 0x80n) === (value < 0n ? 0x80n : 0n); bytes.push(byte); value >>= 8n; } while (!((value === 0n || value === -1n) && alignedSign)); target[position-2] = bytes.length; for (let i = bytes.length; i > 0;) { target[position++] = Number(bytes[--i]); } 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') } } 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++; } } 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 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]; } }; const writeStruct = (object) => { let newPosition = writeStructSlots(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; }, this); if (newPosition === 0) // bail and go to a msgpack object return writeObject(object); position = newPosition; }; } 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)); 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*/, 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$1 ? 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(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$1(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 } function setWriteStructSlots(writeSlots, makeStructures) { writeStructSlots = writeSlots; prepareStructures$1 = makeStructures; } 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 ASCII = 3; // the MIBenum from https://www.iana.org/assignments/character-sets/character-sets.xhtml (and other character encodings could be referenced by MIBenum) const NUMBER = 0; const UTF8 = 2; const OBJECT_DATA = 1; const DATE = 16; const TYPE_NAMES = ['num', 'object', 'string', 'ascii']; TYPE_NAMES[DATE] = 'date'; const float32Headers = [false, true, true, false, false, true, true, false]; let evalSupported; try { new Function(''); evalSupported = true; } catch(error) { // if eval variants are not supported, do not create inline object readers ever } let updatedPosition; const hasNodeBuffer = typeof Buffer !== 'undefined'; let textEncoder, currentSource; try { textEncoder = new TextEncoder(); } catch (error) {} const encodeUtf8 = hasNodeBuffer ? function(target, string, position) { return target.utf8Write(string, position, 0xffffffff) } : (textEncoder && textEncoder.encodeInto) ? function(target, string, position) { return textEncoder.encodeInto(string, target.subarray(position)).written } : false; setWriteStructSlots(writeStruct, prepareStructures); function writeStruct(object, target, encodingStart, position, structures, makeRoom, pack, packr) { let typedStructs = packr.typedStructs || (packr.typedStructs = []); // note that we rely on pack.js to load stored structures before we get to this point let targetView = target.dataView; let refsStartPosition = (typedStructs.lastStringStart || 100) + position; let safeEnd = target.length - 10; let start = position; if (position > safeEnd) { target = makeRoom(position); targetView = target.dataView; position -= encodingStart; start -= encodingStart; refsStartPosition -= encodingStart; encodingStart = 0; safeEnd = target.length - 10; } let refOffset, refPosition = refsStartPosition; let transition = typedStructs.transitions || (typedStructs.transitions = Object.create(null)); let nextId = typedStructs.nextId || typedStructs.length; let headerSize = nextId < 0xf ? 1 : nextId < 0xf0 ? 2 : nextId < 0xf000 ? 3 : nextId < 0xf00000 ? 4 : 0; if (headerSize === 0) return 0; position += headerSize; let queuedReferences = []; let usedAscii0; let keyIndex = 0; for (let key in object) { let value = object[key]; let nextTransition = transition[key]; if (!nextTransition) { transition[key] = nextTransition = { key, parent: transition, enumerationOffset: 0, ascii0: null, ascii8: null, num8: null, string16: null, object16: null, num32: null, float64: null, date64: null }; } if (position > safeEnd) { target = makeRoom(position); targetView = target.dataView; position -= encodingStart; start -= encodingStart; refsStartPosition -= encodingStart; refPosition -= encodingStart; encodingStart = 0; safeEnd = target.length - 10; } switch (typeof value) { case 'number': let number = value; // first check to see if we are using a lot of ids and should default to wide/common format if (nextId < 200 || !nextTransition.num64) { if (number >> 0 === number && number < 0x20000000 && number > -0x1f000000) { if (number < 0xf6 && number >= 0 && (nextTransition.num8 && !(nextId > 200 && nextTransition.num32) || number < 0x20 && !nextTransition.num32)) { transition = nextTransition.num8 || createTypeTransition(nextTransition, NUMBER, 1); target[position++] = number; } else { transition = nextTransition.num32 || createTypeTransition(nextTransition, NUMBER, 4); targetView.setUint32(position, number, true); position += 4; } break; } else if (number < 0x100000000 && number >= -0x80000000) { targetView.setFloat32(position, number, true); if (float32Headers[target[position + 3] >>> 5]) { let xShifted; // this checks for rounding of numbers that were encoded in 32-bit float to nearest significant decimal digit that could be preserved if (((xShifted = number * mult10[((target[position + 3] & 0x7f) << 1) | (target[position + 2] >> 7)]) >> 0) === xShifted) { transition = nextTransition.num32 || createTypeTransition(nextTransition, NUMBER, 4); position += 4; break; } } } } transition = nextTransition.num64 || createTypeTransition(nextTransition, NUMBER, 8); targetView.setFloat64(position, number, true); position += 8; break; case 'string': let strLength = value.length; refOffset = refPosition - refsStartPosition; if ((strLength << 2) + refPosition > safeEnd) { target = makeRoom((strLength << 2) + refPosition); targetView = target.dataView; position -= encodingStart; start -= encodingStart; refsStartPosition -= encodingStart; refPosition -= encodingStart; encodingStart = 0; safeEnd = target.length - 10; } if (strLength > ((0xff00 + refOffset) >> 2)) { queuedReferences.push(key, value, position - start); break; } let isNotAscii; let strStart = refPosition; if (strLength < 0x40) { let i, c1, c2; for (i = 0; i < strLength; i++) { c1 = value.charCodeAt(i); if (c1 < 0x80) { target[refPosition++] = c1; } else if (c1 < 0x800) { isNotAscii = true; target[refPosition++] = c1 >> 6 | 0xc0; target[refPosition++] = c1 & 0x3f | 0x80; } else if ( (c1 & 0xfc00) === 0xd800 && ((c2 = value.charCodeAt(i + 1)) & 0xfc00) === 0xdc00 ) { isNotAscii = true; c1 = 0x10000 + ((c1 & 0x03ff) << 10) + (c2 & 0x03ff); i++; target[refPosition++] = c1 >> 18 | 0xf0; target[refPosition++] = c1 >> 12 & 0x3f | 0x80; target[refPosition++] = c1 >> 6 & 0x3f | 0x80; target[refPosition++] = c1 & 0x3f | 0x80; } else { isNotAscii = true; target[refPosition++] = c1 >> 12 | 0xe0; target[refPosition++] = c1 >> 6 & 0x3f | 0x80; target[refPosition++] = c1 & 0x3f | 0x80; } } } else { refPosition += encodeUtf8(target, value, refPosition); isNotAscii = refPosition - strStart > strLength; } if (refOffset < 0xa0 || (refOffset < 0xf6 && (nextTransition.ascii8 || nextTransition.string8))) { // short strings if (isNotAscii) { if (!(transition = nextTransition.string8)) { if (typedStructs.length > 10 && (transition = nextTransition.ascii8)) { // we can safely change ascii to utf8 in place since they are compatible transition.__type = UTF8; nextTransition.ascii8 = null; nextTransition.string8 = transition; pack(null, 0, true); // special call to notify that structures have been updated } else { transition = createTypeTransition(nextTransition, UTF8, 1); } } } else if (refOffset === 0 && !usedAscii0) { usedAscii0 = true; transition = nextTransition.ascii0 || createTypeTransition(nextTransition, ASCII, 0); break; // don't increment position }// else ascii: else if (!(transition = nextTransition.ascii8) && !(typedStructs.length > 10 && (transition = nextTransition.string8))) transition = createTypeTransition(nextTransition, ASCII, 1); target[position++] = refOffset; } else { // TODO: Enable ascii16 at some point, but get the logic right //if (isNotAscii) transition = nextTransition.string16 || createTypeTransition(nextTransition, UTF8, 2); //else //transition = nextTransition.ascii16 || createTypeTransition(nextTransition, ASCII, 2); targetView.setUint16(position, refOffset, true); position += 2; } break; case 'object': if (value) { if (value.constructor === Date) { transition = nextTransition.date64 || createTypeTransition(nextTransition, DATE, 8); targetView.setFloat64(position, value.getTime(), true); position += 8; } else { queuedReferences.push(key, value, keyIndex); } break; } else { // null nextTransition = anyType(nextTransition, position, targetView, -10); // match CBOR with this if (nextTransition) { transition = nextTransition; position = updatedPosition; } else queuedReferences.push(key, value, keyIndex); } break; case 'boolean': transition = nextTransition.num8 || nextTransition.ascii8 || createTypeTransition(nextTransition, NUMBER, 1); target[position++] = value ? 0xf9 : 0xf8; // match CBOR with these break; case 'undefined': nextTransition = anyType(nextTransition, position, targetView, -9); // match CBOR with this if (nextTransition) { transition = nextTransition; position = updatedPosition; } else queuedReferences.push(key, value, keyIndex); break; default: queuedReferences.push(key, value, keyIndex); } keyIndex++; } for (let i = 0, l = queuedReferences.length; i < l;) { let key = queuedReferences[i++]; let value = queuedReferences[i++]; let propertyIndex = queuedReferences[i++]; let nextTransition = transition[key]; if (!nextTransition) { transition[key] = nextTransition = { key, parent: transition, enumerationOffset: propertyIndex - keyIndex, ascii0: null, ascii8: null, num8: null, string16: null, object16: null, num32: null, float64: null }; } let newPosition; if (value) { /*if (typeof value === 'string') { // TODO: we could re-enable long strings if (position + value.length * 3 > safeEnd) { target = makeRoom(position + value.length * 3); position -= start; targetView = target.dataView; start = 0; } newPosition = position + target.utf8Write(value, position, 0xffffffff); } else { */ let size; refOffset = refPosition - refsStartPosition; if (refOffset < 0xff00) { transition = nextTransition.object16; if (transition) size = 2; else if ((transition = nextTransition.object32)) size = 4; else { transition = createTypeTransition(nextTransition, OBJECT_DATA, 2); size = 2; } } else { transition = nextTransition.object32 || createTypeTransition(nextTransition, OBJECT_DATA, 4); size = 4; } newPosition = pack(value, refPosition); //} if (typeof newPosition === 'object') { // re-allocated refPosition = newPosition.position; targetView = newPosition.targetView; target = newPosition.target; refsStartPosition -= encodingStart; position -= encodingStart; start -= encodingStart; encodingStart = 0; } else refPosition = newPosition; if (size === 2) { targetView.setUint16(position, refOffset, true); position += 2; } else { targetView.setUint32(position, refOffset, true); position += 4; } } else { // null or undefined transition = nextTransition.object16 || createTypeTransition(nextTransition, OBJECT_DATA, 2); targetView.setInt16(position, value === null ? -10 : -9, true); position += 2; } keyIndex++; } let recordId = transition[RECORD_SYMBOL]; if (recordId == null) { recordId = packr.typedStructs.length; let structure = []; let nextTransition = transition; let key, type; while ((type = nextTransition.__type) !== undefined) { let size = nextTransition.__size; nextTransition = nextTransition.__parent; key = nextTransition.key; let property = [type, size, key]; if (nextTransition.enumerationOffset) property.push(nextTransition.enumerationOffset); structure.push(property); nextTransition = nextTransition.parent; } structure.reverse(); transition[RECORD_SYMBOL] = recordId; packr.typedStructs[recordId] = structure; pack(null, 0, true); // special call to notify that structures have been updated } switch (headerSize) { case 1: if (recordId >= 0x10) return 0; target[start] = recordId + 0x20; break; case 2: if (recordId >= 0x100) return 0; target[start] = 0x38; target[start + 1] = recordId; break; case 3: if (recordId >= 0x10000) return 0; target[start] = 0x39; targetView.setUint16(start + 1, recordId, true); break; case 4: if (recordId >= 0x1000000) return 0; targetView.setUint32(start, (recordId << 8) + 0x3a, true); break; } if (position < refsStartPosition) { if (refsStartPosition === refPosition) return position; // no refs // adjust positioning target.copyWithin(position, refsStartPosition, refPosition); refPosition += position - refsStartPosition; typedStructs.lastStringStart = position - start; } else if (position > refsStartPosition) { if (refsStartPosition === refPosition) return position; // no refs typedStructs.lastStringStart = position - start; return writeStruct(object, target, encodingStart, start, structures, makeRoom, pack, packr); } return refPosition; } function anyType(transition, position, targetView, value) { let nextTransition; if ((nextTransition = transition.ascii8 || transition.num8)) { targetView.setInt8(position, value, true); updatedPosition = position + 1; return nextTransition; } if ((nextTransition = transition.string16 || transition.object16)) { targetView.setInt16(position, value, true); updatedPosition = position + 2; return nextTransition; } if (nextTransition = transition.num32) { targetView.setUint32(position, 0xe0000100 + value, true); updatedPosition = position + 4; return nextTransition; } // transition.float64 if (nextTransition = transition.num64) { targetView.setFloat64(position, NaN, true); targetView.setInt8(position, value); updatedPosition = position + 8; return nextTransition; } updatedPosition = position; // TODO: can we do an "any" type where we defer the decision? return; } function createTypeTransition(transition, type, size) { let typeName = TYPE_NAMES[type] + (size << 3); let newTransition = transition[typeName] || (transition[typeName] = Object.create(null)); newTransition.__type = type; newTransition.__size = size; newTransition.__parent = transition; return newTransition; } function onLoadedStructures(sharedData) { if (!(sharedData instanceof Map)) return sharedData; let typed = sharedData.get('typed') || []; if (Object.isFrozen(typed)) typed = typed.map(structure => structure.slice(0)); let named = sharedData.get('named'); let transitions = Object.create(null); for (let i = 0, l = typed.length; i < l; i++) { let structure = typed[i]; let transition = transitions; for (let [type, size, key] of structure) { let nextTransition = transition[key]; if (!nextTransition) { transition[key] = nextTransition = { key, parent: transition, enumerationOffset: 0, ascii0: null, ascii8: null, num8: null, string16: null, object16: null, num32: null, float64: null, date64: null, }; } transition = createTypeTransition(nextTransition, type, size); } transition[RECORD_SYMBOL] = i; } typed.transitions = transitions; this.typedStructs = typed; this.lastTypedStructuresLength = typed.length; return named; } var sourceSymbol = Symbol.for('source'); function readStruct(src, position, srcEnd, unpackr) { let recordId = src[position++] - 0x20; if (recordId >= 24) { switch(recordId) { case 24: recordId = src[position++]; break; // little endian: case 25: recordId = src[position++] + (src[position++] << 8); break; case 26: recordId = src[position++] + (src[position++] << 8) + (src[position++] << 16); break; case 27: recordId = src[position++] + (src[position++] << 8) + (src[position++] << 16) + (src[position++] << 24); break; } } let structure = unpackr.typedStructs && unpackr.typedStructs[recordId]; if (!structure) { // copy src buffer because getStructures will override it src = Uint8Array.prototype.slice.call(src, position, srcEnd); srcEnd -= position; position = 0; if (!unpackr.getStructures) throw new Error(`Reference to shared structure ${recordId} without getStructures method`); unpackr._mergeStructures(unpackr.getStructures()); if (!unpackr.typedStructs) throw new Error('Could not find any shared typed structures'); unpackr.lastTypedStructuresLength = unpackr.typedStructs.length; structure = unpackr.typedStructs[recordId]; if (!structure) throw new Error('Could not find typed structure ' + recordId); } var construct = structure.construct; if (!construct) { construct = structure.construct = function LazyObject() { }; var prototype = construct.prototype; let properties = []; let currentOffset = 0; let lastRefProperty; for (let i = 0, l = structure.length; i < l; i++) { let definition = structure[i]; let [ type, size, key, enumerationOffset ] = definition; if (key === '__proto__') key = '__proto_'; let property = { key, offset: currentOffset, }; if (enumerationOffset) properties.splice(i + enumerationOffset, 0, property); else properties.push(property); let getRef; switch(size) { // TODO: Move into a separate function case 0: getRef = () => 0; break; case 1: getRef = (source, position) => { let ref = source.bytes[position + property.offset]; return ref >= 0xf6 ? toConstant(ref) : ref; }; break; case 2: getRef = (source, position) => { let src = source.bytes; let dataView = src.dataView || (src.dataView = new DataView(src.buffer, src.byteOffset, src.byteLength)); let ref = dataView.getUint16(position + property.offset, true); return ref >= 0xff00 ? toConstant(ref & 0xff) : ref; }; break; case 4: getRef = (source, position) => { let src = source.bytes; let dataView = src.dataView || (src.dataView = new DataView(src.buffer, src.byteOffset, src.byteLength)); let ref = dataView.getUint32(position + property.offset, true); return ref >= 0xffffff00 ? toConstant(ref & 0xff) : ref; }; break; } property.getRef = getRef; currentOffset += size; let get; switch(type) { case ASCII: if (lastRefProperty && !lastRefProperty.next) lastRefProperty.next = property; lastRefProperty = property; property.multiGetCount = 0; get = function(source) { let src = source.bytes; let position = source.position; let refStart = currentOffset + position; let ref = getRef(source, position); if (typeof ref !== 'number') return ref; let end, next = property.next; while(next) { end = next.getRef(source, position); if (typeof end === 'number') break; else end = null; next = next.next; } if (end == null) end = source.bytesEnd - refStart; if (source.srcString) { return source.srcString.slice(ref, end); } /*if (property.multiGetCount > 0) { let asciiEnd; next = firstRefProperty; let dataView = src.dataView || (src.dataView = new DataView(src.buffer, src.byteOffset, src.byteLength)); do { asciiEnd = dataView.getUint16(source.position + next.offset, true); if (asciiEnd < 0xff00) break; else asciiEnd = null; } while((next = next.next)); if (asciiEnd == null) asciiEnd = source.bytesEnd - refStart source.srcString = src.toString('latin1', refStart, refStart + asciiEnd); return source.srcString.slice(ref, end); } if (source.prevStringGet) { source.prevStringGet.multiGetCount += 2; } else { source.prevStringGet = property; property.multiGetCount--; }*/ return readString(src, ref + refStart, end - ref); //return src.toString('latin1', ref + refStart, end + refStart); }; break; case UTF8: case OBJECT_DATA: if (lastRefProperty && !lastRefProperty.next) lastRefProperty.next = property; lastRefProperty = property; get = function(source) { let position = source.position; let refStart = currentOffset + position; let ref = getRef(source, position); if (typeof ref !== 'number') return ref; let src = source.bytes; let end, next = property.next; while(next) { end = next.getRef(source, position); if (typeof end === 'number') break; else end = null; next = next.next; } if (end == null) end = source.bytesEnd - refStart; if (type === UTF8) { return src.toString('utf8', ref + refStart, end + refStart); } else { currentSource = source; try { return unpackr.unpack(src, { start: ref + refStart, end: end + refStart }); } finally { currentSource = null; } } }; break; case NUMBER: switch(size) { case 4: get = function (source) { let src = source.bytes; let dataView = src.dataView || (src.dataView = new DataView(src.buffer, src.byteOffset, src.byteLength)); let position = source.position + property.offset; let value = dataView.getInt32(position, true); if (value < 0x20000000) { if (value > -0x1f000000) return value; if (value > -0x20000000) return toConstant(value & 0xff); } let fValue = dataView.getFloat32(position, true); // 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 + 3] & 0x7f) << 1) | (src[position + 2] >> 7)]; return ((multiplier * fValue + (fValue > 0 ? 0.5 : -0.5)) >> 0) / multiplier; }; break; case 8: get = function (source) { let src = source.bytes; let dataView = src.dataView || (src.dataView = new DataView(src.buffer, src.byteOffset, src.byteLength)); let value = dataView.getFloat64(source.position + property.offset, true); if (isNaN(value)) { let byte = src[source.position + property.offset]; if (byte >= 0xf6) return toConstant(byte); } return value; }; break; case 1: get = function (source) { let src = source.bytes; let value = src[source.position + property.offset]; return value < 0xf6 ? value : toConstant(value); }; break; } break; case DATE: get = function (source) { let src = source.bytes; let dataView = src.dataView || (src.dataView = new DataView(src.buffer, src.byteOffset, src.byteLength)); return new Date(dataView.getFloat64(source.position + property.offset, true)); }; break; } property.get = get; } // TODO: load the srcString for faster string decoding on toJSON if (evalSupported) { let objectLiteralProperties = []; let args = []; let i = 0; let hasInheritedProperties; for (let property of properties) { // assign in enumeration order if (unpackr.alwaysLazyProperty && unpackr.alwaysLazyProperty(property.key)) { // these properties are not eagerly evaluated and this can be used for creating properties // that are not serialized as JSON hasInheritedProperties = true; continue; } Object.defineProperty(prototype, property.key, { get: withSource(property.get), enumerable: true }); let valueFunction = 'v' + i++; args.push(valueFunction); objectLiteralProperties.push('[' + JSON.stringify(property.key) + ']:' + valueFunction + '(s)'); } if (hasInheritedProperties) { objectLiteralProperties.push('__proto__:this'); } let toObject = (new Function(...args, 'return function(s){return{' + objectLiteralProperties.join(',') + '}}')).apply(null, properties.map(prop => prop.get)); Object.defineProperty(prototype, 'toJSON', { value(omitUnderscoredProperties) { return toObject.call(this, this[sourceSymbol]); } }); } else { Object.defineProperty(prototype, 'toJSON', { value(omitUnderscoredProperties) { // return an enumerable object with own properties to JSON stringify let resolved = {}; for (let i = 0, l = properties.length; i < l; i++) { // TODO: check alwaysLazyProperty let key = properties[i].key; resolved[key] = this[key]; } return resolved; }, // not enumerable or anything }); } } var instance = new construct(); instance[sourceSymbol] = { bytes: src, position, srcString: '', bytesEnd: srcEnd }; return instance; } function toConstant(code) { switch(code) { case 0xf6: return null; case 0xf7: return undefined; case 0xf8: return false; case 0xf9: return true; } throw new Error('Unknown constant'); } function withSource(get) { return function() { return get(this[sourceSymbol]); } } function saveState() { if (currentSource) { currentSource.bytes = Uint8Array.prototype.slice.call(currentSource.bytes, currentSource.position, currentSource.bytesEnd); currentSource.position = 0; currentSource.bytesEnd = currentSource.bytes.length; } } function prepareStructures(structures, packr) { if (packr.typedStructs) { let structMap = new Map(); structMap.set('named', structures); structMap.set('typed', packr.typedStructs); structures = structMap; } let lastTypedStructuresLength = packr.lastTypedStructuresLength || 0; structures.isCompatible = existing => { let compatible = true; if (existing instanceof Map) { let named = existing.get('named') || []; if (named.length !== (packr.lastNamedStructuresLength || 0)) compatible = false; let typed = existing.get('typed') || []; if (typed.length !== lastTypedStructuresLength) compatible = false; } else if (existing instanceof Array || Array.isArray(existing)) { if (existing.length !== (packr.lastNamedStructuresLength || 0)) compatible = false; } if (!compatible) packr._mergeStructures(existing); return compatible; }; packr.lastTypedStructuresLength = packr.typedStructs && packr.typedStructs.length; return structures; } setReadStruct(readStruct, onLoadedStructures, saveState); 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((document.currentScript && document.currentScript.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`, (document.currentScript && document.currentScript.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 {} } } let seed = 0; function random() { seed++; let a = seed * 15485863; return (a * a * a % 2038074743) / 2038074743; } //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('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); }); test('pack/unpack varying data with random access structures', function () { let structures = []; let packr = new Packr({ structures, useRecords: true, randomAccessStructure: true, freezeData: true, saveStructures(structures) { }, getStructures() { console.log('getStructures'); } }); for (let i = 0; i < 2000; i++) { let data = {}; let props = ['foo', 'bar', 'a', 'b', 'c', 'name', 'age', 'd']; function makeString() { let str = ''; while (random() < 0.9) { str += random() < 0.8 ? 'hello world' : String.fromCharCode(300); } return str; } for (let i = 0; i < random() * 20; i++) { data[props[Math.floor(random() * 8)]] = random() < 0.3 ? Math.floor(random() * 400) / 2 : random() < 0.3 ? makeString() : random() < 0.3 ? true : random() < 0.3 ? sampleData : null; } var serialized = packr.pack(data); var deserialized = packr.unpack(serialized); for (let key in deserialized) { deserialized[key]; } 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 random access structures ' + snippet, function () { var data = sampleData; let structures = []; let packr = new Packr({ structures, useRecords: true, randomAccessStructure: true, freezeData: true, saveStructures(structures) { }, getStructures() { console.log('getStructures'); } }); for (let i = 0; i < 20; i++) { var serialized = packr.pack(data); var deserialized = packr.unpack(serialized, {lazy: true}); var copied = {}; for (let key in deserialized) { copied[key] = deserialized[key]; } assert.deepEqual(copied, 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, }; 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('moreTyesp: Error with causes', function() { const object = { error: new Error('test'), errorWithCause: new Error('test-1', { cause: new Error('test-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); }); 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: 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]) }; 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); }); 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('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); }); 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('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) }); }); })(chai, null, module, fs); //# sourceMappingURL=test.js.map