cbor-x 1.6.8 → 1.6.9

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,7 +15,6 @@
15
15
  const RECORD_INLINE_ID = 0xdfff; // temporary first-come first-serve tag // proposed tag: 0x7265 // 're'
16
16
  const BUNDLED_STRINGS_ID = 0xdff9;
17
17
  const PACKED_REFERENCE_TAG_ID = 6;
18
- const STOP_CODE = {};
19
18
  let maxArraySize = 112810000; // This is the maximum array size in V8. We would potentially detect and set it higher
20
19
  // for JSC, but this is pretty large and should be sufficient for most use cases
21
20
  let maxMapSize = 16810000; // JavaScript has a fixed maximum map size of about 16710000, but JS itself enforces this,
@@ -126,16 +125,11 @@
126
125
  // re-entrant execution, save the state and restore it after we do this decode
127
126
  return saveState(() => {
128
127
  clearSource();
129
- try {
130
- return this ? this.decode(source, end) : Decoder.prototype.decode.call(defaultOptions, source, end)
131
- } catch (error) {
132
- if (error && typeof error == 'object')
133
- nestedDecodeErrors.add(error);
134
- throw error
135
- }
128
+ return this ? this.decode(source, end) : Decoder.prototype.decode.call(defaultOptions, source, end)
136
129
  })
137
130
  }
138
- srcEnd = end > -1 ? end : source.length;
131
+ // past the view, the JS string readers would decode the missing bytes as zeros instead of failing as truncated
132
+ srcEnd = end > -1 && end < source.length ? end : source.length;
139
133
  position$1 = 0;
140
134
  stringPosition = 0;
141
135
  srcStringEnd = 0;
@@ -175,18 +169,28 @@
175
169
  return checkedRead()
176
170
  }
177
171
  decodeMultiple(source, forEach) {
172
+ if (src) {
173
+ // Keep the complete nested sequence inside its own decoder state.
174
+ return saveState(() => {
175
+ clearSource();
176
+ return this ? this.decodeMultiple(source, forEach) : defaultDecoder.decodeMultiple(source, forEach)
177
+ })
178
+ }
178
179
  let values, lastPosition = 0;
179
180
  try {
180
181
  let size = source.length;
181
182
  sequentialMode = true;
182
183
  let value = this ? this.decode(source, size) : defaultDecoder.decode(source, size);
183
184
  if (forEach) {
185
+ let end = position$1;
184
186
  if (forEach(value) === false) {
185
187
  return
186
188
  }
187
- while(position$1 < size) {
188
- lastPosition = position$1;
189
- if (forEach(checkedRead()) === false) {
189
+ while(end < size) {
190
+ lastPosition = end;
191
+ value = checkedRead();
192
+ end = position$1;
193
+ if (forEach(value) === false) {
190
194
  return
191
195
  }
192
196
  }
@@ -200,8 +204,10 @@
200
204
  return values
201
205
  }
202
206
  } catch(error) {
203
- error.lastPosition = lastPosition;
204
- error.values = values;
207
+ if (error && (typeof error == 'object' || typeof error == 'function')) {
208
+ error.lastPosition = lastPosition;
209
+ error.values = values;
210
+ }
205
211
  throw error
206
212
  } finally {
207
213
  sequentialMode = false;
@@ -255,9 +261,21 @@
255
261
  }
256
262
  }
257
263
 
258
- function endOfCBORError() {
264
+ function readBreak() {
265
+ if (position$1 < srcEnd && src[position$1] === 0xff) {
266
+ position$1++;
267
+ return true
268
+ }
269
+ return false
270
+ }
271
+ // neededEnd, from a length check, is a position that the source must extend to before the item can decode. It is a
272
+ // lower bound for any completion of the source: every byte read before the check was within the source, so a longer
273
+ // source reaches the same check with the same length. Streams wait for it instead of decoding on every chunk.
274
+ function endOfCBORError(neededEnd) {
259
275
  let error = new Error('Unexpected end of CBOR data');
260
276
  error.incomplete = true;
277
+ if (neededEnd)
278
+ error.neededEnd = neededEnd;
261
279
  return error
262
280
  }
263
281
 
@@ -309,6 +327,9 @@
309
327
  } else if (currentDecoder.int64AsNumber) {
310
328
  token = dataView.getUint32(position$1) * 0x100000000;
311
329
  token += dataView.getUint32(position$1 + 4);
330
+ position$1 += 8;
331
+ if (majorType === 1) return -1 - token
332
+ break
312
333
  } else token = dataView.getBigUint64(position$1);
313
334
  position$1 += 8;
314
335
  break
@@ -320,10 +341,10 @@
320
341
  throw new Error('Indefinite length not supported for byte or text strings')
321
342
  case 4: // array
322
343
  let array = [];
323
- let value, i = 0;
324
- while ((value = read()) != STOP_CODE) {
344
+ let i = 0;
345
+ while (!readBreak()) {
325
346
  if (i >= maxArraySize) throw new Error(`Array length exceeds ${maxArraySize}`)
326
- array[i++] = value;
347
+ array[i++] = read();
327
348
  }
328
349
  return majorType == 4 ? array : majorType == 3 ? array.join('') : Buffer.concat(array)
329
350
  case 5: // map
@@ -332,13 +353,15 @@
332
353
  let object = {};
333
354
  let i = 0;
334
355
  if (currentDecoder.keyMap) {
335
- while((key = read()) != STOP_CODE) {
356
+ while (!readBreak()) {
357
+ key = read();
336
358
  if (i++ >= objectSizeLimit) throw new Error(`Property count exceeds ${objectSizeLimit}`)
337
359
  object[safeKey(currentDecoder.decodeKey(key))] = read();
338
360
  }
339
361
  }
340
362
  else {
341
- while ((key = read()) != STOP_CODE) {
363
+ while (!readBreak()) {
364
+ key = read();
342
365
  if (i++ >= objectSizeLimit) throw new Error(`Property count exceeds ${objectSizeLimit}`)
343
366
  object[safeKey(key)] = read();
344
367
  }
@@ -352,7 +375,8 @@
352
375
  let map = new Map();
353
376
  if (currentDecoder.keyMap) {
354
377
  let i = 0;
355
- while((key = read()) != STOP_CODE) {
378
+ while (!readBreak()) {
379
+ key = read();
356
380
  if (i++ >= maxMapSize) {
357
381
  throw new Error(`Map size exceeds ${maxMapSize}`);
358
382
  }
@@ -361,7 +385,8 @@
361
385
  }
362
386
  else {
363
387
  let i = 0;
364
- while ((key = read()) != STOP_CODE) {
388
+ while (!readBreak()) {
389
+ key = read();
365
390
  if (i++ >= maxMapSize) {
366
391
  throw new Error(`Map size exceeds ${maxMapSize}`);
367
392
  }
@@ -371,7 +396,8 @@
371
396
  return map
372
397
  }
373
398
  case 7:
374
- return STOP_CODE
399
+ // readBreak consumes every break that ends an indefinite-length item
400
+ throw new Error('Unexpected break stop code outside of an indefinite-length item')
375
401
  default:
376
402
  throw new Error('Invalid major type for indefinite length ' + majorType)
377
403
  }
@@ -385,13 +411,13 @@
385
411
  case 1: // negative int
386
412
  return ~token
387
413
  case 2: // buffer
388
- if (token > srcEnd - position$1) throw endOfCBORError()
414
+ if (token > srcEnd - position$1) throw endOfCBORError(position$1 + token)
389
415
  return readBin(token)
390
416
  case 3: // string
391
417
  if (srcStringEnd >= position$1) { // already extracted, which only covers complete strings
392
418
  return srcString.slice(position$1 - srcStringStart, (position$1 += token) - srcStringStart)
393
419
  }
394
- if (token > srcEnd - position$1) throw endOfCBORError()
420
+ if (token > srcEnd - position$1) throw endOfCBORError(position$1 + token)
395
421
  if (srcStringEnd == 0 && srcEnd < 140 && token < 32) {
396
422
  // for small blocks, avoiding the overhead of the extract call is helpful
397
423
  let string = token < 16 ? shortStringInJS(token) : longStringInJS(token);
@@ -405,7 +431,7 @@
405
431
  // never be satisfied; check before allocating so a tiny header can not force a huge allocation
406
432
  // nested arrays each pass the check above yet still preallocate, so also spend from a
407
433
  // per-decode budget of the remaining bytes; the summed lengths of any valid tree stay under it
408
- if (token > srcEnd - position$1 || (arrayAllocationBudget -= token) < 0) throw endOfCBORError()
434
+ if (token > srcEnd - position$1 || (arrayAllocationBudget -= token) < 0) throw endOfCBORError(position$1 + token)
409
435
  let array = new Array(token);
410
436
  //if (currentDecoder.keyMap) for (let i = 0; i < token; i++) array[i] = currentDecoder.decodeKey(read())
411
437
  //else
@@ -414,7 +440,7 @@
414
440
  case 5: // map
415
441
  if (currentDecoder.mapsAsObjects) {
416
442
  if (token > objectSizeLimit) throw new Error(`Property count exceeds ${objectSizeLimit}`)
417
- if (token > (srcEnd - position$1) / 2) throw endOfCBORError() // each entry needs at least two bytes
443
+ if (token > (srcEnd - position$1) / 2) throw endOfCBORError(position$1 + 2 * token) // each entry needs at least two bytes
418
444
  let object = {};
419
445
  if (currentDecoder.keyMap) for (let i = 0; i < token; i++) object[safeKey(currentDecoder.decodeKey(read()))] = read();
420
446
  else for (let i = 0; i < token; i++) object[safeKey(read())] = read();
@@ -425,7 +451,7 @@
425
451
  restoreMapsAsObject = false;
426
452
  }
427
453
  if (token > maxMapSize) throw new Error(`Map size exceeds ${maxMapSize}`)
428
- if (token > (srcEnd - position$1) / 2) throw endOfCBORError()
454
+ if (token > (srcEnd - position$1) / 2) throw endOfCBORError(position$1 + 2 * token)
429
455
  let map = new Map();
430
456
  if (currentDecoder.keyMap) for (let i = 0; i < token; i++) map.set(currentDecoder.decodeKey(read()),read());
431
457
  else for (let i = 0; i < token; i++) map.set(read(), read());
@@ -938,6 +964,7 @@
938
964
  }
939
965
  return object
940
966
  };
967
+ const legacyRecordDefinition = currentExtensions[LEGACY_RECORD_INLINE_ID];
941
968
  currentExtensions[14] = (value) => {
942
969
  if (bundledStrings$1)
943
970
  return bundledStrings$1[0].slice(bundledStrings$1.position0, bundledStrings$1.position0 += value)
@@ -1210,6 +1237,8 @@
1210
1237
  }
1211
1238
 
1212
1239
  function saveState(callback) {
1240
+ let savedFragmentReader = fragmentReadString;
1241
+ let savedReadFixedString = readFixedString;
1213
1242
  let savedSrcEnd = srcEnd;
1214
1243
  let savedPosition = position$1;
1215
1244
  let savedStringPosition = stringPosition;
@@ -1219,6 +1248,7 @@
1219
1248
  let savedStrings = strings;
1220
1249
  let savedReferenceMap = referenceMap;
1221
1250
  let savedBundledStrings = bundledStrings$1;
1251
+ let savedPackedValues = packedValues;
1222
1252
 
1223
1253
  // TODO: We may need to revisit this if we do more external calls to user code (since it could be slow)
1224
1254
  let savedSrc = new Uint8Array(src.slice(0, srcEnd)); // we copy the data in case it changes while external data is processed
@@ -1227,10 +1257,20 @@
1227
1257
  let savedSequentialMode = sequentialMode;
1228
1258
  let savedRestoreMapsAsObject = restoreMapsAsObject;
1229
1259
  let savedArrayAllocationBudget = arrayAllocationBudget;
1260
+ if (fragmentReadString) readFixedString = fragmentReadString;
1261
+ fragmentReadString = null;
1230
1262
  restoreMapsAsObject = false;
1231
1263
  try {
1232
1264
  return callback()
1265
+ } catch (error) {
1266
+ // the callback decodes its own source (a shared structures blob, or an extension's embedded data), so a
1267
+ // truncation there cannot be completed by more of the outer input; checkedRead() clears the flag
1268
+ if (error && (typeof error == 'object' || typeof error == 'function'))
1269
+ nestedDecodeErrors.add(error);
1270
+ throw error
1233
1271
  } finally {
1272
+ fragmentReadString = savedFragmentReader;
1273
+ readFixedString = savedReadFixedString;
1234
1274
  srcEnd = savedSrcEnd;
1235
1275
  position$1 = savedPosition;
1236
1276
  stringPosition = savedStringPosition;
@@ -1240,6 +1280,7 @@
1240
1280
  strings = savedStrings;
1241
1281
  referenceMap = savedReferenceMap;
1242
1282
  bundledStrings$1 = savedBundledStrings;
1283
+ packedValues = savedPackedValues;
1243
1284
  src = savedSrc;
1244
1285
  sequentialMode = savedSequentialMode;
1245
1286
  currentStructures = savedStructures;
@@ -1255,8 +1296,15 @@
1255
1296
  currentStructures = null;
1256
1297
  }
1257
1298
 
1299
+ const registeredExtensions = new Map();
1300
+ let fragmentReadString;
1301
+
1302
+ new Map([[LEGACY_RECORD_INLINE_ID, legacyRecordDefinition], [259, currentExtensions[259]]]);
1303
+
1258
1304
  function addExtension$2(extension) {
1259
1305
  currentExtensions[extension.tag] = extension.decode;
1306
+ if (typeof extension.decode === 'function') registeredExtensions.set(extension.tag, extension.decode);
1307
+ else registeredExtensions.delete(extension.tag);
1260
1308
  }
1261
1309
 
1262
1310
  const mult10 = new Array(147); // this is a table matching binary exponents to the multiplier to determine significant digit rounding
@@ -1844,6 +1892,11 @@
1844
1892
  return encode(json)
1845
1893
  }
1846
1894
 
1895
+ // not in extensions, so class instances that reach writeObject do not pay another instanceof
1896
+ if (constructor === DataView) {
1897
+ writeBuffer(new Uint8Array(value.buffer, value.byteOffset, value.byteLength), makeRoom);
1898
+ return
1899
+ }
1847
1900
  // no extension found, write as a plain object
1848
1901
  writeObject(value);
1849
1902
  }
@@ -1937,8 +1990,22 @@
1937
1990
  size++;
1938
1991
  }
1939
1992
  }
1940
- target[objectOffset++ + start] = size >> 8;
1941
- target[objectOffset + start] = size & 0xff;
1993
+ if (size < 0x10000) {
1994
+ // fits in a map-16 (uint16) count: back-patch the reserved 2 bytes
1995
+ target[objectOffset++ + start] = size >> 8;
1996
+ target[objectOffset + start] = size & 0xff;
1997
+ } else {
1998
+ // too many entries for a map-16 count: widen the already-written header
1999
+ // to map-32, mirroring writeArrayHeader's 0x9a/setUint32 path. Shift the
2000
+ // encoded entries 2 bytes forward to make room for the wider (uint32) count.
2001
+ if (position + 2 > safeEnd)
2002
+ makeRoom(position + 2);
2003
+ let headerPosition = objectOffset + start;
2004
+ target.copyWithin(headerPosition + 4, headerPosition + 2, position);
2005
+ position += 2;
2006
+ target[headerPosition - 1] = 0xba; // map 32
2007
+ targetView.setUint32(headerPosition, size);
2008
+ }
1942
2009
  } :
1943
2010
  (object, skipValues) => {
1944
2011
  let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null));
@@ -2677,6 +2744,15 @@
2677
2744
  assert.throws(() => decode$1(Buffer.from('7a10000000', 'hex')));
2678
2745
  });
2679
2746
 
2747
+ test('break stop code outside indefinite-length item', function() {
2748
+ assert.throws(() => decode$1(Buffer.from('ff', 'hex')));
2749
+ assert.throws(() => decode$1(Buffer.from('81ff', 'hex')));
2750
+ assert.throws(() => decode$1(Buffer.from('a16161ff', 'hex')));
2751
+ assert.throws(() => decode$1(Buffer.from('c0ff', 'hex')));
2752
+ assert.deepEqual(decode$1(Buffer.from('9f01ff', 'hex')), [1]);
2753
+ assert.deepEqual(decode$1(Buffer.from('bf616101ff', 'hex')), { a: 1 });
2754
+ });
2755
+
2680
2756
  test('overlong encoding', function() {
2681
2757
  const payload = Buffer.from([0x62, 0xc0, 0xaf]);
2682
2758
  const result = decode$1(payload);
@@ -3016,6 +3092,24 @@
3016
3092
  var decoded = decode(encoded);
3017
3093
  assert.equal(decoded.length, 0);
3018
3094
  });
3095
+ test('fixed map size overflow (map-16 to map-32)', function() {
3096
+ this.timeout(120000);
3097
+ // issue #131: the default encode() serializes objects with a back-patched
3098
+ // map-16 (uint16) header, so an object with >= 65536 keys overflowed the
3099
+ // count and the decoder stopped early with "Data read, but end of buffer not
3100
+ // reached". Verify the round-trip across the map-16 -> map-32 boundary.
3101
+ function roundTripObjectWith(count) {
3102
+ var data = {};
3103
+ for (var i = 0; i < count; i++)
3104
+ data['k' + i] = i;
3105
+ var decoded = decode(encode(data));
3106
+ assert.equal(Object.keys(decoded).length, count);
3107
+ assert.deepEqual(decoded, data);
3108
+ }
3109
+ roundTripObjectWith(65535); // map-16 path, must stay unchanged
3110
+ roundTripObjectWith(65536); // first size that needs map-32
3111
+ roundTripObjectWith(70000); // well past the 16-bit boundary
3112
+ });
3019
3113
 
3020
3114
  test('random strings', function(){
3021
3115
  var data = [];
@@ -3142,6 +3236,14 @@
3142
3236
  var deserialized = encoder.decode(serialized);
3143
3237
  assert.equal(deserialized, data);
3144
3238
  });
3239
+ test('negative int64 as number', function() {
3240
+ let encoded = Buffer.from('3b0000000e96848f5f', 'hex');
3241
+ assert.equal(new Decoder().decode(encoded), -62654812000n);
3242
+
3243
+ let decoder = new Decoder({ int64AsNumber: true });
3244
+ assert.equal(decoder.decode(encoded), -62654812000);
3245
+ assert.equal(decoder.decode(Buffer.from('3b001ffffffffffffe', 'hex')), Number.MIN_SAFE_INTEGER);
3246
+ });
3145
3247
  test('bigint to float', function() {
3146
3248
  var data = {
3147
3249
  a: 325283295382932843n
@@ -3186,6 +3288,21 @@
3186
3288
  assert.equal(decoded[2], 5);
3187
3289
  assert.equal(decoded.byteLength, 3);
3188
3290
  });
3291
+ test('encode DataView', function() {
3292
+ let bytes = new Uint8Array(40).map((_, i) => i);
3293
+ assert.equal(Buffer.from(encode(new DataView(bytes.buffer, 3, 4))).toString('hex'), '4403040506');
3294
+ assert.equal(Buffer.from(encode(new DataView(bytes.buffer, 5, 30))).toString('hex'),
3295
+ '581e' + Buffer.from(bytes.subarray(5, 35)).toString('hex'));
3296
+ assert.equal(Buffer.from(encode(new DataView(bytes.buffer, 0, 0))).toString('hex'), '40');
3297
+ assert.deepEqual(decode(encode({ v: new DataView(bytes.buffer, 1, 2) })), { v: Buffer.from([1, 2]) });
3298
+ assert.equal(Buffer.from(encode(bytes.buffer.slice(1, 4))).toString('hex'), '43010203');
3299
+ assert.equal(Buffer.from(encode(bytes.subarray(1, 4))).toString('hex'), 'd84043010203');
3300
+ assert.equal(Buffer.from(encode(Buffer.from(bytes.subarray(1, 4)))).toString('hex'), '43010203');
3301
+ assert.equal(Buffer.from(new Encoder({ tagUint8Array: false }).encode(bytes.subarray(1, 4))).toString('hex'), '43010203');
3302
+ let uint16Tag = new Uint8Array(new Uint16Array([1]).buffer)[0] == 1 ? '45' : '41';
3303
+ assert.equal(Buffer.from(encode(new Uint16Array(bytes.buffer, 2, 2))).toString('hex'), 'd8' + uint16Tag + '4402030405');
3304
+ assert.equal(Buffer.from(encode(new Uint8ClampedArray(bytes.buffer, 2, 2))).toString('hex'), 'd844420203');
3305
+ });
3189
3306
 
3190
3307
  test('iterator/indefinite length array', function(){
3191
3308
  class NotArray {