msgpackr 1.9.8 → 1.10.0

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
@@ -184,6 +184,7 @@ The following options properties can be provided to the Packr or Unpackr constru
184
184
  * `useTimestamp32` - Encode JS `Date`s in 32-bit format when possible by dropping the milliseconds. This is a more efficient encoding of dates. You can also cause dates to use 32-bit format by manually setting the milliseconds to zero (`date.setMilliseconds(0)`).
185
185
  * `sequential` - Encode structures in serialized data, and reference previously encoded structures with expectation that decoder will read the encoded structures in the same order as encoded, with `unpackMultiple`.
186
186
  * `largeBigIntToFloat` - If a bigint needs to be encoded that is larger than will fit in 64-bit integers, it will be encoded as a float-64 (otherwise will throw a RangeError).
187
+ * `useBigIntExtension` - If a bigint needs to be encoded that is larger than will fit in 64-bit integers, it will be encoded using a custom extension that supports up to about 1000-bits of integer precision.
187
188
  * `encodeUndefinedAsNil` - Encodes a value of `undefined` as a MessagePack `nil`, the same as a `null`.
188
189
  * `int64AsType` - This will decode uint64 and int64 numbers as the specified type. The type can be `bigint` (default), `number`, `string`, or `auto` (where range [-2^53...2^53] is represented by number and everything else by a bigint).
189
190
  * `onInvalidDate` - This can be provided as function that will be called when an invalid date is provided. The function can throw an error, or return a value that will be encoded in place of the invalid date. If not provided, an invalid date will be encoded as an invalid timestamp (which decodes with msgpackr back to an invalid date).
@@ -933,6 +933,17 @@
933
933
  currentExtensions[0] = () => {}; // notepack defines extension 0 to mean undefined, so use that as the default here
934
934
  currentExtensions[0].noBuffer = true;
935
935
 
936
+ currentExtensions[0x42] = (data) => {
937
+ // decode bigint
938
+ let length = data.length;
939
+ let value = BigInt(data[0] & 0x80 ? data[0] - 0x100 : data[0]);
940
+ for (let i = 1; i < length; i++) {
941
+ value <<= 8n;
942
+ value += BigInt(data[i]);
943
+ }
944
+ return value;
945
+ };
946
+
936
947
  let errors = { Error, TypeError, ReferenceError };
937
948
  currentExtensions[0x65] = () => {
938
949
  let data = read();
@@ -1207,6 +1218,7 @@
1207
1218
  }
1208
1219
  if (hasSharedUpdate)
1209
1220
  hasSharedUpdate = false;
1221
+ let encodingError;
1210
1222
  try {
1211
1223
  if (packr.randomAccessStructure && value && value.constructor && value.constructor === Object)
1212
1224
  writeStruct(value);
@@ -1257,6 +1269,9 @@
1257
1269
  return target
1258
1270
  }
1259
1271
  return target.subarray(start, position) // position can change if we call pack again in saveStructures, so we get the buffer now
1272
+ } catch(error) {
1273
+ encodingError = error;
1274
+ throw error;
1260
1275
  } finally {
1261
1276
  if (structures) {
1262
1277
  resetStructures();
@@ -1265,12 +1280,14 @@
1265
1280
  // we can't rely on start/end with REUSE_BUFFER_MODE since they will (probably) change when we save
1266
1281
  let returnBuffer = target.subarray(start, position);
1267
1282
  let newSharedData = prepareStructures(structures, packr);
1268
- if (packr.saveStructures(newSharedData, newSharedData.isCompatible) === false) {
1269
- // get updated structures and try again if the update failed
1270
- return packr.pack(value)
1283
+ if (!encodingError) { // TODO: If there is an encoding error, should make the structures as uninitialized so they get rebuilt next time
1284
+ if (packr.saveStructures(newSharedData, newSharedData.isCompatible) === false) {
1285
+ // get updated structures and try again if the update failed
1286
+ return packr.pack(value, encodeOptions)
1287
+ }
1288
+ packr.lastNamedStructuresLength = sharedLength;
1289
+ return returnBuffer
1271
1290
  }
1272
- packr.lastNamedStructuresLength = sharedLength;
1273
- return returnBuffer
1274
1291
  }
1275
1292
  }
1276
1293
  if (encodeOptions & RESET_BUFFER_MODE)
@@ -1612,8 +1629,26 @@
1612
1629
  if (this.largeBigIntToFloat) {
1613
1630
  target[position++] = 0xcb;
1614
1631
  targetView.setFloat64(position, Number(value));
1632
+ } else if (this.useBigIntExtension && value < 2n**(1023n) && value > -(2n**(1023n))) {
1633
+ target[position++] = 0xc7;
1634
+ position++;
1635
+ target[position++] = 0x42; // "B" for BigInt
1636
+ let bytes = [];
1637
+ let alignedSign;
1638
+ do {
1639
+ let byte = value & 0xffn;
1640
+ alignedSign = (byte & 0x80n) === (value < 0n ? 0x80n : 0n);
1641
+ bytes.push(byte);
1642
+ value >>= 8n;
1643
+ } while (!((value === 0n || value === -1n) && alignedSign));
1644
+ target[position-2] = bytes.length;
1645
+ for (let i = bytes.length; i > 0;) {
1646
+ target[position++] = Number(bytes[--i]);
1647
+ }
1648
+ return
1615
1649
  } else {
1616
- throw new RangeError(value + ' was too large to fit in MessagePack 64-bit integer format, set largeBigIntToFloat to convert to float-64')
1650
+ throw new RangeError(value + ' was too large to fit in MessagePack 64-bit integer format, use' +
1651
+ ' useBigIntExtension or set largeBigIntToFloat to convert to float-64')
1617
1652
  }
1618
1653
  }
1619
1654
  position += 8;
@@ -1861,7 +1896,7 @@
1861
1896
  }
1862
1897
  };
1863
1898
  const writeStruct = (object, safePrototype) => {
1864
- let newPosition = writeStructSlots(object, target, position, structures, makeRoom, (value, newPosition, notifySharedUpdate) => {
1899
+ let newPosition = writeStructSlots(object, target, start, position, structures, makeRoom, (value, newPosition, notifySharedUpdate) => {
1865
1900
  if (notifySharedUpdate)
1866
1901
  return hasSharedUpdate = true;
1867
1902
  position = newPosition;