msgpackr 1.6.2 → 1.7.0-alpha1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/node.cjs CHANGED
@@ -33,6 +33,7 @@ const C1 = new C1Type();
33
33
  C1.name = 'MessagePack 0xC1';
34
34
  var sequentialMode = false;
35
35
  var inlineObjectReadThreshold = 2;
36
+ var readStruct;
36
37
  try {
37
38
  new Function('');
38
39
  } catch(error) {
@@ -172,7 +173,13 @@ function checkedRead() {
172
173
  if (sharedLength < currentStructures.length)
173
174
  currentStructures.length = sharedLength;
174
175
  }
175
- let result = read();
176
+ let result;
177
+ if (currentUnpackr.randomAccessStructure && src[position] < 0x40 && readStruct) {
178
+ let id = (src[position++] << 8) + src[position++];
179
+ result = readStruct(src, position, srcEnd, currentStructures[id - 0x40] || loadStructures()[id - 0x40], currentUnpackr);
180
+ position = srcEnd;
181
+ } else
182
+ result = read();
176
183
  if (bundledStrings) // bundled strings to skip past
177
184
  position = bundledStrings.postBundlePosition;
178
185
 
@@ -249,6 +256,8 @@ function read() {
249
256
  for (let i = 0; i < token; i++) {
250
257
  array[i] = read();
251
258
  }
259
+ if (currentUnpackr.freezeData)
260
+ return Object.freeze(array)
252
261
  return array
253
262
  }
254
263
  } else if (token < 0xc0) {
@@ -459,7 +468,8 @@ function createStructureReader(structure, firstId) {
459
468
  function readObject() {
460
469
  // This initial function is quick to instantiate, but runs slower. After several iterations pay the cost to build the faster function
461
470
  if (readObject.count++ > inlineObjectReadThreshold) {
462
- let readObject = structure.read = (new Function('r', 'return function(){return {' + structure.map(key => validName.test(key) ? key + ':r()' : ('[' + JSON.stringify(key) + ']:r()')).join(',') + '}}'))(read);
471
+ let readObject = structure.read = (new Function('r', 'return function(){return ' + (currentUnpackr.freezeData ? 'Object.freeze' : '') +
472
+ '({' + structure.map(key => validName.test(key) ? key + ':r()' : ('[' + JSON.stringify(key) + ']:r()')).join(',') + '})}'))(read);
463
473
  if (structure.highByte === 0)
464
474
  structure.read = createSecondByteReader(firstId, structure.read);
465
475
  return readObject() // second byte is already read, if there is one so immediately read object
@@ -469,6 +479,8 @@ function createStructureReader(structure, firstId) {
469
479
  let key = structure[i];
470
480
  object[key] = read();
471
481
  }
482
+ if (currentUnpackr.freezeData)
483
+ return Object.freeze(object);
472
484
  return object
473
485
  }
474
486
  readObject.count = 0;
@@ -606,6 +618,8 @@ function readArray(length) {
606
618
  for (let i = 0; i < length; i++) {
607
619
  array[i] = read();
608
620
  }
621
+ if (currentUnpackr.freezeData)
622
+ return Object.freeze(array)
609
623
  return array
610
624
  }
611
625
 
@@ -1061,6 +1075,9 @@ function roundFloat32(float32Number) {
1061
1075
  let multiplier = mult10[((u8Array[3] & 0x7f) << 1) | (u8Array[2] >> 7)];
1062
1076
  return ((multiplier * float32Number + (float32Number > 0 ? 0.5 : -0.5)) >> 0) / multiplier
1063
1077
  }
1078
+ function setReadStruct(func) {
1079
+ readStruct = func;
1080
+ }
1064
1081
 
1065
1082
  let textEncoder;
1066
1083
  try {
@@ -1077,6 +1094,7 @@ let targetView;
1077
1094
  let position$1 = 0;
1078
1095
  let safeEnd;
1079
1096
  let bundledStrings$1 = null;
1097
+ let writeStructSlots;
1080
1098
  const MAX_BUNDLE_SIZE = 0xf000;
1081
1099
  const hasNonLatin = /[\u0080-\uFFFF]/;
1082
1100
  const RECORD_SYMBOL = Symbol('record-id');
@@ -1128,14 +1146,14 @@ class Packr extends Unpackr {
1128
1146
  this.pack = this.encode = function(value, encodeOptions) {
1129
1147
  if (!target) {
1130
1148
  target = new ByteArrayAllocate(8192);
1131
- targetView = new DataView(target.buffer, 0, 8192);
1149
+ targetView = target.dataView = new DataView(target.buffer, 0, 8192);
1132
1150
  position$1 = 0;
1133
1151
  }
1134
1152
  safeEnd = target.length - 10;
1135
1153
  if (safeEnd - position$1 < 0x800) {
1136
1154
  // don't start too close to the end,
1137
1155
  target = new ByteArrayAllocate(target.length);
1138
- targetView = new DataView(target.buffer, 0, target.length);
1156
+ targetView = target.dataView = new DataView(target.buffer, 0, target.length);
1139
1157
  safeEnd = target.length - 10;
1140
1158
  position$1 = 0;
1141
1159
  } else
@@ -1183,7 +1201,10 @@ class Packr extends Unpackr {
1183
1201
  if (hasSharedUpdate)
1184
1202
  hasSharedUpdate = false;
1185
1203
  try {
1186
- pack(value);
1204
+ if (packr.randomAccessStructure)
1205
+ writeStruct(value);
1206
+ else
1207
+ pack(value);
1187
1208
  if (bundledStrings$1) {
1188
1209
  writeBundles(start, pack);
1189
1210
  }
@@ -1657,7 +1678,7 @@ class Packr extends Unpackr {
1657
1678
  } else // faster handling for smaller buffers
1658
1679
  newSize = ((Math.max((end - start) << 2, target.length - 1) >> 12) + 1) << 12;
1659
1680
  let newBuffer = new ByteArrayAllocate(newSize);
1660
- targetView = new DataView(newBuffer.buffer, 0, newSize);
1681
+ targetView = newBuffer.dataView = new DataView(newBuffer.buffer, 0, newSize);
1661
1682
  end = Math.min(end, target.length);
1662
1683
  if (target.copy)
1663
1684
  target.copy(newBuffer, 0, start, end);
@@ -1748,6 +1769,21 @@ class Packr extends Unpackr {
1748
1769
  target[insertionOffset + start] = keysTarget[0];
1749
1770
  }
1750
1771
  };
1772
+ const writeStruct = (object, safePrototype) => {
1773
+ let newPosition = writeStructSlots(object, target, position$1, structures, makeRoom, (value, newPosition) => {
1774
+ position$1 = newPosition;
1775
+ if (start > 0) {
1776
+ pack(value);
1777
+ if (start == 0)
1778
+ return { position: position$1, targetView }; // indicate the buffer was re-allocated
1779
+ } else
1780
+ pack(value);
1781
+ return position$1;
1782
+ });
1783
+ if (newPosition === 0) // bail and go to a msgpack object
1784
+ return writeObject(object, true);
1785
+ position$1 = newPosition;
1786
+ };
1751
1787
  }
1752
1788
  useBuffer(buffer) {
1753
1789
  // this means we are finished using our own buffer and we can write over it safely
@@ -1977,6 +2013,9 @@ function addExtension$1(extension) {
1977
2013
  }
1978
2014
  addExtension(extension);
1979
2015
  }
2016
+ function setWriteStructSlots(func) {
2017
+ writeStructSlots = func;
2018
+ }
1980
2019
 
1981
2020
  let defaultPackr = new Packr({ useRecords: false });
1982
2021
  const pack = defaultPackr.pack;
@@ -1986,6 +2025,236 @@ const { NEVER, ALWAYS, DECIMAL_ROUND, DECIMAL_FIT } = FLOAT32_OPTIONS;
1986
2025
  const REUSE_BUFFER_MODE = 512;
1987
2026
  const RESET_BUFFER_MODE = 1024;
1988
2027
 
2028
+ // first four bits
2029
+ const hasNonLatin$1 = /[\u0080-\uFFFF]/;
2030
+ const float32Headers = [false, true, true, false, false, true, true, false];
2031
+ setWriteStructSlots(writeStruct);
2032
+ function writeStruct(object, target, position, structures, makeRoom, pack) {
2033
+ let transition = structures.transitions || false;
2034
+ let start = position;
2035
+ position += 4;
2036
+ let queuedReferences = [];
2037
+ let uint32 = target.uint32 || (target.uint32 = new Uint32Array(target.buffer));
2038
+ let targetView = target.dataView;
2039
+ let encoded;
2040
+ let stringData = '';
2041
+ let safeEnd = target.length - 10;
2042
+ for (let key in object) {
2043
+ let nextTransition = transition[key];
2044
+ if (!nextTransition) {
2045
+ return 0; // bail
2046
+ //nextTransition = transition[key] = Object.create(null)
2047
+ //newTransitions++
2048
+ }
2049
+ if (position > safeEnd) {
2050
+ let newPosition = position - start;
2051
+ target = makeRoom(position);
2052
+ position = newPosition;
2053
+ start = 0;
2054
+ safeEnd = target.length - 10;
2055
+ }
2056
+ transition = nextTransition;
2057
+ let value = object[key];
2058
+ switch (typeof value) {
2059
+ case 'number':
2060
+ if (value >>> 0 === value && value < 0x20000000) {
2061
+ encoded = value;
2062
+ break;
2063
+ } else if (value < 0x100000000 && value >= -0x80000000) {
2064
+ targetView.setFloat32(position, value, true);
2065
+ if (float32Headers[target[position + 3] >>> 5]) {
2066
+ let xShifted;
2067
+ // this checks for rounding of numbers that were encoded in 32-bit float to nearest significant decimal digit that could be preserved
2068
+ if (((xShifted = value * mult10[((target[position + 3] & 0x7f) << 1) | (target[position + 2] >> 7)]) >> 0) === xShifted) {
2069
+ position += 4;
2070
+ continue;
2071
+ }
2072
+ }
2073
+ }
2074
+ // fall back to msgpack encoding
2075
+ queuedReferences.push(value, position - start);
2076
+ position += 4;
2077
+ continue;
2078
+ case 'string':
2079
+ if (hasNonLatin$1.test(value)) {
2080
+ queuedReferences.push(value, position - start);
2081
+ position += 4;
2082
+ continue;
2083
+ }
2084
+ if (value.length < 4) { // we can inline really small strings
2085
+ encoded = 0xf8000000 + (value.length << 24) + (value.charCodeAt(0) << 16) + (value.charCodeAt(1) << 8) + (value.charCodeAt(2) || 0);
2086
+ // TODO: determining remaining and make max value be a ratio of that (probably 1/256th)
2087
+ } else if (value.length < 256 && stringData.length < 61440) {
2088
+ // bundle these strings
2089
+ encoded = 0x60000000 | (value.length << 16) | stringData.length;
2090
+ stringData += value;
2091
+ } else { // else queue it
2092
+ queuedReferences.push(value, position - start);
2093
+ position += 4;
2094
+ continue;
2095
+ }
2096
+ break;
2097
+ case 'object':
2098
+ if (value) {
2099
+ queuedReferences.push(value, position - start);
2100
+ position += 4;
2101
+ continue;
2102
+ } else { // null
2103
+ encoded = 0xe0000000;
2104
+ }
2105
+ break;
2106
+ case 'boolean':
2107
+ encoded = value ? 0xe3000000 : 0xe2000000;
2108
+ break;
2109
+ case 'undefined':
2110
+ encoded = 0xe1000000;
2111
+ break;
2112
+ }
2113
+ targetView.setUint32(position, encoded, true);
2114
+ position += 4;
2115
+ }
2116
+ let recordId = transition[RECORD_SYMBOL];
2117
+ if (!(recordId < 1024)) {
2118
+ // for now just punt and go back to writeObject
2119
+ return 0;
2120
+ // newRecord(transition, transition.__keys__ || Object.keys(object), newTransitions, true)
2121
+ }
2122
+ let stringLength = stringData.length;
2123
+ if (stringData) {
2124
+ if (position + stringLength > safeEnd) {
2125
+ target = makeRoom(position + stringLength);
2126
+ }
2127
+ position += target.latin1Write(stringData, position, 0xffffffff);
2128
+ }
2129
+ target[start] = recordId >> 8;
2130
+ target[start + 1] = recordId & 0xff;
2131
+ target[start + 2] = stringLength >> 8;
2132
+ target[start + 3] = stringLength & 0xff;
2133
+ let queued32BitReferences;
2134
+ for (let i = 0, l = queuedReferences.length; i < l;) {
2135
+ let value = queuedReferences[i++];
2136
+ let slotOffset = queuedReferences[i++] + start;
2137
+ let offset = position - slotOffset;
2138
+ if (offset < 0x1f000000) {
2139
+ targetView.setUint32(slotOffset, 0x80000000 | (offset), true);
2140
+ } else {
2141
+ if (!queued32BitReferences)
2142
+ queued32BitReferences = [];
2143
+ queued32BitReferences.push({slotOffset, offset: position - start});
2144
+ }
2145
+ let newPosition = pack(value, position);
2146
+ if (typeof newPosition === 'object') {
2147
+ // re-allocated
2148
+ position = newPosition.position;
2149
+ targetView = newPosition.targetView;
2150
+ start = 0;
2151
+ } else
2152
+ position = newPosition;
2153
+ }
2154
+ if (queued32BitReferences) {
2155
+ // TODO: makeRoom
2156
+ for (let i = 0, l = queued32BitReferences.length; i < l; i++) {
2157
+ let ref = queued32BitReferences[i];
2158
+ targetView.setUint32(ref.slotOffset, 0xa0000000 - ((l - i) << 2), true);
2159
+ targetView.setUint32(position, ref.offset, true);
2160
+ position += 4;
2161
+ }
2162
+ }
2163
+
2164
+ return position;
2165
+ }
2166
+ var sourceSymbol = Symbol('source');
2167
+ function readStruct$1(src, position, srcEnd, structure, unpackr) {
2168
+ var stringLength = (src[position++] << 8) | src[position++];
2169
+ var construct = structure.construct;
2170
+ var srcString;
2171
+ if (!construct) {
2172
+ construct = structure.construct = function() {
2173
+ };
2174
+ var prototype = construct.prototype;
2175
+ Object.defineProperty(prototype, 'toJSON', {
2176
+ get() {
2177
+ // return an enumerable object with own properties to JSON stringify
2178
+ let resolved = {};
2179
+ for (let i = 0, l = structure.length; i < l; i++) {
2180
+ let key = structure[i];
2181
+ resolved[key] = this[key];
2182
+ }
2183
+ return resolved;
2184
+ },
2185
+ // not enumerable or anything
2186
+ });
2187
+ for (let i = 0, l = structure.length; i < l; i++) {
2188
+ let key = structure[i];
2189
+ Object.defineProperty(prototype, key, {
2190
+ get() {
2191
+ let source = this[sourceSymbol];
2192
+ let src = source.src;
2193
+ //let uint32 = src.uint32 || (src.uint32 = new Uint32Array(src.buffer, src.byteOffset, src.byteLength));
2194
+ let dataView = src.dataView || (src.dataView = new DataView(src.buffer, src.byteOffset, src.byteLength));
2195
+ let position = source.position + (i << 2);
2196
+ let value = dataView.getUint32(position, true);
2197
+ let start;
2198
+ switch (value >>> 29) {
2199
+ case 0:
2200
+ return value;
2201
+ case 3:
2202
+ if (value & 0x10000000) {
2203
+ start = (value & 0xffff) + position;
2204
+ return src.toString('utf8', start, start + ((value >> 16) & 0x7ff));
2205
+ } else {
2206
+ if (!srcString) {
2207
+ start = source.position + (l << 2);
2208
+ srcString = src.toString('latin1', start, start + stringLength);
2209
+ }
2210
+ start = value & 0xffff;
2211
+ return srcString.slice(start, start + ((value >> 16) & 0x7ff));
2212
+ }
2213
+ case 4:
2214
+ start = (0x1fffffff & value) + position;
2215
+ let end = srcEnd;
2216
+ for (let next = i + 1; next < l; next++) {
2217
+ position = source.position + (next << 2);
2218
+ let nextValue = dataView.getUint32(position, true); if ((nextValue & 0xe0000000) == -0x80000000) {
2219
+ end = (0x1fffffff & nextValue) + position;
2220
+ break;
2221
+ }
2222
+ }
2223
+ return unpackr.unpack(src.slice(start, end));
2224
+ case 1: case 2: case 5: case 6:
2225
+ let fValue = dataView.getFloat32(position, true);
2226
+ // this does rounding of numbers that were encoded in 32-bit float to nearest significant decimal digit that could be preserved
2227
+ let multiplier = mult10[((src[position + 3] & 0x7f) << 1) | (src[position + 2] >> 7)];
2228
+ return ((multiplier * fValue + (fValue > 0 ? 0.5 : -0.5)) >> 0) / multiplier;
2229
+ case 7:
2230
+ switch((value >> 24) & 0x1f) {
2231
+ case 0: return null;
2232
+ case 1: return undefined;
2233
+ case 2: return false;
2234
+ case 3: return true;
2235
+ case 8: return dataView.getFloat64(position + (value & 0x3ffffff), true);
2236
+ case 0x18: return '';
2237
+ case 0x19: return String.fromCharCode((value >> 16) & 0xff);
2238
+ case 0x20: return String.fromCharCode((value >> 16) & 0xff, (value >> 8) & 0xff);
2239
+ case 0x21: return String.fromCharCode((value >> 16) & 0xff, (value >> 8) & 0xff, value & 0xff);
2240
+ default: throw new Error('Unknown constant');
2241
+ }
2242
+ }
2243
+ },
2244
+ enumerable: true,
2245
+ });
2246
+ }
2247
+ }
2248
+ var instance = new construct();
2249
+ instance[sourceSymbol] = {
2250
+ src,
2251
+ uint32: src.uint32,
2252
+ position,
2253
+ };
2254
+ return instance;
2255
+ }
2256
+ setReadStruct(readStruct$1);
2257
+
1989
2258
  class PackrStream extends stream.Transform {
1990
2259
  constructor(options) {
1991
2260
  if (!options)