cbor-x 1.6.7 → 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 +157 -28
- package/dist/decode-no-eval.cjs +158 -28
- package/dist/decode-no-eval.cjs.map +1 -1
- package/dist/index-no-eval.cjs +546 -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 +546 -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 +601 -100
- package/dist/node.cjs.map +1 -1
- package/dist/test.js +153 -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;
|
|
@@ -216,8 +222,10 @@
|
|
|
216
222
|
return position
|
|
217
223
|
}
|
|
218
224
|
const nestedDecodeErrors = new WeakSet();
|
|
225
|
+
let arrayAllocationBudget;
|
|
219
226
|
function checkedRead() {
|
|
220
227
|
try {
|
|
228
|
+
arrayAllocationBudget = srcEnd - position;
|
|
221
229
|
let result = read();
|
|
222
230
|
if (bundledStrings) {
|
|
223
231
|
if (position >= bundledStrings.postBundlePosition)
|
|
@@ -259,9 +267,21 @@
|
|
|
259
267
|
}
|
|
260
268
|
}
|
|
261
269
|
|
|
262
|
-
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) {
|
|
263
281
|
let error = new Error('Unexpected end of CBOR data');
|
|
264
282
|
error.incomplete = true;
|
|
283
|
+
if (neededEnd)
|
|
284
|
+
error.neededEnd = neededEnd;
|
|
265
285
|
return error
|
|
266
286
|
}
|
|
267
287
|
|
|
@@ -313,6 +333,9 @@
|
|
|
313
333
|
} else if (currentDecoder.int64AsNumber) {
|
|
314
334
|
token = dataView.getUint32(position) * 0x100000000;
|
|
315
335
|
token += dataView.getUint32(position + 4);
|
|
336
|
+
position += 8;
|
|
337
|
+
if (majorType === 1) return -1 - token
|
|
338
|
+
break
|
|
316
339
|
} else token = dataView.getBigUint64(position);
|
|
317
340
|
position += 8;
|
|
318
341
|
break
|
|
@@ -324,10 +347,10 @@
|
|
|
324
347
|
throw new Error('Indefinite length not supported for byte or text strings')
|
|
325
348
|
case 4: // array
|
|
326
349
|
let array = [];
|
|
327
|
-
let
|
|
328
|
-
while ((
|
|
350
|
+
let i = 0;
|
|
351
|
+
while (!readBreak()) {
|
|
329
352
|
if (i >= maxArraySize) throw new Error(`Array length exceeds ${maxArraySize}`)
|
|
330
|
-
array[i++] =
|
|
353
|
+
array[i++] = read();
|
|
331
354
|
}
|
|
332
355
|
return majorType == 4 ? array : majorType == 3 ? array.join('') : Buffer.concat(array)
|
|
333
356
|
case 5: // map
|
|
@@ -336,13 +359,15 @@
|
|
|
336
359
|
let object = {};
|
|
337
360
|
let i = 0;
|
|
338
361
|
if (currentDecoder.keyMap) {
|
|
339
|
-
while((
|
|
362
|
+
while (!readBreak()) {
|
|
363
|
+
key = read();
|
|
340
364
|
if (i++ >= objectSizeLimit) throw new Error(`Property count exceeds ${objectSizeLimit}`)
|
|
341
365
|
object[safeKey(currentDecoder.decodeKey(key))] = read();
|
|
342
366
|
}
|
|
343
367
|
}
|
|
344
368
|
else {
|
|
345
|
-
while ((
|
|
369
|
+
while (!readBreak()) {
|
|
370
|
+
key = read();
|
|
346
371
|
if (i++ >= objectSizeLimit) throw new Error(`Property count exceeds ${objectSizeLimit}`)
|
|
347
372
|
object[safeKey(key)] = read();
|
|
348
373
|
}
|
|
@@ -356,7 +381,8 @@
|
|
|
356
381
|
let map = new Map();
|
|
357
382
|
if (currentDecoder.keyMap) {
|
|
358
383
|
let i = 0;
|
|
359
|
-
while((
|
|
384
|
+
while (!readBreak()) {
|
|
385
|
+
key = read();
|
|
360
386
|
if (i++ >= maxMapSize) {
|
|
361
387
|
throw new Error(`Map size exceeds ${maxMapSize}`);
|
|
362
388
|
}
|
|
@@ -365,7 +391,8 @@
|
|
|
365
391
|
}
|
|
366
392
|
else {
|
|
367
393
|
let i = 0;
|
|
368
|
-
while ((
|
|
394
|
+
while (!readBreak()) {
|
|
395
|
+
key = read();
|
|
369
396
|
if (i++ >= maxMapSize) {
|
|
370
397
|
throw new Error(`Map size exceeds ${maxMapSize}`);
|
|
371
398
|
}
|
|
@@ -375,7 +402,8 @@
|
|
|
375
402
|
return map
|
|
376
403
|
}
|
|
377
404
|
case 7:
|
|
378
|
-
|
|
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')
|
|
379
407
|
default:
|
|
380
408
|
throw new Error('Invalid major type for indefinite length ' + majorType)
|
|
381
409
|
}
|
|
@@ -389,13 +417,13 @@
|
|
|
389
417
|
case 1: // negative int
|
|
390
418
|
return ~token
|
|
391
419
|
case 2: // buffer
|
|
392
|
-
if (token > srcEnd - position) throw endOfCBORError()
|
|
420
|
+
if (token > srcEnd - position) throw endOfCBORError(position + token)
|
|
393
421
|
return readBin(token)
|
|
394
422
|
case 3: // string
|
|
395
423
|
if (srcStringEnd >= position) { // already extracted, which only covers complete strings
|
|
396
424
|
return srcString.slice(position - srcStringStart, (position += token) - srcStringStart)
|
|
397
425
|
}
|
|
398
|
-
if (token > srcEnd - position) throw endOfCBORError()
|
|
426
|
+
if (token > srcEnd - position) throw endOfCBORError(position + token)
|
|
399
427
|
if (srcStringEnd == 0 && srcEnd < 140 && token < 32) {
|
|
400
428
|
// for small blocks, avoiding the overhead of the extract call is helpful
|
|
401
429
|
let string = token < 16 ? shortStringInJS(token) : longStringInJS(token);
|
|
@@ -407,7 +435,9 @@
|
|
|
407
435
|
if (token > maxArraySize) throw new Error(`Array length exceeds ${maxArraySize}`)
|
|
408
436
|
// every element occupies at least one byte, so a length beyond what remains in the source can
|
|
409
437
|
// never be satisfied; check before allocating so a tiny header can not force a huge allocation
|
|
410
|
-
|
|
438
|
+
// nested arrays each pass the check above yet still preallocate, so also spend from a
|
|
439
|
+
// per-decode budget of the remaining bytes; the summed lengths of any valid tree stay under it
|
|
440
|
+
if (token > srcEnd - position || (arrayAllocationBudget -= token) < 0) throw endOfCBORError(position + token)
|
|
411
441
|
let array = new Array(token);
|
|
412
442
|
//if (currentDecoder.keyMap) for (let i = 0; i < token; i++) array[i] = currentDecoder.decodeKey(read())
|
|
413
443
|
//else
|
|
@@ -416,7 +446,7 @@
|
|
|
416
446
|
case 5: // map
|
|
417
447
|
if (currentDecoder.mapsAsObjects) {
|
|
418
448
|
if (token > objectSizeLimit) throw new Error(`Property count exceeds ${objectSizeLimit}`)
|
|
419
|
-
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
|
|
420
450
|
let object = {};
|
|
421
451
|
if (currentDecoder.keyMap) for (let i = 0; i < token; i++) object[safeKey(currentDecoder.decodeKey(read()))] = read();
|
|
422
452
|
else for (let i = 0; i < token; i++) object[safeKey(read())] = read();
|
|
@@ -427,7 +457,7 @@
|
|
|
427
457
|
restoreMapsAsObject = false;
|
|
428
458
|
}
|
|
429
459
|
if (token > maxMapSize) throw new Error(`Map size exceeds ${maxMapSize}`)
|
|
430
|
-
if (token > (srcEnd - position) / 2) throw endOfCBORError()
|
|
460
|
+
if (token > (srcEnd - position) / 2) throw endOfCBORError(position + 2 * token)
|
|
431
461
|
let map = new Map();
|
|
432
462
|
if (currentDecoder.keyMap) for (let i = 0; i < token; i++) map.set(currentDecoder.decodeKey(read()),read());
|
|
433
463
|
else for (let i = 0; i < token; i++) map.set(read(), read());
|
|
@@ -943,6 +973,7 @@
|
|
|
943
973
|
}
|
|
944
974
|
return object
|
|
945
975
|
};
|
|
976
|
+
const legacyRecordDefinition = currentExtensions[LEGACY_RECORD_INLINE_ID];
|
|
946
977
|
currentExtensions[14] = (value) => {
|
|
947
978
|
if (bundledStrings)
|
|
948
979
|
return bundledStrings[0].slice(bundledStrings.position0, bundledStrings.position0 += value)
|
|
@@ -1215,6 +1246,9 @@
|
|
|
1215
1246
|
}
|
|
1216
1247
|
|
|
1217
1248
|
function saveState(callback) {
|
|
1249
|
+
let savedFragmentEnd = fragmentEnd;
|
|
1250
|
+
let savedFragmentReader = fragmentReadString;
|
|
1251
|
+
let savedReadFixedString = readFixedString;
|
|
1218
1252
|
let savedSrcEnd = srcEnd;
|
|
1219
1253
|
let savedPosition = position;
|
|
1220
1254
|
let savedStringPosition = stringPosition;
|
|
@@ -1224,6 +1258,7 @@
|
|
|
1224
1258
|
let savedStrings = strings;
|
|
1225
1259
|
let savedReferenceMap = referenceMap;
|
|
1226
1260
|
let savedBundledStrings = bundledStrings;
|
|
1261
|
+
let savedPackedValues = packedValues;
|
|
1227
1262
|
|
|
1228
1263
|
// TODO: We may need to revisit this if we do more external calls to user code (since it could be slow)
|
|
1229
1264
|
let savedSrc = new Uint8Array(src.slice(0, srcEnd)); // we copy the data in case it changes while external data is processed
|
|
@@ -1231,10 +1266,23 @@
|
|
|
1231
1266
|
let savedDecoder = currentDecoder;
|
|
1232
1267
|
let savedSequentialMode = sequentialMode;
|
|
1233
1268
|
let savedRestoreMapsAsObject = restoreMapsAsObject;
|
|
1269
|
+
let savedArrayAllocationBudget = arrayAllocationBudget;
|
|
1270
|
+
fragmentEnd = 0;
|
|
1271
|
+
if (fragmentReadString) readFixedString = fragmentReadString;
|
|
1272
|
+
fragmentReadString = null;
|
|
1234
1273
|
restoreMapsAsObject = false;
|
|
1235
1274
|
try {
|
|
1236
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
|
|
1237
1282
|
} finally {
|
|
1283
|
+
fragmentEnd = savedFragmentEnd;
|
|
1284
|
+
fragmentReadString = savedFragmentReader;
|
|
1285
|
+
readFixedString = savedReadFixedString;
|
|
1238
1286
|
srcEnd = savedSrcEnd;
|
|
1239
1287
|
position = savedPosition;
|
|
1240
1288
|
stringPosition = savedStringPosition;
|
|
@@ -1244,11 +1292,13 @@
|
|
|
1244
1292
|
strings = savedStrings;
|
|
1245
1293
|
referenceMap = savedReferenceMap;
|
|
1246
1294
|
bundledStrings = savedBundledStrings;
|
|
1295
|
+
packedValues = savedPackedValues;
|
|
1247
1296
|
src = savedSrc;
|
|
1248
1297
|
sequentialMode = savedSequentialMode;
|
|
1249
1298
|
currentStructures = savedStructures;
|
|
1250
1299
|
currentDecoder = savedDecoder;
|
|
1251
1300
|
restoreMapsAsObject = savedRestoreMapsAsObject;
|
|
1301
|
+
arrayAllocationBudget = savedArrayAllocationBudget;
|
|
1252
1302
|
dataView = new DataView(src.buffer, src.byteOffset, src.byteLength);
|
|
1253
1303
|
}
|
|
1254
1304
|
}
|
|
@@ -1258,8 +1308,87 @@
|
|
|
1258
1308
|
currentStructures = null;
|
|
1259
1309
|
}
|
|
1260
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
|
+
|
|
1261
1388
|
function addExtension(extension) {
|
|
1262
1389
|
currentExtensions[extension.tag] = extension.decode;
|
|
1390
|
+
if (typeof extension.decode === 'function') registeredExtensions.set(extension.tag, extension.decode);
|
|
1391
|
+
else registeredExtensions.delete(extension.tag);
|
|
1263
1392
|
}
|
|
1264
1393
|
|
|
1265
1394
|
function setSizeLimits(limits) {
|
|
@@ -1291,6 +1420,7 @@
|
|
|
1291
1420
|
exports.Decoder = Decoder;
|
|
1292
1421
|
exports.FLOAT32_OPTIONS = FLOAT32_OPTIONS;
|
|
1293
1422
|
exports.Tag = Tag;
|
|
1423
|
+
exports._stream = _stream;
|
|
1294
1424
|
exports.addExtension = addExtension;
|
|
1295
1425
|
exports.checkedRead = checkedRead;
|
|
1296
1426
|
exports.clearSource = clearSource;
|