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/README.md
CHANGED
|
@@ -52,6 +52,10 @@ receivingStream.on('data', (data) => {
|
|
|
52
52
|
```
|
|
53
53
|
The `EncoderStream` and `DecoderStream` instances will have also the record structure extension enabled by default (see below).
|
|
54
54
|
|
|
55
|
+
When a chunk ends inside a value, `DecoderStream` keeps the bytes of that value until the chunks that complete it arrive. The `maxIncompleteBufferSize` option limits how many bytes it keeps for one incomplete value (default 64 MB, `0x4000000`); past that, the stream emits a "Maximum incomplete buffer size exceeded" error. A value larger than this limit still decodes when it arrives in a single chunk, so raise the limit for streams that carry larger values. If the input ends inside a value, the stream emits an "Unexpected end of CBOR data" error with `incomplete: true`. `decodeIter` takes the same option and reports an incomplete final value the same way.
|
|
56
|
+
|
|
57
|
+
Incomplete values are scanned incrementally without constructing objects, then decoded when their complete bytes arrive. This keeps decoding work linear in the size of a value split across many chunks. All built-in tags and custom extensions that consume exactly one tagged CBOR item are supported. An undocumented custom `handlesRead` decoder that consumes multiple sibling items cannot be framed on fragmented input; it throws an error naming the tag. Whole-buffer decoding of these extensions is unchanged.
|
|
58
|
+
|
|
55
59
|
### Iterables
|
|
56
60
|
In addition to using CBOR with streams, CBOR can also encode to an iterable that can be iterated as a sequence of binary chunks with `encodeAsIterable`, which facilitates progressive encoding:
|
|
57
61
|
```JavaScript
|
package/decode.js
CHANGED
|
@@ -13,7 +13,6 @@ const RECORD_INLINE_ID = 0xdfff // temporary first-come first-serve tag // propo
|
|
|
13
13
|
const BUNDLED_STRINGS_ID = 0xdff9
|
|
14
14
|
const PACKED_TABLE_TAG_ID = 51
|
|
15
15
|
const PACKED_REFERENCE_TAG_ID = 6
|
|
16
|
-
const STOP_CODE = {}
|
|
17
16
|
let maxArraySize = 112810000 // This is the maximum array size in V8. We would potentially detect and set it higher
|
|
18
17
|
// for JSC, but this is pretty large and should be sufficient for most use cases
|
|
19
18
|
let maxMapSize = 16810000 // JavaScript has a fixed maximum map size of about 16710000, but JS itself enforces this,
|
|
@@ -125,16 +124,11 @@ export class Decoder {
|
|
|
125
124
|
// re-entrant execution, save the state and restore it after we do this decode
|
|
126
125
|
return saveState(() => {
|
|
127
126
|
clearSource()
|
|
128
|
-
|
|
129
|
-
return this ? this.decode(source, end) : Decoder.prototype.decode.call(defaultOptions, source, end)
|
|
130
|
-
} catch (error) {
|
|
131
|
-
if (error && typeof error == 'object')
|
|
132
|
-
nestedDecodeErrors.add(error)
|
|
133
|
-
throw error
|
|
134
|
-
}
|
|
127
|
+
return this ? this.decode(source, end) : Decoder.prototype.decode.call(defaultOptions, source, end)
|
|
135
128
|
})
|
|
136
129
|
}
|
|
137
|
-
|
|
130
|
+
// past the view, the JS string readers would decode the missing bytes as zeros instead of failing as truncated
|
|
131
|
+
srcEnd = end > -1 && end < source.length ? end : source.length
|
|
138
132
|
position = 0
|
|
139
133
|
stringPosition = 0
|
|
140
134
|
srcStringEnd = 0
|
|
@@ -174,18 +168,28 @@ export class Decoder {
|
|
|
174
168
|
return checkedRead()
|
|
175
169
|
}
|
|
176
170
|
decodeMultiple(source, forEach) {
|
|
171
|
+
if (src) {
|
|
172
|
+
// Keep the complete nested sequence inside its own decoder state.
|
|
173
|
+
return saveState(() => {
|
|
174
|
+
clearSource()
|
|
175
|
+
return this ? this.decodeMultiple(source, forEach) : defaultDecoder.decodeMultiple(source, forEach)
|
|
176
|
+
})
|
|
177
|
+
}
|
|
177
178
|
let values, lastPosition = 0
|
|
178
179
|
try {
|
|
179
180
|
let size = source.length
|
|
180
181
|
sequentialMode = true
|
|
181
182
|
let value = this ? this.decode(source, size) : defaultDecoder.decode(source, size)
|
|
182
183
|
if (forEach) {
|
|
184
|
+
let end = position
|
|
183
185
|
if (forEach(value) === false) {
|
|
184
186
|
return
|
|
185
187
|
}
|
|
186
|
-
while(
|
|
187
|
-
lastPosition =
|
|
188
|
-
|
|
188
|
+
while(end < size) {
|
|
189
|
+
lastPosition = end
|
|
190
|
+
value = checkedRead()
|
|
191
|
+
end = position
|
|
192
|
+
if (forEach(value) === false) {
|
|
189
193
|
return
|
|
190
194
|
}
|
|
191
195
|
}
|
|
@@ -199,8 +203,10 @@ export class Decoder {
|
|
|
199
203
|
return values
|
|
200
204
|
}
|
|
201
205
|
} catch(error) {
|
|
202
|
-
error
|
|
203
|
-
|
|
206
|
+
if (error && (typeof error == 'object' || typeof error == 'function')) {
|
|
207
|
+
error.lastPosition = lastPosition
|
|
208
|
+
error.values = values
|
|
209
|
+
}
|
|
204
210
|
throw error
|
|
205
211
|
} finally {
|
|
206
212
|
sequentialMode = false
|
|
@@ -212,8 +218,10 @@ export function getPosition() {
|
|
|
212
218
|
return position
|
|
213
219
|
}
|
|
214
220
|
const nestedDecodeErrors = new WeakSet()
|
|
221
|
+
let arrayAllocationBudget
|
|
215
222
|
export function checkedRead() {
|
|
216
223
|
try {
|
|
224
|
+
arrayAllocationBudget = srcEnd - position
|
|
217
225
|
let result = read()
|
|
218
226
|
if (bundledStrings) {
|
|
219
227
|
if (position >= bundledStrings.postBundlePosition)
|
|
@@ -255,9 +263,21 @@ export function checkedRead() {
|
|
|
255
263
|
}
|
|
256
264
|
}
|
|
257
265
|
|
|
258
|
-
function
|
|
266
|
+
function readBreak() {
|
|
267
|
+
if (position < srcEnd && src[position] === 0xff) {
|
|
268
|
+
position++
|
|
269
|
+
return true
|
|
270
|
+
}
|
|
271
|
+
return false
|
|
272
|
+
}
|
|
273
|
+
// neededEnd, from a length check, is a position that the source must extend to before the item can decode. It is a
|
|
274
|
+
// lower bound for any completion of the source: every byte read before the check was within the source, so a longer
|
|
275
|
+
// source reaches the same check with the same length. Streams wait for it instead of decoding on every chunk.
|
|
276
|
+
function endOfCBORError(neededEnd) {
|
|
259
277
|
let error = new Error('Unexpected end of CBOR data')
|
|
260
278
|
error.incomplete = true
|
|
279
|
+
if (neededEnd)
|
|
280
|
+
error.neededEnd = neededEnd
|
|
261
281
|
return error
|
|
262
282
|
}
|
|
263
283
|
|
|
@@ -309,6 +329,9 @@ export function read() {
|
|
|
309
329
|
} else if (currentDecoder.int64AsNumber) {
|
|
310
330
|
token = dataView.getUint32(position) * 0x100000000
|
|
311
331
|
token += dataView.getUint32(position + 4)
|
|
332
|
+
position += 8
|
|
333
|
+
if (majorType === 1) return -1 - token
|
|
334
|
+
break
|
|
312
335
|
} else token = dataView.getBigUint64(position)
|
|
313
336
|
position += 8
|
|
314
337
|
break
|
|
@@ -320,10 +343,10 @@ export function read() {
|
|
|
320
343
|
throw new Error('Indefinite length not supported for byte or text strings')
|
|
321
344
|
case 4: // array
|
|
322
345
|
let array = []
|
|
323
|
-
let
|
|
324
|
-
while ((
|
|
346
|
+
let i = 0
|
|
347
|
+
while (!readBreak()) {
|
|
325
348
|
if (i >= maxArraySize) throw new Error(`Array length exceeds ${maxArraySize}`)
|
|
326
|
-
array[i++] =
|
|
349
|
+
array[i++] = read()
|
|
327
350
|
}
|
|
328
351
|
return majorType == 4 ? array : majorType == 3 ? array.join('') : Buffer.concat(array)
|
|
329
352
|
case 5: // map
|
|
@@ -332,13 +355,15 @@ export function read() {
|
|
|
332
355
|
let object = {}
|
|
333
356
|
let i = 0;
|
|
334
357
|
if (currentDecoder.keyMap) {
|
|
335
|
-
while((
|
|
358
|
+
while (!readBreak()) {
|
|
359
|
+
key = read()
|
|
336
360
|
if (i++ >= objectSizeLimit) throw new Error(`Property count exceeds ${objectSizeLimit}`)
|
|
337
361
|
object[safeKey(currentDecoder.decodeKey(key))] = read()
|
|
338
362
|
}
|
|
339
363
|
}
|
|
340
364
|
else {
|
|
341
|
-
while ((
|
|
365
|
+
while (!readBreak()) {
|
|
366
|
+
key = read()
|
|
342
367
|
if (i++ >= objectSizeLimit) throw new Error(`Property count exceeds ${objectSizeLimit}`)
|
|
343
368
|
object[safeKey(key)] = read()
|
|
344
369
|
}
|
|
@@ -352,7 +377,8 @@ export function read() {
|
|
|
352
377
|
let map = new Map()
|
|
353
378
|
if (currentDecoder.keyMap) {
|
|
354
379
|
let i = 0;
|
|
355
|
-
while((
|
|
380
|
+
while (!readBreak()) {
|
|
381
|
+
key = read()
|
|
356
382
|
if (i++ >= maxMapSize) {
|
|
357
383
|
throw new Error(`Map size exceeds ${maxMapSize}`);
|
|
358
384
|
}
|
|
@@ -361,7 +387,8 @@ export function read() {
|
|
|
361
387
|
}
|
|
362
388
|
else {
|
|
363
389
|
let i = 0;
|
|
364
|
-
while ((
|
|
390
|
+
while (!readBreak()) {
|
|
391
|
+
key = read()
|
|
365
392
|
if (i++ >= maxMapSize) {
|
|
366
393
|
throw new Error(`Map size exceeds ${maxMapSize}`);
|
|
367
394
|
}
|
|
@@ -371,7 +398,8 @@ export function read() {
|
|
|
371
398
|
return map
|
|
372
399
|
}
|
|
373
400
|
case 7:
|
|
374
|
-
|
|
401
|
+
// readBreak consumes every break that ends an indefinite-length item
|
|
402
|
+
throw new Error('Unexpected break stop code outside of an indefinite-length item')
|
|
375
403
|
default:
|
|
376
404
|
throw new Error('Invalid major type for indefinite length ' + majorType)
|
|
377
405
|
}
|
|
@@ -385,13 +413,13 @@ export function read() {
|
|
|
385
413
|
case 1: // negative int
|
|
386
414
|
return ~token
|
|
387
415
|
case 2: // buffer
|
|
388
|
-
if (token > srcEnd - position) throw endOfCBORError()
|
|
416
|
+
if (token > srcEnd - position) throw endOfCBORError(position + token)
|
|
389
417
|
return readBin(token)
|
|
390
418
|
case 3: // string
|
|
391
419
|
if (srcStringEnd >= position) { // already extracted, which only covers complete strings
|
|
392
420
|
return srcString.slice(position - srcStringStart, (position += token) - srcStringStart)
|
|
393
421
|
}
|
|
394
|
-
if (token > srcEnd - position) throw endOfCBORError()
|
|
422
|
+
if (token > srcEnd - position) throw endOfCBORError(position + token)
|
|
395
423
|
if (srcStringEnd == 0 && srcEnd < 140 && token < 32) {
|
|
396
424
|
// for small blocks, avoiding the overhead of the extract call is helpful
|
|
397
425
|
let string = token < 16 ? shortStringInJS(token) : longStringInJS(token)
|
|
@@ -403,7 +431,9 @@ export function read() {
|
|
|
403
431
|
if (token > maxArraySize) throw new Error(`Array length exceeds ${maxArraySize}`)
|
|
404
432
|
// every element occupies at least one byte, so a length beyond what remains in the source can
|
|
405
433
|
// never be satisfied; check before allocating so a tiny header can not force a huge allocation
|
|
406
|
-
|
|
434
|
+
// nested arrays each pass the check above yet still preallocate, so also spend from a
|
|
435
|
+
// per-decode budget of the remaining bytes; the summed lengths of any valid tree stay under it
|
|
436
|
+
if (token > srcEnd - position || (arrayAllocationBudget -= token) < 0) throw endOfCBORError(position + token)
|
|
407
437
|
let array = new Array(token)
|
|
408
438
|
//if (currentDecoder.keyMap) for (let i = 0; i < token; i++) array[i] = currentDecoder.decodeKey(read())
|
|
409
439
|
//else
|
|
@@ -412,7 +442,7 @@ export function read() {
|
|
|
412
442
|
case 5: // map
|
|
413
443
|
if (currentDecoder.mapsAsObjects) {
|
|
414
444
|
if (token > objectSizeLimit) throw new Error(`Property count exceeds ${objectSizeLimit}`)
|
|
415
|
-
if (token > (srcEnd - position) / 2) throw endOfCBORError() // each entry needs at least two bytes
|
|
445
|
+
if (token > (srcEnd - position) / 2) throw endOfCBORError(position + 2 * token) // each entry needs at least two bytes
|
|
416
446
|
let object = {}
|
|
417
447
|
if (currentDecoder.keyMap) for (let i = 0; i < token; i++) object[safeKey(currentDecoder.decodeKey(read()))] = read()
|
|
418
448
|
else for (let i = 0; i < token; i++) object[safeKey(read())] = read()
|
|
@@ -423,7 +453,7 @@ export function read() {
|
|
|
423
453
|
restoreMapsAsObject = false
|
|
424
454
|
}
|
|
425
455
|
if (token > maxMapSize) throw new Error(`Map size exceeds ${maxMapSize}`)
|
|
426
|
-
if (token > (srcEnd - position) / 2) throw endOfCBORError()
|
|
456
|
+
if (token > (srcEnd - position) / 2) throw endOfCBORError(position + 2 * token)
|
|
427
457
|
let map = new Map()
|
|
428
458
|
if (currentDecoder.keyMap) for (let i = 0; i < token; i++) map.set(currentDecoder.decodeKey(read()),read())
|
|
429
459
|
else for (let i = 0; i < token; i++) map.set(read(), read())
|
|
@@ -1016,6 +1046,7 @@ currentExtensions[LEGACY_RECORD_INLINE_ID] = (data) => {
|
|
|
1016
1046
|
}
|
|
1017
1047
|
return object
|
|
1018
1048
|
}
|
|
1049
|
+
const legacyRecordDefinition = currentExtensions[LEGACY_RECORD_INLINE_ID]
|
|
1019
1050
|
currentExtensions[14] = (value) => {
|
|
1020
1051
|
if (bundledStrings)
|
|
1021
1052
|
return bundledStrings[0].slice(bundledStrings.position0, bundledStrings.position0 += value)
|
|
@@ -1288,6 +1319,9 @@ function loadShared() {
|
|
|
1288
1319
|
}
|
|
1289
1320
|
|
|
1290
1321
|
function saveState(callback) {
|
|
1322
|
+
let savedFragmentEnd = fragmentEnd
|
|
1323
|
+
let savedFragmentReader = fragmentReadString
|
|
1324
|
+
let savedReadFixedString = readFixedString
|
|
1291
1325
|
let savedSrcEnd = srcEnd
|
|
1292
1326
|
let savedPosition = position
|
|
1293
1327
|
let savedStringPosition = stringPosition
|
|
@@ -1297,6 +1331,7 @@ function saveState(callback) {
|
|
|
1297
1331
|
let savedStrings = strings
|
|
1298
1332
|
let savedReferenceMap = referenceMap
|
|
1299
1333
|
let savedBundledStrings = bundledStrings
|
|
1334
|
+
let savedPackedValues = packedValues
|
|
1300
1335
|
|
|
1301
1336
|
// TODO: We may need to revisit this if we do more external calls to user code (since it could be slow)
|
|
1302
1337
|
let savedSrc = new Uint8Array(src.slice(0, srcEnd)) // we copy the data in case it changes while external data is processed
|
|
@@ -1304,10 +1339,23 @@ function saveState(callback) {
|
|
|
1304
1339
|
let savedDecoder = currentDecoder
|
|
1305
1340
|
let savedSequentialMode = sequentialMode
|
|
1306
1341
|
let savedRestoreMapsAsObject = restoreMapsAsObject
|
|
1342
|
+
let savedArrayAllocationBudget = arrayAllocationBudget
|
|
1343
|
+
fragmentEnd = 0
|
|
1344
|
+
if (fragmentReadString) readFixedString = fragmentReadString
|
|
1345
|
+
fragmentReadString = null
|
|
1307
1346
|
restoreMapsAsObject = false
|
|
1308
1347
|
try {
|
|
1309
1348
|
return callback()
|
|
1349
|
+
} catch (error) {
|
|
1350
|
+
// the callback decodes its own source (a shared structures blob, or an extension's embedded data), so a
|
|
1351
|
+
// truncation there cannot be completed by more of the outer input; checkedRead() clears the flag
|
|
1352
|
+
if (error && (typeof error == 'object' || typeof error == 'function'))
|
|
1353
|
+
nestedDecodeErrors.add(error)
|
|
1354
|
+
throw error
|
|
1310
1355
|
} finally {
|
|
1356
|
+
fragmentEnd = savedFragmentEnd
|
|
1357
|
+
fragmentReadString = savedFragmentReader
|
|
1358
|
+
readFixedString = savedReadFixedString
|
|
1311
1359
|
srcEnd = savedSrcEnd
|
|
1312
1360
|
position = savedPosition
|
|
1313
1361
|
stringPosition = savedStringPosition
|
|
@@ -1317,11 +1365,13 @@ function saveState(callback) {
|
|
|
1317
1365
|
strings = savedStrings
|
|
1318
1366
|
referenceMap = savedReferenceMap
|
|
1319
1367
|
bundledStrings = savedBundledStrings
|
|
1368
|
+
packedValues = savedPackedValues
|
|
1320
1369
|
src = savedSrc
|
|
1321
1370
|
sequentialMode = savedSequentialMode
|
|
1322
1371
|
currentStructures = savedStructures
|
|
1323
1372
|
currentDecoder = savedDecoder
|
|
1324
1373
|
restoreMapsAsObject = savedRestoreMapsAsObject
|
|
1374
|
+
arrayAllocationBudget = savedArrayAllocationBudget
|
|
1325
1375
|
dataView = new DataView(src.buffer, src.byteOffset, src.byteLength)
|
|
1326
1376
|
}
|
|
1327
1377
|
}
|
|
@@ -1331,8 +1381,87 @@ export function clearSource() {
|
|
|
1331
1381
|
currentStructures = null
|
|
1332
1382
|
}
|
|
1333
1383
|
|
|
1384
|
+
const registeredExtensions = new Map()
|
|
1385
|
+
let fragmentEnd = 0
|
|
1386
|
+
let fragmentReadString
|
|
1387
|
+
|
|
1388
|
+
const builtinScanTags = new Map([[LEGACY_RECORD_INLINE_ID, legacyRecordDefinition], [259, currentExtensions[259]]])
|
|
1389
|
+
function isBuiltinScanTag(tag) {
|
|
1390
|
+
return builtinScanTags.has(tag) && currentExtensions[tag] === builtinScanTags.get(tag)
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
function getScanTag(tag, decoder) {
|
|
1394
|
+
return tag >= BUNDLED_STRINGS_ID && decoder.structures && decoder.structures[tag & 0x1fff]
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
function checkScanContainer(major, length, mapsAsObjects) {
|
|
1398
|
+
if (major === 4) {
|
|
1399
|
+
if (length > maxArraySize) throw new Error(`Array length exceeds ${maxArraySize}`)
|
|
1400
|
+
} else if (mapsAsObjects) {
|
|
1401
|
+
if (length > objectSizeLimit) throw new Error(`Property count exceeds ${objectSizeLimit}`)
|
|
1402
|
+
} else if (length > maxMapSize) throw new Error(`Map size exceeds ${maxMapSize}`)
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
function decodeScanned(decoder, source, end) {
|
|
1406
|
+
const boundStrings = isNativeAccelerationEnabled && end < source.length
|
|
1407
|
+
if (!registeredExtensions.size && !boundStrings) return decoder.decodeMultiple(source)
|
|
1408
|
+
const savedEnd = fragmentEnd
|
|
1409
|
+
const savedReader = readFixedString
|
|
1410
|
+
const savedFragmentReader = fragmentReadString
|
|
1411
|
+
const originals = []
|
|
1412
|
+
fragmentEnd = end
|
|
1413
|
+
fragmentReadString = savedReader
|
|
1414
|
+
try {
|
|
1415
|
+
if (boundStrings) readFixedString = (length) => {
|
|
1416
|
+
if (!fragmentEnd || position >= fragmentEnd) return savedReader(length)
|
|
1417
|
+
const savedSrcEnd = srcEnd
|
|
1418
|
+
srcEnd = fragmentEnd
|
|
1419
|
+
try { return savedReader(length) } finally { srcEnd = savedSrcEnd }
|
|
1420
|
+
}
|
|
1421
|
+
for (const [tag, extension] of registeredExtensions) {
|
|
1422
|
+
if (!extension.handlesRead) continue
|
|
1423
|
+
const original = currentExtensions[tag]
|
|
1424
|
+
const guarded = (read) => {
|
|
1425
|
+
if (!fragmentEnd || position >= fragmentEnd) return extension(read)
|
|
1426
|
+
let calls = 0, framingError
|
|
1427
|
+
const value = extension(() => {
|
|
1428
|
+
if (calls++) {
|
|
1429
|
+
framingError = new Error('Tag ' + tag + ': fragmented decoding cannot frame multi-item handlesRead extensions')
|
|
1430
|
+
throw framingError
|
|
1431
|
+
}
|
|
1432
|
+
return read()
|
|
1433
|
+
})
|
|
1434
|
+
if (framingError) throw framingError
|
|
1435
|
+
return value
|
|
1436
|
+
}
|
|
1437
|
+
guarded.handlesRead = true
|
|
1438
|
+
originals.push([tag, original, guarded])
|
|
1439
|
+
currentExtensions[tag] = guarded
|
|
1440
|
+
}
|
|
1441
|
+
return decoder.decodeMultiple(source)
|
|
1442
|
+
} finally {
|
|
1443
|
+
fragmentEnd = savedEnd
|
|
1444
|
+
fragmentReadString = savedFragmentReader
|
|
1445
|
+
readFixedString = savedReader
|
|
1446
|
+
for (const [tag, original, guarded] of originals) {
|
|
1447
|
+
if (currentExtensions[tag] === guarded) currentExtensions[tag] = original
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1452
|
+
// Internal streaming hooks; not part of the public decoder API.
|
|
1453
|
+
export const _stream = {
|
|
1454
|
+
getTag: getScanTag,
|
|
1455
|
+
isBuiltinTag: isBuiltinScanTag,
|
|
1456
|
+
checkContainer: checkScanContainer,
|
|
1457
|
+
decode: decodeScanned,
|
|
1458
|
+
get readThreshold() { return inlineObjectReadThreshold }
|
|
1459
|
+
}
|
|
1460
|
+
|
|
1334
1461
|
export function addExtension(extension) {
|
|
1335
1462
|
currentExtensions[extension.tag] = extension.decode
|
|
1463
|
+
if (typeof extension.decode === 'function') registeredExtensions.set(extension.tag, extension.decode)
|
|
1464
|
+
else registeredExtensions.delete(extension.tag)
|
|
1336
1465
|
}
|
|
1337
1466
|
|
|
1338
1467
|
export function setSizeLimits(limits) {
|