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/str.cjs ADDED
@@ -0,0 +1,100 @@
1
+ let utfz = require('../../msgpack-benchmark/node_modules/utfz-lib')
2
+
3
+ var safeEnd = 1000000;
4
+ var b = new Uint8Array(32768)
5
+ function writeString(value, target, position) {
6
+ var length, strLength = value.length;
7
+ let headerSize;
8
+ // first we estimate the header size, so we can write to the correct location
9
+ if (strLength < 0x20) {
10
+ headerSize = 1;
11
+ } else if (strLength < 0x100) {
12
+ headerSize = 2;
13
+ } else if (strLength < 0x10000) {
14
+ headerSize = 3;
15
+ } else {
16
+ headerSize = 5;
17
+ }
18
+ let maxBytes = strLength * 3;
19
+ //if (position + maxBytes > safeEnd)
20
+ // target = makeRoom(position + maxBytes);
21
+ for (let i = 0; i < 100; i++) {
22
+ length = pack(value, strLength, target, position + headerSize);
23
+ }
24
+ if (strLength < 0x40 || !encodeUtf8) {
25
+ var strPosition = position + headerSize;
26
+ var c2 = 0;
27
+ for (let i = 0; i < strLength; i++) {
28
+ const c1 = value.charCodeAt(i);
29
+ if (c1 < 0x80) {
30
+ target[strPosition++] = c1;
31
+ } else if (c1 < 0x800) {
32
+ target[strPosition++] = c1 >> 6 | 0xc0;
33
+ target[strPosition++] = c1 & 0x3f | 0x80;
34
+ } else if (
35
+ (c1 & 0xfc00) === 0xd800 &&
36
+ ((c2 = value.charCodeAt(i + 1)) & 0xfc00) === 0xdc00
37
+ ) {
38
+ c1 = 0x10000 + ((c1 & 0x03ff) << 10) + (c2 & 0x03ff);
39
+ i++;
40
+ target[strPosition++] = c1 >> 18 | 0xf0;
41
+ target[strPosition++] = c1 >> 12 & 0x3f | 0x80;
42
+ target[strPosition++] = c1 >> 6 & 0x3f | 0x80;
43
+ target[strPosition++] = c1 & 0x3f | 0x80;
44
+ } else {
45
+ target[strPosition++] = c1 >> 12 | 0xe0;
46
+ target[strPosition++] = c1 >> 6 & 0x3f | 0x80;
47
+ target[strPosition++] = c1 & 0x3f | 0x80;
48
+ }
49
+ }
50
+ length = strPosition - position - headerSize;
51
+ } else {
52
+ length = encodeUtf8(value, position + headerSize, maxBytes);
53
+ }
54
+
55
+
56
+ if (length < 0x20) {
57
+ target[position++] = 0xa0 | length;
58
+ } else if (length < 0x100) {
59
+ if (headerSize < 2) {
60
+ target.copyWithin(position + 2, position + 1, position + 1 + length);
61
+ }
62
+ target[position++] = 0xd9;
63
+ target[position++] = length;
64
+ } else if (length < 0x10000) {
65
+ if (headerSize < 3) {
66
+ target.copyWithin(position + 3, position + 2, position + 2 + length);
67
+ }
68
+ target[position++] = 0xda;
69
+ target[position++] = length >> 8;
70
+ target[position++] = length & 0xff;
71
+ } else {
72
+ if (headerSize < 5) {
73
+ target.copyWithin(position + 5, position + 3, position + 3 + length);
74
+ }
75
+ target[position++] = 0xdb;
76
+ targetView.setUint32(position, length);
77
+ position += 4;
78
+ }
79
+ return position + length
80
+ };
81
+ const pack = (str, length, buf, offset) => {
82
+ const start = offset;
83
+ let currHigh = 0;
84
+ for (let i = 0; i < length; i++) {
85
+ const code = str.charCodeAt(i);
86
+ const high = code >> 8;
87
+ if (high !== currHigh) {
88
+ buf[i + offset++] = 0;
89
+ buf[i + offset++] = high;
90
+ currHigh = high;
91
+ }
92
+ const low = code & 0xff;
93
+ buf[i + offset] = low;
94
+ if (!low) {
95
+ buf[i + ++offset] = currHigh;
96
+ }
97
+ }
98
+ return length + offset - start;
99
+ };
100
+ module.exports = writeString;
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',
@@ -684,6 +703,13 @@
684
703
  var deserialized = packr.unpack(serialized);
685
704
  assert.deepEqual(deserialized, data);
686
705
  });
706
+ test('pack/unpack sample data with bundled strings', function(){
707
+ var data = sampleData;
708
+ let packr = new Packr({ /*structures,*/ useRecords: false, bundleStrings: true });
709
+ var serialized = packr.pack(data);
710
+ var deserialized = packr.unpack(serialized);
711
+ assert.deepEqual(deserialized, data);
712
+ });
687
713
  if (typeof Buffer != 'undefined')
688
714
  test('replace data', function(){
689
715
  var data1 = {
@@ -989,6 +1015,7 @@
989
1015
  date: new Date(1532219539733),
990
1016
  farFutureDate: new Date(3532219539133),
991
1017
  ancient: new Date(-3532219539133),
1018
+ invalidDate: new Date('invalid')
992
1019
  };
993
1020
  let packr = new Packr();
994
1021
  var serialized = packr.pack(data);
@@ -998,6 +1025,7 @@
998
1025
  assert.equal(deserialized.date.getTime(), 1532219539733);
999
1026
  assert.equal(deserialized.farFutureDate.getTime(), 3532219539133);
1000
1027
  assert.equal(deserialized.ancient.getTime(), -3532219539133);
1028
+ assert.equal(deserialized.invalidDate.toString(), 'Invalid Date');
1001
1029
  });
1002
1030
  test('map/date with options', function(){
1003
1031
  var map = new Map();
@@ -1008,16 +1036,19 @@
1008
1036
  var data = {
1009
1037
  map: map,
1010
1038
  date: new Date(1532219539011),
1039
+ invalidDate: new Date('invalid')
1011
1040
  };
1012
1041
  let packr = new Packr({
1013
1042
  mapsAsObjects: true,
1014
1043
  useTimestamp32: true,
1044
+ onInvalidDate: () => 'Custom invalid date'
1015
1045
  });
1016
1046
  var serialized = packr.pack(data);
1017
1047
  var deserialized = packr.unpack(serialized);
1018
1048
  assert.equal(deserialized.map[4], 'four');
1019
1049
  assert.equal(deserialized.map.three, 3);
1020
1050
  assert.equal(deserialized.date.getTime(), 1532219539000);
1051
+ assert.equal(deserialized.invalidDate, 'Custom invalid date');
1021
1052
  });
1022
1053
  test('key caching', function() {
1023
1054
  var data = {
@@ -1197,7 +1228,7 @@
1197
1228
  let structures = [];
1198
1229
  var serialized = pack(data);
1199
1230
  console.log('MessagePack size', serialized.length);
1200
- let packr = new Packr({ structures });
1231
+ let packr = new Packr({ structures, bundleStrings: false });
1201
1232
  var serialized = packr.pack(data);
1202
1233
  console.log('msgpackr w/ record ext size', serialized.length);
1203
1234
  for (var i = 0; i < ITERATIONS; i++) {
@@ -1208,7 +1239,7 @@
1208
1239
  var data = sampleData;
1209
1240
  this.timeout(10000);
1210
1241
  let structures = [];
1211
- let packr = new Packr({ structures });
1242
+ let packr = new Packr({ structures, bundleStrings: false });
1212
1243
  let buffer = typeof Buffer != 'undefined' ? Buffer.alloc(0x10000) : new Uint8Array(0x10000);
1213
1244
 
1214
1245
  for (var i = 0; i < ITERATIONS; i++) {
package/pack.js CHANGED
@@ -9,10 +9,13 @@ const hasNodeBuffer = typeof Buffer !== 'undefined'
9
9
  const ByteArrayAllocate = hasNodeBuffer ? Buffer.allocUnsafeSlow : Uint8Array
10
10
  const ByteArray = hasNodeBuffer ? Buffer : Uint8Array
11
11
  const MAX_BUFFER_SIZE = hasNodeBuffer ? 0x100000000 : 0x7fd00000
12
- let target
12
+ let target, keysTarget
13
13
  let targetView
14
14
  let position = 0
15
15
  let safeEnd
16
+ let bundledStrings = null
17
+ const MAX_BUNDLE_SIZE = 0xf000
18
+ const hasNonLatin = /[\u0080-\uFFFF]/
16
19
  const RECORD_SYMBOL = Symbol('record-id')
17
20
  export class Packr extends Unpackr {
18
21
  constructor(options) {
@@ -20,7 +23,6 @@ export class Packr extends Unpackr {
20
23
  this.offset = 0
21
24
  let typeBuffer
22
25
  let start
23
- let sharedStructures
24
26
  let hasSharedUpdate
25
27
  let structures
26
28
  let referenceMap
@@ -45,7 +47,7 @@ export class Packr extends Unpackr {
45
47
  let maxOwnStructures = options.maxOwnStructures
46
48
  if (maxOwnStructures == null)
47
49
  maxOwnStructures = hasSharedStructures ? 32 : 64
48
- if (isSequential && !options.saveStructures)
50
+ if (!this.structures && options.useRecords != false)
49
51
  this.structures = []
50
52
  // two byte record ids for shared structures
51
53
  let useTwoByteRecords = maxSharedStructures > 32 || (maxOwnStructures + maxSharedStructures > 64)
@@ -75,23 +77,28 @@ export class Packr extends Unpackr {
75
77
  position = (position + 7) & 0x7ffffff8 // Word align to make any future copying of this buffer faster
76
78
  start = position
77
79
  referenceMap = packr.structuredClone ? new Map() : null
78
- sharedStructures = packr.structures
79
- if (sharedStructures) {
80
- if (sharedStructures.uninitialized)
81
- sharedStructures = packr._mergeStructures(packr.getStructures())
82
- let sharedLength = sharedStructures.sharedLength || 0
80
+ if (packr.bundleStrings && typeof value !== 'string') {
81
+ bundledStrings = []
82
+ bundledStrings.size = Infinity // force a new bundle start on first string
83
+ } else
84
+ bundledStrings = null
85
+ structures = packr.structures
86
+ if (structures) {
87
+ if (structures.uninitialized)
88
+ structures = packr._mergeStructures(packr.getStructures())
89
+ let sharedLength = structures.sharedLength || 0
83
90
  if (sharedLength > maxSharedStructures) {
84
- //if (maxSharedStructures <= 32 && sharedStructures.sharedLength > 32) // TODO: could support this, but would need to update the limit ids
85
- throw new Error('Shared structures is larger than maximum shared structures, try increasing maxSharedStructures to ' + sharedStructures.sharedLength)
91
+ //if (maxSharedStructures <= 32 && structures.sharedLength > 32) // TODO: could support this, but would need to update the limit ids
92
+ throw new Error('Shared structures is larger than maximum shared structures, try increasing maxSharedStructures to ' + structures.sharedLength)
86
93
  }
87
- if (!sharedStructures.transitions) {
94
+ if (!structures.transitions) {
88
95
  // rebuild our structure transitions
89
- sharedStructures.transitions = Object.create(null)
96
+ structures.transitions = Object.create(null)
90
97
  for (let i = 0; i < sharedLength; i++) {
91
- let keys = sharedStructures[i]
98
+ let keys = structures[i]
92
99
  if (!keys)
93
100
  continue
94
- let nextTransition, transition = sharedStructures.transitions
101
+ let nextTransition, transition = structures.transitions
95
102
  for (let j = 0, l = keys.length; j < l; j++) {
96
103
  let key = keys[j]
97
104
  nextTransition = transition[key]
@@ -105,14 +112,16 @@ export class Packr extends Unpackr {
105
112
  lastSharedStructuresLength = sharedLength
106
113
  }
107
114
  if (!isSequential) {
108
- sharedStructures.nextId = sharedLength + 0x40
115
+ structures.nextId = sharedLength + 0x40
109
116
  }
110
117
  }
111
118
  if (hasSharedUpdate)
112
119
  hasSharedUpdate = false
113
- structures = sharedStructures || []
114
120
  try {
115
121
  pack(value)
122
+ if (bundledStrings) {
123
+ writeBundles(start, pack)
124
+ }
116
125
  packr.offset = position // update the offset so next serialization doesn't write over our buffer, but can continue writing to same buffer sequentially
117
126
  if (referenceMap && referenceMap.idsToInsert) {
118
127
  position += referenceMap.idsToInsert.length * 6
@@ -123,19 +132,22 @@ export class Packr extends Unpackr {
123
132
  referenceMap = null
124
133
  return serialized
125
134
  }
126
- if (encodeOptions === REUSE_BUFFER_MODE) {
135
+ if (encodeOptions & REUSE_BUFFER_MODE) {
127
136
  target.start = start
128
137
  target.end = position
129
138
  return target
130
139
  }
131
140
  return target.subarray(start, position) // position can change if we call pack again in saveStructures, so we get the buffer now
132
141
  } finally {
133
- if (sharedStructures) {
142
+ if (structures) {
134
143
  if (serializationsSinceTransitionRebuild < 10)
135
144
  serializationsSinceTransitionRebuild++
145
+ let sharedLength = structures.sharedLength || maxSharedStructures
146
+ if (structures.length > sharedLength)
147
+ structures.length = sharedLength
136
148
  if (transitionsCount > 10000) {
137
149
  // force a rebuild occasionally after a lot of transitions so it can get cleaned up
138
- sharedStructures.transitions = null
150
+ structures.transitions = null
139
151
  serializationsSinceTransitionRebuild = 0
140
152
  transitionsCount = 0
141
153
  if (recordIdsToRemove.length > 0)
@@ -147,13 +159,9 @@ export class Packr extends Unpackr {
147
159
  recordIdsToRemove = []
148
160
  }
149
161
  if (hasSharedUpdate && packr.saveStructures) {
150
- let sharedLength = sharedStructures.sharedLength || maxSharedStructures
151
- if (sharedStructures.length > sharedLength) {
152
- sharedStructures = sharedStructures.slice(0, sharedLength)
153
- }
154
162
  // we can't rely on start/end with REUSE_BUFFER_MODE since they will (probably) change when we save
155
163
  let returnBuffer = target.subarray(start, position)
156
- if (packr.saveStructures(sharedStructures, lastSharedStructuresLength) === false) {
164
+ if (packr.saveStructures(structures, lastSharedStructuresLength) === false) {
157
165
  // get updated structures and try again if the update failed
158
166
  packr._mergeStructures(packr.getStructures())
159
167
  return packr.pack(value)
@@ -162,6 +170,8 @@ export class Packr extends Unpackr {
162
170
  return returnBuffer
163
171
  }
164
172
  }
173
+ if (encodeOptions & RESET_BUFFER_MODE)
174
+ position = start
165
175
  }
166
176
  }
167
177
  const pack = (value) => {
@@ -172,6 +182,36 @@ export class Packr extends Unpackr {
172
182
  var length
173
183
  if (type === 'string') {
174
184
  let strLength = value.length
185
+ if (bundledStrings && strLength >= 4 && strLength < 0x1000) {
186
+ if ((bundledStrings.size += strLength) > MAX_BUNDLE_SIZE) {
187
+ let extStart
188
+ let maxBytes = (bundledStrings[0] ? bundledStrings[0].length * 3 + bundledStrings[1].length : 0) + 10
189
+ if (position + maxBytes > safeEnd)
190
+ target = makeRoom(position + maxBytes)
191
+ if (bundledStrings.position) { // here we use the 0x62 extension to write the last bundle and reserve sapce for the reference pointer to the next/current bundle
192
+ target[position] = 0xc8 // ext 16
193
+ position += 3 // reserve for the writing bundle size
194
+ target[position++] = 0x62 // 'b'
195
+ extStart = position - start
196
+ position += 4 // reserve for writing bundle reference
197
+ writeBundles(start, pack) // write the last bundles
198
+ targetView.setUint16(extStart + start - 3, position - start - extStart)
199
+ } 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)
200
+ target[position++] = 0xd6 // fixext 4
201
+ target[position++] = 0x62 // 'b'
202
+ extStart = position - start
203
+ position += 4 // reserve for writing bundle reference
204
+ }
205
+ bundledStrings = ['', ''] // create new ones
206
+ bundledStrings.size = 0
207
+ bundledStrings.position = extStart
208
+ }
209
+ let twoByte = hasNonLatin.test(value)
210
+ bundledStrings[twoByte ? 0 : 1] += value
211
+ target[position++] = 0xc1
212
+ pack(twoByte ? -strLength : strLength);
213
+ return
214
+ }
175
215
  let headerSize
176
216
  // first we estimate the header size, so we can write to the correct location
177
217
  if (strLength < 0x20) {
@@ -470,8 +510,7 @@ export class Packr extends Unpackr {
470
510
  target[objectOffset++ + start] = size >> 8
471
511
  target[objectOffset + start] = size & 0xff
472
512
  } :
473
-
474
- /* sharedStructures ? // For highly stable structures, using for-in can a little bit faster
513
+ (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)
475
514
  (object, safePrototype) => {
476
515
  let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null))
477
516
  let objectOffset = position++ - start
@@ -479,44 +518,47 @@ export class Packr extends Unpackr {
479
518
  for (let key in object) {
480
519
  if (safePrototype || object.hasOwnProperty(key)) {
481
520
  nextTransition = transition[key]
482
- if (!nextTransition) {
483
- nextTransition = transition[key] = Object.create(null)
484
- nextTransition.__keys__ = (transition.__keys__ || []).concat([key])
485
- /*let keys = Object.keys(object)
486
- if
487
- let size = 0
488
- let startBranch = transition.__keys__ ? transition.__keys__.length : 0
489
- for (let i = 0, l = keys.length; i++) {
521
+ if (nextTransition)
522
+ transition = nextTransition
523
+ else {
524
+ // record doesn't exist, create full new record and insert it
525
+ let keys = Object.keys(object)
526
+ let lastTransition = transition
527
+ transition = structures.transitions
528
+ let newTransitions = 0
529
+ for (let i = 0, l = keys.length; i < l; i++) {
490
530
  let key = keys[i]
491
- size += key.length << 2
492
- if (i >= startBranch) {
493
- nextTransition = nextTransition[key] = Object.create(null)
494
- nextTransition.__keys__ = keys.slice(0, i + 1)
531
+ nextTransition = transition[key]
532
+ if (!nextTransition) {
533
+ nextTransition = transition[key] = Object.create(null)
534
+ newTransitions++
495
535
  }
536
+ transition = nextTransition
496
537
  }
497
- makeRoom(position + size)
498
- nextTransition = transition[key]
499
- target.copy(target, )
500
- objectOffset
538
+ if (objectOffset + start + 1 == position) {
539
+ // first key, so we don't need to insert, we can just write record directly
540
+ position--
541
+ newRecord(transition, keys, newTransitions)
542
+ } else // otherwise we need to insert the record, moving existing data after the record
543
+ insertNewRecord(transition, keys, objectOffset, newTransitions)
544
+ wroteKeys = true
545
+ transition = lastTransition[key]
501
546
  }
502
- transition = nextTransition
503
547
  pack(object[key])
504
548
  }
505
549
  }
506
- let id = transition.id
507
- if (!id) {
508
- id = transition.id = structures.push(transition.__keys__) + 63
509
- if (sharedStructures.onUpdate)
510
- sharedStructures.onUpdate(id, transition.__keys__)
550
+ if (!wroteKeys) {
551
+ let recordId = transition[RECORD_SYMBOL]
552
+ if (recordId)
553
+ target[objectOffset + start] = recordId
554
+ else
555
+ insertNewRecord(transition, Object.keys(object), objectOffset, 0)
511
556
  }
512
- target[objectOffset + start] = id
513
- }*/
514
- (object) => {
515
- let keys = Object.keys(object)
557
+ } :
558
+ (object, safePrototype) => {
516
559
  let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null))
517
560
  let newTransitions = 0
518
- for (let i = 0, l = keys.length; i < l; i++) {
519
- let key = keys[i]
561
+ for (let key in object) if (safePrototype || object.hasOwnProperty(key)) {
520
562
  nextTransition = transition[key]
521
563
  if (!nextTransition) {
522
564
  nextTransition = transition[key] = Object.create(null)
@@ -532,57 +574,12 @@ export class Packr extends Unpackr {
532
574
  } else
533
575
  target[position++] = recordId
534
576
  } else {
535
- recordId = structures.nextId
536
- if (!recordId)
537
- recordId = 0x40
538
- if (recordId < sharedLimitId && this.shouldShareStructure && !this.shouldShareStructure(keys)) {
539
- recordId = structures.nextOwnId
540
- if (!(recordId < maxStructureId))
541
- recordId = sharedLimitId
542
- structures.nextOwnId = recordId + 1
543
- } else {
544
- if (recordId >= maxStructureId)// cycle back around
545
- recordId = sharedLimitId
546
- structures.nextId = recordId + 1
547
- }
548
- let highByte = keys.highByte = recordId >= 0x60 && useTwoByteRecords ? (recordId - 0x60) >> 5 : -1
549
- transition[RECORD_SYMBOL] = recordId
550
- structures[recordId - 0x40] = keys
551
-
552
- if (recordId < sharedLimitId) {
553
- keys.isShared = true
554
- structures.sharedLength = recordId - 0x3f
555
- hasSharedUpdate = true
556
- if (highByte >= 0) {
557
- target[position++] = (recordId & 0x1f) + 0x60
558
- target[position++] = highByte
559
- } else {
560
- target[position++] = recordId
561
- }
562
- } else {
563
- if (highByte >= 0) {
564
- target[position++] = 0xd5 // fixext 2
565
- target[position++] = 0x72 // "r" record defintion extension type
566
- target[position++] = (recordId & 0x1f) + 0x60
567
- target[position++] = highByte
568
- } else {
569
- target[position++] = 0xd4 // fixext 1
570
- target[position++] = 0x72 // "r" record defintion extension type
571
- target[position++] = recordId
572
- }
573
-
574
- if (newTransitions)
575
- transitionsCount += serializationsSinceTransitionRebuild * newTransitions
576
- // record the removal of the id, we can maintain our shared structure
577
- if (recordIdsToRemove.length >= maxOwnStructures)
578
- recordIdsToRemove.shift()[RECORD_SYMBOL] = 0 // we are cycling back through, and have to remove old ones
579
- recordIdsToRemove.push(transition)
580
- pack(keys)
581
- }
577
+ newRecord(transition, transition.__keys__ || Object.keys(object), newTransitions)
582
578
  }
583
579
  // now write the values
584
- for (let i = 0, l = keys.length; i < l; i++)
585
- pack(object[keys[i]])
580
+ for (let key in object)
581
+ if (safePrototype || object.hasOwnProperty(key))
582
+ pack(object[key])
586
583
  }
587
584
  const makeRoom = (end) => {
588
585
  let newSize
@@ -605,6 +602,86 @@ export class Packr extends Unpackr {
605
602
  safeEnd = newBuffer.length - 10
606
603
  return target = newBuffer
607
604
  }
605
+ const newRecord = (transition, keys, newTransitions) => {
606
+ let recordId = structures.nextId
607
+ if (!recordId)
608
+ recordId = 0x40
609
+ if (recordId < sharedLimitId && this.shouldShareStructure && !this.shouldShareStructure(keys)) {
610
+ recordId = structures.nextOwnId
611
+ if (!(recordId < maxStructureId))
612
+ recordId = sharedLimitId
613
+ structures.nextOwnId = recordId + 1
614
+ } else {
615
+ if (recordId >= maxStructureId)// cycle back around
616
+ recordId = sharedLimitId
617
+ structures.nextId = recordId + 1
618
+ }
619
+ let highByte = keys.highByte = recordId >= 0x60 && useTwoByteRecords ? (recordId - 0x60) >> 5 : -1
620
+ transition[RECORD_SYMBOL] = recordId
621
+ transition.__keys__ = keys
622
+ structures[recordId - 0x40] = keys
623
+
624
+ if (recordId < sharedLimitId) {
625
+ keys.isShared = true
626
+ structures.sharedLength = recordId - 0x3f
627
+ hasSharedUpdate = true
628
+ if (highByte >= 0) {
629
+ target[position++] = (recordId & 0x1f) + 0x60
630
+ target[position++] = highByte
631
+ } else {
632
+ target[position++] = recordId
633
+ }
634
+ } else {
635
+ if (highByte >= 0) {
636
+ target[position++] = 0xd5 // fixext 2
637
+ target[position++] = 0x72 // "r" record defintion extension type
638
+ target[position++] = (recordId & 0x1f) + 0x60
639
+ target[position++] = highByte
640
+ } else {
641
+ target[position++] = 0xd4 // fixext 1
642
+ target[position++] = 0x72 // "r" record defintion extension type
643
+ target[position++] = recordId
644
+ }
645
+
646
+ if (newTransitions)
647
+ transitionsCount += serializationsSinceTransitionRebuild * newTransitions
648
+ // record the removal of the id, we can maintain our shared structure
649
+ if (recordIdsToRemove.length >= maxOwnStructures)
650
+ recordIdsToRemove.shift()[RECORD_SYMBOL] = 0 // we are cycling back through, and have to remove old ones
651
+ recordIdsToRemove.push(transition)
652
+ pack(keys)
653
+ }
654
+ }
655
+ const insertNewRecord = (transition, keys, insertionOffset, newTransitions) => {
656
+ let mainTarget = target
657
+ let mainPosition = position
658
+ let mainSafeEnd = safeEnd
659
+ let mainStart = start
660
+ target = keysTarget
661
+ position = 0
662
+ start = 0
663
+ if (!target)
664
+ keysTarget = target = new ByteArrayAllocate(8192)
665
+ safeEnd = target.length - 10
666
+ newRecord(transition, keys, newTransitions)
667
+ keysTarget = target
668
+ let keysPosition = position
669
+ target = mainTarget
670
+ position = mainPosition
671
+ safeEnd = mainSafeEnd
672
+ start = mainStart
673
+ if (keysPosition > 1) {
674
+ let newEnd = position + keysPosition - 1
675
+ if (newEnd > safeEnd)
676
+ makeRoom(newEnd)
677
+ let insertionPosition = insertionOffset + start
678
+ target.copyWithin(insertionPosition + keysPosition, insertionPosition + 1, position)
679
+ target.set(keysTarget.slice(0, keysPosition), insertionPosition)
680
+ position = newEnd
681
+ } else {
682
+ target[insertionOffset + start] = keysTarget[0]
683
+ }
684
+ }
608
685
  }
609
686
  useBuffer(buffer) {
610
687
  // this means we are finished using our own buffer and we can write over it safely
@@ -612,6 +689,10 @@ export class Packr extends Unpackr {
612
689
  targetView = new DataView(target.buffer, target.byteOffset, target.byteLength)
613
690
  position = 0
614
691
  }
692
+ clearSharedData() {
693
+ if (this.structures)
694
+ this.structures = []
695
+ }
615
696
  }
616
697
 
617
698
  function copyBinary(source, target, targetOffset, offset, endOffset) {
@@ -622,7 +703,7 @@ function copyBinary(source, target, targetOffset, offset, endOffset) {
622
703
 
623
704
  extensionClasses = [ Date, Set, Error, RegExp, ArrayBuffer, Object.getPrototypeOf(Uint8Array.prototype).constructor /*TypedArray*/, C1Type ]
624
705
  extensions = [{
625
- pack(date, allocateForWrite) {
706
+ pack(date, allocateForWrite, pack) {
626
707
  let seconds = date.getTime() / 1000
627
708
  if ((this.useTimestamp32 || date.getMilliseconds() === 0) && seconds >= 0 && seconds < 0x100000000) {
628
709
  // Timestamp 32
@@ -637,6 +718,16 @@ extensions = [{
637
718
  target[position++] = 0xff
638
719
  targetView.setUint32(position, date.getMilliseconds() * 4000000 + ((seconds / 1000 / 0x100000000) >> 0))
639
720
  targetView.setUint32(position + 4, seconds)
721
+ } else if (isNaN(seconds)) {
722
+ if (this.onInvalidDate) {
723
+ allocateForWrite(0)
724
+ return pack(this.onInvalidDate())
725
+ }
726
+ // Intentionally invalid timestamp
727
+ let { target, targetView, position} = allocateForWrite(3)
728
+ target[position++] = 0xd4
729
+ target[position++] = 0xff
730
+ target[position++] = 0xff
640
731
  } else {
641
732
  // Timestamp 96
642
733
  let { target, targetView, position} = allocateForWrite(15)
@@ -805,6 +896,15 @@ function insertIds(serialized, idsToInsert) {
805
896
  return serialized
806
897
  }
807
898
 
899
+ function writeBundles(start, pack) {
900
+ targetView.setUint32(bundledStrings.position + start, position - bundledStrings.position - start)
901
+ let writeStrings = bundledStrings
902
+ bundledStrings = null
903
+ let startPosition = position
904
+ pack(writeStrings[0])
905
+ pack(writeStrings[1])
906
+ }
907
+
808
908
  export function addExtension(extension) {
809
909
  if (extension.Class) {
810
910
  if (!extension.pack && !extension.write)
@@ -824,4 +924,5 @@ export const Encoder = Packr
824
924
  export { FLOAT32_OPTIONS } from './unpack.js'
825
925
  import { FLOAT32_OPTIONS } from './unpack.js'
826
926
  export const { NEVER, ALWAYS, DECIMAL_ROUND, DECIMAL_FIT } = FLOAT32_OPTIONS
827
- export const REUSE_BUFFER_MODE = 1000
927
+ export const REUSE_BUFFER_MODE = 512
928
+ export const RESET_BUFFER_MODE = 1024
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "msgpackr",
3
3
  "author": "Kris Zyp",
4
- "version": "1.5.0",
4
+ "version": "1.5.4",
5
5
  "description": "Ultra-fast MessagePack implementation with extensions for records and structured cloning",
6
6
  "license": "MIT",
7
7
  "types": "./index.d.ts",
@@ -49,6 +49,12 @@
49
49
  "default": "./unpack.js"
50
50
  }
51
51
  },
52
+ "files": [
53
+ "/dist",
54
+ "*.md",
55
+ "/*.js",
56
+ "/*.ts"
57
+ ],
52
58
  "optionalDependencies": {
53
59
  "msgpackr-extract": "^1.0.14"
54
60
  },