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/index-no-eval.cjs
CHANGED
|
@@ -16,7 +16,6 @@
|
|
|
16
16
|
const RECORD_INLINE_ID = 0xdfff; // temporary first-come first-serve tag // proposed tag: 0x7265 // 're'
|
|
17
17
|
const BUNDLED_STRINGS_ID = 0xdff9;
|
|
18
18
|
const PACKED_REFERENCE_TAG_ID = 6;
|
|
19
|
-
const STOP_CODE = {};
|
|
20
19
|
let maxArraySize = 112810000; // This is the maximum array size in V8. We would potentially detect and set it higher
|
|
21
20
|
// for JSC, but this is pretty large and should be sufficient for most use cases
|
|
22
21
|
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
|
-
|
|
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
|
-
|
|
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
|
srcStringEnd = 0;
|
|
141
135
|
srcString = null;
|
|
@@ -173,18 +167,28 @@
|
|
|
173
167
|
return checkedRead()
|
|
174
168
|
}
|
|
175
169
|
decodeMultiple(source, forEach) {
|
|
170
|
+
if (src) {
|
|
171
|
+
// Keep the complete nested sequence inside its own decoder state.
|
|
172
|
+
return saveState(() => {
|
|
173
|
+
clearSource();
|
|
174
|
+
return this ? this.decodeMultiple(source, forEach) : defaultDecoder.decodeMultiple(source, forEach)
|
|
175
|
+
})
|
|
176
|
+
}
|
|
176
177
|
let values, lastPosition = 0;
|
|
177
178
|
try {
|
|
178
179
|
let size = source.length;
|
|
179
180
|
sequentialMode = true;
|
|
180
181
|
let value = this ? this.decode(source, size) : defaultDecoder.decode(source, size);
|
|
181
182
|
if (forEach) {
|
|
183
|
+
let end = position$1;
|
|
182
184
|
if (forEach(value) === false) {
|
|
183
185
|
return
|
|
184
186
|
}
|
|
185
|
-
while(
|
|
186
|
-
lastPosition =
|
|
187
|
-
|
|
187
|
+
while(end < size) {
|
|
188
|
+
lastPosition = end;
|
|
189
|
+
value = checkedRead();
|
|
190
|
+
end = position$1;
|
|
191
|
+
if (forEach(value) === false) {
|
|
188
192
|
return
|
|
189
193
|
}
|
|
190
194
|
}
|
|
@@ -198,8 +202,10 @@
|
|
|
198
202
|
return values
|
|
199
203
|
}
|
|
200
204
|
} catch(error) {
|
|
201
|
-
error
|
|
202
|
-
|
|
205
|
+
if (error && (typeof error == 'object' || typeof error == 'function')) {
|
|
206
|
+
error.lastPosition = lastPosition;
|
|
207
|
+
error.values = values;
|
|
208
|
+
}
|
|
203
209
|
throw error
|
|
204
210
|
} finally {
|
|
205
211
|
sequentialMode = false;
|
|
@@ -208,8 +214,10 @@
|
|
|
208
214
|
}
|
|
209
215
|
}
|
|
210
216
|
const nestedDecodeErrors = new WeakSet();
|
|
217
|
+
let arrayAllocationBudget;
|
|
211
218
|
function checkedRead() {
|
|
212
219
|
try {
|
|
220
|
+
arrayAllocationBudget = srcEnd - position$1;
|
|
213
221
|
let result = read();
|
|
214
222
|
if (bundledStrings$1) {
|
|
215
223
|
if (position$1 >= bundledStrings$1.postBundlePosition)
|
|
@@ -251,9 +259,21 @@
|
|
|
251
259
|
}
|
|
252
260
|
}
|
|
253
261
|
|
|
254
|
-
function
|
|
262
|
+
function readBreak() {
|
|
263
|
+
if (position$1 < srcEnd && src[position$1] === 0xff) {
|
|
264
|
+
position$1++;
|
|
265
|
+
return true
|
|
266
|
+
}
|
|
267
|
+
return false
|
|
268
|
+
}
|
|
269
|
+
// neededEnd, from a length check, is a position that the source must extend to before the item can decode. It is a
|
|
270
|
+
// lower bound for any completion of the source: every byte read before the check was within the source, so a longer
|
|
271
|
+
// source reaches the same check with the same length. Streams wait for it instead of decoding on every chunk.
|
|
272
|
+
function endOfCBORError(neededEnd) {
|
|
255
273
|
let error = new Error('Unexpected end of CBOR data');
|
|
256
274
|
error.incomplete = true;
|
|
275
|
+
if (neededEnd)
|
|
276
|
+
error.neededEnd = neededEnd;
|
|
257
277
|
return error
|
|
258
278
|
}
|
|
259
279
|
|
|
@@ -305,6 +325,9 @@
|
|
|
305
325
|
} else if (currentDecoder.int64AsNumber) {
|
|
306
326
|
token = dataView.getUint32(position$1) * 0x100000000;
|
|
307
327
|
token += dataView.getUint32(position$1 + 4);
|
|
328
|
+
position$1 += 8;
|
|
329
|
+
if (majorType === 1) return -1 - token
|
|
330
|
+
break
|
|
308
331
|
} else token = dataView.getBigUint64(position$1);
|
|
309
332
|
position$1 += 8;
|
|
310
333
|
break
|
|
@@ -316,10 +339,10 @@
|
|
|
316
339
|
throw new Error('Indefinite length not supported for byte or text strings')
|
|
317
340
|
case 4: // array
|
|
318
341
|
let array = [];
|
|
319
|
-
let
|
|
320
|
-
while ((
|
|
342
|
+
let i = 0;
|
|
343
|
+
while (!readBreak()) {
|
|
321
344
|
if (i >= maxArraySize) throw new Error(`Array length exceeds ${maxArraySize}`)
|
|
322
|
-
array[i++] =
|
|
345
|
+
array[i++] = read();
|
|
323
346
|
}
|
|
324
347
|
return majorType == 4 ? array : majorType == 3 ? array.join('') : Buffer.concat(array)
|
|
325
348
|
case 5: // map
|
|
@@ -328,13 +351,15 @@
|
|
|
328
351
|
let object = {};
|
|
329
352
|
let i = 0;
|
|
330
353
|
if (currentDecoder.keyMap) {
|
|
331
|
-
while((
|
|
354
|
+
while (!readBreak()) {
|
|
355
|
+
key = read();
|
|
332
356
|
if (i++ >= objectSizeLimit) throw new Error(`Property count exceeds ${objectSizeLimit}`)
|
|
333
357
|
object[safeKey(currentDecoder.decodeKey(key))] = read();
|
|
334
358
|
}
|
|
335
359
|
}
|
|
336
360
|
else {
|
|
337
|
-
while ((
|
|
361
|
+
while (!readBreak()) {
|
|
362
|
+
key = read();
|
|
338
363
|
if (i++ >= objectSizeLimit) throw new Error(`Property count exceeds ${objectSizeLimit}`)
|
|
339
364
|
object[safeKey(key)] = read();
|
|
340
365
|
}
|
|
@@ -348,7 +373,8 @@
|
|
|
348
373
|
let map = new Map();
|
|
349
374
|
if (currentDecoder.keyMap) {
|
|
350
375
|
let i = 0;
|
|
351
|
-
while((
|
|
376
|
+
while (!readBreak()) {
|
|
377
|
+
key = read();
|
|
352
378
|
if (i++ >= maxMapSize) {
|
|
353
379
|
throw new Error(`Map size exceeds ${maxMapSize}`);
|
|
354
380
|
}
|
|
@@ -357,7 +383,8 @@
|
|
|
357
383
|
}
|
|
358
384
|
else {
|
|
359
385
|
let i = 0;
|
|
360
|
-
while ((
|
|
386
|
+
while (!readBreak()) {
|
|
387
|
+
key = read();
|
|
361
388
|
if (i++ >= maxMapSize) {
|
|
362
389
|
throw new Error(`Map size exceeds ${maxMapSize}`);
|
|
363
390
|
}
|
|
@@ -367,7 +394,8 @@
|
|
|
367
394
|
return map
|
|
368
395
|
}
|
|
369
396
|
case 7:
|
|
370
|
-
|
|
397
|
+
// readBreak consumes every break that ends an indefinite-length item
|
|
398
|
+
throw new Error('Unexpected break stop code outside of an indefinite-length item')
|
|
371
399
|
default:
|
|
372
400
|
throw new Error('Invalid major type for indefinite length ' + majorType)
|
|
373
401
|
}
|
|
@@ -381,13 +409,13 @@
|
|
|
381
409
|
case 1: // negative int
|
|
382
410
|
return ~token
|
|
383
411
|
case 2: // buffer
|
|
384
|
-
if (token > srcEnd - position$1) throw endOfCBORError()
|
|
412
|
+
if (token > srcEnd - position$1) throw endOfCBORError(position$1 + token)
|
|
385
413
|
return readBin(token)
|
|
386
414
|
case 3: // string
|
|
387
415
|
if (srcStringEnd >= position$1) { // already extracted, which only covers complete strings
|
|
388
416
|
return srcString.slice(position$1 - srcStringStart, (position$1 += token) - srcStringStart)
|
|
389
417
|
}
|
|
390
|
-
if (token > srcEnd - position$1) throw endOfCBORError()
|
|
418
|
+
if (token > srcEnd - position$1) throw endOfCBORError(position$1 + token)
|
|
391
419
|
if (srcStringEnd == 0 && srcEnd < 140 && token < 32) {
|
|
392
420
|
// for small blocks, avoiding the overhead of the extract call is helpful
|
|
393
421
|
let string = token < 16 ? shortStringInJS(token) : longStringInJS(token);
|
|
@@ -399,7 +427,9 @@
|
|
|
399
427
|
if (token > maxArraySize) throw new Error(`Array length exceeds ${maxArraySize}`)
|
|
400
428
|
// every element occupies at least one byte, so a length beyond what remains in the source can
|
|
401
429
|
// never be satisfied; check before allocating so a tiny header can not force a huge allocation
|
|
402
|
-
|
|
430
|
+
// nested arrays each pass the check above yet still preallocate, so also spend from a
|
|
431
|
+
// per-decode budget of the remaining bytes; the summed lengths of any valid tree stay under it
|
|
432
|
+
if (token > srcEnd - position$1 || (arrayAllocationBudget -= token) < 0) throw endOfCBORError(position$1 + token)
|
|
403
433
|
let array = new Array(token);
|
|
404
434
|
//if (currentDecoder.keyMap) for (let i = 0; i < token; i++) array[i] = currentDecoder.decodeKey(read())
|
|
405
435
|
//else
|
|
@@ -408,7 +438,7 @@
|
|
|
408
438
|
case 5: // map
|
|
409
439
|
if (currentDecoder.mapsAsObjects) {
|
|
410
440
|
if (token > objectSizeLimit) throw new Error(`Property count exceeds ${objectSizeLimit}`)
|
|
411
|
-
if (token > (srcEnd - position$1) / 2) throw endOfCBORError() // each entry needs at least two bytes
|
|
441
|
+
if (token > (srcEnd - position$1) / 2) throw endOfCBORError(position$1 + 2 * token) // each entry needs at least two bytes
|
|
412
442
|
let object = {};
|
|
413
443
|
if (currentDecoder.keyMap) for (let i = 0; i < token; i++) object[safeKey(currentDecoder.decodeKey(read()))] = read();
|
|
414
444
|
else for (let i = 0; i < token; i++) object[safeKey(read())] = read();
|
|
@@ -419,7 +449,7 @@
|
|
|
419
449
|
restoreMapsAsObject = false;
|
|
420
450
|
}
|
|
421
451
|
if (token > maxMapSize) throw new Error(`Map size exceeds ${maxMapSize}`)
|
|
422
|
-
if (token > (srcEnd - position$1) / 2) throw endOfCBORError()
|
|
452
|
+
if (token > (srcEnd - position$1) / 2) throw endOfCBORError(position$1 + 2 * token)
|
|
423
453
|
let map = new Map();
|
|
424
454
|
if (currentDecoder.keyMap) for (let i = 0; i < token; i++) map.set(currentDecoder.decodeKey(read()),read());
|
|
425
455
|
else for (let i = 0; i < token; i++) map.set(read(), read());
|
|
@@ -900,6 +930,7 @@
|
|
|
900
930
|
}
|
|
901
931
|
return object
|
|
902
932
|
};
|
|
933
|
+
const legacyRecordDefinition = currentExtensions[LEGACY_RECORD_INLINE_ID];
|
|
903
934
|
currentExtensions[14] = (value) => {
|
|
904
935
|
if (bundledStrings$1)
|
|
905
936
|
return bundledStrings$1[0].slice(bundledStrings$1.position0, bundledStrings$1.position0 += value)
|
|
@@ -1172,6 +1203,9 @@
|
|
|
1172
1203
|
}
|
|
1173
1204
|
|
|
1174
1205
|
function saveState(callback) {
|
|
1206
|
+
let savedFragmentEnd = fragmentEnd;
|
|
1207
|
+
let savedFragmentReader = fragmentReadString;
|
|
1208
|
+
let savedReadFixedString = readFixedString;
|
|
1175
1209
|
let savedSrcEnd = srcEnd;
|
|
1176
1210
|
let savedPosition = position$1;
|
|
1177
1211
|
let savedSrcStringStart = srcStringStart;
|
|
@@ -1179,6 +1213,7 @@
|
|
|
1179
1213
|
let savedSrcString = srcString;
|
|
1180
1214
|
let savedReferenceMap = referenceMap;
|
|
1181
1215
|
let savedBundledStrings = bundledStrings$1;
|
|
1216
|
+
let savedPackedValues = packedValues;
|
|
1182
1217
|
|
|
1183
1218
|
// TODO: We may need to revisit this if we do more external calls to user code (since it could be slow)
|
|
1184
1219
|
let savedSrc = new Uint8Array(src.slice(0, srcEnd)); // we copy the data in case it changes while external data is processed
|
|
@@ -1186,10 +1221,23 @@
|
|
|
1186
1221
|
let savedDecoder = currentDecoder;
|
|
1187
1222
|
let savedSequentialMode = sequentialMode;
|
|
1188
1223
|
let savedRestoreMapsAsObject = restoreMapsAsObject;
|
|
1224
|
+
let savedArrayAllocationBudget = arrayAllocationBudget;
|
|
1225
|
+
fragmentEnd = 0;
|
|
1226
|
+
if (fragmentReadString) readFixedString = fragmentReadString;
|
|
1227
|
+
fragmentReadString = null;
|
|
1189
1228
|
restoreMapsAsObject = false;
|
|
1190
1229
|
try {
|
|
1191
1230
|
return callback()
|
|
1231
|
+
} catch (error) {
|
|
1232
|
+
// the callback decodes its own source (a shared structures blob, or an extension's embedded data), so a
|
|
1233
|
+
// truncation there cannot be completed by more of the outer input; checkedRead() clears the flag
|
|
1234
|
+
if (error && (typeof error == 'object' || typeof error == 'function'))
|
|
1235
|
+
nestedDecodeErrors.add(error);
|
|
1236
|
+
throw error
|
|
1192
1237
|
} finally {
|
|
1238
|
+
fragmentEnd = savedFragmentEnd;
|
|
1239
|
+
fragmentReadString = savedFragmentReader;
|
|
1240
|
+
readFixedString = savedReadFixedString;
|
|
1193
1241
|
srcEnd = savedSrcEnd;
|
|
1194
1242
|
position$1 = savedPosition;
|
|
1195
1243
|
srcStringStart = savedSrcStringStart;
|
|
@@ -1197,11 +1245,13 @@
|
|
|
1197
1245
|
srcString = savedSrcString;
|
|
1198
1246
|
referenceMap = savedReferenceMap;
|
|
1199
1247
|
bundledStrings$1 = savedBundledStrings;
|
|
1248
|
+
packedValues = savedPackedValues;
|
|
1200
1249
|
src = savedSrc;
|
|
1201
1250
|
sequentialMode = savedSequentialMode;
|
|
1202
1251
|
currentStructures = savedStructures;
|
|
1203
1252
|
currentDecoder = savedDecoder;
|
|
1204
1253
|
restoreMapsAsObject = savedRestoreMapsAsObject;
|
|
1254
|
+
arrayAllocationBudget = savedArrayAllocationBudget;
|
|
1205
1255
|
dataView = new DataView(src.buffer, src.byteOffset, src.byteLength);
|
|
1206
1256
|
}
|
|
1207
1257
|
}
|
|
@@ -1211,8 +1261,82 @@
|
|
|
1211
1261
|
currentStructures = null;
|
|
1212
1262
|
}
|
|
1213
1263
|
|
|
1264
|
+
const registeredExtensions = new Map();
|
|
1265
|
+
let fragmentEnd = 0;
|
|
1266
|
+
let fragmentReadString;
|
|
1267
|
+
|
|
1268
|
+
const builtinScanTags = new Map([[LEGACY_RECORD_INLINE_ID, legacyRecordDefinition], [259, currentExtensions[259]]]);
|
|
1269
|
+
function isBuiltinScanTag(tag) {
|
|
1270
|
+
return builtinScanTags.has(tag) && currentExtensions[tag] === builtinScanTags.get(tag)
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
function getScanTag(tag, decoder) {
|
|
1274
|
+
return tag >= BUNDLED_STRINGS_ID && decoder.structures && decoder.structures[tag & 0x1fff]
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
function checkScanContainer(major, length, mapsAsObjects) {
|
|
1278
|
+
if (major === 4) {
|
|
1279
|
+
if (length > maxArraySize) throw new Error(`Array length exceeds ${maxArraySize}`)
|
|
1280
|
+
} else if (mapsAsObjects) {
|
|
1281
|
+
if (length > objectSizeLimit) throw new Error(`Property count exceeds ${objectSizeLimit}`)
|
|
1282
|
+
} else if (length > maxMapSize) throw new Error(`Map size exceeds ${maxMapSize}`)
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
function decodeScanned(decoder, source, end) {
|
|
1286
|
+
const boundStrings = isNativeAccelerationEnabled ;
|
|
1287
|
+
if (!registeredExtensions.size && !boundStrings) return decoder.decodeMultiple(source)
|
|
1288
|
+
const savedEnd = fragmentEnd;
|
|
1289
|
+
const savedReader = readFixedString;
|
|
1290
|
+
const savedFragmentReader = fragmentReadString;
|
|
1291
|
+
const originals = [];
|
|
1292
|
+
fragmentEnd = end;
|
|
1293
|
+
fragmentReadString = savedReader;
|
|
1294
|
+
try {
|
|
1295
|
+
if (boundStrings) ;
|
|
1296
|
+
for (const [tag, extension] of registeredExtensions) {
|
|
1297
|
+
if (!extension.handlesRead) continue
|
|
1298
|
+
const original = currentExtensions[tag];
|
|
1299
|
+
const guarded = (read) => {
|
|
1300
|
+
if (!fragmentEnd || position$1 >= fragmentEnd) return extension(read)
|
|
1301
|
+
let calls = 0, framingError;
|
|
1302
|
+
const value = extension(() => {
|
|
1303
|
+
if (calls++) {
|
|
1304
|
+
framingError = new Error('Tag ' + tag + ': fragmented decoding cannot frame multi-item handlesRead extensions');
|
|
1305
|
+
throw framingError
|
|
1306
|
+
}
|
|
1307
|
+
return read()
|
|
1308
|
+
});
|
|
1309
|
+
if (framingError) throw framingError
|
|
1310
|
+
return value
|
|
1311
|
+
};
|
|
1312
|
+
guarded.handlesRead = true;
|
|
1313
|
+
originals.push([tag, original, guarded]);
|
|
1314
|
+
currentExtensions[tag] = guarded;
|
|
1315
|
+
}
|
|
1316
|
+
return decoder.decodeMultiple(source)
|
|
1317
|
+
} finally {
|
|
1318
|
+
fragmentEnd = savedEnd;
|
|
1319
|
+
fragmentReadString = savedFragmentReader;
|
|
1320
|
+
readFixedString = savedReader;
|
|
1321
|
+
for (const [tag, original, guarded] of originals) {
|
|
1322
|
+
if (currentExtensions[tag] === guarded) currentExtensions[tag] = original;
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
// Internal streaming hooks; not part of the public decoder API.
|
|
1328
|
+
const _stream = {
|
|
1329
|
+
getTag: getScanTag,
|
|
1330
|
+
isBuiltinTag: isBuiltinScanTag,
|
|
1331
|
+
checkContainer: checkScanContainer,
|
|
1332
|
+
decode: decodeScanned,
|
|
1333
|
+
get readThreshold() { return inlineObjectReadThreshold }
|
|
1334
|
+
};
|
|
1335
|
+
|
|
1214
1336
|
function addExtension$1(extension) {
|
|
1215
1337
|
currentExtensions[extension.tag] = extension.decode;
|
|
1338
|
+
if (typeof extension.decode === 'function') registeredExtensions.set(extension.tag, extension.decode);
|
|
1339
|
+
else registeredExtensions.delete(extension.tag);
|
|
1216
1340
|
}
|
|
1217
1341
|
|
|
1218
1342
|
function setSizeLimits(limits) {
|
|
@@ -1812,6 +1936,11 @@
|
|
|
1812
1936
|
return encode(json)
|
|
1813
1937
|
}
|
|
1814
1938
|
|
|
1939
|
+
// not in extensions, so class instances that reach writeObject do not pay another instanceof
|
|
1940
|
+
if (constructor === DataView) {
|
|
1941
|
+
writeBuffer(new Uint8Array(value.buffer, value.byteOffset, value.byteLength), makeRoom);
|
|
1942
|
+
return
|
|
1943
|
+
}
|
|
1815
1944
|
// no extension found, write as a plain object
|
|
1816
1945
|
writeObject(value);
|
|
1817
1946
|
}
|
|
@@ -1905,8 +2034,22 @@
|
|
|
1905
2034
|
size++;
|
|
1906
2035
|
}
|
|
1907
2036
|
}
|
|
1908
|
-
|
|
1909
|
-
|
|
2037
|
+
if (size < 0x10000) {
|
|
2038
|
+
// fits in a map-16 (uint16) count: back-patch the reserved 2 bytes
|
|
2039
|
+
target[objectOffset++ + start] = size >> 8;
|
|
2040
|
+
target[objectOffset + start] = size & 0xff;
|
|
2041
|
+
} else {
|
|
2042
|
+
// too many entries for a map-16 count: widen the already-written header
|
|
2043
|
+
// to map-32, mirroring writeArrayHeader's 0x9a/setUint32 path. Shift the
|
|
2044
|
+
// encoded entries 2 bytes forward to make room for the wider (uint32) count.
|
|
2045
|
+
if (position + 2 > safeEnd)
|
|
2046
|
+
makeRoom(position + 2);
|
|
2047
|
+
let headerPosition = objectOffset + start;
|
|
2048
|
+
target.copyWithin(headerPosition + 4, headerPosition + 2, position);
|
|
2049
|
+
position += 2;
|
|
2050
|
+
target[headerPosition - 1] = 0xba; // map 32
|
|
2051
|
+
targetView.setUint32(headerPosition, size);
|
|
2052
|
+
}
|
|
1910
2053
|
} :
|
|
1911
2054
|
(object, skipValues) => {
|
|
1912
2055
|
let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null));
|
|
@@ -2473,6 +2616,258 @@
|
|
|
2473
2616
|
const RESET_BUFFER_MODE = 1024;
|
|
2474
2617
|
const THROW_ON_ITERABLE = 2048;
|
|
2475
2618
|
|
|
2619
|
+
// Positions are relative to the retained value, including bundle pointers. Frames and their storage survive appends.
|
|
2620
|
+
class StructureScanner {
|
|
2621
|
+
constructor (decoder) {
|
|
2622
|
+
this.decoder = decoder;
|
|
2623
|
+
this.frames = [];
|
|
2624
|
+
this.records = new Map();
|
|
2625
|
+
this.readers = new Map();
|
|
2626
|
+
this.reset();
|
|
2627
|
+
}
|
|
2628
|
+
|
|
2629
|
+
reset () {
|
|
2630
|
+
this.position = 0;
|
|
2631
|
+
this.depth = 0;
|
|
2632
|
+
this.neededLength = 0;
|
|
2633
|
+
this.postBundlePosition = 0;
|
|
2634
|
+
this.headers = 0;
|
|
2635
|
+
this.mapsAsObjects = !!this.decoder.mapsAsObjects;
|
|
2636
|
+
this.restoreMap = false;
|
|
2637
|
+
this.records.clear();
|
|
2638
|
+
this.readers.clear();
|
|
2639
|
+
this.push('items', 1);
|
|
2640
|
+
}
|
|
2641
|
+
|
|
2642
|
+
push (kind, remaining) {
|
|
2643
|
+
if (this.depth === 16384) throw new RangeError('Maximum CBOR nesting depth exceeded')
|
|
2644
|
+
const frame = this.frames[this.depth] || (this.frames[this.depth] = {});
|
|
2645
|
+
frame.kind = kind;
|
|
2646
|
+
frame.remaining = remaining;
|
|
2647
|
+
frame.stage = 0;
|
|
2648
|
+
frame.id = undefined;
|
|
2649
|
+
frame.restoreMap = false;
|
|
2650
|
+
frame.legacy = false;
|
|
2651
|
+
frame.definition = undefined;
|
|
2652
|
+
frame.limitMajor = 0;
|
|
2653
|
+
frame.count = 0;
|
|
2654
|
+
this.depth++;
|
|
2655
|
+
return frame
|
|
2656
|
+
}
|
|
2657
|
+
|
|
2658
|
+
header (source, end, lengthOnly = false) {
|
|
2659
|
+
const start = this.position;
|
|
2660
|
+
if (start >= end) {
|
|
2661
|
+
this.neededLength = start + 1;
|
|
2662
|
+
return false
|
|
2663
|
+
}
|
|
2664
|
+
const byte = source[start];
|
|
2665
|
+
const major = byte >> 5;
|
|
2666
|
+
const info = byte & 31;
|
|
2667
|
+
const width = info < 24 || (lengthOnly && info > 26) ? 0 : info === 24 ? 1 : info === 25 ? 2 : info === 26 ? 4 : info === 27 ? 8 : 0;
|
|
2668
|
+
if (!lengthOnly && info >= 28 && info <= 30) throw new Error('Unknown token ' + info)
|
|
2669
|
+
if (start + 1 + width > end) {
|
|
2670
|
+
this.neededLength = start + 1 + width;
|
|
2671
|
+
return false
|
|
2672
|
+
}
|
|
2673
|
+
let value = info;
|
|
2674
|
+
if (width) {
|
|
2675
|
+
value = 0;
|
|
2676
|
+
for (let i = start + 1; i <= start + width; i++) value = value * 256 + source[i];
|
|
2677
|
+
if (width === 8 && major > 1 && major !== 7 && value > 0xffffffff) {
|
|
2678
|
+
throw new Error('JavaScript does not support arrays, maps, or strings with length over 4294967295')
|
|
2679
|
+
}
|
|
2680
|
+
}
|
|
2681
|
+
this.major = major;
|
|
2682
|
+
this.info = info;
|
|
2683
|
+
this.argument = value;
|
|
2684
|
+
this.position = start + 1 + width;
|
|
2685
|
+
this.headers++;
|
|
2686
|
+
return true
|
|
2687
|
+
}
|
|
2688
|
+
|
|
2689
|
+
scan (source, end) {
|
|
2690
|
+
while (this.depth) {
|
|
2691
|
+
if (this.position > end) {
|
|
2692
|
+
this.neededLength = this.position;
|
|
2693
|
+
return false
|
|
2694
|
+
}
|
|
2695
|
+
const frame = this.frames[this.depth - 1];
|
|
2696
|
+
if (frame.kind === 'bundle') {
|
|
2697
|
+
if (frame.stage === 0) {
|
|
2698
|
+
if (!this.header(source, end, true)) return false
|
|
2699
|
+
frame.remaining = Math.max(0, this.argument - 2);
|
|
2700
|
+
frame.stage = 1;
|
|
2701
|
+
}
|
|
2702
|
+
if (frame.stage === 1) {
|
|
2703
|
+
const offsetStart = this.position;
|
|
2704
|
+
if (!this.header(source, end)) return false
|
|
2705
|
+
if (this.major > 1 || this.info === 31) throw new Error('Invalid bundled string offset')
|
|
2706
|
+
frame.target = offsetStart + (this.major === 1 ? -1 - this.argument : this.argument);
|
|
2707
|
+
if (!Number.isSafeInteger(frame.target) || frame.target < 0) throw new Error('Invalid bundled string offset')
|
|
2708
|
+
frame.stage = 2;
|
|
2709
|
+
}
|
|
2710
|
+
if (frame.stage === 2) {
|
|
2711
|
+
if (frame.remaining) {
|
|
2712
|
+
if (!this.header(source, end, true)) return false
|
|
2713
|
+
this.position += this.argument;
|
|
2714
|
+
frame.remaining--;
|
|
2715
|
+
continue
|
|
2716
|
+
}
|
|
2717
|
+
frame.resume = this.position;
|
|
2718
|
+
this.position = frame.target;
|
|
2719
|
+
frame.remaining = 2;
|
|
2720
|
+
frame.stage = 3;
|
|
2721
|
+
continue
|
|
2722
|
+
}
|
|
2723
|
+
if (frame.stage === 3) {
|
|
2724
|
+
if (frame.remaining) {
|
|
2725
|
+
if (!this.header(source, end, true)) return false
|
|
2726
|
+
this.position += this.argument;
|
|
2727
|
+
frame.remaining--;
|
|
2728
|
+
continue
|
|
2729
|
+
}
|
|
2730
|
+
this.postBundlePosition = this.position;
|
|
2731
|
+
this.position = frame.resume;
|
|
2732
|
+
frame.kind = 'items';
|
|
2733
|
+
frame.remaining = 1;
|
|
2734
|
+
continue
|
|
2735
|
+
}
|
|
2736
|
+
}
|
|
2737
|
+
if (frame.kind === 'record') {
|
|
2738
|
+
if (!this.header(source, end, true)) return false
|
|
2739
|
+
if (this.info > 26) throw new Error('Expected array header, but got ' + source[this.position - 1])
|
|
2740
|
+
frame.kind = 'items';
|
|
2741
|
+
frame.remaining = this.argument;
|
|
2742
|
+
const reader = frame.reader;
|
|
2743
|
+
if (reader.compiled.has(this.argument)) frame.remaining = Math.min(this.argument, reader.length);
|
|
2744
|
+
else if (reader.slowReads++ >= _stream.readThreshold) {
|
|
2745
|
+
reader.compiled.add(this.argument);
|
|
2746
|
+
frame.remaining = Math.min(this.argument, reader.length);
|
|
2747
|
+
}
|
|
2748
|
+
continue
|
|
2749
|
+
}
|
|
2750
|
+
if (frame.kind === 'definition' && frame.stage === 0) {
|
|
2751
|
+
const start = this.position;
|
|
2752
|
+
if (!this.header(source, end, !frame.legacy)) return false
|
|
2753
|
+
if (frame.legacy && this.major !== 4) {
|
|
2754
|
+
this.position = start;
|
|
2755
|
+
this.headers--;
|
|
2756
|
+
frame.kind = 'items';
|
|
2757
|
+
frame.remaining = 1;
|
|
2758
|
+
continue
|
|
2759
|
+
}
|
|
2760
|
+
frame.remaining = frame.legacy ? (this.info === 31 ? -1 : this.argument) : Math.max(2, this.argument);
|
|
2761
|
+
if (frame.legacy) {
|
|
2762
|
+
if (frame.remaining >= 0) _stream.checkContainer(4, frame.remaining);
|
|
2763
|
+
else frame.limitMajor = 4;
|
|
2764
|
+
frame.index = 0;
|
|
2765
|
+
}
|
|
2766
|
+
frame.total = frame.remaining;
|
|
2767
|
+
frame.stage = 1;
|
|
2768
|
+
continue
|
|
2769
|
+
}
|
|
2770
|
+
if (frame.remaining === 0) {
|
|
2771
|
+
if (frame.definition) this.records.set(frame.id - 0xe000, frame.definition);
|
|
2772
|
+
if (frame.restoreMap && this.restoreMap) {
|
|
2773
|
+
this.mapsAsObjects = true;
|
|
2774
|
+
this.restoreMap = false;
|
|
2775
|
+
}
|
|
2776
|
+
this.depth--;
|
|
2777
|
+
continue
|
|
2778
|
+
}
|
|
2779
|
+
if (!this.header(source, end)) return false
|
|
2780
|
+
let { major, argument, info } = this;
|
|
2781
|
+
if (frame.kind === 'definition') {
|
|
2782
|
+
const index = frame.legacy ? frame.index++ : frame.total - frame.remaining;
|
|
2783
|
+
if (index === 0 && major === 0) frame.id = argument;
|
|
2784
|
+
if ((frame.multiple ? index >= 1 && index < frame.total - 1 : index === 1) && major === 4 && info !== 31 && frame.id !== undefined) {
|
|
2785
|
+
const definition = { length: argument, slowReads: 0, compiled: new Set() };
|
|
2786
|
+
if (frame.legacy) frame.definition = definition;
|
|
2787
|
+
else this.records.set(frame.id - 0xe000 + (frame.multiple ? index - 1 : 0), definition);
|
|
2788
|
+
}
|
|
2789
|
+
}
|
|
2790
|
+
if (major === 7 && info === 31 && (frame.remaining === -1 || frame.remaining === -2)) {
|
|
2791
|
+
if (frame.definition) this.records.set(frame.id - 0xe000, frame.definition);
|
|
2792
|
+
this.depth--;
|
|
2793
|
+
continue
|
|
2794
|
+
}
|
|
2795
|
+
if (frame.limitMajor && (frame.remaining === -1 || frame.remaining === -2)) {
|
|
2796
|
+
_stream.checkContainer(frame.limitMajor, ++frame.count, frame.mapsAsObjects);
|
|
2797
|
+
}
|
|
2798
|
+
if (frame.remaining > 0) frame.remaining--;
|
|
2799
|
+
else if (frame.remaining === -2) frame.remaining = -3;
|
|
2800
|
+
else if (frame.remaining === -3) frame.remaining = -2;
|
|
2801
|
+
if (info === 31) {
|
|
2802
|
+
if (major === 2 || major === 3) throw new Error('Indefinite length not supported for byte or text strings')
|
|
2803
|
+
if (major === 4 || major === 5) {
|
|
2804
|
+
const container = this.push('items', major === 4 ? -1 : -2);
|
|
2805
|
+
container.limitMajor = major;
|
|
2806
|
+
container.mapsAsObjects = this.mapsAsObjects;
|
|
2807
|
+
if (major === 5 && this.restoreMap) {
|
|
2808
|
+
this.mapsAsObjects = true;
|
|
2809
|
+
this.restoreMap = false;
|
|
2810
|
+
}
|
|
2811
|
+
}
|
|
2812
|
+
else if (major === 7) throw new Error('Unexpected break stop code outside of an indefinite-length item')
|
|
2813
|
+
else throw new Error('Invalid major type for indefinite length ' + major)
|
|
2814
|
+
continue
|
|
2815
|
+
}
|
|
2816
|
+
switch (major) {
|
|
2817
|
+
case 2:
|
|
2818
|
+
case 3:
|
|
2819
|
+
this.position += argument;
|
|
2820
|
+
break
|
|
2821
|
+
case 4:
|
|
2822
|
+
case 5:
|
|
2823
|
+
_stream.checkContainer(major, argument, this.mapsAsObjects);
|
|
2824
|
+
if (major === 5 && this.restoreMap) {
|
|
2825
|
+
this.mapsAsObjects = true;
|
|
2826
|
+
this.restoreMap = false;
|
|
2827
|
+
}
|
|
2828
|
+
if (argument) this.push('items', major === 5 ? argument * 2 : argument);
|
|
2829
|
+
break
|
|
2830
|
+
case 6: {
|
|
2831
|
+
const structure = _stream.getTag(argument, this.decoder);
|
|
2832
|
+
let reader = this.records.get(argument & 0x1fff);
|
|
2833
|
+
if (argument >= 0xdff9 && (structure || reader)) {
|
|
2834
|
+
if (!reader) {
|
|
2835
|
+
reader = this.readers.get(structure);
|
|
2836
|
+
if (!reader) {
|
|
2837
|
+
reader = { length: structure.length, slowReads: structure.slowReads || 0, compiled: new Set() };
|
|
2838
|
+
for (let compiled = structure.compiledReader; compiled; compiled = compiled.next) reader.compiled.add(compiled.propertyCount);
|
|
2839
|
+
this.readers.set(structure, reader);
|
|
2840
|
+
}
|
|
2841
|
+
}
|
|
2842
|
+
this.push('record').reader = reader;
|
|
2843
|
+
} else if (argument === 0xdff9) {
|
|
2844
|
+
this.push('bundle');
|
|
2845
|
+
} else if (argument === 0xdfff || argument === 0xdffe) {
|
|
2846
|
+
this.push('definition').multiple = argument === 0xdffe;
|
|
2847
|
+
} else if (argument === 259 && _stream.isBuiltinTag(argument) && this.mapsAsObjects) {
|
|
2848
|
+
this.push('items', 1).restoreMap = true;
|
|
2849
|
+
this.mapsAsObjects = false;
|
|
2850
|
+
this.restoreMap = true;
|
|
2851
|
+
} else if (argument === 105 && _stream.isBuiltinTag(argument)) {
|
|
2852
|
+
const definition = this.push('definition');
|
|
2853
|
+
definition.multiple = false;
|
|
2854
|
+
definition.legacy = true;
|
|
2855
|
+
} else {
|
|
2856
|
+
this.push('items', 1);
|
|
2857
|
+
}
|
|
2858
|
+
break
|
|
2859
|
+
}
|
|
2860
|
+
}
|
|
2861
|
+
}
|
|
2862
|
+
if (this.postBundlePosition) {
|
|
2863
|
+
if (this.position >= this.postBundlePosition) throw new Error('Unexpected bundle position')
|
|
2864
|
+
this.position = this.postBundlePosition;
|
|
2865
|
+
}
|
|
2866
|
+
if (this.frames.length > 256) this.frames.length = 256;
|
|
2867
|
+
return true
|
|
2868
|
+
}
|
|
2869
|
+
}
|
|
2870
|
+
|
|
2476
2871
|
/**
|
|
2477
2872
|
* Given an Iterable first argument, returns an Iterable where each value is encoded as a Buffer
|
|
2478
2873
|
* If the argument is only Async Iterable, the return value will be an Async Iterable.
|
|
@@ -2510,7 +2905,7 @@
|
|
|
2510
2905
|
* Given an Iterable/Iterator input which yields buffers, returns an IterableIterator which yields sync decoded objects
|
|
2511
2906
|
* Or, given an Async Iterable/Iterator which yields promises resolving in buffers, returns an AsyncIterableIterator.
|
|
2512
2907
|
* @param {Iterable|Iterator|AsyncIterable|AsyncIterableIterator} bufferIterator
|
|
2513
|
-
* @param {object} [options] - Decoder options
|
|
2908
|
+
* @param {object} [options] - Decoder options, and maxIncompleteBufferSize
|
|
2514
2909
|
* @returns {IterableIterator|Promise.<AsyncIterableIterator}
|
|
2515
2910
|
*/
|
|
2516
2911
|
function decodeIter (bufferIterator, options = {}) {
|
|
@@ -2519,26 +2914,15 @@
|
|
|
2519
2914
|
}
|
|
2520
2915
|
|
|
2521
2916
|
const decoder = new Decoder(options);
|
|
2522
|
-
|
|
2917
|
+
const chunkDecoder = new ChunkDecoder(decoder, options.maxIncompleteBufferSize);
|
|
2918
|
+
let fatalError;
|
|
2523
2919
|
const parser = (chunk) => {
|
|
2524
|
-
let yields;
|
|
2525
|
-
// if there's incomplete data from previous chunk, concatinate and try again
|
|
2526
|
-
if (incomplete) {
|
|
2527
|
-
chunk = Buffer.concat([incomplete, chunk]);
|
|
2528
|
-
incomplete = undefined;
|
|
2529
|
-
}
|
|
2530
|
-
|
|
2531
2920
|
try {
|
|
2532
|
-
|
|
2533
|
-
} catch (
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
incomplete = chunk.slice(err.lastPosition);
|
|
2537
|
-
} else {
|
|
2538
|
-
fatalError = err;
|
|
2539
|
-
}
|
|
2921
|
+
return chunkDecoder.buffer ? chunkDecoder.decodeAfterIncomplete(chunk) : decoder.decodeMultiple(chunk)
|
|
2922
|
+
} catch (error) {
|
|
2923
|
+
fatalError = chunkDecoder.keepIncomplete(error, chunk);
|
|
2924
|
+
return (error && error.values) || []
|
|
2540
2925
|
}
|
|
2541
|
-
return yields || []
|
|
2542
2926
|
};
|
|
2543
2927
|
|
|
2544
2928
|
if (typeof bufferIterator[Symbol.iterator] === 'function') {
|
|
@@ -2547,6 +2931,8 @@
|
|
|
2547
2931
|
yield * parser(value);
|
|
2548
2932
|
if (fatalError) throw fatalError
|
|
2549
2933
|
}
|
|
2934
|
+
const endError = chunkDecoder.end();
|
|
2935
|
+
if (endError) throw endError
|
|
2550
2936
|
})()
|
|
2551
2937
|
} else if (typeof bufferIterator[Symbol.asyncIterator] === 'function') {
|
|
2552
2938
|
return (async function * iter () {
|
|
@@ -2554,10 +2940,122 @@
|
|
|
2554
2940
|
yield * parser(value);
|
|
2555
2941
|
if (fatalError) throw fatalError
|
|
2556
2942
|
}
|
|
2943
|
+
const endError = chunkDecoder.end();
|
|
2944
|
+
if (endError) throw endError
|
|
2557
2945
|
})()
|
|
2558
2946
|
}
|
|
2559
2947
|
}
|
|
2560
2948
|
|
|
2949
|
+
/**
|
|
2950
|
+
* Keeps the bytes of a value that a chunk of a stream ends inside of, for DecoderStream and decodeIter, and decodes
|
|
2951
|
+
* them again once enough have arrived to possibly complete it. Callers decode a chunk with decodeAfterIncomplete if
|
|
2952
|
+
* buffer is set, or else directly with decodeMultiple, and pass any error to keepIncomplete, which returns the error
|
|
2953
|
+
* to report, if any. end returns an error if the input ended inside a value.
|
|
2954
|
+
*/
|
|
2955
|
+
class ChunkDecoder {
|
|
2956
|
+
constructor (decoder, maxIncompleteBufferSize) {
|
|
2957
|
+
this.decoder = decoder;
|
|
2958
|
+
if (maxIncompleteBufferSize === undefined) maxIncompleteBufferSize = 0x4000000;
|
|
2959
|
+
if (typeof maxIncompleteBufferSize !== 'number' || !(maxIncompleteBufferSize >= 0)) {
|
|
2960
|
+
throw new TypeError('maxIncompleteBufferSize must be a non-negative number')
|
|
2961
|
+
}
|
|
2962
|
+
this.maxIncompleteBufferSize = Math.floor(maxIncompleteBufferSize);
|
|
2963
|
+
// buffer[0, bufferEnd) is the incomplete value; no decoded value is a view of buffer while it is kept here
|
|
2964
|
+
this.buffer = null;
|
|
2965
|
+
this.bufferEnd = 0;
|
|
2966
|
+
this.appends = 0;
|
|
2967
|
+
this.scanner = null;
|
|
2968
|
+
this.scanned = false;
|
|
2969
|
+
this.resetScanner = false;
|
|
2970
|
+
this.neededLength = 0; // a lower bound on the incomplete value's length
|
|
2971
|
+
}
|
|
2972
|
+
|
|
2973
|
+
decodeAfterIncomplete (chunk) {
|
|
2974
|
+
if (!(ArrayBuffer.isView(chunk) && chunk.BYTES_PER_ELEMENT === 1)) {
|
|
2975
|
+
throw new TypeError('Source must be a Uint8Array or Buffer')
|
|
2976
|
+
}
|
|
2977
|
+
const length = this.bufferEnd + chunk.length;
|
|
2978
|
+
if (length < this.neededLength && length > this.maxIncompleteBufferSize) throw maxIncompleteBufferSizeError()
|
|
2979
|
+
if (length > this.buffer.length) {
|
|
2980
|
+
// Values usually complete within two appends, which are sized exactly. After that the buffer grows by at least
|
|
2981
|
+
// the bytes already kept, so that they are copied a bounded number of times however small the chunks are.
|
|
2982
|
+
const size = this.appends < 2 ? length
|
|
2983
|
+
: Math.max(length, Math.min(length + this.bufferEnd, this.maxIncompleteBufferSize));
|
|
2984
|
+
const buffer = Buffer.allocUnsafe(size);
|
|
2985
|
+
if (size > length) buffer.fill(0, length); // decoded values are views of this buffer, so no uninitialized memory
|
|
2986
|
+
buffer.set(this.buffer.subarray(0, this.bufferEnd));
|
|
2987
|
+
this.buffer = buffer;
|
|
2988
|
+
}
|
|
2989
|
+
this.buffer.set(chunk, this.bufferEnd);
|
|
2990
|
+
this.bufferEnd = length;
|
|
2991
|
+
this.appends++;
|
|
2992
|
+
if (length < this.neededLength) return []
|
|
2993
|
+
if (!this.scanner) this.scanner = new StructureScanner(this.decoder);
|
|
2994
|
+
if (this.resetScanner) {
|
|
2995
|
+
this.scanner.reset();
|
|
2996
|
+
this.resetScanner = false;
|
|
2997
|
+
}
|
|
2998
|
+
if (!this.scanner.scan(this.buffer, length)) {
|
|
2999
|
+
this.neededLength = this.scanner.neededLength;
|
|
3000
|
+
if (length > this.maxIncompleteBufferSize) throw maxIncompleteBufferSizeError()
|
|
3001
|
+
return []
|
|
3002
|
+
}
|
|
3003
|
+
this.scanned = true;
|
|
3004
|
+
const values = _stream.decode(this.decoder, this.buffer.subarray(0, length), this.scanner.position);
|
|
3005
|
+
this.scanned = false;
|
|
3006
|
+
this.buffer = null;
|
|
3007
|
+
return values
|
|
3008
|
+
}
|
|
3009
|
+
|
|
3010
|
+
keepIncomplete (error, chunk) {
|
|
3011
|
+
// an extension may throw any value, and a falsy one would read as success
|
|
3012
|
+
if (!error) return this.fail(new Error('Decoding failed: ' + error))
|
|
3013
|
+
if (!error.incomplete) return this.fail(error)
|
|
3014
|
+
if (this.scanned && error.lastPosition === 0) {
|
|
3015
|
+
error.incomplete = false;
|
|
3016
|
+
return this.fail(error)
|
|
3017
|
+
}
|
|
3018
|
+
const source = this.buffer ? this.buffer.subarray(0, this.bufferEnd) : chunk;
|
|
3019
|
+
const length = source.length - error.lastPosition;
|
|
3020
|
+
if (length > this.maxIncompleteBufferSize) return this.fail(maxIncompleteBufferSizeError())
|
|
3021
|
+
if (!this.buffer || error.lastPosition > 0) {
|
|
3022
|
+
if (length === 0) return
|
|
3023
|
+
try {
|
|
3024
|
+
// copied, so that the remainder does not keep in memory the chunk, or the buffer that the values before it
|
|
3025
|
+
// are views of
|
|
3026
|
+
this.buffer = Buffer.from(source.subarray(error.lastPosition));
|
|
3027
|
+
} catch (copyError) {
|
|
3028
|
+
return this.fail(copyError)
|
|
3029
|
+
}
|
|
3030
|
+
this.bufferEnd = length;
|
|
3031
|
+
this.appends = 0;
|
|
3032
|
+
}
|
|
3033
|
+
this.scanned = false;
|
|
3034
|
+
this.resetScanner = true;
|
|
3035
|
+
this.neededLength = Math.max(length + 1, error.neededEnd ? error.neededEnd - error.lastPosition : 0);
|
|
3036
|
+
}
|
|
3037
|
+
|
|
3038
|
+
end () {
|
|
3039
|
+
if (this.buffer) {
|
|
3040
|
+
const error = this.fail(new Error('Unexpected end of CBOR data'));
|
|
3041
|
+
error.incomplete = true;
|
|
3042
|
+
return error
|
|
3043
|
+
}
|
|
3044
|
+
}
|
|
3045
|
+
|
|
3046
|
+
fail (error) {
|
|
3047
|
+
this.buffer = null;
|
|
3048
|
+
this.scanner = null;
|
|
3049
|
+
this.scanned = false;
|
|
3050
|
+
this.resetScanner = false;
|
|
3051
|
+
return error
|
|
3052
|
+
}
|
|
3053
|
+
}
|
|
3054
|
+
|
|
3055
|
+
function maxIncompleteBufferSizeError () {
|
|
3056
|
+
return new Error('Maximum incomplete buffer size exceeded')
|
|
3057
|
+
}
|
|
3058
|
+
|
|
2561
3059
|
exports.ALWAYS = ALWAYS;
|
|
2562
3060
|
exports.DECIMAL_FIT = DECIMAL_FIT;
|
|
2563
3061
|
exports.DECIMAL_ROUND = DECIMAL_ROUND;
|