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.
@@ -17,7 +17,6 @@
17
17
  const RECORD_INLINE_ID = 0xdfff; // temporary first-come first-serve tag // proposed tag: 0x7265 // 're'
18
18
  const BUNDLED_STRINGS_ID = 0xdff9;
19
19
  const PACKED_REFERENCE_TAG_ID = 6;
20
- const STOP_CODE = {};
21
20
  let maxArraySize = 112810000; // This is the maximum array size in V8. We would potentially detect and set it higher
22
21
  // for JSC, but this is pretty large and should be sufficient for most use cases
23
22
  let maxMapSize = 16810000; // JavaScript has a fixed maximum map size of about 16710000, but JS itself enforces this,
@@ -129,16 +128,11 @@
129
128
  // re-entrant execution, save the state and restore it after we do this decode
130
129
  return saveState(() => {
131
130
  clearSource();
132
- try {
133
- return this ? this.decode(source, end) : Decoder.prototype.decode.call(defaultOptions, source, end)
134
- } catch (error) {
135
- if (error && typeof error == 'object')
136
- nestedDecodeErrors.add(error);
137
- throw error
138
- }
131
+ return this ? this.decode(source, end) : Decoder.prototype.decode.call(defaultOptions, source, end)
139
132
  })
140
133
  }
141
- srcEnd = end > -1 ? end : source.length;
134
+ // past the view, the JS string readers would decode the missing bytes as zeros instead of failing as truncated
135
+ srcEnd = end > -1 && end < source.length ? end : source.length;
142
136
  position = 0;
143
137
  stringPosition = 0;
144
138
  srcStringEnd = 0;
@@ -178,18 +172,28 @@
178
172
  return checkedRead()
179
173
  }
180
174
  decodeMultiple(source, forEach) {
175
+ if (src) {
176
+ // Keep the complete nested sequence inside its own decoder state.
177
+ return saveState(() => {
178
+ clearSource();
179
+ return this ? this.decodeMultiple(source, forEach) : defaultDecoder.decodeMultiple(source, forEach)
180
+ })
181
+ }
181
182
  let values, lastPosition = 0;
182
183
  try {
183
184
  let size = source.length;
184
185
  sequentialMode = true;
185
186
  let value = this ? this.decode(source, size) : defaultDecoder.decode(source, size);
186
187
  if (forEach) {
188
+ let end = position;
187
189
  if (forEach(value) === false) {
188
190
  return
189
191
  }
190
- while(position < size) {
191
- lastPosition = position;
192
- if (forEach(checkedRead()) === false) {
192
+ while(end < size) {
193
+ lastPosition = end;
194
+ value = checkedRead();
195
+ end = position;
196
+ if (forEach(value) === false) {
193
197
  return
194
198
  }
195
199
  }
@@ -203,8 +207,10 @@
203
207
  return values
204
208
  }
205
209
  } catch(error) {
206
- error.lastPosition = lastPosition;
207
- error.values = values;
210
+ if (error && (typeof error == 'object' || typeof error == 'function')) {
211
+ error.lastPosition = lastPosition;
212
+ error.values = values;
213
+ }
208
214
  throw error
209
215
  } finally {
210
216
  sequentialMode = false;
@@ -261,9 +267,21 @@
261
267
  }
262
268
  }
263
269
 
264
- function endOfCBORError() {
270
+ function readBreak() {
271
+ if (position < srcEnd && src[position] === 0xff) {
272
+ position++;
273
+ return true
274
+ }
275
+ return false
276
+ }
277
+ // neededEnd, from a length check, is a position that the source must extend to before the item can decode. It is a
278
+ // lower bound for any completion of the source: every byte read before the check was within the source, so a longer
279
+ // source reaches the same check with the same length. Streams wait for it instead of decoding on every chunk.
280
+ function endOfCBORError(neededEnd) {
265
281
  let error = new Error('Unexpected end of CBOR data');
266
282
  error.incomplete = true;
283
+ if (neededEnd)
284
+ error.neededEnd = neededEnd;
267
285
  return error
268
286
  }
269
287
 
@@ -315,6 +333,9 @@
315
333
  } else if (currentDecoder.int64AsNumber) {
316
334
  token = dataView.getUint32(position) * 0x100000000;
317
335
  token += dataView.getUint32(position + 4);
336
+ position += 8;
337
+ if (majorType === 1) return -1 - token
338
+ break
318
339
  } else token = dataView.getBigUint64(position);
319
340
  position += 8;
320
341
  break
@@ -326,10 +347,10 @@
326
347
  throw new Error('Indefinite length not supported for byte or text strings')
327
348
  case 4: // array
328
349
  let array = [];
329
- let value, i = 0;
330
- while ((value = read()) != STOP_CODE) {
350
+ let i = 0;
351
+ while (!readBreak()) {
331
352
  if (i >= maxArraySize) throw new Error(`Array length exceeds ${maxArraySize}`)
332
- array[i++] = value;
353
+ array[i++] = read();
333
354
  }
334
355
  return majorType == 4 ? array : majorType == 3 ? array.join('') : Buffer.concat(array)
335
356
  case 5: // map
@@ -338,13 +359,15 @@
338
359
  let object = {};
339
360
  let i = 0;
340
361
  if (currentDecoder.keyMap) {
341
- while((key = read()) != STOP_CODE) {
362
+ while (!readBreak()) {
363
+ key = read();
342
364
  if (i++ >= objectSizeLimit) throw new Error(`Property count exceeds ${objectSizeLimit}`)
343
365
  object[safeKey(currentDecoder.decodeKey(key))] = read();
344
366
  }
345
367
  }
346
368
  else {
347
- while ((key = read()) != STOP_CODE) {
369
+ while (!readBreak()) {
370
+ key = read();
348
371
  if (i++ >= objectSizeLimit) throw new Error(`Property count exceeds ${objectSizeLimit}`)
349
372
  object[safeKey(key)] = read();
350
373
  }
@@ -358,7 +381,8 @@
358
381
  let map = new Map();
359
382
  if (currentDecoder.keyMap) {
360
383
  let i = 0;
361
- while((key = read()) != STOP_CODE) {
384
+ while (!readBreak()) {
385
+ key = read();
362
386
  if (i++ >= maxMapSize) {
363
387
  throw new Error(`Map size exceeds ${maxMapSize}`);
364
388
  }
@@ -367,7 +391,8 @@
367
391
  }
368
392
  else {
369
393
  let i = 0;
370
- while ((key = read()) != STOP_CODE) {
394
+ while (!readBreak()) {
395
+ key = read();
371
396
  if (i++ >= maxMapSize) {
372
397
  throw new Error(`Map size exceeds ${maxMapSize}`);
373
398
  }
@@ -377,7 +402,8 @@
377
402
  return map
378
403
  }
379
404
  case 7:
380
- return STOP_CODE
405
+ // readBreak consumes every break that ends an indefinite-length item
406
+ throw new Error('Unexpected break stop code outside of an indefinite-length item')
381
407
  default:
382
408
  throw new Error('Invalid major type for indefinite length ' + majorType)
383
409
  }
@@ -391,13 +417,13 @@
391
417
  case 1: // negative int
392
418
  return ~token
393
419
  case 2: // buffer
394
- if (token > srcEnd - position) throw endOfCBORError()
420
+ if (token > srcEnd - position) throw endOfCBORError(position + token)
395
421
  return readBin(token)
396
422
  case 3: // string
397
423
  if (srcStringEnd >= position) { // already extracted, which only covers complete strings
398
424
  return srcString.slice(position - srcStringStart, (position += token) - srcStringStart)
399
425
  }
400
- if (token > srcEnd - position) throw endOfCBORError()
426
+ if (token > srcEnd - position) throw endOfCBORError(position + token)
401
427
  if (srcStringEnd == 0 && srcEnd < 140 && token < 32) {
402
428
  // for small blocks, avoiding the overhead of the extract call is helpful
403
429
  let string = token < 16 ? shortStringInJS(token) : longStringInJS(token);
@@ -411,7 +437,7 @@
411
437
  // never be satisfied; check before allocating so a tiny header can not force a huge allocation
412
438
  // nested arrays each pass the check above yet still preallocate, so also spend from a
413
439
  // per-decode budget of the remaining bytes; the summed lengths of any valid tree stay under it
414
- if (token > srcEnd - position || (arrayAllocationBudget -= token) < 0) throw endOfCBORError()
440
+ if (token > srcEnd - position || (arrayAllocationBudget -= token) < 0) throw endOfCBORError(position + token)
415
441
  let array = new Array(token);
416
442
  //if (currentDecoder.keyMap) for (let i = 0; i < token; i++) array[i] = currentDecoder.decodeKey(read())
417
443
  //else
@@ -420,7 +446,7 @@
420
446
  case 5: // map
421
447
  if (currentDecoder.mapsAsObjects) {
422
448
  if (token > objectSizeLimit) throw new Error(`Property count exceeds ${objectSizeLimit}`)
423
- if (token > (srcEnd - position) / 2) throw endOfCBORError() // each entry needs at least two bytes
449
+ if (token > (srcEnd - position) / 2) throw endOfCBORError(position + 2 * token) // each entry needs at least two bytes
424
450
  let object = {};
425
451
  if (currentDecoder.keyMap) for (let i = 0; i < token; i++) object[safeKey(currentDecoder.decodeKey(read()))] = read();
426
452
  else for (let i = 0; i < token; i++) object[safeKey(read())] = read();
@@ -431,7 +457,7 @@
431
457
  restoreMapsAsObject = false;
432
458
  }
433
459
  if (token > maxMapSize) throw new Error(`Map size exceeds ${maxMapSize}`)
434
- if (token > (srcEnd - position) / 2) throw endOfCBORError()
460
+ if (token > (srcEnd - position) / 2) throw endOfCBORError(position + 2 * token)
435
461
  let map = new Map();
436
462
  if (currentDecoder.keyMap) for (let i = 0; i < token; i++) map.set(currentDecoder.decodeKey(read()),read());
437
463
  else for (let i = 0; i < token; i++) map.set(read(), read());
@@ -947,6 +973,7 @@
947
973
  }
948
974
  return object
949
975
  };
976
+ const legacyRecordDefinition = currentExtensions[LEGACY_RECORD_INLINE_ID];
950
977
  currentExtensions[14] = (value) => {
951
978
  if (bundledStrings)
952
979
  return bundledStrings[0].slice(bundledStrings.position0, bundledStrings.position0 += value)
@@ -1219,6 +1246,9 @@
1219
1246
  }
1220
1247
 
1221
1248
  function saveState(callback) {
1249
+ let savedFragmentEnd = fragmentEnd;
1250
+ let savedFragmentReader = fragmentReadString;
1251
+ let savedReadFixedString = readFixedString;
1222
1252
  let savedSrcEnd = srcEnd;
1223
1253
  let savedPosition = position;
1224
1254
  let savedStringPosition = stringPosition;
@@ -1228,6 +1258,7 @@
1228
1258
  let savedStrings = strings;
1229
1259
  let savedReferenceMap = referenceMap;
1230
1260
  let savedBundledStrings = bundledStrings;
1261
+ let savedPackedValues = packedValues;
1231
1262
 
1232
1263
  // TODO: We may need to revisit this if we do more external calls to user code (since it could be slow)
1233
1264
  let savedSrc = new Uint8Array(src.slice(0, srcEnd)); // we copy the data in case it changes while external data is processed
@@ -1236,10 +1267,22 @@
1236
1267
  let savedSequentialMode = sequentialMode;
1237
1268
  let savedRestoreMapsAsObject = restoreMapsAsObject;
1238
1269
  let savedArrayAllocationBudget = arrayAllocationBudget;
1270
+ fragmentEnd = 0;
1271
+ if (fragmentReadString) readFixedString = fragmentReadString;
1272
+ fragmentReadString = null;
1239
1273
  restoreMapsAsObject = false;
1240
1274
  try {
1241
1275
  return callback()
1276
+ } catch (error) {
1277
+ // the callback decodes its own source (a shared structures blob, or an extension's embedded data), so a
1278
+ // truncation there cannot be completed by more of the outer input; checkedRead() clears the flag
1279
+ if (error && (typeof error == 'object' || typeof error == 'function'))
1280
+ nestedDecodeErrors.add(error);
1281
+ throw error
1242
1282
  } finally {
1283
+ fragmentEnd = savedFragmentEnd;
1284
+ fragmentReadString = savedFragmentReader;
1285
+ readFixedString = savedReadFixedString;
1243
1286
  srcEnd = savedSrcEnd;
1244
1287
  position = savedPosition;
1245
1288
  stringPosition = savedStringPosition;
@@ -1249,6 +1292,7 @@
1249
1292
  strings = savedStrings;
1250
1293
  referenceMap = savedReferenceMap;
1251
1294
  bundledStrings = savedBundledStrings;
1295
+ packedValues = savedPackedValues;
1252
1296
  src = savedSrc;
1253
1297
  sequentialMode = savedSequentialMode;
1254
1298
  currentStructures = savedStructures;
@@ -1264,8 +1308,87 @@
1264
1308
  currentStructures = null;
1265
1309
  }
1266
1310
 
1311
+ const registeredExtensions = new Map();
1312
+ let fragmentEnd = 0;
1313
+ let fragmentReadString;
1314
+
1315
+ const builtinScanTags = new Map([[LEGACY_RECORD_INLINE_ID, legacyRecordDefinition], [259, currentExtensions[259]]]);
1316
+ function isBuiltinScanTag(tag) {
1317
+ return builtinScanTags.has(tag) && currentExtensions[tag] === builtinScanTags.get(tag)
1318
+ }
1319
+
1320
+ function getScanTag(tag, decoder) {
1321
+ return tag >= BUNDLED_STRINGS_ID && decoder.structures && decoder.structures[tag & 0x1fff]
1322
+ }
1323
+
1324
+ function checkScanContainer(major, length, mapsAsObjects) {
1325
+ if (major === 4) {
1326
+ if (length > maxArraySize) throw new Error(`Array length exceeds ${maxArraySize}`)
1327
+ } else if (mapsAsObjects) {
1328
+ if (length > objectSizeLimit) throw new Error(`Property count exceeds ${objectSizeLimit}`)
1329
+ } else if (length > maxMapSize) throw new Error(`Map size exceeds ${maxMapSize}`)
1330
+ }
1331
+
1332
+ function decodeScanned(decoder, source, end) {
1333
+ const boundStrings = exports.isNativeAccelerationEnabled && end < source.length;
1334
+ if (!registeredExtensions.size && !boundStrings) return decoder.decodeMultiple(source)
1335
+ const savedEnd = fragmentEnd;
1336
+ const savedReader = readFixedString;
1337
+ const savedFragmentReader = fragmentReadString;
1338
+ const originals = [];
1339
+ fragmentEnd = end;
1340
+ fragmentReadString = savedReader;
1341
+ try {
1342
+ if (boundStrings) readFixedString = (length) => {
1343
+ if (!fragmentEnd || position >= fragmentEnd) return savedReader(length)
1344
+ const savedSrcEnd = srcEnd;
1345
+ srcEnd = fragmentEnd;
1346
+ try { return savedReader(length) } finally { srcEnd = savedSrcEnd; }
1347
+ };
1348
+ for (const [tag, extension] of registeredExtensions) {
1349
+ if (!extension.handlesRead) continue
1350
+ const original = currentExtensions[tag];
1351
+ const guarded = (read) => {
1352
+ if (!fragmentEnd || position >= fragmentEnd) return extension(read)
1353
+ let calls = 0, framingError;
1354
+ const value = extension(() => {
1355
+ if (calls++) {
1356
+ framingError = new Error('Tag ' + tag + ': fragmented decoding cannot frame multi-item handlesRead extensions');
1357
+ throw framingError
1358
+ }
1359
+ return read()
1360
+ });
1361
+ if (framingError) throw framingError
1362
+ return value
1363
+ };
1364
+ guarded.handlesRead = true;
1365
+ originals.push([tag, original, guarded]);
1366
+ currentExtensions[tag] = guarded;
1367
+ }
1368
+ return decoder.decodeMultiple(source)
1369
+ } finally {
1370
+ fragmentEnd = savedEnd;
1371
+ fragmentReadString = savedFragmentReader;
1372
+ readFixedString = savedReader;
1373
+ for (const [tag, original, guarded] of originals) {
1374
+ if (currentExtensions[tag] === guarded) currentExtensions[tag] = original;
1375
+ }
1376
+ }
1377
+ }
1378
+
1379
+ // Internal streaming hooks; not part of the public decoder API.
1380
+ const _stream = {
1381
+ getTag: getScanTag,
1382
+ isBuiltinTag: isBuiltinScanTag,
1383
+ checkContainer: checkScanContainer,
1384
+ decode: decodeScanned,
1385
+ get readThreshold() { return inlineObjectReadThreshold }
1386
+ };
1387
+
1267
1388
  function addExtension(extension) {
1268
1389
  currentExtensions[extension.tag] = extension.decode;
1390
+ if (typeof extension.decode === 'function') registeredExtensions.set(extension.tag, extension.decode);
1391
+ else registeredExtensions.delete(extension.tag);
1269
1392
  }
1270
1393
 
1271
1394
  function setSizeLimits(limits) {
@@ -1297,6 +1420,7 @@
1297
1420
  exports.Decoder = Decoder;
1298
1421
  exports.FLOAT32_OPTIONS = FLOAT32_OPTIONS;
1299
1422
  exports.Tag = Tag;
1423
+ exports._stream = _stream;
1300
1424
  exports.addExtension = addExtension;
1301
1425
  exports.checkedRead = checkedRead;
1302
1426
  exports.clearSource = clearSource;