cbor-x 1.5.8 → 1.6.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/dist/test.js CHANGED
@@ -15,6 +15,10 @@
15
15
  const BUNDLED_STRINGS_ID = 0xdff9;
16
16
  const PACKED_REFERENCE_TAG_ID = 6;
17
17
  const STOP_CODE = {};
18
+ let maxArraySize = 112810000; // This is the maximum array size in V8. We would potentially detect and set it higher
19
+ // for JSC, but this is pretty large and should be sufficient for most use cases
20
+ let maxMapSize = 16810000; // JavaScript has a fixed maximum map size of about 16710000, but JS itself enforces this,
21
+ // many keys in an object, so also probably a reasonable choice there.
18
22
  let strings = EMPTY_ARRAY;
19
23
  let stringPosition = 0;
20
24
  let currentDecoder = {};
@@ -291,6 +295,7 @@
291
295
  let array = [];
292
296
  let value, i = 0;
293
297
  while ((value = read()) != STOP_CODE) {
298
+ if (i >= maxArraySize) throw new Error(`Array length exceeds ${maxArraySize}`)
294
299
  array[i++] = value;
295
300
  }
296
301
  return majorType == 4 ? array : majorType == 3 ? array.join('') : Buffer.concat(array)
@@ -298,8 +303,19 @@
298
303
  let key;
299
304
  if (currentDecoder.mapsAsObjects) {
300
305
  let object = {};
301
- if (currentDecoder.keyMap) while((key = read()) != STOP_CODE) object[safeKey(currentDecoder.decodeKey(key))] = read();
302
- else while ((key = read()) != STOP_CODE) object[safeKey(key)] = read();
306
+ let i = 0;
307
+ if (currentDecoder.keyMap) {
308
+ while((key = read()) != STOP_CODE) {
309
+ if (i++ >= maxMapSize) throw new Error(`Property count exceeds ${maxMapSize}`)
310
+ object[safeKey(currentDecoder.decodeKey(key))] = read();
311
+ }
312
+ }
313
+ else {
314
+ while ((key = read()) != STOP_CODE) {
315
+ if (i++ >= maxMapSize) throw new Error(`Property count exceeds ${maxMapSize}`)
316
+ object[safeKey(key)] = read();
317
+ }
318
+ }
303
319
  return object
304
320
  } else {
305
321
  if (restoreMapsAsObject) {
@@ -307,8 +323,24 @@
307
323
  restoreMapsAsObject = false;
308
324
  }
309
325
  let map = new Map();
310
- if (currentDecoder.keyMap) while((key = read()) != STOP_CODE) map.set(currentDecoder.decodeKey(key), read());
311
- else while ((key = read()) != STOP_CODE) map.set(key, read());
326
+ if (currentDecoder.keyMap) {
327
+ let i = 0;
328
+ while((key = read()) != STOP_CODE) {
329
+ if (i++ >= maxMapSize) {
330
+ throw new Error(`Map size exceeds ${maxMapSize}`);
331
+ }
332
+ map.set(currentDecoder.decodeKey(key), read());
333
+ }
334
+ }
335
+ else {
336
+ let i = 0;
337
+ while ((key = read()) != STOP_CODE) {
338
+ if (i++ >= maxMapSize) {
339
+ throw new Error(`Map size exceeds ${maxMapSize}`);
340
+ }
341
+ map.set(key, read());
342
+ }
343
+ }
312
344
  return map
313
345
  }
314
346
  case 7:
@@ -339,12 +371,14 @@
339
371
  }
340
372
  return readFixedString(token)
341
373
  case 4: // array
374
+ if (token >= maxArraySize) throw new Error(`Array length exceeds ${maxArraySize}`)
342
375
  let array = new Array(token);
343
376
  //if (currentDecoder.keyMap) for (let i = 0; i < token; i++) array[i] = currentDecoder.decodeKey(read())
344
377
  //else
345
378
  for (let i = 0; i < token; i++) array[i] = read();
346
379
  return array
347
380
  case 5: // map
381
+ if (token >= maxMapSize) throw new Error(`Map size exceeds ${maxArraySize}`)
348
382
  if (currentDecoder.mapsAsObjects) {
349
383
  let object = {};
350
384
  if (currentDecoder.keyMap) for (let i = 0; i < token; i++) object[safeKey(currentDecoder.decodeKey(read()))] = read();
@@ -446,6 +480,7 @@
446
480
  }
447
481
  const validName = /^[a-zA-Z_$][a-zA-Z\d_$]*$/;
448
482
  function createStructureReader(structure) {
483
+ if (!structure) throw new Error('Structure is required in record definition');
449
484
  function readObject() {
450
485
  // get the array size from the header
451
486
  let length = src[position$1++];
@@ -501,7 +536,8 @@
501
536
  function safeKey(key) {
502
537
  // protect against prototype pollution
503
538
  if (typeof key === 'string') return key === '__proto__' ? '__proto_' : key
504
- if (typeof key !== 'object') return key.toString()
539
+ if (typeof key === 'number' || typeof key === 'boolean' || typeof key === 'bigint') return key.toString();
540
+ if (key == null) return key + '';
505
541
  // protect against expensive (DoS) string conversions
506
542
  throw new Error('Invalid property name type ' + typeof key);
507
543
  }
@@ -803,7 +839,7 @@
803
839
  // bigint extension
804
840
  let value = BigInt(0);
805
841
  for (let i = 0, l = buffer.byteLength; i < l; i++) {
806
- value = BigInt(buffer[i]) + value << BigInt(8);
842
+ value = BigInt(buffer[i]) + (value << BigInt(8));
807
843
  }
808
844
  return value
809
845
  };
@@ -918,6 +954,7 @@
918
954
  referenceMap.id = 0;
919
955
  }
920
956
  let id = referenceMap.id++;
957
+ let startingPosition = position$1;
921
958
  let token = src[position$1];
922
959
  let target;
923
960
  // TODO: handle Maps, Sets, and other types that can cycle; this is complicated, because you potentially need to read
@@ -930,8 +967,20 @@
930
967
  let refEntry = { target }; // a placeholder object
931
968
  referenceMap.set(id, refEntry);
932
969
  let targetProperties = read(); // read the next value as the target object to id
933
- if (refEntry.used) // there is a cycle, so we have to assign properties to original target
970
+ if (refEntry.used) {// there is a cycle, so we have to assign properties to original target
971
+ if (Object.getPrototypeOf(target) !== Object.getPrototypeOf(targetProperties)) {
972
+ // this means that the returned target does not match the targetProperties, so we need rerun the read to
973
+ // have the correctly create instance be assigned as a reference, then we do the copy the properties back to the
974
+ // target
975
+ // reset the position so that the read can be repeated
976
+ position$1 = startingPosition;
977
+ // the returned instance is our new target for references
978
+ target = targetProperties;
979
+ referenceMap.set(id, { target });
980
+ targetProperties = read();
981
+ }
934
982
  return Object.assign(target, targetProperties)
983
+ }
935
984
  refEntry.target = targetProperties; // the placeholder wasn't used, replace with the deserialized one
936
985
  return targetProperties // no cycle, can just use the returned read object
937
986
  };
@@ -1013,7 +1062,7 @@
1013
1062
  for (let littleEndian = 0; littleEndian < 2; littleEndian++) {
1014
1063
  if (!littleEndian && bytesPerElement == 1)
1015
1064
  continue
1016
- let sizeShift = bytesPerElement == 2 ? 1 : bytesPerElement == 4 ? 2 : 3;
1065
+ let sizeShift = bytesPerElement == 2 ? 1 : bytesPerElement == 4 ? 2 : bytesPerElement == 8 ? 3 : 0;
1017
1066
  currentExtensions[littleEndian ? tag : (tag - 4)] = (bytesPerElement == 1 || littleEndian == isLittleEndianMachine$1) ? (buffer) => {
1018
1067
  if (!TypedArray)
1019
1068
  throw new Error('Could not find typed array for code ' + tag)
@@ -1023,7 +1072,7 @@
1023
1072
  bytesPerElement === 2 && !(buffer.byteOffset & 1) ||
1024
1073
  bytesPerElement === 4 && !(buffer.byteOffset & 3) ||
1025
1074
  bytesPerElement === 8 && !(buffer.byteOffset & 7))
1026
- return new TypedArray(buffer.buffer, buffer.byteOffset, buffer.byteLength);
1075
+ return new TypedArray(buffer.buffer, buffer.byteOffset, buffer.byteLength >> sizeShift);
1027
1076
  }
1028
1077
  // we have to slice/copy here to get a new ArrayBuffer, if we are not word/byte aligned
1029
1078
  return new TypedArray(Uint8Array.prototype.slice.call(buffer, 0).buffer)
@@ -1622,7 +1671,7 @@
1622
1671
  }
1623
1672
  let constructor = value.constructor;
1624
1673
  if (constructor === Object) {
1625
- writeObject(value, true);
1674
+ writeObject(value);
1626
1675
  } else if (constructor === Array) {
1627
1676
  length = value.length;
1628
1677
  if (length < 0x18) {
@@ -1717,8 +1766,8 @@
1717
1766
  return encode(json)
1718
1767
  }
1719
1768
 
1720
- // no extension found, write as object
1721
- writeObject(value, !value.hasOwnProperty); // if it doesn't have hasOwnProperty, don't do hasOwnProperty checks
1769
+ // no extension found, write as a plain object
1770
+ writeObject(value);
1722
1771
  }
1723
1772
  }
1724
1773
  } else if (type === 'boolean') {
@@ -1738,7 +1787,19 @@
1738
1787
  target[position++] = 0xfb;
1739
1788
  targetView.setFloat64(position, Number(value));
1740
1789
  } else {
1741
- throw new RangeError(value + ' was too large to fit in CBOR 64-bit integer format, set largeBigIntToFloat to convert to float-64')
1790
+ if (value >= BigInt(0))
1791
+ target[position++] = 0xc2; // tag 2
1792
+ else {
1793
+ target[position++] = 0xc3; // tag 2
1794
+ value = BigInt(-1) - value;
1795
+ }
1796
+ let bytes = [];
1797
+ while (value) {
1798
+ bytes.push(Number(value & BigInt(0xff)));
1799
+ value >>= BigInt(8);
1800
+ }
1801
+ writeBuffer(new Uint8Array(bytes.reverse()), makeRoom);
1802
+ return;
1742
1803
  }
1743
1804
  }
1744
1805
  position += 8;
@@ -1780,19 +1841,19 @@
1780
1841
  }
1781
1842
  }
1782
1843
  } :
1783
- (object, safePrototype) => {
1844
+ (object) => {
1784
1845
  target[position++] = 0xb9; // always use map 16, so we can preallocate and set the length afterwards
1785
1846
  let objectOffset = position - start;
1786
1847
  position += 2;
1787
1848
  let size = 0;
1788
- if (encoder.keyMap) {
1789
- for (let key in object) if (safePrototype || object.hasOwnProperty(key)) {
1849
+ if (encoder.keyMap) {
1850
+ for (let key in object) if (typeof object.hasOwnProperty !== 'function' || object.hasOwnProperty(key)) {
1790
1851
  encode(encoder.encodeKey(key));
1791
1852
  encode(object[key]);
1792
1853
  size++;
1793
1854
  }
1794
1855
  } else {
1795
- for (let key in object) if (safePrototype || object.hasOwnProperty(key)) {
1856
+ for (let key in object) if (typeof object.hasOwnProperty !== 'function' || object.hasOwnProperty(key)) {
1796
1857
  encode(key);
1797
1858
  encode(object[key]);
1798
1859
  size++;
@@ -1801,7 +1862,7 @@
1801
1862
  target[objectOffset++ + start] = size >> 8;
1802
1863
  target[objectOffset + start] = size & 0xff;
1803
1864
  } :
1804
- (object, safePrototype) => {
1865
+ (object, skipValues) => {
1805
1866
  let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null));
1806
1867
  let newTransitions = 0;
1807
1868
  let length = 0;
@@ -1820,7 +1881,7 @@
1820
1881
  transition = nextTransition;
1821
1882
  }
1822
1883
  } else {
1823
- for (let key in object) if (safePrototype || object.hasOwnProperty(key)) {
1884
+ for (let key in object) if (typeof object.hasOwnProperty !== 'function' || object.hasOwnProperty(key)) {
1824
1885
  nextTransition = transition[key];
1825
1886
  if (!nextTransition) {
1826
1887
  if (transition[RECORD_SYMBOL] & 0x100000) {// this indicates it is a brancheable/extendable terminal node, so we will use this record id and extend it
@@ -1880,9 +1941,9 @@
1880
1941
  writeArrayHeader(length + 2);
1881
1942
  encode(0xe000 + recordId);
1882
1943
  encode(keys);
1883
- if (safePrototype === null) return; // special exit for iterator
1944
+ if (skipValues) return; // special exit for iterator
1884
1945
  for (let key in object)
1885
- if (safePrototype || object.hasOwnProperty(key))
1946
+ if (typeof object.hasOwnProperty !== 'function' || object.hasOwnProperty(key))
1886
1947
  encode(object[key]);
1887
1948
  return
1888
1949
  }
@@ -1892,9 +1953,9 @@
1892
1953
  } else {
1893
1954
  writeArrayHeader(length);
1894
1955
  }
1895
- if (safePrototype === null) return; // special exit for iterator
1956
+ if (skipValues) return; // special exit for iterator
1896
1957
  for (let key in object)
1897
- if (safePrototype || object.hasOwnProperty(key))
1958
+ if (typeof object.hasOwnProperty !== 'function' || object.hasOwnProperty(key))
1898
1959
  encode(object[key]);
1899
1960
  };
1900
1961
  const makeRoom = (end) => {
@@ -1932,7 +1993,7 @@
1932
1993
  if (constructor === Object) {
1933
1994
  let useRecords = encoder.useRecords !== false;
1934
1995
  if (useRecords)
1935
- writeObject(object, null); // write the record identifier
1996
+ writeObject(object, true); // write the record identifier
1936
1997
  else
1937
1998
  writeEntityLength(Object.keys(object).length, 0xa0);
1938
1999
  for (let key in object) {
@@ -1957,7 +2018,7 @@
1957
2018
  yield* tryEncode(value, iterateProperties, 'element');
1958
2019
  } else encode(value);
1959
2020
  }
1960
- } else if (object[Symbol.iterator]) {
2021
+ } else if (object[Symbol.iterator] && !object.buffer) { // iterator, but exclude typed arrays
1961
2022
  target[position++] = 0x9f; // start indefinite array
1962
2023
  for (let value of object) {
1963
2024
  if (value && (typeof value === 'object' || position - start > chunkThreshold)) {
@@ -2649,22 +2710,17 @@
2649
2710
  assert.deepEqual(deserialized1, data1);
2650
2711
  assert.deepEqual(deserialized2, data2);
2651
2712
  });
2652
- test('extended class encode/decode', function(){
2713
+ test('extended class encode/decode', function() {
2653
2714
  function Extended() {
2654
2715
 
2655
2716
  }
2717
+
2656
2718
  Extended.prototype.getDouble = function() {
2657
2719
  return this.value * 2
2658
2720
  };
2659
2721
  var instance = new Extended();
2660
2722
  instance.value = 4;
2661
2723
  instance.string = 'decode this: ᾜ';
2662
- var data = {
2663
- prop1: 'has multi-byte: ᾜ',
2664
- extendedInstance: instance,
2665
- prop2: 'more string',
2666
- num: 3,
2667
- };
2668
2724
  let encoder = new Encoder();
2669
2725
  addExtension({
2670
2726
  Class: Extended,
@@ -2679,10 +2735,37 @@
2679
2735
  return encoder.encode([instance.value, instance.string])
2680
2736
  }
2681
2737
  });
2682
- var serialized = encode(data);
2683
- var deserialized = decode(serialized);
2684
- assert.deepEqual(data, deserialized);
2685
- assert.equal(deserialized.extendedInstance.getDouble(), 8);
2738
+ });
2739
+ test('extended class encode/decode with self reference in structered clone', function(){
2740
+ function Extended() {
2741
+
2742
+ }
2743
+ addExtension({
2744
+ Class: Extended,
2745
+ tag: 301,
2746
+ decode: function(data) {
2747
+ let e = new Extended();
2748
+ e.value = data[0];
2749
+ e.string = data[1];
2750
+ return e
2751
+ },
2752
+ encode: function(instance, encode) {
2753
+ return encode([instance.value, instance.string])
2754
+ }
2755
+ });
2756
+ var instance = new Extended();
2757
+ instance.value = instance;
2758
+ instance.string = 'hi';
2759
+ let data = {
2760
+ extended: instance
2761
+ };
2762
+ let encoder = new Encoder({
2763
+ structuredClone: true,
2764
+ });
2765
+ let serialized = encoder.encode(data);
2766
+ encoder.decode(serialized);
2767
+ assert(data.extended.value.value === data.extended);
2768
+ assert(data.extended instanceof Extended);
2686
2769
  });
2687
2770
 
2688
2771
  test('addExtension with map', function(){
@@ -2824,6 +2907,12 @@
2824
2907
  var deserialized = decode(serialized);
2825
2908
  assert.deepEqual(deserialized, data);
2826
2909
  });
2910
+ test('object with __proto__', function(){
2911
+ const data = { foo: 'bar', __proto__: { isAdmin: true } };
2912
+ var serialized = encode(data);
2913
+ var deserialized = decode(serialized);
2914
+ assert.deepEqual(deserialized, { foo: 'bar' });
2915
+ });
2827
2916
 
2828
2917
  test('big buffer', function() {
2829
2918
  var size = 100000000;
@@ -3018,23 +3107,27 @@
3018
3107
  var data = {
3019
3108
  bigintSmall: 352n,
3020
3109
  bigintSmallNegative: -333335252n,
3021
- bigintBig: 2n**64n - 1n, // biggest possible
3110
+ bigintBig: 2n**64n - 1n, // biggest 64-bit possible
3022
3111
  bigintBigNegative: -(2n**63n), // largest negative
3023
3112
  mixedWithNormal: 44,
3024
3113
  };
3025
3114
  var serialized = encode(data);
3026
3115
  var deserialized = decode(serialized);
3027
3116
  assert.deepEqual(deserialized, data);
3028
- var tooBigInt = {
3029
- tooBig: 2n**66n
3117
+ var evenBiggerInt = {
3118
+ big: 2n**66n,
3119
+ bigger: 53285732853728573289573289573289573289583725892358732859532n,
3120
+ negBig: -93025879203578903275903285903285903289502n,
3030
3121
  };
3031
- assert.throws(function(){ serialized = encode(tooBigInt); });
3122
+ var serialized = encode(evenBiggerInt);
3123
+ var deserialized = decode(serialized);
3124
+ assert.deepEqual(deserialized, evenBiggerInt);
3032
3125
  let encoder = new Encoder({
3033
3126
  largeBigIntToFloat: true
3034
3127
  });
3035
- serialized = encoder.encode(tooBigInt);
3128
+ serialized = encoder.encode(evenBiggerInt);
3036
3129
  deserialized = decode(serialized);
3037
- assert.isTrue(deserialized.tooBig > 2n**65n);
3130
+ assert.isTrue(deserialized.bigger > 2n**65n);
3038
3131
  });
3039
3132
 
3040
3133
  test('buffers', function() {
@@ -3092,6 +3185,11 @@
3092
3185
  let badInput = Buffer.from('7b2273657269616c6e6f223a2265343a30222c226970223a223139322e3136382e312e3335222c226b6579223a226770735f736563726574227d', 'hex');
3093
3186
  assert.throws(function(){ decode(badInput); }); // should throw, not crash
3094
3187
  });
3188
+ test('buffer key', function() {
3189
+ let encoder = new Encoder({ mapsAsObjects: false });
3190
+ let test = encoder.decode(Buffer.from('D87982A1446E616D654361626301', 'hex'));
3191
+ console.log(test);
3192
+ });
3095
3193
  test('encode as iterator', function() {
3096
3194
  let hasIterables = {
3097
3195
  a: 1,
@@ -3184,6 +3282,15 @@
3184
3282
  decode(Buffer.concat(result));
3185
3283
  console.log(performance.now() - start, result.length);
3186
3284
  });
3285
+
3286
+ test('little-endian typed array with aligned data', function() {
3287
+ // array[1] { uint32-little-endian-typed-array { bytes <00 00 00 00> } }
3288
+ let data = new Uint8Array([ 129, 216, 70, 68, 0, 0, 0, 0 ]);
3289
+ assert.deepEqual(decode(data), [new Uint32Array([0])]);
3290
+
3291
+ let value = {x: new Float32Array([1, 2, 3])};
3292
+ assert.deepEqual(decode(encode(value)), value);
3293
+ });
3187
3294
  });
3188
3295
  suite('CBOR performance tests', function(){
3189
3296
  test('performance JSON.parse', function() {