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/README.md +16 -2
- package/dist/index.js +278 -100
- package/dist/index.min.js +68 -99
- package/dist/node.cjs +281 -103
- package/dist/str.cjs +100 -0
- package/dist/test.js +44 -3
- package/index.d.ts +1 -1
- package/index.js +1 -1
- package/node-index.js +2 -4
- package/pack.js +191 -85
- package/package.json +7 -1
- package/unpack.d.ts +3 -0
- package/unpack.js +84 -11
- 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 -640
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
|
|
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
|
|
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 (
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
let
|
|
488
|
-
|
|
489
|
-
|
|
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
|
-
|
|
492
|
-
if (
|
|
493
|
-
nextTransition =
|
|
494
|
-
|
|
536
|
+
nextTransition = transition[key]
|
|
537
|
+
if (!nextTransition) {
|
|
538
|
+
nextTransition = transition[key] = Object.create(null)
|
|
539
|
+
newTransitions++
|
|
495
540
|
}
|
|
541
|
+
transition = nextTransition
|
|
496
542
|
}
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
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
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
585
|
-
|
|
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),
|
|
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 =
|
|
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
|
+
"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
|
},
|