msgpackr 1.5.0 → 1.5.4

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/dist/node.cjs CHANGED
@@ -20,6 +20,7 @@ var currentStructures;
20
20
  var srcString;
21
21
  var srcStringStart = 0;
22
22
  var srcStringEnd = 0;
23
+ var bundledStrings;
23
24
  var referenceMap;
24
25
  var currentExtensions = [];
25
26
  var dataView;
@@ -60,6 +61,7 @@ class Unpackr {
60
61
  srcStringEnd = 0;
61
62
  srcString = null;
62
63
  strings = EMPTY_ARRAY;
64
+ bundledStrings = null;
63
65
  src = source;
64
66
  // this provides cached access to the data view for a buffer if it is getting reused, which is a recommend
65
67
  // technique for getting data from a database where it can be copied into an existing buffer instead of creating
@@ -156,6 +158,9 @@ function checkedRead() {
156
158
  currentStructures.length = sharedLength;
157
159
  }
158
160
  let result = read();
161
+ if (bundledStrings) // bundled strings to skip past
162
+ position = bundledStrings.postBundlePosition;
163
+
159
164
  if (position == srcEnd) {
160
165
  // finished reading this source, cleanup references
161
166
  if (currentStructures.restoreStructures)
@@ -250,7 +255,15 @@ function read() {
250
255
  let value;
251
256
  switch (token) {
252
257
  case 0xc0: return null
253
- case 0xc1: return C1; // "never-used", return special object to denote that
258
+ case 0xc1:
259
+ if (bundledStrings) {
260
+ value = read(); // followed by the length of the string in characters (not bytes!)
261
+ if (value > 0)
262
+ return bundledStrings[1].slice(bundledStrings.position1, bundledStrings.position1 += value)
263
+ else
264
+ return bundledStrings[0].slice(bundledStrings.position0, bundledStrings.position0 -= value)
265
+ }
266
+ return C1; // "never-used", return special object to denote that
254
267
  case 0xc2: return false
255
268
  case 0xc3: return true
256
269
  case 0xc4:
@@ -490,6 +503,8 @@ function setExtractor(extractStrings) {
490
503
  return function readString(length) {
491
504
  let string = strings[stringPosition++];
492
505
  if (string == null) {
506
+ if (bundledStrings)
507
+ return readStringJS(length)
493
508
  let extraction = extractStrings(position - headerLength, srcEnd, src);
494
509
  if (typeof extraction == 'string') {
495
510
  string = extraction;
@@ -748,6 +763,36 @@ function shortStringInJS(length) {
748
763
  }
749
764
  }
750
765
 
766
+ function readOnlyJSString() {
767
+ let token = src[position++];
768
+ let length;
769
+ if (token < 0xc0) {
770
+ // fixstr
771
+ length = token - 0xa0;
772
+ } else {
773
+ switch(token) {
774
+ case 0xd9:
775
+ // str 8
776
+ length = src[position++];
777
+ break
778
+ case 0xda:
779
+ // str 16
780
+ length = dataView.getUint16(position);
781
+ position += 2;
782
+ break
783
+ case 0xdb:
784
+ // str 32
785
+ length = dataView.getUint32(position);
786
+ position += 4;
787
+ break
788
+ default:
789
+ throw new Error('Expected string')
790
+ }
791
+ }
792
+ return readStringJS(length)
793
+ }
794
+
795
+
751
796
  function readBin(length) {
752
797
  return currentUnpackr.copyBuffers ?
753
798
  // specifically use the copying slice (not the node one)
@@ -899,6 +944,19 @@ currentExtensions[0x78] = () => {
899
944
  let data = read();
900
945
  return new RegExp(data[0], data[1])
901
946
  };
947
+ const TEMP_BUNDLE = [];
948
+ currentExtensions[0x62] = (data) => {
949
+ let dataSize = (data[0] << 24) + (data[1] << 16) + (data[2] << 8) + data[3];
950
+ let dataPosition = position;
951
+ position += dataSize - data.length;
952
+ bundledStrings = TEMP_BUNDLE;
953
+ bundledStrings = [readOnlyJSString(), readOnlyJSString()];
954
+ bundledStrings.position0 = 0;
955
+ bundledStrings.position1 = 0;
956
+ bundledStrings.postBundlePosition = position;
957
+ position = dataPosition;
958
+ return read()
959
+ };
902
960
 
903
961
  currentExtensions[0xff] = (data) => {
904
962
  // 32-bit date extension
@@ -913,7 +971,7 @@ currentExtensions[0xff] = (data) => {
913
971
  ((data[0] << 24) + (data[1] << 16) + (data[2] << 8) + data[3]) / 1000000 +
914
972
  (((data[4] & 0x80) ? -0x1000000000000 : 0) + data[6] * 0x10000000000 + data[7] * 0x100000000 + data[8] * 0x1000000 + (data[9] << 16) + (data[10] << 8) + data[11]) * 1000)
915
973
  else
916
- throw new Error('Invalid timestamp length')
974
+ return new Date('invalid')
917
975
  }; // notepack defines extension 0 to mean undefined, so use that as the default here
918
976
  // registration of bulk record definition?
919
977
  // currentExtensions[0x52] = () =>
@@ -927,6 +985,7 @@ function saveState(callback) {
927
985
  let savedSrcString = srcString;
928
986
  let savedStrings = strings;
929
987
  let savedReferenceMap = referenceMap;
988
+ let savedBundledStrings = bundledStrings;
930
989
 
931
990
  // TODO: We may need to revisit this if we do more external calls to user code (since it could be slow)
932
991
  let savedSrc = new Uint8Array(src.slice(0, srcEnd)); // we copy the data in case it changes while external data is processed
@@ -943,6 +1002,7 @@ function saveState(callback) {
943
1002
  srcString = savedSrcString;
944
1003
  strings = savedStrings;
945
1004
  referenceMap = savedReferenceMap;
1005
+ bundledStrings = savedBundledStrings;
946
1006
  src = savedSrc;
947
1007
  sequentialMode = savedSequentialMode;
948
1008
  currentStructures = savedStructures;
@@ -996,17 +1056,19 @@ const hasNodeBuffer = typeof Buffer !== 'undefined';
996
1056
  const ByteArrayAllocate = hasNodeBuffer ? Buffer.allocUnsafeSlow : Uint8Array;
997
1057
  const ByteArray = hasNodeBuffer ? Buffer : Uint8Array;
998
1058
  const MAX_BUFFER_SIZE = hasNodeBuffer ? 0x100000000 : 0x7fd00000;
999
- let target;
1059
+ let target, keysTarget;
1000
1060
  let targetView;
1001
1061
  let position$1 = 0;
1002
1062
  let safeEnd;
1063
+ let bundledStrings$1 = null;
1064
+ const MAX_BUNDLE_SIZE = 0xf000;
1065
+ const hasNonLatin = /[\u0080-\uFFFF]/;
1003
1066
  const RECORD_SYMBOL = Symbol('record-id');
1004
1067
  class Packr extends Unpackr {
1005
1068
  constructor(options) {
1006
1069
  super(options);
1007
1070
  this.offset = 0;
1008
1071
  let start;
1009
- let sharedStructures;
1010
1072
  let hasSharedUpdate;
1011
1073
  let structures;
1012
1074
  let referenceMap;
@@ -1031,7 +1093,7 @@ class Packr extends Unpackr {
1031
1093
  let maxOwnStructures = options.maxOwnStructures;
1032
1094
  if (maxOwnStructures == null)
1033
1095
  maxOwnStructures = hasSharedStructures ? 32 : 64;
1034
- if (isSequential && !options.saveStructures)
1096
+ if (!this.structures && options.useRecords != false)
1035
1097
  this.structures = [];
1036
1098
  // two byte record ids for shared structures
1037
1099
  let useTwoByteRecords = maxSharedStructures > 32 || (maxOwnStructures + maxSharedStructures > 64);
@@ -1061,23 +1123,28 @@ class Packr extends Unpackr {
1061
1123
  position$1 = (position$1 + 7) & 0x7ffffff8; // Word align to make any future copying of this buffer faster
1062
1124
  start = position$1;
1063
1125
  referenceMap = packr.structuredClone ? new Map() : null;
1064
- sharedStructures = packr.structures;
1065
- if (sharedStructures) {
1066
- if (sharedStructures.uninitialized)
1067
- sharedStructures = packr._mergeStructures(packr.getStructures());
1068
- let sharedLength = sharedStructures.sharedLength || 0;
1126
+ if (packr.bundleStrings && typeof value !== 'string') {
1127
+ bundledStrings$1 = [];
1128
+ bundledStrings$1.size = Infinity; // force a new bundle start on first string
1129
+ } else
1130
+ bundledStrings$1 = null;
1131
+ structures = packr.structures;
1132
+ if (structures) {
1133
+ if (structures.uninitialized)
1134
+ structures = packr._mergeStructures(packr.getStructures());
1135
+ let sharedLength = structures.sharedLength || 0;
1069
1136
  if (sharedLength > maxSharedStructures) {
1070
- //if (maxSharedStructures <= 32 && sharedStructures.sharedLength > 32) // TODO: could support this, but would need to update the limit ids
1071
- throw new Error('Shared structures is larger than maximum shared structures, try increasing maxSharedStructures to ' + sharedStructures.sharedLength)
1137
+ //if (maxSharedStructures <= 32 && structures.sharedLength > 32) // TODO: could support this, but would need to update the limit ids
1138
+ throw new Error('Shared structures is larger than maximum shared structures, try increasing maxSharedStructures to ' + structures.sharedLength)
1072
1139
  }
1073
- if (!sharedStructures.transitions) {
1140
+ if (!structures.transitions) {
1074
1141
  // rebuild our structure transitions
1075
- sharedStructures.transitions = Object.create(null);
1142
+ structures.transitions = Object.create(null);
1076
1143
  for (let i = 0; i < sharedLength; i++) {
1077
- let keys = sharedStructures[i];
1144
+ let keys = structures[i];
1078
1145
  if (!keys)
1079
1146
  continue
1080
- let nextTransition, transition = sharedStructures.transitions;
1147
+ let nextTransition, transition = structures.transitions;
1081
1148
  for (let j = 0, l = keys.length; j < l; j++) {
1082
1149
  let key = keys[j];
1083
1150
  nextTransition = transition[key];
@@ -1091,14 +1158,16 @@ class Packr extends Unpackr {
1091
1158
  lastSharedStructuresLength = sharedLength;
1092
1159
  }
1093
1160
  if (!isSequential) {
1094
- sharedStructures.nextId = sharedLength + 0x40;
1161
+ structures.nextId = sharedLength + 0x40;
1095
1162
  }
1096
1163
  }
1097
1164
  if (hasSharedUpdate)
1098
1165
  hasSharedUpdate = false;
1099
- structures = sharedStructures || [];
1100
1166
  try {
1101
1167
  pack(value);
1168
+ if (bundledStrings$1) {
1169
+ writeBundles(start, pack);
1170
+ }
1102
1171
  packr.offset = position$1; // update the offset so next serialization doesn't write over our buffer, but can continue writing to same buffer sequentially
1103
1172
  if (referenceMap && referenceMap.idsToInsert) {
1104
1173
  position$1 += referenceMap.idsToInsert.length * 6;
@@ -1109,19 +1178,22 @@ class Packr extends Unpackr {
1109
1178
  referenceMap = null;
1110
1179
  return serialized
1111
1180
  }
1112
- if (encodeOptions === REUSE_BUFFER_MODE) {
1181
+ if (encodeOptions & REUSE_BUFFER_MODE) {
1113
1182
  target.start = start;
1114
1183
  target.end = position$1;
1115
1184
  return target
1116
1185
  }
1117
1186
  return target.subarray(start, position$1) // position can change if we call pack again in saveStructures, so we get the buffer now
1118
1187
  } finally {
1119
- if (sharedStructures) {
1188
+ if (structures) {
1120
1189
  if (serializationsSinceTransitionRebuild < 10)
1121
1190
  serializationsSinceTransitionRebuild++;
1191
+ let sharedLength = structures.sharedLength || maxSharedStructures;
1192
+ if (structures.length > sharedLength)
1193
+ structures.length = sharedLength;
1122
1194
  if (transitionsCount > 10000) {
1123
1195
  // force a rebuild occasionally after a lot of transitions so it can get cleaned up
1124
- sharedStructures.transitions = null;
1196
+ structures.transitions = null;
1125
1197
  serializationsSinceTransitionRebuild = 0;
1126
1198
  transitionsCount = 0;
1127
1199
  if (recordIdsToRemove.length > 0)
@@ -1133,13 +1205,9 @@ class Packr extends Unpackr {
1133
1205
  recordIdsToRemove = [];
1134
1206
  }
1135
1207
  if (hasSharedUpdate && packr.saveStructures) {
1136
- let sharedLength = sharedStructures.sharedLength || maxSharedStructures;
1137
- if (sharedStructures.length > sharedLength) {
1138
- sharedStructures = sharedStructures.slice(0, sharedLength);
1139
- }
1140
1208
  // we can't rely on start/end with REUSE_BUFFER_MODE since they will (probably) change when we save
1141
1209
  let returnBuffer = target.subarray(start, position$1);
1142
- if (packr.saveStructures(sharedStructures, lastSharedStructuresLength) === false) {
1210
+ if (packr.saveStructures(structures, lastSharedStructuresLength) === false) {
1143
1211
  // get updated structures and try again if the update failed
1144
1212
  packr._mergeStructures(packr.getStructures());
1145
1213
  return packr.pack(value)
@@ -1148,6 +1216,8 @@ class Packr extends Unpackr {
1148
1216
  return returnBuffer
1149
1217
  }
1150
1218
  }
1219
+ if (encodeOptions & RESET_BUFFER_MODE)
1220
+ position$1 = start;
1151
1221
  }
1152
1222
  };
1153
1223
  const pack = (value) => {
@@ -1158,6 +1228,36 @@ class Packr extends Unpackr {
1158
1228
  var length;
1159
1229
  if (type === 'string') {
1160
1230
  let strLength = value.length;
1231
+ if (bundledStrings$1 && strLength >= 4 && strLength < 0x1000) {
1232
+ if ((bundledStrings$1.size += strLength) > MAX_BUNDLE_SIZE) {
1233
+ let extStart;
1234
+ let maxBytes = (bundledStrings$1[0] ? bundledStrings$1[0].length * 3 + bundledStrings$1[1].length : 0) + 10;
1235
+ if (position$1 + maxBytes > safeEnd)
1236
+ target = makeRoom(position$1 + maxBytes);
1237
+ if (bundledStrings$1.position) { // here we use the 0x62 extension to write the last bundle and reserve sapce for the reference pointer to the next/current bundle
1238
+ target[position$1] = 0xc8; // ext 16
1239
+ position$1 += 3; // reserve for the writing bundle size
1240
+ target[position$1++] = 0x62; // 'b'
1241
+ extStart = position$1 - start;
1242
+ position$1 += 4; // reserve for writing bundle reference
1243
+ writeBundles(start, pack); // write the last bundles
1244
+ targetView.setUint16(extStart + start - 3, position$1 - start - extStart);
1245
+ } else { // here we use the 0x62 extension just to reserve the space for the reference pointer to the bundle (will be updated once the bundle is written)
1246
+ target[position$1++] = 0xd6; // fixext 4
1247
+ target[position$1++] = 0x62; // 'b'
1248
+ extStart = position$1 - start;
1249
+ position$1 += 4; // reserve for writing bundle reference
1250
+ }
1251
+ bundledStrings$1 = ['', '']; // create new ones
1252
+ bundledStrings$1.size = 0;
1253
+ bundledStrings$1.position = extStart;
1254
+ }
1255
+ let twoByte = hasNonLatin.test(value);
1256
+ bundledStrings$1[twoByte ? 0 : 1] += value;
1257
+ target[position$1++] = 0xc1;
1258
+ pack(twoByte ? -strLength : strLength);
1259
+ return
1260
+ }
1161
1261
  let headerSize;
1162
1262
  // first we estimate the header size, so we can write to the correct location
1163
1263
  if (strLength < 0x20) {
@@ -1456,53 +1556,55 @@ class Packr extends Unpackr {
1456
1556
  target[objectOffset++ + start] = size >> 8;
1457
1557
  target[objectOffset + start] = size & 0xff;
1458
1558
  } :
1459
-
1460
- /* sharedStructures ? // For highly stable structures, using for-in can a little bit faster
1559
+ (options.progressiveRecords && !useTwoByteRecords) ? // this is about 2% faster for highly stable structures, since it only requires one for-in loop (but much more expensive when new structure needs to be written)
1461
1560
  (object, safePrototype) => {
1462
- let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null))
1463
- let objectOffset = position++ - start
1464
- let wroteKeys
1561
+ let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null));
1562
+ let objectOffset = position$1++ - start;
1563
+ let wroteKeys;
1465
1564
  for (let key in object) {
1466
1565
  if (safePrototype || object.hasOwnProperty(key)) {
1467
- nextTransition = transition[key]
1468
- if (!nextTransition) {
1469
- nextTransition = transition[key] = Object.create(null)
1470
- nextTransition.__keys__ = (transition.__keys__ || []).concat([key])
1471
- /*let keys = Object.keys(object)
1472
- if
1473
- let size = 0
1474
- let startBranch = transition.__keys__ ? transition.__keys__.length : 0
1475
- for (let i = 0, l = keys.length; i++) {
1476
- let key = keys[i]
1477
- size += key.length << 2
1478
- if (i >= startBranch) {
1479
- nextTransition = nextTransition[key] = Object.create(null)
1480
- nextTransition.__keys__ = keys.slice(0, i + 1)
1566
+ nextTransition = transition[key];
1567
+ if (nextTransition)
1568
+ transition = nextTransition;
1569
+ else {
1570
+ // record doesn't exist, create full new record and insert it
1571
+ let keys = Object.keys(object);
1572
+ let lastTransition = transition;
1573
+ transition = structures.transitions;
1574
+ let newTransitions = 0;
1575
+ for (let i = 0, l = keys.length; i < l; i++) {
1576
+ let key = keys[i];
1577
+ nextTransition = transition[key];
1578
+ if (!nextTransition) {
1579
+ nextTransition = transition[key] = Object.create(null);
1580
+ newTransitions++;
1481
1581
  }
1582
+ transition = nextTransition;
1482
1583
  }
1483
- makeRoom(position + size)
1484
- nextTransition = transition[key]
1485
- target.copy(target, )
1486
- objectOffset
1584
+ if (objectOffset + start + 1 == position$1) {
1585
+ // first key, so we don't need to insert, we can just write record directly
1586
+ position$1--;
1587
+ newRecord(transition, keys, newTransitions);
1588
+ } else // otherwise we need to insert the record, moving existing data after the record
1589
+ insertNewRecord(transition, keys, objectOffset, newTransitions);
1590
+ wroteKeys = true;
1591
+ transition = lastTransition[key];
1487
1592
  }
1488
- transition = nextTransition
1489
- pack(object[key])
1593
+ pack(object[key]);
1490
1594
  }
1491
1595
  }
1492
- let id = transition.id
1493
- if (!id) {
1494
- id = transition.id = structures.push(transition.__keys__) + 63
1495
- if (sharedStructures.onUpdate)
1496
- sharedStructures.onUpdate(id, transition.__keys__)
1596
+ if (!wroteKeys) {
1597
+ let recordId = transition[RECORD_SYMBOL];
1598
+ if (recordId)
1599
+ target[objectOffset + start] = recordId;
1600
+ else
1601
+ insertNewRecord(transition, Object.keys(object), objectOffset, 0);
1497
1602
  }
1498
- target[objectOffset + start] = id
1499
- }*/
1500
- (object) => {
1501
- let keys = Object.keys(object);
1603
+ } :
1604
+ (object, safePrototype) => {
1502
1605
  let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null));
1503
1606
  let newTransitions = 0;
1504
- for (let i = 0, l = keys.length; i < l; i++) {
1505
- let key = keys[i];
1607
+ for (let key in object) if (safePrototype || object.hasOwnProperty(key)) {
1506
1608
  nextTransition = transition[key];
1507
1609
  if (!nextTransition) {
1508
1610
  nextTransition = transition[key] = Object.create(null);
@@ -1518,57 +1620,12 @@ class Packr extends Unpackr {
1518
1620
  } else
1519
1621
  target[position$1++] = recordId;
1520
1622
  } else {
1521
- recordId = structures.nextId;
1522
- if (!recordId)
1523
- recordId = 0x40;
1524
- if (recordId < sharedLimitId && this.shouldShareStructure && !this.shouldShareStructure(keys)) {
1525
- recordId = structures.nextOwnId;
1526
- if (!(recordId < maxStructureId))
1527
- recordId = sharedLimitId;
1528
- structures.nextOwnId = recordId + 1;
1529
- } else {
1530
- if (recordId >= maxStructureId)// cycle back around
1531
- recordId = sharedLimitId;
1532
- structures.nextId = recordId + 1;
1533
- }
1534
- let highByte = keys.highByte = recordId >= 0x60 && useTwoByteRecords ? (recordId - 0x60) >> 5 : -1;
1535
- transition[RECORD_SYMBOL] = recordId;
1536
- structures[recordId - 0x40] = keys;
1537
-
1538
- if (recordId < sharedLimitId) {
1539
- keys.isShared = true;
1540
- structures.sharedLength = recordId - 0x3f;
1541
- hasSharedUpdate = true;
1542
- if (highByte >= 0) {
1543
- target[position$1++] = (recordId & 0x1f) + 0x60;
1544
- target[position$1++] = highByte;
1545
- } else {
1546
- target[position$1++] = recordId;
1547
- }
1548
- } else {
1549
- if (highByte >= 0) {
1550
- target[position$1++] = 0xd5; // fixext 2
1551
- target[position$1++] = 0x72; // "r" record defintion extension type
1552
- target[position$1++] = (recordId & 0x1f) + 0x60;
1553
- target[position$1++] = highByte;
1554
- } else {
1555
- target[position$1++] = 0xd4; // fixext 1
1556
- target[position$1++] = 0x72; // "r" record defintion extension type
1557
- target[position$1++] = recordId;
1558
- }
1559
-
1560
- if (newTransitions)
1561
- transitionsCount += serializationsSinceTransitionRebuild * newTransitions;
1562
- // record the removal of the id, we can maintain our shared structure
1563
- if (recordIdsToRemove.length >= maxOwnStructures)
1564
- recordIdsToRemove.shift()[RECORD_SYMBOL] = 0; // we are cycling back through, and have to remove old ones
1565
- recordIdsToRemove.push(transition);
1566
- pack(keys);
1567
- }
1623
+ newRecord(transition, transition.__keys__ || Object.keys(object), newTransitions);
1568
1624
  }
1569
1625
  // now write the values
1570
- for (let i = 0, l = keys.length; i < l; i++)
1571
- pack(object[keys[i]]);
1626
+ for (let key in object)
1627
+ if (safePrototype || object.hasOwnProperty(key))
1628
+ pack(object[key]);
1572
1629
  };
1573
1630
  const makeRoom = (end) => {
1574
1631
  let newSize;
@@ -1591,6 +1648,86 @@ class Packr extends Unpackr {
1591
1648
  safeEnd = newBuffer.length - 10;
1592
1649
  return target = newBuffer
1593
1650
  };
1651
+ const newRecord = (transition, keys, newTransitions) => {
1652
+ let recordId = structures.nextId;
1653
+ if (!recordId)
1654
+ recordId = 0x40;
1655
+ if (recordId < sharedLimitId && this.shouldShareStructure && !this.shouldShareStructure(keys)) {
1656
+ recordId = structures.nextOwnId;
1657
+ if (!(recordId < maxStructureId))
1658
+ recordId = sharedLimitId;
1659
+ structures.nextOwnId = recordId + 1;
1660
+ } else {
1661
+ if (recordId >= maxStructureId)// cycle back around
1662
+ recordId = sharedLimitId;
1663
+ structures.nextId = recordId + 1;
1664
+ }
1665
+ let highByte = keys.highByte = recordId >= 0x60 && useTwoByteRecords ? (recordId - 0x60) >> 5 : -1;
1666
+ transition[RECORD_SYMBOL] = recordId;
1667
+ transition.__keys__ = keys;
1668
+ structures[recordId - 0x40] = keys;
1669
+
1670
+ if (recordId < sharedLimitId) {
1671
+ keys.isShared = true;
1672
+ structures.sharedLength = recordId - 0x3f;
1673
+ hasSharedUpdate = true;
1674
+ if (highByte >= 0) {
1675
+ target[position$1++] = (recordId & 0x1f) + 0x60;
1676
+ target[position$1++] = highByte;
1677
+ } else {
1678
+ target[position$1++] = recordId;
1679
+ }
1680
+ } else {
1681
+ if (highByte >= 0) {
1682
+ target[position$1++] = 0xd5; // fixext 2
1683
+ target[position$1++] = 0x72; // "r" record defintion extension type
1684
+ target[position$1++] = (recordId & 0x1f) + 0x60;
1685
+ target[position$1++] = highByte;
1686
+ } else {
1687
+ target[position$1++] = 0xd4; // fixext 1
1688
+ target[position$1++] = 0x72; // "r" record defintion extension type
1689
+ target[position$1++] = recordId;
1690
+ }
1691
+
1692
+ if (newTransitions)
1693
+ transitionsCount += serializationsSinceTransitionRebuild * newTransitions;
1694
+ // record the removal of the id, we can maintain our shared structure
1695
+ if (recordIdsToRemove.length >= maxOwnStructures)
1696
+ recordIdsToRemove.shift()[RECORD_SYMBOL] = 0; // we are cycling back through, and have to remove old ones
1697
+ recordIdsToRemove.push(transition);
1698
+ pack(keys);
1699
+ }
1700
+ };
1701
+ const insertNewRecord = (transition, keys, insertionOffset, newTransitions) => {
1702
+ let mainTarget = target;
1703
+ let mainPosition = position$1;
1704
+ let mainSafeEnd = safeEnd;
1705
+ let mainStart = start;
1706
+ target = keysTarget;
1707
+ position$1 = 0;
1708
+ start = 0;
1709
+ if (!target)
1710
+ keysTarget = target = new ByteArrayAllocate(8192);
1711
+ safeEnd = target.length - 10;
1712
+ newRecord(transition, keys, newTransitions);
1713
+ keysTarget = target;
1714
+ let keysPosition = position$1;
1715
+ target = mainTarget;
1716
+ position$1 = mainPosition;
1717
+ safeEnd = mainSafeEnd;
1718
+ start = mainStart;
1719
+ if (keysPosition > 1) {
1720
+ let newEnd = position$1 + keysPosition - 1;
1721
+ if (newEnd > safeEnd)
1722
+ makeRoom(newEnd);
1723
+ let insertionPosition = insertionOffset + start;
1724
+ target.copyWithin(insertionPosition + keysPosition, insertionPosition + 1, position$1);
1725
+ target.set(keysTarget.slice(0, keysPosition), insertionPosition);
1726
+ position$1 = newEnd;
1727
+ } else {
1728
+ target[insertionOffset + start] = keysTarget[0];
1729
+ }
1730
+ };
1594
1731
  }
1595
1732
  useBuffer(buffer) {
1596
1733
  // this means we are finished using our own buffer and we can write over it safely
@@ -1598,11 +1735,15 @@ class Packr extends Unpackr {
1598
1735
  targetView = new DataView(target.buffer, target.byteOffset, target.byteLength);
1599
1736
  position$1 = 0;
1600
1737
  }
1738
+ clearSharedData() {
1739
+ if (this.structures)
1740
+ this.structures = [];
1741
+ }
1601
1742
  }
1602
1743
 
1603
1744
  extensionClasses = [ Date, Set, Error, RegExp, ArrayBuffer, Object.getPrototypeOf(Uint8Array.prototype).constructor /*TypedArray*/, C1Type ];
1604
1745
  extensions = [{
1605
- pack(date, allocateForWrite) {
1746
+ pack(date, allocateForWrite, pack) {
1606
1747
  let seconds = date.getTime() / 1000;
1607
1748
  if ((this.useTimestamp32 || date.getMilliseconds() === 0) && seconds >= 0 && seconds < 0x100000000) {
1608
1749
  // Timestamp 32
@@ -1617,6 +1758,16 @@ extensions = [{
1617
1758
  target[position++] = 0xff;
1618
1759
  targetView.setUint32(position, date.getMilliseconds() * 4000000 + ((seconds / 1000 / 0x100000000) >> 0));
1619
1760
  targetView.setUint32(position + 4, seconds);
1761
+ } else if (isNaN(seconds)) {
1762
+ if (this.onInvalidDate) {
1763
+ allocateForWrite(0);
1764
+ return pack(this.onInvalidDate())
1765
+ }
1766
+ // Intentionally invalid timestamp
1767
+ let { target, targetView, position} = allocateForWrite(3);
1768
+ target[position++] = 0xd4;
1769
+ target[position++] = 0xff;
1770
+ target[position++] = 0xff;
1620
1771
  } else {
1621
1772
  // Timestamp 96
1622
1773
  let { target, targetView, position} = allocateForWrite(15);
@@ -1785,6 +1936,14 @@ function insertIds(serialized, idsToInsert) {
1785
1936
  return serialized
1786
1937
  }
1787
1938
 
1939
+ function writeBundles(start, pack) {
1940
+ targetView.setUint32(bundledStrings$1.position + start, position$1 - bundledStrings$1.position - start);
1941
+ let writeStrings = bundledStrings$1;
1942
+ bundledStrings$1 = null;
1943
+ pack(writeStrings[0]);
1944
+ pack(writeStrings[1]);
1945
+ }
1946
+
1788
1947
  function addExtension$1(extension) {
1789
1948
  if (extension.Class) {
1790
1949
  if (!extension.pack && !extension.write)
@@ -1802,7 +1961,8 @@ const pack = defaultPackr.pack;
1802
1961
  const encode = defaultPackr.pack;
1803
1962
  const Encoder = Packr;
1804
1963
  const { NEVER, ALWAYS, DECIMAL_ROUND, DECIMAL_FIT } = FLOAT32_OPTIONS;
1805
- const REUSE_BUFFER_MODE = 1000;
1964
+ const REUSE_BUFFER_MODE = 512;
1965
+ const RESET_BUFFER_MODE = 1024;
1806
1966
 
1807
1967
  class PackrStream extends stream.Transform {
1808
1968
  constructor(options) {