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/README.md +4 -1
- package/dist/index.js +271 -113
- package/dist/index.min.js +66 -100
- package/dist/node.cjs +273 -113
- package/dist/str.cjs +100 -0
- package/dist/test.js +33 -2
- package/pack.js +206 -105
- package/package.json +7 -1
- package/unpack.d.ts +2 -0
- package/unpack.js +62 -2
- package/.vscode/launch.json +0 -23
- package/tests/benchmark-stream.cjs +0 -282
- package/tests/benchmark.cjs +0 -199
- package/tests/example.json +0 -52
- package/tests/example2.json +0 -26
- package/tests/example3.json +0 -22
- package/tests/example4.json +0 -1
- package/tests/example5.json +0 -12
- package/tests/floats.json +0 -1
- package/tests/index.html +0 -28
- package/tests/sample-large.json +0 -231
- package/tests/strings2.json +0 -1
- package/tests/test-compatibility.cjs +0 -64
- package/tests/test-incomplete.js +0 -41
- package/tests/test-node-iterators.js +0 -72
- package/tests/test-node-stream.js +0 -76
- package/tests/test.js +0 -650
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 (
|
|
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
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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 &&
|
|
85
|
-
throw new Error('Shared structures is larger than maximum shared structures, try increasing maxSharedStructures to ' +
|
|
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 (!
|
|
94
|
+
if (!structures.transitions) {
|
|
88
95
|
// rebuild our structure transitions
|
|
89
|
-
|
|
96
|
+
structures.transitions = Object.create(null)
|
|
90
97
|
for (let i = 0; i < sharedLength; i++) {
|
|
91
|
-
let keys =
|
|
98
|
+
let keys = structures[i]
|
|
92
99
|
if (!keys)
|
|
93
100
|
continue
|
|
94
|
-
let nextTransition, transition =
|
|
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
|
-
|
|
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
|
|
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 (
|
|
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
|
-
|
|
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(
|
|
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 (
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
let
|
|
488
|
-
|
|
489
|
-
|
|
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
|
-
|
|
492
|
-
if (
|
|
493
|
-
nextTransition =
|
|
494
|
-
|
|
531
|
+
nextTransition = transition[key]
|
|
532
|
+
if (!nextTransition) {
|
|
533
|
+
nextTransition = transition[key] = Object.create(null)
|
|
534
|
+
newTransitions++
|
|
495
535
|
}
|
|
536
|
+
transition = nextTransition
|
|
496
537
|
}
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
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
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
585
|
-
|
|
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 =
|
|
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.
|
|
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
|
},
|