msgpackr 1.7.0-alpha2 → 1.7.0-alpha5
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/LICENSE +21 -21
- package/README.md +335 -335
- package/SECURITY.md +11 -11
- package/benchmark.md +67 -67
- package/dist/index.js +128 -102
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +78 -77
- package/dist/index.min.js.map +1 -1
- package/dist/node.cjs +803 -319
- package/dist/node.cjs.map +1 -1
- package/dist/test.js +719 -189
- package/dist/test.js.map +1 -1
- package/index.js +5 -5
- package/iterators.js +86 -86
- package/pack.d.ts +9 -9
- package/pack.js +25 -12
- package/package.json +1 -1
- package/rollup.config.js +49 -49
- package/stream.js +57 -57
- package/struct.js +645 -190
- package/unpack.js +44 -15
- package/dist/str.cjs +0 -100
package/struct.js
CHANGED
|
@@ -1,262 +1,717 @@
|
|
|
1
|
-
// first four bits
|
|
2
|
-
// 0000 - unsigned int
|
|
3
|
-
// 0010 - float32
|
|
4
|
-
// 0011 - float32
|
|
5
|
-
// 0100 - float32
|
|
6
|
-
// 0101 - float32
|
|
7
|
-
// 0110 - latin string reference
|
|
8
|
-
// 0111 - plain reference
|
|
9
|
-
// 1000 - structure reference
|
|
10
|
-
// 1001 - random access structure reference
|
|
11
|
-
// 1010 - float32
|
|
12
|
-
// 1011 - float32
|
|
13
|
-
// 1100 - float32
|
|
14
|
-
// 1101 - float32
|
|
15
|
-
// 1110 - constants and 3-byte strings
|
|
16
|
-
// 1111 - negative int
|
|
17
1
|
|
|
18
|
-
|
|
19
|
-
// 000 - unsigned int
|
|
20
|
-
// 001 - float32
|
|
21
|
-
// 010 - float32
|
|
22
|
-
// 011 - latin string reference
|
|
23
|
-
// 100 - reference
|
|
24
|
-
// 101 - float32
|
|
25
|
-
// 110 - float32
|
|
26
|
-
// 111 - constants and 3-byte strings
|
|
2
|
+
/*
|
|
27
3
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
4
|
+
For "any-data":
|
|
5
|
+
32-55 - record with record ids (-32)
|
|
6
|
+
56 - 8-bit record ids
|
|
7
|
+
57 - 16-bit record ids
|
|
8
|
+
58 - 24-bit record ids
|
|
9
|
+
59 - 32-bit record ids
|
|
10
|
+
250-255 - followed by typed fixed width values
|
|
11
|
+
64-250 msgpackr/cbor/paired data
|
|
12
|
+
arrays and strings within arrays are handled by paired encoding
|
|
13
|
+
|
|
14
|
+
Structure encoding:
|
|
15
|
+
(type - string (using paired encoding))+
|
|
16
|
+
|
|
17
|
+
Type encoding
|
|
18
|
+
encoding byte - fixed width byte - next reference+
|
|
19
|
+
|
|
20
|
+
Encoding byte:
|
|
21
|
+
first bit:
|
|
22
|
+
0 - inline
|
|
23
|
+
1 - reference
|
|
24
|
+
second bit:
|
|
25
|
+
0 - data or number
|
|
26
|
+
1 - string
|
|
27
|
+
|
|
28
|
+
remaining bits:
|
|
29
|
+
character encoding - ISO-8859-x
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
null (0xff)+ 0xf6
|
|
33
|
+
null (0xff)+ 0xf7
|
|
34
|
+
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
import {setWriteStructSlots, RECORD_SYMBOL, addExtension} from './pack.js'
|
|
39
|
+
import {setReadStruct, mult10, readString} from './unpack.js';
|
|
40
|
+
const ASCII = 3; // the MIBenum from https://www.iana.org/assignments/character-sets/character-sets.xhtml (and other character encodings could be referenced by MIBenum)
|
|
41
|
+
const NUMBER = 0;
|
|
42
|
+
const UTF8 = 2;
|
|
43
|
+
const OBJECT_DATA = 1;
|
|
44
|
+
const TYPE_NAMES = ['num', 'object', 'string', 'ascii'];
|
|
45
|
+
const float32Headers = [false, true, true, false, false, true, true, false];
|
|
46
|
+
let updatedPosition;
|
|
47
|
+
const hasNodeBuffer = typeof Buffer !== 'undefined'
|
|
48
|
+
let textEncoder
|
|
49
|
+
try {
|
|
50
|
+
textEncoder = new TextEncoder()
|
|
51
|
+
} catch (error) {}
|
|
52
|
+
const encodeUtf8 = hasNodeBuffer ? function(target, string, position) {
|
|
53
|
+
return target.utf8Write(string, position, 0xffffffff)
|
|
54
|
+
} : (textEncoder && textEncoder.encodeInto) ?
|
|
55
|
+
function(target, string, position) {
|
|
56
|
+
return textEncoder.encodeInto(string, target.subarray(position)).written
|
|
57
|
+
} : false
|
|
58
|
+
|
|
59
|
+
const TYPE = Symbol('type');
|
|
60
|
+
const PARENT = Symbol('parent');
|
|
61
|
+
setWriteStructSlots(writeStruct, prepareStructures);
|
|
62
|
+
function writeStruct(object, target, position, structures, makeRoom, pack, packr) {
|
|
63
|
+
let typedStructs = packr.typedStructs || (packr.typedStructs = []);
|
|
64
|
+
// note that we rely on pack.js to load stored structures before we get to this point
|
|
41
65
|
let targetView = target.dataView;
|
|
42
|
-
let
|
|
43
|
-
let stringData = '';
|
|
66
|
+
let refsStartPosition = (typedStructs.lastStringStart || 100) + position;
|
|
44
67
|
let safeEnd = target.length - 10;
|
|
68
|
+
let start = position;
|
|
69
|
+
if (position > safeEnd) {
|
|
70
|
+
let lastStart = start;
|
|
71
|
+
target = makeRoom(position);
|
|
72
|
+
targetView = target.dataView;
|
|
73
|
+
position -= lastStart;
|
|
74
|
+
refsStartPosition -= lastStart;
|
|
75
|
+
start = 0;
|
|
76
|
+
safeEnd = target.length - 10
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
let refOffset, refPosition = refsStartPosition;
|
|
80
|
+
|
|
81
|
+
let transition = typedStructs.transitions || (typedStructs.transitions = Object.create(null));
|
|
82
|
+
let nextId = typedStructs.nextId || typedStructs.length;
|
|
83
|
+
let headerSize =
|
|
84
|
+
nextId < 0xf ? 1 :
|
|
85
|
+
nextId < 0xf0 ? 2 :
|
|
86
|
+
nextId < 0xf000 ? 3 :
|
|
87
|
+
nextId < 0xf00000 ? 4 : 0;
|
|
88
|
+
if (headerSize === 0)
|
|
89
|
+
return 0;
|
|
90
|
+
position += headerSize;
|
|
91
|
+
let queuedReferences = [];
|
|
92
|
+
let usedLatin0;
|
|
93
|
+
let keyIndex = 0;
|
|
45
94
|
for (let key in object) {
|
|
46
|
-
let
|
|
95
|
+
let value = object[key];
|
|
96
|
+
let nextTransition = transition[key];
|
|
47
97
|
if (!nextTransition) {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
98
|
+
transition[key] = nextTransition = {
|
|
99
|
+
key,
|
|
100
|
+
parent: transition,
|
|
101
|
+
enumerationOffset: 0,
|
|
102
|
+
ascii0: null,
|
|
103
|
+
ascii8: null,
|
|
104
|
+
num8: null,
|
|
105
|
+
string16: null,
|
|
106
|
+
object16: null,
|
|
107
|
+
num32: null,
|
|
108
|
+
float64: null
|
|
109
|
+
};
|
|
51
110
|
}
|
|
52
111
|
if (position > safeEnd) {
|
|
53
|
-
let
|
|
54
|
-
target = makeRoom(position)
|
|
55
|
-
|
|
56
|
-
|
|
112
|
+
let lastStart = start;
|
|
113
|
+
target = makeRoom(position);
|
|
114
|
+
targetView = target.dataView;
|
|
115
|
+
position -= lastStart;
|
|
116
|
+
refsStartPosition -= lastStart;
|
|
117
|
+
refPosition -= lastStart;
|
|
118
|
+
start = 0;
|
|
57
119
|
safeEnd = target.length - 10
|
|
58
120
|
}
|
|
59
|
-
transition = nextTransition
|
|
60
|
-
let value = object[key];
|
|
61
121
|
switch (typeof value) {
|
|
62
122
|
case 'number':
|
|
63
|
-
|
|
64
|
-
|
|
123
|
+
let number = value;
|
|
124
|
+
if (number >> 0 === number && number < 0x20000000 && number > -0x1f000000) {
|
|
125
|
+
if (number < 0xf6 && number >= 0 && (nextTransition.num8 || number < 0x20 && !nextTransition.num32)) {
|
|
126
|
+
transition = nextTransition.num8 || createTypeTransition(nextTransition, NUMBER, 1);
|
|
127
|
+
target[position++] = number;
|
|
128
|
+
} else {
|
|
129
|
+
transition = nextTransition.num32 || createTypeTransition(nextTransition, NUMBER, 4);
|
|
130
|
+
targetView.setUint32(position, number, true);
|
|
131
|
+
position += 4;
|
|
132
|
+
}
|
|
65
133
|
break;
|
|
66
|
-
} else if (
|
|
67
|
-
targetView.setFloat32(position,
|
|
134
|
+
} else if (number < 0x100000000 && number >= -0x80000000) {
|
|
135
|
+
targetView.setFloat32(position, number, true);
|
|
68
136
|
if (float32Headers[target[position + 3] >>> 5]) {
|
|
69
137
|
let xShifted
|
|
70
138
|
// this checks for rounding of numbers that were encoded in 32-bit float to nearest significant decimal digit that could be preserved
|
|
71
|
-
if (((xShifted =
|
|
139
|
+
if (((xShifted = number * mult10[((target[position + 3] & 0x7f) << 1) | (target[position + 2] >> 7)]) >> 0) === xShifted) {
|
|
140
|
+
transition = nextTransition.num32 || createTypeTransition(nextTransition, NUMBER, 4);
|
|
72
141
|
position += 4;
|
|
73
|
-
|
|
142
|
+
break;
|
|
74
143
|
}
|
|
75
144
|
}
|
|
76
145
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
position +=
|
|
80
|
-
|
|
146
|
+
transition = nextTransition.num64 || createTypeTransition(nextTransition, NUMBER, 8);
|
|
147
|
+
targetView.setFloat64(position, number, true);
|
|
148
|
+
position += 8;
|
|
149
|
+
break;
|
|
81
150
|
case 'string':
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
151
|
+
let strLength = value.length;
|
|
152
|
+
refOffset = refPosition - refsStartPosition;
|
|
153
|
+
if ((strLength << 2) + position > safeEnd) {
|
|
154
|
+
let lastStart = start;
|
|
155
|
+
target = makeRoom(refPosition);
|
|
156
|
+
targetView = target.dataView;
|
|
157
|
+
position -= lastStart;
|
|
158
|
+
refsStartPosition -= lastStart;
|
|
159
|
+
refPosition -= lastStart;
|
|
160
|
+
start = 0;
|
|
161
|
+
safeEnd = target.length - 10
|
|
162
|
+
}
|
|
163
|
+
if (strLength > ((0xff00 + refOffset) >> 2)) {
|
|
164
|
+
queuedReferences.push(key, value, position - start);
|
|
165
|
+
break;
|
|
86
166
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
167
|
+
let isNotAscii
|
|
168
|
+
let strStart = refPosition;
|
|
169
|
+
if (strLength < 0x40) {
|
|
170
|
+
let i, c1, c2;
|
|
171
|
+
for (i = 0; i < strLength; i++) {
|
|
172
|
+
c1 = value.charCodeAt(i)
|
|
173
|
+
if (c1 < 0x80) {
|
|
174
|
+
target[refPosition++] = c1
|
|
175
|
+
} else if (c1 < 0x800) {
|
|
176
|
+
isNotAscii = true;
|
|
177
|
+
target[refPosition++] = c1 >> 6 | 0xc0
|
|
178
|
+
target[refPosition++] = c1 & 0x3f | 0x80
|
|
179
|
+
} else if (
|
|
180
|
+
(c1 & 0xfc00) === 0xd800 &&
|
|
181
|
+
((c2 = value.charCodeAt(i + 1)) & 0xfc00) === 0xdc00
|
|
182
|
+
) {
|
|
183
|
+
isNotAscii = true;
|
|
184
|
+
c1 = 0x10000 + ((c1 & 0x03ff) << 10) + (c2 & 0x03ff)
|
|
185
|
+
i++
|
|
186
|
+
target[refPosition++] = c1 >> 18 | 0xf0
|
|
187
|
+
target[refPosition++] = c1 >> 12 & 0x3f | 0x80
|
|
188
|
+
target[refPosition++] = c1 >> 6 & 0x3f | 0x80
|
|
189
|
+
target[refPosition++] = c1 & 0x3f | 0x80
|
|
190
|
+
} else {
|
|
191
|
+
isNotAscii = true;
|
|
192
|
+
target[refPosition++] = c1 >> 12 | 0xe0
|
|
193
|
+
target[refPosition++] = c1 >> 6 & 0x3f | 0x80
|
|
194
|
+
target[refPosition++] = c1 & 0x3f | 0x80
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
} else {
|
|
198
|
+
refPosition += encodeUtf8(target, value, refPosition);
|
|
199
|
+
isNotAscii = refPosition - strStart > strLength;
|
|
200
|
+
}
|
|
201
|
+
if (refOffset < 0x100) {
|
|
202
|
+
if (isNotAscii)
|
|
203
|
+
transition = nextTransition.string8 || createTypeTransition(nextTransition, UTF8, 1);
|
|
204
|
+
else
|
|
205
|
+
transition = nextTransition.ascii8 || createTypeTransition(nextTransition, ASCII, 1);
|
|
206
|
+
target[position++] = refOffset;
|
|
207
|
+
} else {
|
|
208
|
+
if (isNotAscii)
|
|
209
|
+
transition = nextTransition.string16 || createTypeTransition(nextTransition, UTF8, 2);
|
|
210
|
+
else
|
|
211
|
+
transition = nextTransition.ascii16 || createTypeTransition(nextTransition, ASCII, 2);
|
|
212
|
+
targetView.setUint16(position, refOffset, true);
|
|
213
|
+
position += 2;
|
|
98
214
|
}
|
|
99
215
|
break;
|
|
100
216
|
case 'object':
|
|
101
217
|
if (value) {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
218
|
+
//transition = nextTransition.object16 || createTypeTransition(nextTransition, OBJECT_DATA, 2);
|
|
219
|
+
queuedReferences.push(key, value, keyIndex);
|
|
220
|
+
break;
|
|
105
221
|
} else { // null
|
|
106
|
-
|
|
222
|
+
nextTransition = anyType(nextTransition, position, targetView, -10); // match CBOR with this
|
|
223
|
+
if (nextTransition) {
|
|
224
|
+
transition = nextTransition;
|
|
225
|
+
position = updatedPosition;
|
|
226
|
+
} else queuedReferences.push(key, value, keyIndex);
|
|
107
227
|
}
|
|
108
228
|
break;
|
|
109
229
|
case 'boolean':
|
|
110
|
-
|
|
230
|
+
transition = nextTransition.num8 || nextTransition.ascii8 || createTypeTransition(nextTransition, NUMBER, 1);
|
|
231
|
+
target[position++] = value ? 0xf9 : 0xf8; // match CBOR with these
|
|
111
232
|
break;
|
|
112
233
|
case 'undefined':
|
|
113
|
-
|
|
234
|
+
nextTransition = anyType(nextTransition, position, targetView, -9); // match CBOR with this
|
|
235
|
+
if (nextTransition) {
|
|
236
|
+
transition = nextTransition;
|
|
237
|
+
position = updatedPosition;
|
|
238
|
+
} else queuedReferences.push(key, value, keyIndex);
|
|
114
239
|
break;
|
|
115
240
|
}
|
|
116
|
-
|
|
117
|
-
position += 4;
|
|
118
|
-
}
|
|
119
|
-
let recordId = transition[RECORD_SYMBOL]
|
|
120
|
-
if (!(recordId < 1024)) {
|
|
121
|
-
// for now just punt and go back to writeObject
|
|
122
|
-
return 0;
|
|
123
|
-
// newRecord(transition, transition.__keys__ || Object.keys(object), newTransitions, true)
|
|
124
|
-
}
|
|
125
|
-
let stringLength = stringData.length;
|
|
126
|
-
if (stringData) {
|
|
127
|
-
if (position + stringLength > safeEnd) {
|
|
128
|
-
target = makeRoom(position + stringLength);
|
|
129
|
-
}
|
|
130
|
-
position += target.latin1Write(stringData, position, 0xffffffff);
|
|
241
|
+
keyIndex++;
|
|
131
242
|
}
|
|
132
|
-
|
|
133
|
-
target[start + 1] = recordId & 0xff;
|
|
134
|
-
target[start + 2] = stringLength >> 8;
|
|
135
|
-
target[start + 3] = stringLength & 0xff;
|
|
136
|
-
let queued32BitReferences;
|
|
243
|
+
|
|
137
244
|
for (let i = 0, l = queuedReferences.length; i < l;) {
|
|
245
|
+
let key = queuedReferences[i++];
|
|
138
246
|
let value = queuedReferences[i++];
|
|
139
|
-
let
|
|
140
|
-
let
|
|
141
|
-
if (
|
|
142
|
-
|
|
247
|
+
let propertyIndex = queuedReferences[i++];
|
|
248
|
+
let nextTransition = transition[key];
|
|
249
|
+
if (!nextTransition) {
|
|
250
|
+
transition[key] = nextTransition = {
|
|
251
|
+
key,
|
|
252
|
+
parent: transition,
|
|
253
|
+
enumerationOffset: propertyIndex - keyIndex,
|
|
254
|
+
ascii0: null,
|
|
255
|
+
ascii8: null,
|
|
256
|
+
num8: null,
|
|
257
|
+
string16: null,
|
|
258
|
+
object16: null,
|
|
259
|
+
num32: null,
|
|
260
|
+
float64: null
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
let newPosition;
|
|
264
|
+
if (value) {
|
|
265
|
+
/*if (typeof value === 'string') { // TODO: we could re-enable long strings
|
|
266
|
+
if (position + value.length * 3 > safeEnd) {
|
|
267
|
+
target = makeRoom(position + value.length * 3);
|
|
268
|
+
position -= start;
|
|
269
|
+
targetView = target.dataView;
|
|
270
|
+
start = 0;
|
|
271
|
+
}
|
|
272
|
+
newPosition = position + target.utf8Write(value, position, 0xffffffff);
|
|
273
|
+
} else { */
|
|
274
|
+
let size;
|
|
275
|
+
refOffset = refPosition - refsStartPosition;
|
|
276
|
+
if (refOffset < 0xff00) {
|
|
277
|
+
transition = nextTransition.object16;
|
|
278
|
+
if (transition)
|
|
279
|
+
size = 2;
|
|
280
|
+
else if ((transition = nextTransition.object32))
|
|
281
|
+
size = 4;
|
|
282
|
+
else {
|
|
283
|
+
transition = createTypeTransition(nextTransition, OBJECT_DATA, 2);
|
|
284
|
+
size = 2;
|
|
285
|
+
}
|
|
286
|
+
} else {
|
|
287
|
+
transition = nextTransition.object32 || createTypeTransition(nextTransition, OBJECT_DATA, 4);
|
|
288
|
+
size = 4;
|
|
289
|
+
}
|
|
290
|
+
newPosition = pack(value, refPosition);
|
|
291
|
+
//}
|
|
292
|
+
if (typeof newPosition === 'object') {
|
|
293
|
+
// re-allocated
|
|
294
|
+
refPosition = newPosition.position;
|
|
295
|
+
targetView = newPosition.targetView;
|
|
296
|
+
target = newPosition.target;
|
|
297
|
+
refsStartPosition -= start;
|
|
298
|
+
position -= start;
|
|
299
|
+
start = 0;
|
|
300
|
+
} else
|
|
301
|
+
refPosition = newPosition;
|
|
302
|
+
if (size === 2) {
|
|
303
|
+
targetView.setUint16(position, refOffset, true);
|
|
304
|
+
position += 2;
|
|
305
|
+
} else {
|
|
306
|
+
targetView.setUint32(position, refOffset, true);
|
|
307
|
+
position += 4;
|
|
308
|
+
}
|
|
143
309
|
} else {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
310
|
+
transition = nextTransition.object16 || createTypeTransition(nextTransition, OBJECT_DATA, 2);
|
|
311
|
+
targetView.setInt16(position, value === null ? -10 : -9, true);
|
|
312
|
+
position += 2;
|
|
147
313
|
}
|
|
148
|
-
|
|
149
|
-
if (typeof newPosition === 'object') {
|
|
150
|
-
// re-allocated
|
|
151
|
-
position = newPosition.position;
|
|
152
|
-
targetView = newPosition.targetView;
|
|
153
|
-
start = 0;
|
|
154
|
-
} else
|
|
155
|
-
position = newPosition;
|
|
314
|
+
keyIndex++;
|
|
156
315
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
let recordId = transition[RECORD_SYMBOL];
|
|
319
|
+
if (recordId == null) {
|
|
320
|
+
recordId = packr.typedStructs.length;
|
|
321
|
+
let structure = [];
|
|
322
|
+
let nextTransition = transition;
|
|
323
|
+
let key, type;
|
|
324
|
+
while ((type = nextTransition.__type) !== undefined) {
|
|
325
|
+
let size = nextTransition.__size;
|
|
326
|
+
nextTransition = nextTransition.__parent;
|
|
327
|
+
key = nextTransition.key;
|
|
328
|
+
let property = [type, size, key];
|
|
329
|
+
if (nextTransition.enumerationOffset)
|
|
330
|
+
property.push(nextTransition.enumerationOffset);
|
|
331
|
+
structure.push(property);
|
|
332
|
+
nextTransition = nextTransition.parent;
|
|
164
333
|
}
|
|
334
|
+
structure.reverse();
|
|
335
|
+
transition[RECORD_SYMBOL] = recordId;
|
|
336
|
+
packr.typedStructs[recordId] = structure;
|
|
337
|
+
pack(null, 0, true); // special call to notify that structures have been updated
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
switch (headerSize) {
|
|
342
|
+
case 1:
|
|
343
|
+
if (recordId >= 0x10) return 0;
|
|
344
|
+
target[start] = recordId + 0x20;
|
|
345
|
+
break;
|
|
346
|
+
case 2:
|
|
347
|
+
if (recordId >= 0x100) return 0;
|
|
348
|
+
target[start] = 0x38;
|
|
349
|
+
target[start + 1] = recordId;
|
|
350
|
+
break;
|
|
351
|
+
case 3:
|
|
352
|
+
if (recordId >= 0x10000) return 0;
|
|
353
|
+
target[start] = 0x39;
|
|
354
|
+
target.setUint16(start + 1, recordId, true);
|
|
355
|
+
break;
|
|
356
|
+
case 4:
|
|
357
|
+
if (recordId >= 0x1000000) return 0;
|
|
358
|
+
target.setUint32(start, (recordId << 8) + 0x3a, true);
|
|
359
|
+
break;
|
|
165
360
|
}
|
|
166
361
|
|
|
167
|
-
|
|
362
|
+
if (position < refsStartPosition) {
|
|
363
|
+
if (refsStartPosition === refPosition)
|
|
364
|
+
return position; // no refs
|
|
365
|
+
// adjust positioning
|
|
366
|
+
target.copyWithin(position, refsStartPosition, refPosition);
|
|
367
|
+
refPosition += position - refsStartPosition;
|
|
368
|
+
typedStructs.lastStringStart = position - start;
|
|
369
|
+
} else if (position > refsStartPosition) {
|
|
370
|
+
if (refsStartPosition === refPosition)
|
|
371
|
+
return position; // no refs
|
|
372
|
+
typedStructs.lastStringStart = position - start;
|
|
373
|
+
return writeStruct(object, target, start, structures, makeRoom, pack, packr);
|
|
374
|
+
}
|
|
375
|
+
return refPosition;
|
|
376
|
+
}
|
|
377
|
+
function anyType(transition, position, targetView, value) {
|
|
378
|
+
let nextTransition;
|
|
379
|
+
if ((nextTransition = transition.ascii8 || transition.num8)) {
|
|
380
|
+
targetView.setInt8(position, value, true);
|
|
381
|
+
updatedPosition = position + 1;
|
|
382
|
+
return nextTransition;
|
|
383
|
+
}
|
|
384
|
+
if ((nextTransition = transition.string16 || transition.object16)) {
|
|
385
|
+
targetView.setInt16(position, value, true);
|
|
386
|
+
updatedPosition = position + 2;
|
|
387
|
+
return nextTransition;
|
|
388
|
+
}
|
|
389
|
+
if (nextTransition = transition.num32) {
|
|
390
|
+
targetView.setUint32(position, 0xe0000100 + value, true);
|
|
391
|
+
updatedPosition = position + 4;
|
|
392
|
+
return nextTransition;
|
|
393
|
+
}
|
|
394
|
+
// transition.float64
|
|
395
|
+
if (nextTransition = transition.num64) {
|
|
396
|
+
targetView.setFloat64(position, NaN, true);
|
|
397
|
+
targetView.setInt8(position, value);
|
|
398
|
+
updatedPosition = position + 8;
|
|
399
|
+
return nextTransition;
|
|
400
|
+
}
|
|
401
|
+
updatedPosition = position;
|
|
402
|
+
// TODO: can we do an "any" type where we defer the decision?
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
function createTypeTransition(transition, type, size) {
|
|
406
|
+
let typeName = TYPE_NAMES[type] + (size << 3);
|
|
407
|
+
let newTransition = transition[typeName] || (transition[typeName] = Object.create(null));
|
|
408
|
+
newTransition.__type = type;
|
|
409
|
+
newTransition.__size = size;
|
|
410
|
+
newTransition.__parent = transition;
|
|
411
|
+
return newTransition;
|
|
412
|
+
}
|
|
413
|
+
function onLoadedStructures(sharedData) {
|
|
414
|
+
if (!(sharedData instanceof Map))
|
|
415
|
+
return sharedData;
|
|
416
|
+
let typed = sharedData.get('typed') || [];
|
|
417
|
+
if (Object.isFrozen(typed))
|
|
418
|
+
typed = typed.map(structure => structure.slice(0));
|
|
419
|
+
let named = sharedData.get('named');
|
|
420
|
+
let transitions = Object.create(null);
|
|
421
|
+
for (let i = 0, l = typed.length; i < l; i++) {
|
|
422
|
+
let structure = typed[i];
|
|
423
|
+
let transition = transitions;
|
|
424
|
+
for (let [type, size, key] of structure) {
|
|
425
|
+
let nextTransition = transition[key];
|
|
426
|
+
if (!nextTransition) {
|
|
427
|
+
transition[key] = nextTransition = {
|
|
428
|
+
key,
|
|
429
|
+
parent: transition,
|
|
430
|
+
enumerationOffset: 0,
|
|
431
|
+
ascii0: null,
|
|
432
|
+
ascii8: null,
|
|
433
|
+
num8: null,
|
|
434
|
+
string16: null,
|
|
435
|
+
object16: null,
|
|
436
|
+
num32: null,
|
|
437
|
+
float64: null
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
transition = createTypeTransition(nextTransition, type, size);
|
|
441
|
+
}
|
|
442
|
+
transition[RECORD_SYMBOL] = i;
|
|
443
|
+
}
|
|
444
|
+
typed.transitions = transitions;
|
|
445
|
+
this.typedStructs = typed;
|
|
446
|
+
this.lastTypedStructuresLength = typed.length;
|
|
447
|
+
return named;
|
|
168
448
|
}
|
|
169
449
|
var sourceSymbol = Symbol('source')
|
|
170
|
-
function readStruct(src, position, srcEnd,
|
|
171
|
-
var stringLength = (src[position++] << 8) | src[position++];
|
|
450
|
+
function readStruct(src, position, srcEnd, unpackr) {
|
|
451
|
+
// var stringLength = (src[position++] << 8) | src[position++];
|
|
452
|
+
let recordId = src[position++] - 0x20;
|
|
453
|
+
if (recordId >= 24) {
|
|
454
|
+
switch(recordId) {
|
|
455
|
+
case 24: recordId = src[position++]; break;
|
|
456
|
+
// little endian:
|
|
457
|
+
case 25: recordId = src[position++] + (src[position++] << 8); break;
|
|
458
|
+
case 26: recordId = src[position++] + (src[position++] << 8) + (src[position++] << 16); break;
|
|
459
|
+
case 27: recordId = src[position++] + (src[position++] << 8) + (src[position++] << 16) + (src[position++] << 24); break;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
let structure = unpackr.typedStructs?.[recordId];
|
|
463
|
+
if (!structure) {
|
|
464
|
+
// copy src buffer because getStructures will override it
|
|
465
|
+
src = Uint8Array.prototype.slice.call(src, position, srcEnd);
|
|
466
|
+
srcEnd -= position;
|
|
467
|
+
position = 0;
|
|
468
|
+
unpackr._mergeStructures(unpackr.getStructures());
|
|
469
|
+
if (!unpackr.typedStructs)
|
|
470
|
+
throw new Error('Could not find any shared typed structures');
|
|
471
|
+
unpackr.lastTypedStructuresLength = unpackr.typedStructs.length;
|
|
472
|
+
structure = unpackr.typedStructs[recordId];
|
|
473
|
+
if (!structure)
|
|
474
|
+
throw new Error('Could not find typed structure ' + recordId);
|
|
475
|
+
}
|
|
172
476
|
var construct = structure.construct;
|
|
173
477
|
if (!construct) {
|
|
174
|
-
construct = structure.construct = function() {
|
|
478
|
+
construct = structure.construct = function LazyObject() {
|
|
175
479
|
}
|
|
176
480
|
var prototype = construct.prototype;
|
|
481
|
+
let properties = [];
|
|
177
482
|
Object.defineProperty(prototype, 'toJSON', {
|
|
178
|
-
|
|
483
|
+
value() {
|
|
179
484
|
// return an enumerable object with own properties to JSON stringify
|
|
180
485
|
let resolved = {};
|
|
181
|
-
for (let i = 0, l =
|
|
182
|
-
let key =
|
|
486
|
+
for (let i = 0, l = properties.length; i < l; i++) {
|
|
487
|
+
let key = properties[i].key;
|
|
183
488
|
resolved[key] = this[key];
|
|
184
489
|
}
|
|
185
490
|
return resolved;
|
|
186
491
|
},
|
|
187
492
|
// not enumerable or anything
|
|
188
493
|
});
|
|
494
|
+
let currentOffset = 0;
|
|
495
|
+
let lastRefProperty;
|
|
189
496
|
for (let i = 0, l = structure.length; i < l; i++) {
|
|
190
|
-
let
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
497
|
+
let definition = structure[i];
|
|
498
|
+
let [ type, size, key, enumerationOffset ] = definition;
|
|
499
|
+
let property = {
|
|
500
|
+
key,
|
|
501
|
+
offset: currentOffset,
|
|
502
|
+
}
|
|
503
|
+
if (enumerationOffset)
|
|
504
|
+
properties.splice(i + enumerationOffset, 0, property);
|
|
505
|
+
else
|
|
506
|
+
properties.push(property);
|
|
507
|
+
let getRef;
|
|
508
|
+
switch(size) { // TODO: Move into a separate function
|
|
509
|
+
case 0: getRef = () => 0; break;
|
|
510
|
+
case 1:
|
|
511
|
+
getRef = (source, position) => {
|
|
512
|
+
let ref = source.src[position + property.offset];
|
|
513
|
+
return ref >= 0xf6 ? toConstant(ref) : ref;
|
|
514
|
+
};
|
|
515
|
+
break;
|
|
516
|
+
case 2:
|
|
517
|
+
getRef = (source, position) => {
|
|
518
|
+
let src = source.src;
|
|
519
|
+
let dataView = src.dataView || (src.dataView = new DataView(src.buffer, src.byteOffset, src.byteLength));
|
|
520
|
+
let ref = dataView.getUint16(position + property.offset, true);
|
|
521
|
+
return ref >= 0xff00 ? toConstant(ref & 0xff) : ref;
|
|
522
|
+
};
|
|
523
|
+
break;
|
|
524
|
+
case 4:
|
|
525
|
+
getRef = (source, position) => {
|
|
526
|
+
let src = source.src;
|
|
527
|
+
let dataView = src.dataView || (src.dataView = new DataView(src.buffer, src.byteOffset, src.byteLength));
|
|
528
|
+
let ref = dataView.getUint32(position + property.offset, true);
|
|
529
|
+
return ref >= 0xffffff00 ? toConstant(ref & 0xff) : ref;
|
|
530
|
+
};
|
|
531
|
+
break;
|
|
532
|
+
}
|
|
533
|
+
property.getRef = getRef;
|
|
534
|
+
currentOffset += size;
|
|
535
|
+
let get;
|
|
536
|
+
switch(type) {
|
|
537
|
+
case ASCII:
|
|
538
|
+
if (lastRefProperty && !lastRefProperty.next)
|
|
539
|
+
lastRefProperty.next = property;
|
|
540
|
+
lastRefProperty = property;
|
|
541
|
+
property.multiGetCount = 0;
|
|
542
|
+
get = function() {
|
|
543
|
+
let source = this[sourceSymbol];
|
|
544
|
+
let src = source.src;
|
|
545
|
+
let position = source.position;
|
|
546
|
+
let refStart = currentOffset + position;
|
|
547
|
+
let ref = getRef(source, position);
|
|
548
|
+
if (typeof ref !== 'number') return ref;
|
|
549
|
+
|
|
550
|
+
let end, next = property.next;
|
|
551
|
+
while(next) {
|
|
552
|
+
end = next.getRef(source, position);
|
|
553
|
+
if (typeof end === 'number')
|
|
554
|
+
break;
|
|
555
|
+
else
|
|
556
|
+
end = null;
|
|
557
|
+
next = next.next;
|
|
558
|
+
}
|
|
559
|
+
if (end == null)
|
|
560
|
+
end = source.srcEnd - refStart;
|
|
561
|
+
if (source.srcString) {
|
|
562
|
+
return source.srcString.slice(ref, end);
|
|
563
|
+
}
|
|
564
|
+
/*if (property.multiGetCount > 0) {
|
|
565
|
+
let asciiEnd;
|
|
566
|
+
next = firstRefProperty;
|
|
567
|
+
let dataView = src.dataView || (src.dataView = new DataView(src.buffer, src.byteOffset, src.byteLength));
|
|
568
|
+
do {
|
|
569
|
+
asciiEnd = dataView.getUint16(source.position + next.offset, true);
|
|
570
|
+
if (asciiEnd < 0xff00)
|
|
223
571
|
break;
|
|
572
|
+
else
|
|
573
|
+
asciiEnd = null;
|
|
574
|
+
} while((next = next.next));
|
|
575
|
+
if (asciiEnd == null)
|
|
576
|
+
asciiEnd = source.srcEnd - refStart
|
|
577
|
+
source.srcString = src.toString('latin1', refStart, refStart + asciiEnd);
|
|
578
|
+
return source.srcString.slice(ref, end);
|
|
579
|
+
}
|
|
580
|
+
if (source.prevStringGet) {
|
|
581
|
+
source.prevStringGet.multiGetCount += 2;
|
|
582
|
+
} else {
|
|
583
|
+
source.prevStringGet = property;
|
|
584
|
+
property.multiGetCount--;
|
|
585
|
+
}*/
|
|
586
|
+
return readString(src, ref + refStart, end - ref);
|
|
587
|
+
//return src.toString('latin1', ref + refStart, end + refStart);
|
|
588
|
+
};
|
|
589
|
+
break;
|
|
590
|
+
case UTF8: case OBJECT_DATA:
|
|
591
|
+
if (lastRefProperty && !lastRefProperty.next)
|
|
592
|
+
lastRefProperty.next = property;
|
|
593
|
+
lastRefProperty = property;
|
|
594
|
+
get = function() {
|
|
595
|
+
let source = this[sourceSymbol];
|
|
596
|
+
let position = source.position;
|
|
597
|
+
let refStart = currentOffset + position;
|
|
598
|
+
let ref = getRef(source, position);
|
|
599
|
+
if (typeof ref !== 'number') return ref;
|
|
600
|
+
let src = source.src;
|
|
601
|
+
let end, next = property.next;
|
|
602
|
+
while(next) {
|
|
603
|
+
end = next.getRef(source, position);
|
|
604
|
+
if (typeof end === 'number')
|
|
605
|
+
break;
|
|
606
|
+
else
|
|
607
|
+
end = null;
|
|
608
|
+
next = next.next;
|
|
609
|
+
}
|
|
610
|
+
if (end == null)
|
|
611
|
+
end = source.srcEnd - refStart;
|
|
612
|
+
if (type === UTF8) {
|
|
613
|
+
return src.toString('utf8', ref + refStart, end + refStart);
|
|
614
|
+
} else {
|
|
615
|
+
return unpackr.unpack(src, { start: ref + refStart, end: end + refStart }); // could reuse this object
|
|
616
|
+
}
|
|
617
|
+
};
|
|
618
|
+
break;
|
|
619
|
+
case NUMBER:
|
|
620
|
+
switch(size) {
|
|
621
|
+
case 4:
|
|
622
|
+
get = function () {
|
|
623
|
+
let source = this[sourceSymbol];
|
|
624
|
+
let src = source.src;
|
|
625
|
+
let dataView = src.dataView || (src.dataView = new DataView(src.buffer, src.byteOffset, src.byteLength));
|
|
626
|
+
let position = source.position + property.offset;
|
|
627
|
+
let value = dataView.getInt32(position, true)
|
|
628
|
+
if (value < 0x20000000) {
|
|
629
|
+
if (value > -0x1f000000)
|
|
630
|
+
return value;
|
|
631
|
+
if (value > -0x20000000)
|
|
632
|
+
return toConstant(value & 0xff);
|
|
633
|
+
}
|
|
634
|
+
let fValue = dataView.getFloat32(position, true);
|
|
635
|
+
// this does rounding of numbers that were encoded in 32-bit float to nearest significant decimal digit that could be preserved
|
|
636
|
+
let multiplier = mult10[((src[position + 3] & 0x7f) << 1) | (src[position + 2] >> 7)]
|
|
637
|
+
return ((multiplier * fValue + (fValue > 0 ? 0.5 : -0.5)) >> 0) / multiplier;
|
|
638
|
+
};
|
|
639
|
+
break;
|
|
640
|
+
case 8:
|
|
641
|
+
get = function () {
|
|
642
|
+
let source = this[sourceSymbol];
|
|
643
|
+
let src = source.src;
|
|
644
|
+
let dataView = src.dataView || (src.dataView = new DataView(src.buffer, src.byteOffset, src.byteLength));
|
|
645
|
+
let value = dataView.getFloat64(source.position + property.offset, true);
|
|
646
|
+
if (isNaN(value)) {
|
|
647
|
+
let byte = src[source.position + property.offset];
|
|
648
|
+
if (byte >= 0xf6)
|
|
649
|
+
return toConstant(byte);
|
|
224
650
|
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
case 2: return false;
|
|
237
|
-
case 3: return true;
|
|
238
|
-
case 8: return dataView.getFloat64(position + (value & 0x3ffffff), true);
|
|
239
|
-
case 0x18: return '';
|
|
240
|
-
case 0x19: return String.fromCharCode((value >> 16) & 0xff);
|
|
241
|
-
case 0x1a: return String.fromCharCode((value >> 16) & 0xff, (value >> 8) & 0xff);
|
|
242
|
-
case 0x1b: return String.fromCharCode((value >> 16) & 0xff, (value >> 8) & 0xff, value & 0xff);
|
|
243
|
-
default: throw new Error('Unknown constant');
|
|
244
|
-
}
|
|
651
|
+
return value;
|
|
652
|
+
};
|
|
653
|
+
break;
|
|
654
|
+
case 1:
|
|
655
|
+
get = function () {
|
|
656
|
+
let source = this[sourceSymbol];
|
|
657
|
+
let src = source.src;
|
|
658
|
+
let value = src[source.position + property.offset];
|
|
659
|
+
return value < 0xf6 ? value : toConstant(value);
|
|
660
|
+
};
|
|
661
|
+
break;
|
|
245
662
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
});
|
|
663
|
+
}
|
|
664
|
+
property.get = get;
|
|
249
665
|
}
|
|
666
|
+
for (let property of properties) // assign in enumeration order
|
|
667
|
+
Object.defineProperty(prototype, property.key, { get: property.get, enumerable: true });
|
|
250
668
|
}
|
|
251
669
|
var instance = new construct();
|
|
252
670
|
instance[sourceSymbol] = {
|
|
253
671
|
src,
|
|
254
|
-
uint32: src.uint32,
|
|
255
672
|
position,
|
|
256
673
|
srcString: '',
|
|
257
|
-
srcEnd
|
|
258
|
-
stringLength
|
|
674
|
+
srcEnd
|
|
259
675
|
}
|
|
260
676
|
return instance;
|
|
261
677
|
}
|
|
262
|
-
|
|
678
|
+
function toConstant(code) {
|
|
679
|
+
switch(code) {
|
|
680
|
+
case 0xf6: return null;
|
|
681
|
+
case 0xf7: return undefined;
|
|
682
|
+
case 0xf8: return false;
|
|
683
|
+
case 0xf9: return true;
|
|
684
|
+
}
|
|
685
|
+
throw new Error('Unknown constant');
|
|
686
|
+
}
|
|
687
|
+
function prepareStructures(structures, packr) {
|
|
688
|
+
if (packr.typedStructs) {
|
|
689
|
+
let structMap = new Map();
|
|
690
|
+
structMap.set('named', structures);
|
|
691
|
+
structMap.set('typed', packr.typedStructs);
|
|
692
|
+
structures = structMap;
|
|
693
|
+
}
|
|
694
|
+
let lastTypedStructuresLength = packr.lastTypedStructuresLength || 0;
|
|
695
|
+
structures.isCompatible = existing => {
|
|
696
|
+
let compatible = true;
|
|
697
|
+
if (existing instanceof Map) {
|
|
698
|
+
let named = existing.get('named') || [];
|
|
699
|
+
if (named.length !== (packr.lastNamedStructuresLength || 0))
|
|
700
|
+
compatible = false;
|
|
701
|
+
let typed = existing.get('typed') || [];
|
|
702
|
+
if (typed.length !== lastTypedStructuresLength)
|
|
703
|
+
compatible = false;
|
|
704
|
+
} else if (existing instanceof Array) {
|
|
705
|
+
if (existing.length !== (packr.lastNamedStructuresLength || 0))
|
|
706
|
+
compatible = false;
|
|
707
|
+
}
|
|
708
|
+
if (!compatible)
|
|
709
|
+
packr._mergeStructures(existing);
|
|
710
|
+
return compatible;
|
|
711
|
+
};
|
|
712
|
+
packr.lastTypedStructuresLength = packr.typedStructs?.length;
|
|
713
|
+
return structures;
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
setReadStruct(readStruct, onLoadedStructures);
|
|
717
|
+
|