msgpackr 1.5.2 → 1.5.3

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
@@ -158,6 +158,9 @@ function checkedRead() {
158
158
  currentStructures.length = sharedLength;
159
159
  }
160
160
  let result = read();
161
+ if (bundledStrings) // bundled strings to skip past
162
+ position = bundledStrings.postBundlePosition;
163
+
161
164
  if (position == srcEnd) {
162
165
  // finished reading this source, cleanup references
163
166
  if (currentStructures.restoreStructures)
@@ -500,6 +503,8 @@ function setExtractor(extractStrings) {
500
503
  return function readString(length) {
501
504
  let string = strings[stringPosition++];
502
505
  if (string == null) {
506
+ if (bundledStrings)
507
+ return readStringJS(length)
503
508
  let extraction = extractStrings(position - headerLength, srcEnd, src);
504
509
  if (typeof extraction == 'string') {
505
510
  string = extraction;
@@ -758,6 +763,36 @@ function shortStringInJS(length) {
758
763
  }
759
764
  }
760
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
+
761
796
  function readBin(length) {
762
797
  return currentUnpackr.copyBuffers ?
763
798
  // specifically use the copying slice (not the node one)
@@ -909,21 +944,18 @@ currentExtensions[0x78] = () => {
909
944
  let data = read();
910
945
  return new RegExp(data[0], data[1])
911
946
  };
912
-
947
+ const TEMP_BUNDLE = [];
913
948
  currentExtensions[0x62] = (data) => {
914
949
  let dataSize = (data[0] << 24) + (data[1] << 16) + (data[2] << 8) + data[3];
915
950
  let dataPosition = position;
916
- position += dataSize - 4;
917
- bundledStrings = [read(), read()];
951
+ position += dataSize - data.length;
952
+ bundledStrings = TEMP_BUNDLE;
953
+ bundledStrings = [readOnlyJSString(), readOnlyJSString()];
918
954
  bundledStrings.position0 = 0;
919
955
  bundledStrings.position1 = 0;
920
- let postBundlePosition = position;
956
+ bundledStrings.postBundlePosition = position;
921
957
  position = dataPosition;
922
- try {
923
- return read()
924
- } finally {
925
- position = postBundlePosition;
926
- }
958
+ return read()
927
959
  };
928
960
 
929
961
  currentExtensions[0xff] = (data) => {
@@ -1024,11 +1056,12 @@ const hasNodeBuffer = typeof Buffer !== 'undefined';
1024
1056
  const ByteArrayAllocate = hasNodeBuffer ? Buffer.allocUnsafeSlow : Uint8Array;
1025
1057
  const ByteArray = hasNodeBuffer ? Buffer : Uint8Array;
1026
1058
  const MAX_BUFFER_SIZE = hasNodeBuffer ? 0x100000000 : 0x7fd00000;
1027
- let target;
1059
+ let target, keysTarget;
1028
1060
  let targetView;
1029
1061
  let position$1 = 0;
1030
1062
  let safeEnd;
1031
1063
  let bundledStrings$1 = null;
1064
+ const MAX_BUNDLE_SIZE = 0xf000;
1032
1065
  const hasNonLatin = /[\u0080-\uFFFF]/;
1033
1066
  const RECORD_SYMBOL = Symbol('record-id');
1034
1067
  class Packr extends Unpackr {
@@ -1091,12 +1124,9 @@ class Packr extends Unpackr {
1091
1124
  position$1 = (position$1 + 7) & 0x7ffffff8; // Word align to make any future copying of this buffer faster
1092
1125
  start = position$1;
1093
1126
  referenceMap = packr.structuredClone ? new Map() : null;
1094
- if (packr.bundleStrings) {
1095
- bundledStrings$1 = ['', ''];
1096
- target[position$1++] = 0xd6;
1097
- target[position$1++] = 0x62; // 'b'
1098
- bundledStrings$1.position = position$1 - start;
1099
- position$1 += 4;
1127
+ if (packr.bundleStrings && typeof value !== 'string') {
1128
+ bundledStrings$1 = [];
1129
+ bundledStrings$1.size = Infinity; // force a new bundle start on first string
1100
1130
  } else
1101
1131
  bundledStrings$1 = null;
1102
1132
  sharedStructures = packr.structures;
@@ -1134,15 +1164,11 @@ class Packr extends Unpackr {
1134
1164
  }
1135
1165
  if (hasSharedUpdate)
1136
1166
  hasSharedUpdate = false;
1137
- structures = sharedStructures || [];
1167
+ structures = sharedStructures || (packr.structures = []);
1138
1168
  try {
1139
1169
  pack(value);
1140
1170
  if (bundledStrings$1) {
1141
- targetView.setUint32(bundledStrings$1.position + start, position$1 - bundledStrings$1.position - start);
1142
- let writeStrings = bundledStrings$1;
1143
- bundledStrings$1 = null;
1144
- pack(writeStrings[0]);
1145
- pack(writeStrings[1]);
1171
+ writeBundles(start, pack);
1146
1172
  }
1147
1173
  packr.offset = position$1; // update the offset so next serialization doesn't write over our buffer, but can continue writing to same buffer sequentially
1148
1174
  if (referenceMap && referenceMap.idsToInsert) {
@@ -1164,6 +1190,8 @@ class Packr extends Unpackr {
1164
1190
  if (sharedStructures) {
1165
1191
  if (serializationsSinceTransitionRebuild < 10)
1166
1192
  serializationsSinceTransitionRebuild++;
1193
+ if (sharedStructures.length > maxSharedStructures)
1194
+ sharedStructures.length = maxSharedStructures;
1167
1195
  if (transitionsCount > 10000) {
1168
1196
  // force a rebuild occasionally after a lot of transitions so it can get cleaned up
1169
1197
  sharedStructures.transitions = null;
@@ -1205,7 +1233,30 @@ class Packr extends Unpackr {
1205
1233
  var length;
1206
1234
  if (type === 'string') {
1207
1235
  let strLength = value.length;
1208
- if (bundledStrings$1 && strLength >= 8 && strLength < 0x1000) {
1236
+ if (bundledStrings$1 && strLength >= 4 && strLength < 0x1000) {
1237
+ if ((bundledStrings$1.size += strLength) > MAX_BUNDLE_SIZE) {
1238
+ let extStart;
1239
+ let maxBytes = (bundledStrings$1[0] ? bundledStrings$1[0].length * 3 + bundledStrings$1[1].length : 0) + 10;
1240
+ if (position$1 + maxBytes > safeEnd)
1241
+ target = makeRoom(position$1 + maxBytes);
1242
+ 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
1243
+ target[position$1] = 0xc8; // ext 16
1244
+ position$1 += 3; // reserve for the writing bundle size
1245
+ target[position$1++] = 0x62; // 'b'
1246
+ extStart = position$1 - start;
1247
+ position$1 += 4; // reserve for writing bundle reference
1248
+ writeBundles(start, pack); // write the last bundles
1249
+ targetView.setUint16(extStart + start - 3, position$1 - start - extStart);
1250
+ } 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)
1251
+ target[position$1++] = 0xd6; // fixext 4
1252
+ target[position$1++] = 0x62; // 'b'
1253
+ extStart = position$1 - start;
1254
+ position$1 += 4; // reserve for writing bundle reference
1255
+ }
1256
+ bundledStrings$1 = ['', '']; // create new ones
1257
+ bundledStrings$1.size = 0;
1258
+ bundledStrings$1.position = extStart;
1259
+ }
1209
1260
  let twoByte = hasNonLatin.test(value);
1210
1261
  bundledStrings$1[twoByte ? 0 : 1] += value;
1211
1262
  target[position$1++] = 0xc1;
@@ -1510,53 +1561,55 @@ class Packr extends Unpackr {
1510
1561
  target[objectOffset++ + start] = size >> 8;
1511
1562
  target[objectOffset + start] = size & 0xff;
1512
1563
  } :
1513
-
1514
- /* sharedStructures ? // For highly stable structures, using for-in can a little bit faster
1564
+ (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)
1515
1565
  (object, safePrototype) => {
1516
- let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null))
1517
- let objectOffset = position++ - start
1518
- let wroteKeys
1566
+ let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null));
1567
+ let objectOffset = position$1++ - start;
1568
+ let wroteKeys;
1519
1569
  for (let key in object) {
1520
1570
  if (safePrototype || object.hasOwnProperty(key)) {
1521
- nextTransition = transition[key]
1522
- if (!nextTransition) {
1523
- nextTransition = transition[key] = Object.create(null)
1524
- nextTransition.__keys__ = (transition.__keys__ || []).concat([key])
1525
- /*let keys = Object.keys(object)
1526
- if
1527
- let size = 0
1528
- let startBranch = transition.__keys__ ? transition.__keys__.length : 0
1529
- for (let i = 0, l = keys.length; i++) {
1530
- let key = keys[i]
1531
- size += key.length << 2
1532
- if (i >= startBranch) {
1533
- nextTransition = nextTransition[key] = Object.create(null)
1534
- nextTransition.__keys__ = keys.slice(0, i + 1)
1571
+ nextTransition = transition[key];
1572
+ if (nextTransition)
1573
+ transition = nextTransition;
1574
+ else {
1575
+ // record doesn't exist, create full new record and insert it
1576
+ let keys = Object.keys(object);
1577
+ let lastTransition = transition;
1578
+ transition = structures.transitions;
1579
+ let newTransitions = 0;
1580
+ for (let i = 0, l = keys.length; i < l; i++) {
1581
+ let key = keys[i];
1582
+ nextTransition = transition[key];
1583
+ if (!nextTransition) {
1584
+ nextTransition = transition[key] = Object.create(null);
1585
+ newTransitions++;
1535
1586
  }
1587
+ transition = nextTransition;
1536
1588
  }
1537
- makeRoom(position + size)
1538
- nextTransition = transition[key]
1539
- target.copy(target, )
1540
- objectOffset
1589
+ if (objectOffset + start + 1 == position$1) {
1590
+ // first key, so we don't need to insert, we can just write record directly
1591
+ position$1--;
1592
+ newRecord(transition, keys, newTransitions);
1593
+ } else // otherwise we need to insert the record, moving existing data after the record
1594
+ insertNewRecord(transition, keys, objectOffset, newTransitions);
1595
+ wroteKeys = true;
1596
+ transition = lastTransition[key];
1541
1597
  }
1542
- transition = nextTransition
1543
- pack(object[key])
1598
+ pack(object[key]);
1544
1599
  }
1545
1600
  }
1546
- let id = transition.id
1547
- if (!id) {
1548
- id = transition.id = structures.push(transition.__keys__) + 63
1549
- if (sharedStructures.onUpdate)
1550
- sharedStructures.onUpdate(id, transition.__keys__)
1601
+ if (!wroteKeys) {
1602
+ let recordId = transition[RECORD_SYMBOL];
1603
+ if (recordId)
1604
+ target[objectOffset + start] = recordId;
1605
+ else
1606
+ insertNewRecord(transition, Object.keys(object), objectOffset, 0);
1551
1607
  }
1552
- target[objectOffset + start] = id
1553
- }*/
1554
- (object) => {
1555
- let keys = Object.keys(object);
1608
+ } :
1609
+ (object, safePrototype) => {
1556
1610
  let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null));
1557
1611
  let newTransitions = 0;
1558
- for (let i = 0, l = keys.length; i < l; i++) {
1559
- let key = keys[i];
1612
+ for (let key in object) if (safePrototype || object.hasOwnProperty(key)) {
1560
1613
  nextTransition = transition[key];
1561
1614
  if (!nextTransition) {
1562
1615
  nextTransition = transition[key] = Object.create(null);
@@ -1572,57 +1625,12 @@ class Packr extends Unpackr {
1572
1625
  } else
1573
1626
  target[position$1++] = recordId;
1574
1627
  } else {
1575
- recordId = structures.nextId;
1576
- if (!recordId)
1577
- recordId = 0x40;
1578
- if (recordId < sharedLimitId && this.shouldShareStructure && !this.shouldShareStructure(keys)) {
1579
- recordId = structures.nextOwnId;
1580
- if (!(recordId < maxStructureId))
1581
- recordId = sharedLimitId;
1582
- structures.nextOwnId = recordId + 1;
1583
- } else {
1584
- if (recordId >= maxStructureId)// cycle back around
1585
- recordId = sharedLimitId;
1586
- structures.nextId = recordId + 1;
1587
- }
1588
- let highByte = keys.highByte = recordId >= 0x60 && useTwoByteRecords ? (recordId - 0x60) >> 5 : -1;
1589
- transition[RECORD_SYMBOL] = recordId;
1590
- structures[recordId - 0x40] = keys;
1591
-
1592
- if (recordId < sharedLimitId) {
1593
- keys.isShared = true;
1594
- structures.sharedLength = recordId - 0x3f;
1595
- hasSharedUpdate = true;
1596
- if (highByte >= 0) {
1597
- target[position$1++] = (recordId & 0x1f) + 0x60;
1598
- target[position$1++] = highByte;
1599
- } else {
1600
- target[position$1++] = recordId;
1601
- }
1602
- } else {
1603
- if (highByte >= 0) {
1604
- target[position$1++] = 0xd5; // fixext 2
1605
- target[position$1++] = 0x72; // "r" record defintion extension type
1606
- target[position$1++] = (recordId & 0x1f) + 0x60;
1607
- target[position$1++] = highByte;
1608
- } else {
1609
- target[position$1++] = 0xd4; // fixext 1
1610
- target[position$1++] = 0x72; // "r" record defintion extension type
1611
- target[position$1++] = recordId;
1612
- }
1613
-
1614
- if (newTransitions)
1615
- transitionsCount += serializationsSinceTransitionRebuild * newTransitions;
1616
- // record the removal of the id, we can maintain our shared structure
1617
- if (recordIdsToRemove.length >= maxOwnStructures)
1618
- recordIdsToRemove.shift()[RECORD_SYMBOL] = 0; // we are cycling back through, and have to remove old ones
1619
- recordIdsToRemove.push(transition);
1620
- pack(keys);
1621
- }
1628
+ newRecord(transition, transition.__keys__ || Object.keys(object), newTransitions);
1622
1629
  }
1623
1630
  // now write the values
1624
- for (let i = 0, l = keys.length; i < l; i++)
1625
- pack(object[keys[i]]);
1631
+ for (let key in object)
1632
+ if (safePrototype || object.hasOwnProperty(key))
1633
+ pack(object[key]);
1626
1634
  };
1627
1635
  const makeRoom = (end) => {
1628
1636
  let newSize;
@@ -1645,6 +1653,86 @@ class Packr extends Unpackr {
1645
1653
  safeEnd = newBuffer.length - 10;
1646
1654
  return target = newBuffer
1647
1655
  };
1656
+ const newRecord = (transition, keys, newTransitions) => {
1657
+ let recordId = structures.nextId;
1658
+ if (!recordId)
1659
+ recordId = 0x40;
1660
+ if (recordId < sharedLimitId && this.shouldShareStructure && !this.shouldShareStructure(keys)) {
1661
+ recordId = structures.nextOwnId;
1662
+ if (!(recordId < maxStructureId))
1663
+ recordId = sharedLimitId;
1664
+ structures.nextOwnId = recordId + 1;
1665
+ } else {
1666
+ if (recordId >= maxStructureId)// cycle back around
1667
+ recordId = sharedLimitId;
1668
+ structures.nextId = recordId + 1;
1669
+ }
1670
+ let highByte = keys.highByte = recordId >= 0x60 && useTwoByteRecords ? (recordId - 0x60) >> 5 : -1;
1671
+ transition[RECORD_SYMBOL] = recordId;
1672
+ transition.__keys__ = keys;
1673
+ structures[recordId - 0x40] = keys;
1674
+
1675
+ if (recordId < sharedLimitId) {
1676
+ keys.isShared = true;
1677
+ structures.sharedLength = recordId - 0x3f;
1678
+ hasSharedUpdate = true;
1679
+ if (highByte >= 0) {
1680
+ target[position$1++] = (recordId & 0x1f) + 0x60;
1681
+ target[position$1++] = highByte;
1682
+ } else {
1683
+ target[position$1++] = recordId;
1684
+ }
1685
+ } else {
1686
+ if (highByte >= 0) {
1687
+ target[position$1++] = 0xd5; // fixext 2
1688
+ target[position$1++] = 0x72; // "r" record defintion extension type
1689
+ target[position$1++] = (recordId & 0x1f) + 0x60;
1690
+ target[position$1++] = highByte;
1691
+ } else {
1692
+ target[position$1++] = 0xd4; // fixext 1
1693
+ target[position$1++] = 0x72; // "r" record defintion extension type
1694
+ target[position$1++] = recordId;
1695
+ }
1696
+
1697
+ if (newTransitions)
1698
+ transitionsCount += serializationsSinceTransitionRebuild * newTransitions;
1699
+ // record the removal of the id, we can maintain our shared structure
1700
+ if (recordIdsToRemove.length >= maxOwnStructures)
1701
+ recordIdsToRemove.shift()[RECORD_SYMBOL] = 0; // we are cycling back through, and have to remove old ones
1702
+ recordIdsToRemove.push(transition);
1703
+ pack(keys);
1704
+ }
1705
+ };
1706
+ const insertNewRecord = (transition, keys, insertionOffset, newTransitions) => {
1707
+ let mainTarget = target;
1708
+ let mainPosition = position$1;
1709
+ let mainSafeEnd = safeEnd;
1710
+ let mainStart = start;
1711
+ target = keysTarget;
1712
+ position$1 = 0;
1713
+ start = 0;
1714
+ if (!target)
1715
+ keysTarget = target = new ByteArrayAllocate(8192);
1716
+ safeEnd = target.length - 10;
1717
+ newRecord(transition, keys, newTransitions);
1718
+ keysTarget = target;
1719
+ let keysPosition = position$1;
1720
+ target = mainTarget;
1721
+ position$1 = mainPosition;
1722
+ safeEnd = mainSafeEnd;
1723
+ start = mainStart;
1724
+ if (keysPosition > 1) {
1725
+ let newEnd = position$1 + keysPosition - 1;
1726
+ if (newEnd > safeEnd)
1727
+ makeRoom(newEnd);
1728
+ let insertionPosition = insertionOffset + start;
1729
+ target.copyWithin(insertionPosition + keysPosition, insertionPosition + 1, position$1);
1730
+ target.set(keysTarget.slice(0, keysPosition), insertionPosition);
1731
+ position$1 = newEnd;
1732
+ } else {
1733
+ target[insertionOffset + start] = keysTarget[0];
1734
+ }
1735
+ };
1648
1736
  }
1649
1737
  useBuffer(buffer) {
1650
1738
  // this means we are finished using our own buffer and we can write over it safely
@@ -1652,6 +1740,10 @@ class Packr extends Unpackr {
1652
1740
  targetView = new DataView(target.buffer, target.byteOffset, target.byteLength);
1653
1741
  position$1 = 0;
1654
1742
  }
1743
+ clearSharedData() {
1744
+ if (this.structures)
1745
+ this.structures = [];
1746
+ }
1655
1747
  }
1656
1748
 
1657
1749
  extensionClasses = [ Date, Set, Error, RegExp, ArrayBuffer, Object.getPrototypeOf(Uint8Array.prototype).constructor /*TypedArray*/, C1Type ];
@@ -1849,6 +1941,14 @@ function insertIds(serialized, idsToInsert) {
1849
1941
  return serialized
1850
1942
  }
1851
1943
 
1944
+ function writeBundles(start, pack) {
1945
+ targetView.setUint32(bundledStrings$1.position + start, position$1 - bundledStrings$1.position - start);
1946
+ let writeStrings = bundledStrings$1;
1947
+ bundledStrings$1 = null;
1948
+ pack(writeStrings[0]);
1949
+ pack(writeStrings[1]);
1950
+ }
1951
+
1852
1952
  function addExtension$1(extension) {
1853
1953
  if (extension.Class) {
1854
1954
  if (!extension.pack && !extension.write)
package/dist/test.js CHANGED
@@ -634,10 +634,29 @@
634
634
  let structures = [];
635
635
  let packr = new Packr({ structures });
636
636
  var serialized = packr.pack(data);
637
+ serialized = packr.pack(data);
638
+ serialized = packr.pack(data);
637
639
  var deserialized = packr.unpack(serialized);
638
640
  assert.deepEqual(deserialized, data);
639
641
  });
640
642
 
643
+ test('mixed structures', function(){
644
+ let data1 = { a: 1, b: 2, c: 3 };
645
+ let data2 = { a: 1, b: 2, d: 4 };
646
+ let data3 = { a: 1, b: 2, e: 5 };
647
+ let structures = [];
648
+ let packr = new Packr({ structures });
649
+ var serialized = packr.pack(data1);
650
+ var deserialized = packr.unpack(serialized);
651
+ assert.deepEqual(deserialized, data1);
652
+ var serialized = packr.pack(data2);
653
+ var deserialized = packr.unpack(serialized);
654
+ assert.deepEqual(deserialized, data2);
655
+ var serialized = packr.pack(data3);
656
+ var deserialized = packr.unpack(serialized);
657
+ assert.deepEqual(deserialized, data3);
658
+ });
659
+
641
660
  test('mixed array', function(){
642
661
  var data = [
643
662
  'one',
@@ -686,8 +705,7 @@
686
705
  });
687
706
  test('pack/unpack sample data with bundled strings', function(){
688
707
  var data = sampleData;
689
- let structures = [];
690
- let packr = new Packr({ structures, bundleStrings: true });
708
+ let packr = new Packr({ /*structures,*/ useRecords: false, bundleStrings: true });
691
709
  var serialized = packr.pack(data);
692
710
  var deserialized = packr.unpack(serialized);
693
711
  assert.deepEqual(deserialized, data);