msgpackr 1.9.9 → 1.10.1

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).
@@ -859,7 +859,7 @@
859
859
  return readFixedString(length)
860
860
  } else { // not cacheable, go back and do a standard read
861
861
  position$1--;
862
- return read().toString()
862
+ return asSafeString(read())
863
863
  }
864
864
  let key = ((length << 5) ^ (length > 1 ? dataView.getUint16(position$1) : length > 0 ? src[position$1] : 0)) & 0xfff;
865
865
  let entry = keyCache[key];
@@ -911,9 +911,15 @@
911
911
  return entry.string = readFixedString(length)
912
912
  }
913
913
 
914
+ function asSafeString(property) {
915
+ if (typeof property === 'string') return property;
916
+ if (typeof property === 'number') return property.toString();
917
+ throw new Error('Invalid property type for record', typeof property);
918
+ }
914
919
  // the registration of the record definition extension (as "r")
915
920
  const recordDefinition = (id, highByte) => {
916
- let structure = read().map(property => property.toString()); // ensure that all keys are strings and that the array is mutable
921
+ let structure = read().map(asSafeString); // ensure that all keys are strings and
922
+ // that the array is mutable
917
923
  let firstByte = id;
918
924
  if (highByte !== undefined) {
919
925
  id = id < 32 ? -((highByte << 5) + id) : ((highByte << 5) + id);
@@ -933,6 +939,17 @@
933
939
  currentExtensions[0] = () => {}; // notepack defines extension 0 to mean undefined, so use that as the default here
934
940
  currentExtensions[0].noBuffer = true;
935
941
 
942
+ currentExtensions[0x42] = (data) => {
943
+ // decode bigint
944
+ let length = data.length;
945
+ let value = BigInt(data[0] & 0x80 ? data[0] - 0x100 : data[0]);
946
+ for (let i = 1; i < length; i++) {
947
+ value <<= 8n;
948
+ value += BigInt(data[i]);
949
+ }
950
+ return value;
951
+ };
952
+
936
953
  let errors = { Error, TypeError, ReferenceError };
937
954
  currentExtensions[0x65] = () => {
938
955
  let data = read();
@@ -941,6 +958,7 @@
941
958
 
942
959
  currentExtensions[0x69] = (data) => {
943
960
  // id extension (for structured clones)
961
+ if (currentUnpackr.structuredClone === false) throw new Error('Structured clone extension is disabled')
944
962
  let id = dataView.getUint32(position$1 - 4);
945
963
  if (!referenceMap)
946
964
  referenceMap = new Map();
@@ -964,6 +982,7 @@
964
982
 
965
983
  currentExtensions[0x70] = (data) => {
966
984
  // pointer extension (for structured clones)
985
+ if (currentUnpackr.structuredClone === false) throw new Error('Structured clone extension is disabled')
967
986
  let id = dataView.getUint32(position$1 - 4);
968
987
  let refEntry = referenceMap.get(id);
969
988
  refEntry.used = true;
@@ -1207,6 +1226,7 @@
1207
1226
  }
1208
1227
  if (hasSharedUpdate)
1209
1228
  hasSharedUpdate = false;
1229
+ let encodingError;
1210
1230
  try {
1211
1231
  if (packr.randomAccessStructure && value && value.constructor && value.constructor === Object)
1212
1232
  writeStruct(value);
@@ -1257,6 +1277,9 @@
1257
1277
  return target
1258
1278
  }
1259
1279
  return target.subarray(start, position) // position can change if we call pack again in saveStructures, so we get the buffer now
1280
+ } catch(error) {
1281
+ encodingError = error;
1282
+ throw error;
1260
1283
  } finally {
1261
1284
  if (structures) {
1262
1285
  resetStructures();
@@ -1265,12 +1288,14 @@
1265
1288
  // we can't rely on start/end with REUSE_BUFFER_MODE since they will (probably) change when we save
1266
1289
  let returnBuffer = target.subarray(start, position);
1267
1290
  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, encodeOptions)
1291
+ if (!encodingError) { // TODO: If there is an encoding error, should make the structures as uninitialized so they get rebuilt next time
1292
+ if (packr.saveStructures(newSharedData, newSharedData.isCompatible) === false) {
1293
+ // get updated structures and try again if the update failed
1294
+ return packr.pack(value, encodeOptions)
1295
+ }
1296
+ packr.lastNamedStructuresLength = sharedLength;
1297
+ return returnBuffer
1271
1298
  }
1272
- packr.lastNamedStructuresLength = sharedLength;
1273
- return returnBuffer
1274
1299
  }
1275
1300
  }
1276
1301
  if (encodeOptions & RESET_BUFFER_MODE)
@@ -1612,8 +1637,26 @@
1612
1637
  if (this.largeBigIntToFloat) {
1613
1638
  target[position++] = 0xcb;
1614
1639
  targetView.setFloat64(position, Number(value));
1640
+ } else if (this.useBigIntExtension && value < 2n**(1023n) && value > -(2n**(1023n))) {
1641
+ target[position++] = 0xc7;
1642
+ position++;
1643
+ target[position++] = 0x42; // "B" for BigInt
1644
+ let bytes = [];
1645
+ let alignedSign;
1646
+ do {
1647
+ let byte = value & 0xffn;
1648
+ alignedSign = (byte & 0x80n) === (value < 0n ? 0x80n : 0n);
1649
+ bytes.push(byte);
1650
+ value >>= 8n;
1651
+ } while (!((value === 0n || value === -1n) && alignedSign));
1652
+ target[position-2] = bytes.length;
1653
+ for (let i = bytes.length; i > 0;) {
1654
+ target[position++] = Number(bytes[--i]);
1655
+ }
1656
+ return
1615
1657
  } else {
1616
- throw new RangeError(value + ' was too large to fit in MessagePack 64-bit integer format, set largeBigIntToFloat to convert to float-64')
1658
+ throw new RangeError(value + ' was too large to fit in MessagePack 64-bit integer format, use' +
1659
+ ' useBigIntExtension or set largeBigIntToFloat to convert to float-64')
1617
1660
  }
1618
1661
  }
1619
1662
  position += 8;