msgpackr 1.4.7 → 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/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 = {
@@ -1071,7 +1102,17 @@
1071
1102
  var deserialized = packr.unpack(serialized);
1072
1103
  assert.deepEqual(deserialized, data);
1073
1104
  });
1074
-
1105
+ test('bigint to float', function() {
1106
+ var data = {
1107
+ a: 325283295382932843n
1108
+ };
1109
+ let packr = new Packr({
1110
+ int64AsNumber: true
1111
+ });
1112
+ var serialized = packr.pack(data);
1113
+ var deserialized = packr.unpack(serialized);
1114
+ assert.deepEqual(deserialized.a, 325283295382932843);
1115
+ });
1075
1116
  test('numbers', function(){
1076
1117
  var data = {
1077
1118
  bigEncodable: 48978578104322,
@@ -1187,7 +1228,7 @@
1187
1228
  let structures = [];
1188
1229
  var serialized = pack(data);
1189
1230
  console.log('MessagePack size', serialized.length);
1190
- let packr = new Packr({ structures });
1231
+ let packr = new Packr({ structures, bundleStrings: false });
1191
1232
  var serialized = packr.pack(data);
1192
1233
  console.log('msgpackr w/ record ext size', serialized.length);
1193
1234
  for (var i = 0; i < ITERATIONS; i++) {
@@ -1198,7 +1239,7 @@
1198
1239
  var data = sampleData;
1199
1240
  this.timeout(10000);
1200
1241
  let structures = [];
1201
- let packr = new Packr({ structures });
1242
+ let packr = new Packr({ structures, bundleStrings: false });
1202
1243
  let buffer = typeof Buffer != 'undefined' ? Buffer.alloc(0x10000) : new Uint8Array(0x10000);
1203
1244
 
1204
1245
  for (var i = 0; i < ITERATIONS; i++) {
package/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { Unpackr, Decoder, unpack, decode, addExtension, FLOAT32_OPTIONS, clearSource, roundFloat32 } from './unpack'
1
+ export { Unpackr, Decoder, unpack, decode, addExtension, FLOAT32_OPTIONS, clearSource, roundFloat32, isNativeAccelerationEnabled } from './unpack'
2
2
  import { Options } from './unpack'
3
3
  export { Packr, Encoder, pack, encode } from './pack'
4
4
  import { Transform, Readable } from 'stream'
package/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  export { Packr, Encoder, addExtension, pack, encode, NEVER, ALWAYS, DECIMAL_ROUND, DECIMAL_FIT, REUSE_BUFFER_MODE } from './pack.js'
2
- export { Unpackr, Decoder, C1, unpack, unpackMultiple, decode, FLOAT32_OPTIONS, clearSource, roundFloat32 } from './unpack.js'
2
+ export { Unpackr, Decoder, C1, unpack, unpackMultiple, decode, FLOAT32_OPTIONS, clearSource, roundFloat32, isNativeAccelerationEnabled } from './unpack.js'
3
3
  export { decodeIter, encodeIter } from './iterators.js'
4
4
  export const useRecords = false
5
5
  export const mapsAsObjects = true
package/node-index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  export { Packr, Encoder, addExtension, pack, encode, NEVER, ALWAYS, DECIMAL_ROUND, DECIMAL_FIT } from './pack.js'
2
- export { Unpackr, Decoder, C1, unpack, unpackMultiple, decode, FLOAT32_OPTIONS, clearSource, roundFloat32 } from './unpack.js'
2
+ export { Unpackr, Decoder, C1, unpack, unpackMultiple, decode, FLOAT32_OPTIONS, clearSource, roundFloat32, isNativeAccelerationEnabled } from './unpack.js'
3
3
  export { PackrStream, UnpackrStream, PackrStream as EncoderStream, UnpackrStream as DecoderStream } from './stream.js'
4
4
  export { decodeIter, encodeIter } from './iterators.js'
5
5
  export const useRecords = false
@@ -16,9 +16,7 @@ function tryRequire(moduleId) {
16
16
  let require = createRequire(import.meta.url)
17
17
  return require(moduleId)
18
18
  } catch (error) {
19
- if (typeof window == 'undefined')
20
- console.warn('Native extraction module not loaded, msgpackr will still run, but with decreased performance. ' + error.message.split('\n')[0])
21
- else
19
+ if (typeof window != 'undefined')
22
20
  console.warn('For browser usage, directly use msgpackr/unpack or msgpackr/pack modules. ' + error.message.split('\n')[0])
23
21
  }
24
22
  }
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) {
@@ -75,6 +78,11 @@ export class Packr extends Unpackr {
75
78
  position = (position + 7) & 0x7ffffff8 // Word align to make any future copying of this buffer faster
76
79
  start = position
77
80
  referenceMap = packr.structuredClone ? new Map() : null
81
+ if (packr.bundleStrings && typeof value !== 'string') {
82
+ bundledStrings = []
83
+ bundledStrings.size = Infinity // force a new bundle start on first string
84
+ } else
85
+ bundledStrings = null
78
86
  sharedStructures = packr.structures
79
87
  if (sharedStructures) {
80
88
  if (sharedStructures.uninitialized)
@@ -110,9 +118,12 @@ export class Packr extends Unpackr {
110
118
  }
111
119
  if (hasSharedUpdate)
112
120
  hasSharedUpdate = false
113
- structures = sharedStructures || []
121
+ structures = sharedStructures || (packr.structures = [])
114
122
  try {
115
123
  pack(value)
124
+ if (bundledStrings) {
125
+ writeBundles(start, pack)
126
+ }
116
127
  packr.offset = position // update the offset so next serialization doesn't write over our buffer, but can continue writing to same buffer sequentially
117
128
  if (referenceMap && referenceMap.idsToInsert) {
118
129
  position += referenceMap.idsToInsert.length * 6
@@ -123,7 +134,7 @@ export class Packr extends Unpackr {
123
134
  referenceMap = null
124
135
  return serialized
125
136
  }
126
- if (encodeOptions === REUSE_BUFFER_MODE) {
137
+ if (encodeOptions & REUSE_BUFFER_MODE) {
127
138
  target.start = start
128
139
  target.end = position
129
140
  return target
@@ -133,6 +144,8 @@ export class Packr extends Unpackr {
133
144
  if (sharedStructures) {
134
145
  if (serializationsSinceTransitionRebuild < 10)
135
146
  serializationsSinceTransitionRebuild++
147
+ if (sharedStructures.length > maxSharedStructures)
148
+ sharedStructures.length = maxSharedStructures
136
149
  if (transitionsCount > 10000) {
137
150
  // force a rebuild occasionally after a lot of transitions so it can get cleaned up
138
151
  sharedStructures.transitions = null
@@ -162,6 +175,8 @@ export class Packr extends Unpackr {
162
175
  return returnBuffer
163
176
  }
164
177
  }
178
+ if (encodeOptions & RESET_BUFFER_MODE)
179
+ position = start
165
180
  }
166
181
  }
167
182
  const pack = (value) => {
@@ -172,6 +187,36 @@ export class Packr extends Unpackr {
172
187
  var length
173
188
  if (type === 'string') {
174
189
  let strLength = value.length
190
+ if (bundledStrings && strLength >= 4 && strLength < 0x1000) {
191
+ if ((bundledStrings.size += strLength) > MAX_BUNDLE_SIZE) {
192
+ let extStart
193
+ let maxBytes = (bundledStrings[0] ? bundledStrings[0].length * 3 + bundledStrings[1].length : 0) + 10
194
+ if (position + maxBytes > safeEnd)
195
+ target = makeRoom(position + maxBytes)
196
+ 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
197
+ target[position] = 0xc8 // ext 16
198
+ position += 3 // reserve for the writing bundle size
199
+ target[position++] = 0x62 // 'b'
200
+ extStart = position - start
201
+ position += 4 // reserve for writing bundle reference
202
+ writeBundles(start, pack) // write the last bundles
203
+ targetView.setUint16(extStart + start - 3, position - start - extStart)
204
+ } 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)
205
+ target[position++] = 0xd6 // fixext 4
206
+ target[position++] = 0x62 // 'b'
207
+ extStart = position - start
208
+ position += 4 // reserve for writing bundle reference
209
+ }
210
+ bundledStrings = ['', ''] // create new ones
211
+ bundledStrings.size = 0
212
+ bundledStrings.position = extStart
213
+ }
214
+ let twoByte = hasNonLatin.test(value)
215
+ bundledStrings[twoByte ? 0 : 1] += value
216
+ target[position++] = 0xc1
217
+ pack(twoByte ? -strLength : strLength);
218
+ return
219
+ }
175
220
  let headerSize
176
221
  // first we estimate the header size, so we can write to the correct location
177
222
  if (strLength < 0x20) {
@@ -470,8 +515,7 @@ export class Packr extends Unpackr {
470
515
  target[objectOffset++ + start] = size >> 8
471
516
  target[objectOffset + start] = size & 0xff
472
517
  } :
473
-
474
- /* sharedStructures ? // For highly stable structures, using for-in can a little bit faster
518
+ (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
519
  (object, safePrototype) => {
476
520
  let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null))
477
521
  let objectOffset = position++ - start
@@ -479,44 +523,47 @@ export class Packr extends Unpackr {
479
523
  for (let key in object) {
480
524
  if (safePrototype || object.hasOwnProperty(key)) {
481
525
  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++) {
526
+ if (nextTransition)
527
+ transition = nextTransition
528
+ else {
529
+ // record doesn't exist, create full new record and insert it
530
+ let keys = Object.keys(object)
531
+ let lastTransition = transition
532
+ transition = structures.transitions
533
+ let newTransitions = 0
534
+ for (let i = 0, l = keys.length; i < l; i++) {
490
535
  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)
536
+ nextTransition = transition[key]
537
+ if (!nextTransition) {
538
+ nextTransition = transition[key] = Object.create(null)
539
+ newTransitions++
495
540
  }
541
+ transition = nextTransition
496
542
  }
497
- makeRoom(position + size)
498
- nextTransition = transition[key]
499
- target.copy(target, )
500
- objectOffset
543
+ if (objectOffset + start + 1 == position) {
544
+ // first key, so we don't need to insert, we can just write record directly
545
+ position--
546
+ newRecord(transition, keys, newTransitions)
547
+ } else // otherwise we need to insert the record, moving existing data after the record
548
+ insertNewRecord(transition, keys, objectOffset, newTransitions)
549
+ wroteKeys = true
550
+ transition = lastTransition[key]
501
551
  }
502
- transition = nextTransition
503
552
  pack(object[key])
504
553
  }
505
554
  }
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__)
555
+ if (!wroteKeys) {
556
+ let recordId = transition[RECORD_SYMBOL]
557
+ if (recordId)
558
+ target[objectOffset + start] = recordId
559
+ else
560
+ insertNewRecord(transition, Object.keys(object), objectOffset, 0)
511
561
  }
512
- target[objectOffset + start] = id
513
- }*/
514
- (object) => {
515
- let keys = Object.keys(object)
562
+ } :
563
+ (object, safePrototype) => {
516
564
  let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null))
517
565
  let newTransitions = 0
518
- for (let i = 0, l = keys.length; i < l; i++) {
519
- let key = keys[i]
566
+ for (let key in object) if (safePrototype || object.hasOwnProperty(key)) {
520
567
  nextTransition = transition[key]
521
568
  if (!nextTransition) {
522
569
  nextTransition = transition[key] = Object.create(null)
@@ -532,57 +579,12 @@ export class Packr extends Unpackr {
532
579
  } else
533
580
  target[position++] = recordId
534
581
  } 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
- }
582
+ newRecord(transition, transition.__keys__ || Object.keys(object), newTransitions)
582
583
  }
583
584
  // now write the values
584
- for (let i = 0, l = keys.length; i < l; i++)
585
- pack(object[keys[i]])
585
+ for (let key in object)
586
+ if (safePrototype || object.hasOwnProperty(key))
587
+ pack(object[key])
586
588
  }
587
589
  const makeRoom = (end) => {
588
590
  let newSize
@@ -591,7 +593,7 @@ export class Packr extends Unpackr {
591
593
  if ((end - start) > MAX_BUFFER_SIZE)
592
594
  throw new Error('Packed buffer would be larger than maximum buffer size')
593
595
  newSize = Math.min(MAX_BUFFER_SIZE,
594
- Math.round(Math.max((end - start) * (end > 0x4000000 ? 1.25 : 2), 0x1000000) / 0x1000) * 0x1000)
596
+ Math.round(Math.max((end - start) * (end > 0x4000000 ? 1.25 : 2), 0x400000) / 0x1000) * 0x1000)
595
597
  } else // faster handling for smaller buffers
596
598
  newSize = ((Math.max((end - start) << 2, target.length - 1) >> 12) + 1) << 12
597
599
  let newBuffer = new ByteArrayAllocate(newSize)
@@ -605,6 +607,86 @@ export class Packr extends Unpackr {
605
607
  safeEnd = newBuffer.length - 10
606
608
  return target = newBuffer
607
609
  }
610
+ const newRecord = (transition, keys, newTransitions) => {
611
+ let recordId = structures.nextId
612
+ if (!recordId)
613
+ recordId = 0x40
614
+ if (recordId < sharedLimitId && this.shouldShareStructure && !this.shouldShareStructure(keys)) {
615
+ recordId = structures.nextOwnId
616
+ if (!(recordId < maxStructureId))
617
+ recordId = sharedLimitId
618
+ structures.nextOwnId = recordId + 1
619
+ } else {
620
+ if (recordId >= maxStructureId)// cycle back around
621
+ recordId = sharedLimitId
622
+ structures.nextId = recordId + 1
623
+ }
624
+ let highByte = keys.highByte = recordId >= 0x60 && useTwoByteRecords ? (recordId - 0x60) >> 5 : -1
625
+ transition[RECORD_SYMBOL] = recordId
626
+ transition.__keys__ = keys
627
+ structures[recordId - 0x40] = keys
628
+
629
+ if (recordId < sharedLimitId) {
630
+ keys.isShared = true
631
+ structures.sharedLength = recordId - 0x3f
632
+ hasSharedUpdate = true
633
+ if (highByte >= 0) {
634
+ target[position++] = (recordId & 0x1f) + 0x60
635
+ target[position++] = highByte
636
+ } else {
637
+ target[position++] = recordId
638
+ }
639
+ } else {
640
+ if (highByte >= 0) {
641
+ target[position++] = 0xd5 // fixext 2
642
+ target[position++] = 0x72 // "r" record defintion extension type
643
+ target[position++] = (recordId & 0x1f) + 0x60
644
+ target[position++] = highByte
645
+ } else {
646
+ target[position++] = 0xd4 // fixext 1
647
+ target[position++] = 0x72 // "r" record defintion extension type
648
+ target[position++] = recordId
649
+ }
650
+
651
+ if (newTransitions)
652
+ transitionsCount += serializationsSinceTransitionRebuild * newTransitions
653
+ // record the removal of the id, we can maintain our shared structure
654
+ if (recordIdsToRemove.length >= maxOwnStructures)
655
+ recordIdsToRemove.shift()[RECORD_SYMBOL] = 0 // we are cycling back through, and have to remove old ones
656
+ recordIdsToRemove.push(transition)
657
+ pack(keys)
658
+ }
659
+ }
660
+ const insertNewRecord = (transition, keys, insertionOffset, newTransitions) => {
661
+ let mainTarget = target
662
+ let mainPosition = position
663
+ let mainSafeEnd = safeEnd
664
+ let mainStart = start
665
+ target = keysTarget
666
+ position = 0
667
+ start = 0
668
+ if (!target)
669
+ keysTarget = target = new ByteArrayAllocate(8192)
670
+ safeEnd = target.length - 10
671
+ newRecord(transition, keys, newTransitions)
672
+ keysTarget = target
673
+ let keysPosition = position
674
+ target = mainTarget
675
+ position = mainPosition
676
+ safeEnd = mainSafeEnd
677
+ start = mainStart
678
+ if (keysPosition > 1) {
679
+ let newEnd = position + keysPosition - 1
680
+ if (newEnd > safeEnd)
681
+ makeRoom(newEnd)
682
+ let insertionPosition = insertionOffset + start
683
+ target.copyWithin(insertionPosition + keysPosition, insertionPosition + 1, position)
684
+ target.set(keysTarget.slice(0, keysPosition), insertionPosition)
685
+ position = newEnd
686
+ } else {
687
+ target[insertionOffset + start] = keysTarget[0]
688
+ }
689
+ }
608
690
  }
609
691
  useBuffer(buffer) {
610
692
  // this means we are finished using our own buffer and we can write over it safely
@@ -612,6 +694,10 @@ export class Packr extends Unpackr {
612
694
  targetView = new DataView(target.buffer, target.byteOffset, target.byteLength)
613
695
  position = 0
614
696
  }
697
+ clearSharedData() {
698
+ if (this.structures)
699
+ this.structures = []
700
+ }
615
701
  }
616
702
 
617
703
  function copyBinary(source, target, targetOffset, offset, endOffset) {
@@ -622,7 +708,7 @@ function copyBinary(source, target, targetOffset, offset, endOffset) {
622
708
 
623
709
  extensionClasses = [ Date, Set, Error, RegExp, ArrayBuffer, Object.getPrototypeOf(Uint8Array.prototype).constructor /*TypedArray*/, C1Type ]
624
710
  extensions = [{
625
- pack(date, allocateForWrite) {
711
+ pack(date, allocateForWrite, pack) {
626
712
  let seconds = date.getTime() / 1000
627
713
  if ((this.useTimestamp32 || date.getMilliseconds() === 0) && seconds >= 0 && seconds < 0x100000000) {
628
714
  // Timestamp 32
@@ -637,6 +723,16 @@ extensions = [{
637
723
  target[position++] = 0xff
638
724
  targetView.setUint32(position, date.getMilliseconds() * 4000000 + ((seconds / 1000 / 0x100000000) >> 0))
639
725
  targetView.setUint32(position + 4, seconds)
726
+ } else if (isNaN(seconds)) {
727
+ if (this.onInvalidDate) {
728
+ allocateForWrite(0)
729
+ return pack(this.onInvalidDate())
730
+ }
731
+ // Intentionally invalid timestamp
732
+ let { target, targetView, position} = allocateForWrite(3)
733
+ target[position++] = 0xd4
734
+ target[position++] = 0xff
735
+ target[position++] = 0xff
640
736
  } else {
641
737
  // Timestamp 96
642
738
  let { target, targetView, position} = allocateForWrite(15)
@@ -805,6 +901,15 @@ function insertIds(serialized, idsToInsert) {
805
901
  return serialized
806
902
  }
807
903
 
904
+ function writeBundles(start, pack) {
905
+ targetView.setUint32(bundledStrings.position + start, position - bundledStrings.position - start)
906
+ let writeStrings = bundledStrings
907
+ bundledStrings = null
908
+ let startPosition = position
909
+ pack(writeStrings[0])
910
+ pack(writeStrings[1])
911
+ }
912
+
808
913
  export function addExtension(extension) {
809
914
  if (extension.Class) {
810
915
  if (!extension.pack && !extension.write)
@@ -824,4 +929,5 @@ export const Encoder = Packr
824
929
  export { FLOAT32_OPTIONS } from './unpack.js'
825
930
  import { FLOAT32_OPTIONS } from './unpack.js'
826
931
  export const { NEVER, ALWAYS, DECIMAL_ROUND, DECIMAL_FIT } = FLOAT32_OPTIONS
827
- export const REUSE_BUFFER_MODE = 1000
932
+ export const REUSE_BUFFER_MODE = 512
933
+ 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.4.7",
4
+ "version": "1.5.3",
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
  },