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/README.md +4 -0
- package/decode.js +151 -28
- package/dist/decode-no-eval.cjs +152 -28
- package/dist/decode-no-eval.cjs.map +1 -1
- package/dist/index-no-eval.cjs +540 -48
- package/dist/index-no-eval.cjs.map +1 -1
- package/dist/index-no-eval.min.js +1 -1
- package/dist/index-no-eval.min.js.map +1 -1
- package/dist/index.js +540 -48
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/dist/node.cjs +595 -100
- package/dist/node.cjs.map +1 -1
- package/dist/test.js +147 -30
- package/dist/test.js.map +1 -1
- package/encode.js +21 -2
- package/index.d.ts +1 -1
- package/iterators.js +123 -19
- package/package.json +2 -2
- package/scan.js +253 -0
- package/stream.js +9 -10
package/dist/decode-no-eval.cjs
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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(
|
|
191
|
-
lastPosition =
|
|
192
|
-
|
|
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
|
|
207
|
-
|
|
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
|
|
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
|
|
330
|
-
while ((
|
|
350
|
+
let i = 0;
|
|
351
|
+
while (!readBreak()) {
|
|
331
352
|
if (i >= maxArraySize) throw new Error(`Array length exceeds ${maxArraySize}`)
|
|
332
|
-
array[i++] =
|
|
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((
|
|
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 ((
|
|
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((
|
|
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 ((
|
|
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
|
-
|
|
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;
|