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 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; // This is the maximum number of keys in a Map. It takes over a minute to create this
23
- // many keys in an object, so also probably a reasonable choice there.
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
- return this ? this.decode(source, end) : Decoder.prototype.decode.call(defaultOptions, source, end)
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
- let error = new Error('Unexpected bundle position');
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 (error instanceof RangeError || error.message.startsWith('Unexpected end of buffer')) {
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++ >= maxMapSize) throw new Error(`Property count exceeds ${maxMapSize}`)
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++ >= maxMapSize) throw new Error(`Property count exceeds ${maxMapSize}`)
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 >= maxArraySize) throw new Error(`Array length exceeds ${maxArraySize}`)
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 (src[position++] != 0x84) {
1018
- let error = new Error('Packed values structure must be followed by a 4 element array')
1019
- if (src.length < position)
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
- let error = new Error('Packed values structure must be followed by a 4 element array')
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
- let error = new Error('No support for non-integer packed references yet')
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
- if (currentDecoder.mapsAsObjects) {
1120
- currentDecoder.mapsAsObjects = false
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 value = callback()
1287
- srcEnd = savedSrcEnd
1288
- position = savedPosition
1289
- stringPosition = savedStringPosition
1290
- srcStringStart = savedSrcStringStart
1291
- srcStringEnd = savedSrcStringEnd
1292
- srcString = savedSrcString
1293
- strings = savedStrings
1294
- referenceMap = savedReferenceMap
1295
- bundledStrings = savedBundledStrings
1296
- src = savedSrc
1297
- sequentialMode = savedSequentialMode
1298
- currentStructures = savedStructures
1299
- currentDecoder = savedDecoder
1300
- dataView = new DataView(src.buffer, src.byteOffset, src.byteLength)
1301
- return value
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
@@ -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
- // many keys in an object, so also probably a reasonable choice there.
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
- return this ? this.decode(source, end) : Decoder.prototype.decode.call(defaultOptions, source, end)
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
- let error = new Error('Unexpected bundle position');
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 (error instanceof RangeError || error.message.startsWith('Unexpected end of buffer')) {
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++ >= maxMapSize) throw new Error(`Property count exceeds ${maxMapSize}`)
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++ >= maxMapSize) throw new Error(`Property count exceeds ${maxMapSize}`)
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 >= maxArraySize) throw new Error(`Array length exceeds ${maxArraySize}`)
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 (src[position++] != 0x84) {
943
- let error = new Error('Packed values structure must be followed by a 4 element array');
944
- if (src.length < position)
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
- let error = new Error('Packed values structure must be followed by a 4 element array');
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
- let error = new Error('No support for non-integer packed references yet');
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
- if (currentDecoder.mapsAsObjects) {
1045
- currentDecoder.mapsAsObjects = false;
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 value = callback();
1212
- srcEnd = savedSrcEnd;
1213
- position = savedPosition;
1214
- stringPosition = savedStringPosition;
1215
- srcStringStart = savedSrcStringStart;
1216
- srcStringEnd = savedSrcStringEnd;
1217
- srcString = savedSrcString;
1218
- strings = savedStrings;
1219
- referenceMap = savedReferenceMap;
1220
- bundledStrings = savedBundledStrings;
1221
- src = savedSrc;
1222
- sequentialMode = savedSequentialMode;
1223
- currentStructures = savedStructures;
1224
- currentDecoder = savedDecoder;
1225
- dataView = new DataView(src.buffer, src.byteOffset, src.byteLength);
1226
- return value
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