123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811 |
- /*
- For "any-data":
- 32-55 - record with record ids (-32)
- 56 - 8-bit record ids
- 57 - 16-bit record ids
- 58 - 24-bit record ids
- 59 - 32-bit record ids
- 250-255 - followed by typed fixed width values
- 64-250 msgpackr/cbor/paired data
- arrays and strings within arrays are handled by paired encoding
- Structure encoding:
- (type - string (using paired encoding))+
- Type encoding
- encoding byte - fixed width byte - next reference+
- Encoding byte:
- first bit:
- 0 - inline
- 1 - reference
- second bit:
- 0 - data or number
- 1 - string
- remaining bits:
- character encoding - ISO-8859-x
- null (0xff)+ 0xf6
- null (0xff)+ 0xf7
- */
- import {setWriteStructSlots, RECORD_SYMBOL, addExtension} from './pack.js'
- import {setReadStruct, mult10, readString} from './unpack.js';
- 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, target.byteLength - position)
- } : (textEncoder && textEncoder.encodeInto) ?
- function(target, string, position) {
- return textEncoder.encodeInto(string, target.subarray(position)).written
- } : false
- const TYPE = Symbol('type');
- const PARENT = Symbol('parent');
- 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);
|