msgpackr 1.6.2 → 1.7.0-alpha3

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