cbor-x 1.6.6 → 1.6.7
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 +74 -48
- package/dist/decode-no-eval.cjs +76 -48
- package/dist/decode-no-eval.cjs.map +1 -1
- package/dist/index-no-eval.cjs +81 -50
- 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 +81 -50
- 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 +94 -66
- package/dist/node.cjs.map +1 -1
- package/dist/test.js +75 -48
- package/dist/test.js.map +1 -1
- package/encode.js +1 -1
- package/iterators.js +6 -4
- package/package.json +1 -1
- package/stream.js +11 -13
package/README.md
CHANGED
|
@@ -241,8 +241,12 @@ setSizeLimits({
|
|
|
241
241
|
});
|
|
242
242
|
```
|
|
243
243
|
|
|
244
|
+
`maxMapSize` limits the entries of a decoded `Map`. A map decoded as an object (with `mapsAsObjects`) is limited to the smaller of `maxObjectSize` and `maxMapSize`.
|
|
245
|
+
|
|
244
246
|
Declared array and map lengths are also always validated against the number of bytes remaining in the source, so truncated or malformed input is rejected immediately instead of allocating for the length it claims. These limits are for bounding legitimately encoded (but unwanted) large data.
|
|
245
247
|
|
|
248
|
+
Nesting depth is bounded by the JavaScript call stack: input nested deeper than the stack allows (typically several thousand levels) throws the engine's stack overflow error (a `RangeError` in V8 and JavaScriptCore). Like other malformed input, this is not marked as `incomplete`, so a `DecoderStream` emits it as an error rather than waiting for more data. (An error from the last few bytes of a chunk may only be reported once the next chunk arrives.)
|
|
249
|
+
|
|
246
250
|
## Performance
|
|
247
251
|
Cbor-x is fast. Really fast. Here is comparison with the next fastest JS projects using the benchmark tool from `msgpack-lite` (and the sample data is from some clinical research data we use that has a good mix of different value types and structures). It also includes comparison to V8 native JSON functionality, and JavaScript Avro (`avsc`, a very optimized Avro implementation):
|
|
248
252
|
|
package/decode.js
CHANGED
|
@@ -19,8 +19,9 @@ let maxArraySize = 112810000 // This is the maximum array size in V8. We would p
|
|
|
19
19
|
let maxMapSize = 16810000 // JavaScript has a fixed maximum map size of about 16710000, but JS itself enforces this,
|
|
20
20
|
// so we don't need to
|
|
21
21
|
|
|
22
|
-
let maxObjectSize = 16710000; //
|
|
23
|
-
//
|
|
22
|
+
let maxObjectSize = 16710000; // It takes over a minute to create this many keys in an object, so this is probably a
|
|
23
|
+
// reasonable choice.
|
|
24
|
+
let objectSizeLimit = maxObjectSize // objects are limited by both maxObjectSize and maxMapSize
|
|
24
25
|
let strings = EMPTY_ARRAY
|
|
25
26
|
let stringPosition = 0
|
|
26
27
|
let currentDecoder = {}
|
|
@@ -124,7 +125,13 @@ export class Decoder {
|
|
|
124
125
|
// re-entrant execution, save the state and restore it after we do this decode
|
|
125
126
|
return saveState(() => {
|
|
126
127
|
clearSource()
|
|
127
|
-
|
|
128
|
+
try {
|
|
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
|
+
}
|
|
128
135
|
})
|
|
129
136
|
}
|
|
130
137
|
srcEnd = end > -1 ? end : source.length
|
|
@@ -204,15 +211,13 @@ export class Decoder {
|
|
|
204
211
|
export function getPosition() {
|
|
205
212
|
return position
|
|
206
213
|
}
|
|
214
|
+
const nestedDecodeErrors = new WeakSet()
|
|
207
215
|
export function checkedRead() {
|
|
208
216
|
try {
|
|
209
217
|
let result = read()
|
|
210
218
|
if (bundledStrings) {
|
|
211
|
-
if (position >= bundledStrings.postBundlePosition)
|
|
212
|
-
|
|
213
|
-
error.incomplete = true;
|
|
214
|
-
throw error
|
|
215
|
-
}
|
|
219
|
+
if (position >= bundledStrings.postBundlePosition)
|
|
220
|
+
throw new Error('Unexpected bundle position')
|
|
216
221
|
// bundled strings to skip past
|
|
217
222
|
position = bundledStrings.postBundlePosition;
|
|
218
223
|
bundledStrings = null;
|
|
@@ -236,7 +241,14 @@ export function checkedRead() {
|
|
|
236
241
|
return result
|
|
237
242
|
} catch(error) {
|
|
238
243
|
clearSource()
|
|
239
|
-
if (
|
|
244
|
+
if (nestedDecodeErrors.has(error)) {
|
|
245
|
+
// from a nested decode of embedded data (by an extension), which was complete in this source
|
|
246
|
+
if (error.incomplete)
|
|
247
|
+
error.incomplete = false
|
|
248
|
+
} else if (error instanceof RangeError && position + 8 > srcEnd ||
|
|
249
|
+
error && typeof error.message == 'string' && error.message.startsWith('Unexpected end of buffer')) {
|
|
250
|
+
// a DataView read past the end of the source; these read at most 8 bytes and precede advancing position.
|
|
251
|
+
// Any other RangeError (such as a stack overflow from deeply nested input) is fatal.
|
|
240
252
|
error.incomplete = true
|
|
241
253
|
}
|
|
242
254
|
throw error
|
|
@@ -321,13 +333,13 @@ export function read() {
|
|
|
321
333
|
let i = 0;
|
|
322
334
|
if (currentDecoder.keyMap) {
|
|
323
335
|
while((key = read()) != STOP_CODE) {
|
|
324
|
-
if (i++ >=
|
|
336
|
+
if (i++ >= objectSizeLimit) throw new Error(`Property count exceeds ${objectSizeLimit}`)
|
|
325
337
|
object[safeKey(currentDecoder.decodeKey(key))] = read()
|
|
326
338
|
}
|
|
327
339
|
}
|
|
328
340
|
else {
|
|
329
341
|
while ((key = read()) != STOP_CODE) {
|
|
330
|
-
if (i++ >=
|
|
342
|
+
if (i++ >= objectSizeLimit) throw new Error(`Property count exceeds ${objectSizeLimit}`)
|
|
331
343
|
object[safeKey(key)] = read()
|
|
332
344
|
}
|
|
333
345
|
}
|
|
@@ -373,11 +385,13 @@ export function read() {
|
|
|
373
385
|
case 1: // negative int
|
|
374
386
|
return ~token
|
|
375
387
|
case 2: // buffer
|
|
388
|
+
if (token > srcEnd - position) throw endOfCBORError()
|
|
376
389
|
return readBin(token)
|
|
377
390
|
case 3: // string
|
|
378
|
-
if (srcStringEnd >= position) {
|
|
391
|
+
if (srcStringEnd >= position) { // already extracted, which only covers complete strings
|
|
379
392
|
return srcString.slice(position - srcStringStart, (position += token) - srcStringStart)
|
|
380
393
|
}
|
|
394
|
+
if (token > srcEnd - position) throw endOfCBORError()
|
|
381
395
|
if (srcStringEnd == 0 && srcEnd < 140 && token < 32) {
|
|
382
396
|
// for small blocks, avoiding the overhead of the extract call is helpful
|
|
383
397
|
let string = token < 16 ? shortStringInJS(token) : longStringInJS(token)
|
|
@@ -386,7 +400,7 @@ export function read() {
|
|
|
386
400
|
}
|
|
387
401
|
return readFixedString(token)
|
|
388
402
|
case 4: // array
|
|
389
|
-
if (token
|
|
403
|
+
if (token > maxArraySize) throw new Error(`Array length exceeds ${maxArraySize}`)
|
|
390
404
|
// every element occupies at least one byte, so a length beyond what remains in the source can
|
|
391
405
|
// never be satisfied; check before allocating so a tiny header can not force a huge allocation
|
|
392
406
|
if (token > srcEnd - position) throw endOfCBORError()
|
|
@@ -396,9 +410,9 @@ export function read() {
|
|
|
396
410
|
for (let i = 0; i < token; i++) array[i] = read()
|
|
397
411
|
return array
|
|
398
412
|
case 5: // map
|
|
399
|
-
if (token >= maxMapSize) throw new Error(`Map size exceeds ${maxArraySize}`)
|
|
400
|
-
if (token > (srcEnd - position) / 2) throw endOfCBORError() // each entry needs at least two bytes
|
|
401
413
|
if (currentDecoder.mapsAsObjects) {
|
|
414
|
+
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
|
|
402
416
|
let object = {}
|
|
403
417
|
if (currentDecoder.keyMap) for (let i = 0; i < token; i++) object[safeKey(currentDecoder.decodeKey(read()))] = read()
|
|
404
418
|
else for (let i = 0; i < token; i++) object[safeKey(read())] = read()
|
|
@@ -408,6 +422,8 @@ export function read() {
|
|
|
408
422
|
currentDecoder.mapsAsObjects = true
|
|
409
423
|
restoreMapsAsObject = false
|
|
410
424
|
}
|
|
425
|
+
if (token > maxMapSize) throw new Error(`Map size exceeds ${maxMapSize}`)
|
|
426
|
+
if (token > (srcEnd - position) / 2) throw endOfCBORError()
|
|
411
427
|
let map = new Map()
|
|
412
428
|
if (currentDecoder.keyMap) for (let i = 0; i < token; i++) map.set(currentDecoder.decodeKey(read()),read())
|
|
413
429
|
else for (let i = 0; i < token; i++) map.set(read(), read())
|
|
@@ -879,6 +895,7 @@ function readKey() {
|
|
|
879
895
|
if (length >= 0x60 && length < 0x78) {
|
|
880
896
|
// fixstr, potentially use key cache
|
|
881
897
|
length = length - 0x60
|
|
898
|
+
if (length > srcEnd - position) throw endOfCBORError()
|
|
882
899
|
if (srcStringEnd >= position) // if it has been extracted, must use it (and faster anyway)
|
|
883
900
|
return srcString.slice(position - srcStringStart, (position += length) - srcStringStart)
|
|
884
901
|
else if (!(srcStringEnd == 0 && srcEnd < 180))
|
|
@@ -1014,18 +1031,12 @@ currentExtensions[27] = (data) => { // http://cbor.schmorp.de/generic-object
|
|
|
1014
1031
|
return (glbl[data[0]] || Error)(data[1], data[2])
|
|
1015
1032
|
}
|
|
1016
1033
|
const packedTable = (read) => {
|
|
1017
|
-
if (
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
error.incomplete = true
|
|
1021
|
-
throw error
|
|
1022
|
-
}
|
|
1034
|
+
if (!(position < srcEnd)) throw endOfCBORError()
|
|
1035
|
+
if (src[position++] != 0x84)
|
|
1036
|
+
throw new Error('Packed values structure must be followed by a 4 element array')
|
|
1023
1037
|
let newPackedValues = read() // packed values
|
|
1024
|
-
if (!newPackedValues || !newPackedValues.length)
|
|
1025
|
-
|
|
1026
|
-
error.incomplete = true
|
|
1027
|
-
throw error
|
|
1028
|
-
}
|
|
1038
|
+
if (!Array.isArray(newPackedValues) || !newPackedValues.length)
|
|
1039
|
+
throw new Error('Packed values structure must be followed by a 4 element array')
|
|
1029
1040
|
packedValues = packedValues ? newPackedValues.concat(packedValues.slice(newPackedValues.length)) : newPackedValues
|
|
1030
1041
|
packedValues.prefixes = read()
|
|
1031
1042
|
packedValues.suffixes = read()
|
|
@@ -1043,10 +1054,7 @@ currentExtensions[PACKED_REFERENCE_TAG_ID] = (data) => { // packed reference
|
|
|
1043
1054
|
}
|
|
1044
1055
|
if (typeof data == 'number')
|
|
1045
1056
|
return packedValues[16 + (data >= 0 ? 2 * data : (-2 * data - 1))]
|
|
1046
|
-
|
|
1047
|
-
if (data === undefined)
|
|
1048
|
-
error.incomplete = true
|
|
1049
|
-
throw error
|
|
1057
|
+
throw new Error('No support for non-integer packed references yet')
|
|
1050
1058
|
}
|
|
1051
1059
|
|
|
1052
1060
|
// The following code is an incomplete implementation of http://cbor.schmorp.de/stringref
|
|
@@ -1116,9 +1124,19 @@ currentExtensions[258] = (array) => new Set(array); // https://github.com/input-
|
|
|
1116
1124
|
(currentExtensions[259] = (read) => {
|
|
1117
1125
|
// https://github.com/shanewholloway/js-cbor-codec/blob/master/docs/CBOR-259-spec
|
|
1118
1126
|
// for decoding as a standard Map
|
|
1119
|
-
|
|
1120
|
-
|
|
1127
|
+
let decoder = currentDecoder
|
|
1128
|
+
if (decoder.mapsAsObjects) {
|
|
1129
|
+
decoder.mapsAsObjects = false
|
|
1121
1130
|
restoreMapsAsObject = true
|
|
1131
|
+
try {
|
|
1132
|
+
return read()
|
|
1133
|
+
} finally {
|
|
1134
|
+
// normally restored by the map header, but not if the tagged value is not a map or the read fails
|
|
1135
|
+
if (restoreMapsAsObject) {
|
|
1136
|
+
decoder.mapsAsObjects = true
|
|
1137
|
+
restoreMapsAsObject = false
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1122
1140
|
}
|
|
1123
1141
|
return read()
|
|
1124
1142
|
}).handlesRead = true
|
|
@@ -1238,10 +1256,12 @@ function readJustLength() {
|
|
|
1238
1256
|
token = src[position++]
|
|
1239
1257
|
break
|
|
1240
1258
|
case 0x19:
|
|
1259
|
+
if (position + 2 > srcEnd) throw endOfCBORError()
|
|
1241
1260
|
token = dataView.getUint16(position)
|
|
1242
1261
|
position += 2
|
|
1243
1262
|
break
|
|
1244
1263
|
case 0x1a:
|
|
1264
|
+
if (position + 4 > srcEnd) throw endOfCBORError()
|
|
1245
1265
|
token = dataView.getUint32(position)
|
|
1246
1266
|
position += 4
|
|
1247
1267
|
break
|
|
@@ -1283,22 +1303,27 @@ function saveState(callback) {
|
|
|
1283
1303
|
let savedStructures = currentStructures
|
|
1284
1304
|
let savedDecoder = currentDecoder
|
|
1285
1305
|
let savedSequentialMode = sequentialMode
|
|
1286
|
-
let
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1306
|
+
let savedRestoreMapsAsObject = restoreMapsAsObject
|
|
1307
|
+
restoreMapsAsObject = false
|
|
1308
|
+
try {
|
|
1309
|
+
return callback()
|
|
1310
|
+
} finally {
|
|
1311
|
+
srcEnd = savedSrcEnd
|
|
1312
|
+
position = savedPosition
|
|
1313
|
+
stringPosition = savedStringPosition
|
|
1314
|
+
srcStringStart = savedSrcStringStart
|
|
1315
|
+
srcStringEnd = savedSrcStringEnd
|
|
1316
|
+
srcString = savedSrcString
|
|
1317
|
+
strings = savedStrings
|
|
1318
|
+
referenceMap = savedReferenceMap
|
|
1319
|
+
bundledStrings = savedBundledStrings
|
|
1320
|
+
src = savedSrc
|
|
1321
|
+
sequentialMode = savedSequentialMode
|
|
1322
|
+
currentStructures = savedStructures
|
|
1323
|
+
currentDecoder = savedDecoder
|
|
1324
|
+
restoreMapsAsObject = savedRestoreMapsAsObject
|
|
1325
|
+
dataView = new DataView(src.buffer, src.byteOffset, src.byteLength)
|
|
1326
|
+
}
|
|
1302
1327
|
}
|
|
1303
1328
|
export function clearSource() {
|
|
1304
1329
|
src = null
|
|
@@ -1314,6 +1339,7 @@ export function setSizeLimits(limits) {
|
|
|
1314
1339
|
if (limits.maxMapSize) maxMapSize = limits.maxMapSize;
|
|
1315
1340
|
if (limits.maxArraySize) maxArraySize = limits.maxArraySize;
|
|
1316
1341
|
if (limits.maxObjectSize) maxObjectSize = limits.maxObjectSize;
|
|
1342
|
+
objectSizeLimit = Math.min(maxObjectSize, maxMapSize)
|
|
1317
1343
|
}
|
|
1318
1344
|
|
|
1319
1345
|
export const mult10 = new Array(147) // this is a table matching binary exponents to the multiplier to determine significant digit rounding
|
package/dist/decode-no-eval.cjs
CHANGED
|
@@ -21,7 +21,11 @@
|
|
|
21
21
|
let maxArraySize = 112810000; // This is the maximum array size in V8. We would potentially detect and set it higher
|
|
22
22
|
// for JSC, but this is pretty large and should be sufficient for most use cases
|
|
23
23
|
let maxMapSize = 16810000; // JavaScript has a fixed maximum map size of about 16710000, but JS itself enforces this,
|
|
24
|
-
//
|
|
24
|
+
// so we don't need to
|
|
25
|
+
|
|
26
|
+
let maxObjectSize = 16710000; // It takes over a minute to create this many keys in an object, so this is probably a
|
|
27
|
+
// reasonable choice.
|
|
28
|
+
let objectSizeLimit = maxObjectSize; // objects are limited by both maxObjectSize and maxMapSize
|
|
25
29
|
let strings = EMPTY_ARRAY;
|
|
26
30
|
let stringPosition = 0;
|
|
27
31
|
let currentDecoder = {};
|
|
@@ -125,7 +129,13 @@
|
|
|
125
129
|
// re-entrant execution, save the state and restore it after we do this decode
|
|
126
130
|
return saveState(() => {
|
|
127
131
|
clearSource();
|
|
128
|
-
|
|
132
|
+
try {
|
|
133
|
+
return this ? this.decode(source, end) : Decoder.prototype.decode.call(defaultOptions, source, end)
|
|
134
|
+
} catch (error) {
|
|
135
|
+
if (error && typeof error == 'object')
|
|
136
|
+
nestedDecodeErrors.add(error);
|
|
137
|
+
throw error
|
|
138
|
+
}
|
|
129
139
|
})
|
|
130
140
|
}
|
|
131
141
|
srcEnd = end > -1 ? end : source.length;
|
|
@@ -205,15 +215,13 @@
|
|
|
205
215
|
function getPosition() {
|
|
206
216
|
return position
|
|
207
217
|
}
|
|
218
|
+
const nestedDecodeErrors = new WeakSet();
|
|
208
219
|
function checkedRead() {
|
|
209
220
|
try {
|
|
210
221
|
let result = read();
|
|
211
222
|
if (bundledStrings) {
|
|
212
|
-
if (position >= bundledStrings.postBundlePosition)
|
|
213
|
-
|
|
214
|
-
error.incomplete = true;
|
|
215
|
-
throw error
|
|
216
|
-
}
|
|
223
|
+
if (position >= bundledStrings.postBundlePosition)
|
|
224
|
+
throw new Error('Unexpected bundle position')
|
|
217
225
|
// bundled strings to skip past
|
|
218
226
|
position = bundledStrings.postBundlePosition;
|
|
219
227
|
bundledStrings = null;
|
|
@@ -237,7 +245,14 @@
|
|
|
237
245
|
return result
|
|
238
246
|
} catch(error) {
|
|
239
247
|
clearSource();
|
|
240
|
-
if (
|
|
248
|
+
if (nestedDecodeErrors.has(error)) {
|
|
249
|
+
// from a nested decode of embedded data (by an extension), which was complete in this source
|
|
250
|
+
if (error.incomplete)
|
|
251
|
+
error.incomplete = false;
|
|
252
|
+
} else if (error instanceof RangeError && position + 8 > srcEnd ||
|
|
253
|
+
error && typeof error.message == 'string' && error.message.startsWith('Unexpected end of buffer')) {
|
|
254
|
+
// a DataView read past the end of the source; these read at most 8 bytes and precede advancing position.
|
|
255
|
+
// Any other RangeError (such as a stack overflow from deeply nested input) is fatal.
|
|
241
256
|
error.incomplete = true;
|
|
242
257
|
}
|
|
243
258
|
throw error
|
|
@@ -322,13 +337,13 @@
|
|
|
322
337
|
let i = 0;
|
|
323
338
|
if (currentDecoder.keyMap) {
|
|
324
339
|
while((key = read()) != STOP_CODE) {
|
|
325
|
-
if (i++ >=
|
|
340
|
+
if (i++ >= objectSizeLimit) throw new Error(`Property count exceeds ${objectSizeLimit}`)
|
|
326
341
|
object[safeKey(currentDecoder.decodeKey(key))] = read();
|
|
327
342
|
}
|
|
328
343
|
}
|
|
329
344
|
else {
|
|
330
345
|
while ((key = read()) != STOP_CODE) {
|
|
331
|
-
if (i++ >=
|
|
346
|
+
if (i++ >= objectSizeLimit) throw new Error(`Property count exceeds ${objectSizeLimit}`)
|
|
332
347
|
object[safeKey(key)] = read();
|
|
333
348
|
}
|
|
334
349
|
}
|
|
@@ -374,11 +389,13 @@
|
|
|
374
389
|
case 1: // negative int
|
|
375
390
|
return ~token
|
|
376
391
|
case 2: // buffer
|
|
392
|
+
if (token > srcEnd - position) throw endOfCBORError()
|
|
377
393
|
return readBin(token)
|
|
378
394
|
case 3: // string
|
|
379
|
-
if (srcStringEnd >= position) {
|
|
395
|
+
if (srcStringEnd >= position) { // already extracted, which only covers complete strings
|
|
380
396
|
return srcString.slice(position - srcStringStart, (position += token) - srcStringStart)
|
|
381
397
|
}
|
|
398
|
+
if (token > srcEnd - position) throw endOfCBORError()
|
|
382
399
|
if (srcStringEnd == 0 && srcEnd < 140 && token < 32) {
|
|
383
400
|
// for small blocks, avoiding the overhead of the extract call is helpful
|
|
384
401
|
let string = token < 16 ? shortStringInJS(token) : longStringInJS(token);
|
|
@@ -387,7 +404,7 @@
|
|
|
387
404
|
}
|
|
388
405
|
return readFixedString(token)
|
|
389
406
|
case 4: // array
|
|
390
|
-
if (token
|
|
407
|
+
if (token > maxArraySize) throw new Error(`Array length exceeds ${maxArraySize}`)
|
|
391
408
|
// every element occupies at least one byte, so a length beyond what remains in the source can
|
|
392
409
|
// never be satisfied; check before allocating so a tiny header can not force a huge allocation
|
|
393
410
|
if (token > srcEnd - position) throw endOfCBORError()
|
|
@@ -397,9 +414,9 @@
|
|
|
397
414
|
for (let i = 0; i < token; i++) array[i] = read();
|
|
398
415
|
return array
|
|
399
416
|
case 5: // map
|
|
400
|
-
if (token >= maxMapSize) throw new Error(`Map size exceeds ${maxArraySize}`)
|
|
401
|
-
if (token > (srcEnd - position) / 2) throw endOfCBORError() // each entry needs at least two bytes
|
|
402
417
|
if (currentDecoder.mapsAsObjects) {
|
|
418
|
+
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
|
|
403
420
|
let object = {};
|
|
404
421
|
if (currentDecoder.keyMap) for (let i = 0; i < token; i++) object[safeKey(currentDecoder.decodeKey(read()))] = read();
|
|
405
422
|
else for (let i = 0; i < token; i++) object[safeKey(read())] = read();
|
|
@@ -409,6 +426,8 @@
|
|
|
409
426
|
currentDecoder.mapsAsObjects = true;
|
|
410
427
|
restoreMapsAsObject = false;
|
|
411
428
|
}
|
|
429
|
+
if (token > maxMapSize) throw new Error(`Map size exceeds ${maxMapSize}`)
|
|
430
|
+
if (token > (srcEnd - position) / 2) throw endOfCBORError()
|
|
412
431
|
let map = new Map();
|
|
413
432
|
if (currentDecoder.keyMap) for (let i = 0; i < token; i++) map.set(currentDecoder.decodeKey(read()),read());
|
|
414
433
|
else for (let i = 0; i < token; i++) map.set(read(), read());
|
|
@@ -939,18 +958,12 @@
|
|
|
939
958
|
return (glbl[data[0]] || Error)(data[1], data[2])
|
|
940
959
|
};
|
|
941
960
|
const packedTable = (read) => {
|
|
942
|
-
if (
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
error.incomplete = true;
|
|
946
|
-
throw error
|
|
947
|
-
}
|
|
961
|
+
if (!(position < srcEnd)) throw endOfCBORError()
|
|
962
|
+
if (src[position++] != 0x84)
|
|
963
|
+
throw new Error('Packed values structure must be followed by a 4 element array')
|
|
948
964
|
let newPackedValues = read(); // packed values
|
|
949
|
-
if (!newPackedValues || !newPackedValues.length)
|
|
950
|
-
|
|
951
|
-
error.incomplete = true;
|
|
952
|
-
throw error
|
|
953
|
-
}
|
|
965
|
+
if (!Array.isArray(newPackedValues) || !newPackedValues.length)
|
|
966
|
+
throw new Error('Packed values structure must be followed by a 4 element array')
|
|
954
967
|
packedValues = packedValues ? newPackedValues.concat(packedValues.slice(newPackedValues.length)) : newPackedValues;
|
|
955
968
|
packedValues.prefixes = read();
|
|
956
969
|
packedValues.suffixes = read();
|
|
@@ -968,10 +981,7 @@
|
|
|
968
981
|
}
|
|
969
982
|
if (typeof data == 'number')
|
|
970
983
|
return packedValues[16 + (data >= 0 ? 2 * data : (-2 * data - 1))]
|
|
971
|
-
|
|
972
|
-
if (data === undefined)
|
|
973
|
-
error.incomplete = true;
|
|
974
|
-
throw error
|
|
984
|
+
throw new Error('No support for non-integer packed references yet')
|
|
975
985
|
};
|
|
976
986
|
|
|
977
987
|
// The following code is an incomplete implementation of http://cbor.schmorp.de/stringref
|
|
@@ -1041,9 +1051,19 @@
|
|
|
1041
1051
|
(currentExtensions[259] = (read) => {
|
|
1042
1052
|
// https://github.com/shanewholloway/js-cbor-codec/blob/master/docs/CBOR-259-spec
|
|
1043
1053
|
// for decoding as a standard Map
|
|
1044
|
-
|
|
1045
|
-
|
|
1054
|
+
let decoder = currentDecoder;
|
|
1055
|
+
if (decoder.mapsAsObjects) {
|
|
1056
|
+
decoder.mapsAsObjects = false;
|
|
1046
1057
|
restoreMapsAsObject = true;
|
|
1058
|
+
try {
|
|
1059
|
+
return read()
|
|
1060
|
+
} finally {
|
|
1061
|
+
// normally restored by the map header, but not if the tagged value is not a map or the read fails
|
|
1062
|
+
if (restoreMapsAsObject) {
|
|
1063
|
+
decoder.mapsAsObjects = true;
|
|
1064
|
+
restoreMapsAsObject = false;
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1047
1067
|
}
|
|
1048
1068
|
return read()
|
|
1049
1069
|
}).handlesRead = true;
|
|
@@ -1163,10 +1183,12 @@
|
|
|
1163
1183
|
token = src[position++];
|
|
1164
1184
|
break
|
|
1165
1185
|
case 0x19:
|
|
1186
|
+
if (position + 2 > srcEnd) throw endOfCBORError()
|
|
1166
1187
|
token = dataView.getUint16(position);
|
|
1167
1188
|
position += 2;
|
|
1168
1189
|
break
|
|
1169
1190
|
case 0x1a:
|
|
1191
|
+
if (position + 4 > srcEnd) throw endOfCBORError()
|
|
1170
1192
|
token = dataView.getUint32(position);
|
|
1171
1193
|
position += 4;
|
|
1172
1194
|
break
|
|
@@ -1208,22 +1230,27 @@
|
|
|
1208
1230
|
let savedStructures = currentStructures;
|
|
1209
1231
|
let savedDecoder = currentDecoder;
|
|
1210
1232
|
let savedSequentialMode = sequentialMode;
|
|
1211
|
-
let
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1233
|
+
let savedRestoreMapsAsObject = restoreMapsAsObject;
|
|
1234
|
+
restoreMapsAsObject = false;
|
|
1235
|
+
try {
|
|
1236
|
+
return callback()
|
|
1237
|
+
} finally {
|
|
1238
|
+
srcEnd = savedSrcEnd;
|
|
1239
|
+
position = savedPosition;
|
|
1240
|
+
stringPosition = savedStringPosition;
|
|
1241
|
+
srcStringStart = savedSrcStringStart;
|
|
1242
|
+
srcStringEnd = savedSrcStringEnd;
|
|
1243
|
+
srcString = savedSrcString;
|
|
1244
|
+
strings = savedStrings;
|
|
1245
|
+
referenceMap = savedReferenceMap;
|
|
1246
|
+
bundledStrings = savedBundledStrings;
|
|
1247
|
+
src = savedSrc;
|
|
1248
|
+
sequentialMode = savedSequentialMode;
|
|
1249
|
+
currentStructures = savedStructures;
|
|
1250
|
+
currentDecoder = savedDecoder;
|
|
1251
|
+
restoreMapsAsObject = savedRestoreMapsAsObject;
|
|
1252
|
+
dataView = new DataView(src.buffer, src.byteOffset, src.byteLength);
|
|
1253
|
+
}
|
|
1227
1254
|
}
|
|
1228
1255
|
function clearSource() {
|
|
1229
1256
|
src = null;
|
|
@@ -1238,7 +1265,8 @@
|
|
|
1238
1265
|
function setSizeLimits(limits) {
|
|
1239
1266
|
if (limits.maxMapSize) maxMapSize = limits.maxMapSize;
|
|
1240
1267
|
if (limits.maxArraySize) maxArraySize = limits.maxArraySize;
|
|
1241
|
-
if (limits.maxObjectSize) limits.maxObjectSize;
|
|
1268
|
+
if (limits.maxObjectSize) maxObjectSize = limits.maxObjectSize;
|
|
1269
|
+
objectSizeLimit = Math.min(maxObjectSize, maxMapSize);
|
|
1242
1270
|
}
|
|
1243
1271
|
|
|
1244
1272
|
const mult10 = new Array(147); // this is a table matching binary exponents to the multiplier to determine significant digit rounding
|