msgpackr 1.9.1 → 1.9.3

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/README.md CHANGED
@@ -157,10 +157,22 @@ unpackMultiple(data, (value) => {
157
157
  })
158
158
  ```
159
159
 
160
+ If you need to know the start and end offsets of the unpacked values, these are
161
+ provided as optional parameters in the callback:
162
+ ```js
163
+ let data = new Uint8Array([1, 2, 3]) // encodings of values 1, 2, and 3
164
+ unpackMultiple(data, (value,start,end) => {
165
+ // called for each value
166
+ // `start` is the data buffer offset where the value was read from
167
+ // `end` is `start` plus the byte length of the encoded value
168
+ // return false if you wish to end the parsing
169
+ })
170
+ ```
171
+
160
172
  ## Options
161
173
  The following options properties can be provided to the Packr or Unpackr constructor:
162
174
 
163
- * `useRecords` - Setting this to `false` disables the record extension and stores JavaScript objects as MessagePack maps, and unpacks maps as JavaScript `Object`s, which ensures compatibilty with other decoders.
175
+ * `useRecords` - Setting this to `false` disables the record extension and stores JavaScript objects as MessagePack maps, and unpacks maps as JavaScript `Object`s, which ensures compatibilty with other decoders. Setting this to a function will use records for objects where `useRecords(object)` returns `true`.
164
176
  * `structures` - Provides the array of structures that is to be used for record extension, if you want the structures saved and used again. This array will be modified in place with new record structures that are serialized (if less than 32 structures are in the array).
165
177
  * `moreTypes` - Enable serialization of additional built-in types/classes including typed arrays, `Set`s, `Map`s, and `Error`s.
166
178
  * `structuredClone` - This enables the structured cloning extensions that will encode object/cyclic references. `moreTypes` is enabled by default when this is enabled.
@@ -119,10 +119,10 @@
119
119
  let size = source.length;
120
120
  let value = this ? this.unpack(source, size) : defaultUnpackr.unpack(source, size);
121
121
  if (forEach) {
122
- if (forEach(value) === false) return;
122
+ if (forEach(value, lastPosition, position$1) === false) return;
123
123
  while(position$1 < size) {
124
124
  lastPosition = position$1;
125
- if (forEach(checkedRead()) === false) {
125
+ if (forEach(checkedRead(), lastPosition, position$1) === false) {
126
126
  return
127
127
  }
128
128
  }
@@ -194,6 +194,10 @@
194
194
  position$1 = bundledStrings$1.postBundlePosition;
195
195
  bundledStrings$1 = null;
196
196
  }
197
+ if (sequentialMode)
198
+ // we only need to restore the structures if there was an error, but if we completed a read,
199
+ // we can clear this out and keep the structures we read
200
+ currentStructures.restoreStructures = null;
197
201
 
198
202
  if (position$1 == srcEnd) {
199
203
  // finished reading this source, cleanup references
@@ -916,7 +920,10 @@
916
920
  structure.highByte = highByte;
917
921
  }
918
922
  let existingStructure = currentStructures[id];
919
- if (existingStructure && existingStructure.isShared) {
923
+ // If it is a shared structure, we need to restore any changes after reading.
924
+ // Also in sequential mode, we may get incomplete reads and thus errors, and we need to restore
925
+ // to the state prior to an incomplete read in order to properly resume.
926
+ if (existingStructure && (existingStructure.isShared || sequentialMode)) {
920
927
  (currentStructures.restoreStructures || (currentStructures.restoreStructures = []))[id] = existingStructure;
921
928
  }
922
929
  currentStructures[id] = structure;
@@ -926,10 +933,10 @@
926
933
  currentExtensions[0] = () => {}; // notepack defines extension 0 to mean undefined, so use that as the default here
927
934
  currentExtensions[0].noBuffer = true;
928
935
 
929
- let glbl = typeof globalThis === 'object' ? globalThis : window;
936
+ let errors = { Error, TypeError, ReferenceError };
930
937
  currentExtensions[0x65] = () => {
931
938
  let data = read();
932
- return (glbl[data[0]] || Error)(data[1])
939
+ return (errors[data[0]] || Error)(data[1])
933
940
  };
934
941
 
935
942
  currentExtensions[0x69] = (data) => {
@@ -967,6 +974,7 @@
967
974
 
968
975
  const typedArrays = ['Int8','Uint8','Uint8Clamped','Int16','Uint16','Int32','Uint32','Float32','Float64','BigInt64','BigUint64'].map(type => type + 'Array');
969
976
 
977
+ let glbl = typeof globalThis === 'object' ? globalThis : window;
970
978
  currentExtensions[0x74] = (data) => {
971
979
  let typeCode = data[0];
972
980
  let typedArrayName = typedArrays[typeCode];
@@ -1253,7 +1261,7 @@
1253
1261
  if (serializationsSinceTransitionRebuild < 10)
1254
1262
  serializationsSinceTransitionRebuild++;
1255
1263
  let sharedLength = structures.sharedLength || 0;
1256
- if (structures.length > sharedLength)
1264
+ if (structures.length > sharedLength && !isSequential)
1257
1265
  structures.length = sharedLength;
1258
1266
  if (transitionsCount > 10000) {
1259
1267
  // force a rebuild occasionally after a lot of transitions so it can get cleaned up
@@ -1461,7 +1469,7 @@
1461
1469
  targetView.setFloat64(position, value);
1462
1470
  position += 8;
1463
1471
  }
1464
- } else if (type === 'object') {
1472
+ } else if (type === 'object' || type === 'function') {
1465
1473
  if (!value)
1466
1474
  target[position++] = 0xc0;
1467
1475
  else {
@@ -1568,6 +1576,11 @@
1568
1576
  } else {
1569
1577
  if (value.toJSON) // use this as an alternate mechanism for expressing how to serialize
1570
1578
  return pack(value.toJSON());
1579
+
1580
+ // if there is a writeFunction, use it, otherwise just encode as undefined
1581
+ if (type === 'function')
1582
+ return pack(this.writeFunction && this.writeFunction(value));
1583
+
1571
1584
  // no extension found, write as object
1572
1585
  writeObject(value, !value.hasOwnProperty); // if it doesn't have hasOwnProperty, don't do hasOwnProperty checks
1573
1586
  }
@@ -1602,14 +1615,12 @@
1602
1615
  target[position++] = 0;
1603
1616
  target[position++] = 0;
1604
1617
  }
1605
- } else if (type === 'function') {
1606
- pack(this.writeFunction && this.writeFunction()); // if there is a writeFunction, use it, otherwise just encode as undefined
1607
1618
  } else {
1608
1619
  throw new Error('Unknown type: ' + type)
1609
1620
  }
1610
1621
  };
1611
1622
 
1612
- const writeObject = this.useRecords === false ? this.variableMapSize ? (object) => {
1623
+ const writePlainObject = this.variableMapSize ? (object) => {
1613
1624
  // this method is slightly slower, but generates "preferred serialization" (optimally small for smaller objects)
1614
1625
  let keys = Object.keys(object);
1615
1626
  let length = keys.length;
@@ -1644,7 +1655,9 @@
1644
1655
  }
1645
1656
  target[objectOffset++ + start] = size >> 8;
1646
1657
  target[objectOffset + start] = size & 0xff;
1647
- } :
1658
+ };
1659
+
1660
+ const writeRecord = this.useRecords === false ? writePlainObject :
1648
1661
  (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)
1649
1662
  (object, safePrototype) => {
1650
1663
  let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null));
@@ -1716,6 +1729,14 @@
1716
1729
  if (safePrototype || object.hasOwnProperty(key))
1717
1730
  pack(object[key]);
1718
1731
  };
1732
+
1733
+ // craete reference to useRecords if useRecords is a function
1734
+ const checkUseRecords = typeof this.useRecords == 'function' && this.useRecords;
1735
+
1736
+ const writeObject = checkUseRecords ? (object, safePrototype) => {
1737
+ checkUseRecords(object) ? writeRecord(object,safePrototype) : writePlainObject(object,safePrototype);
1738
+ } : writeRecord;
1739
+
1719
1740
  const makeRoom = (end) => {
1720
1741
  let newSize;
1721
1742
  if (end > 0x1000000) {
@@ -1888,7 +1909,10 @@
1888
1909
  }
1889
1910
  }, {
1890
1911
  pack(set, allocateForWrite, pack) {
1891
- if (this.setAsEmptyObject) return pack({})
1912
+ if (this.setAsEmptyObject) {
1913
+ allocateForWrite(0);
1914
+ return pack({})
1915
+ }
1892
1916
  let array = Array.from(set);
1893
1917
  let { target, position} = allocateForWrite(this.moreTypes ? 3 : 0);
1894
1918
  if (this.moreTypes) {