miijs 1.8.1 → 2.0.1

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-fu.js ADDED
@@ -0,0 +1,1140 @@
1
+ // @ts-check
2
+ /*!
3
+ * struct-fu library: https://github.com/natevw/struct-fu
4
+ * forked by ariankordi: https://github.com/ariankordi/struct-fu
5
+ * @author Nathan Vander Wilt <https://github.com/natevw>
6
+ * @license Apache-2.0
7
+ * @license BSD-2-Clause
8
+ */
9
+
10
+ // // ---------------------------------------------------------------------
11
+ // // Exported JSDoc Type Definitions
12
+ // // ---------------------------------------------------------------------
13
+
14
+ /**
15
+ * A type representing either a raw ArrayBuffer or a typed view of it.
16
+ * @typedef {ArrayBuffer | Uint8Array} BufferSource
17
+ */
18
+
19
+ /**
20
+ * A type for the offset/"cursor" used to track the byte and bit offsets.
21
+ * @typedef {Object} Offset
22
+ * @property {number} bytes - Current byte offset.
23
+ * @property {number} [bits] - Current bit offset.
24
+ */
25
+
26
+ /**
27
+ * A transform interface for bitfields, converting between raw number bits and user values.
28
+ * Notably, the input/output can be number or boolean. (Used by `b2v` and `v2b` methods.)
29
+ * @typedef {Object} BitTransform
30
+ * @property {function(this: {width:number}, number): number|boolean} b2v - Called when reading bits from buffer.
31
+ * @property {function(this: {width:number}, number|boolean): number} v2b - Called when writing bits into buffer.
32
+ * @property {number} [width=1] - The width (in bits) of this bitfield.
33
+ * @property {boolean|null} [littleEndian] - Whether or not the bitfield is little-endian.
34
+ */
35
+
36
+ /**
37
+ * A transform interface for byte fields, converting between raw bytes and user values.
38
+ * (Used by `b2v` and `vTb` methods.)
39
+ * @typedef {Object} ByteTransform
40
+ * @property {function(BufferSource): *} b2v - Called when reading bytes from buffer.
41
+ * @property {function(*, Uint8Array): number} vTb - Called when writing values into buffer.
42
+ * @property {number} [size=1] - The size (in bytes) of this field.
43
+ */
44
+
45
+ /**
46
+ * Represents a padding field that ensures proper byte alignment in a struct.
47
+ * It does not hold a value but affects struct layout.
48
+ * @typedef {Object} PaddingField
49
+ * @property {number} _padTo - The byte offset to pad to.
50
+ * @property {string} [_id] - Internal unique ID.
51
+ */
52
+
53
+ /**
54
+ * A single field definition, which must define how to read (unpack) and write (pack) data.
55
+ * This is the base type that all specialized fields (bitfields, bytefields, etc.) implement.
56
+ * @typedef {Object} Field
57
+ * @property {string} [name] - The name of the field (if any).
58
+ * @property {number} size - The size of the field in bytes (for non-bitfield types).
59
+ * @property {number} [width] - The size of the field in bits (for bitfields).
60
+ * @property {number | Offset} [offset] - Byte or (byte, bits) offset.
61
+ * @property {function(BufferSource, Offset=): *} unpack - Unpacks the field value from a buffer (`valueFromBytes`).
62
+ * @property {function(*, BufferSource=, Offset=): Uint8Array} pack - Packs a value into a buffer (`bytesFromValue`).
63
+ * @property {Object<string, Field>|null} [_hoistFields] - If this is a nested struct, this maps sub-fields to their definitions.
64
+ * @property {Object<string, Field>} [fields] - An object mapping field names to their definitions.
65
+ */
66
+
67
+ /**
68
+ * Template for the return type of _.struct(), representing an instance of a structure with pack/unpack methods.
69
+ * It is generic in case you want to define a typed object for the data.
70
+ * @template T
71
+ * @typedef StructInstance
72
+ * @property {function(BufferSource, Offset=): T} unpack - Deserialize from a BufferSource into the structured object.
73
+ * @property {function(T, BufferSource=, Offset=): Uint8Array} pack - Serialize the structured object into a Uint8Array.
74
+ * @property {Object<string, Field>} fields - Field definitions keyed by field name.
75
+ * @property {number} size - The total size in bytes of the packed structure.
76
+ * @property {string} [name] - The name of the struct (if provided).
77
+ * @property {Object<string, Field>|null} [_hoistFields] - If this is a nested struct, this maps sub-fields to their definitions.
78
+ */
79
+
80
+ // // ---------------------------------------------------------------------
81
+ // // UMD / factory setup
82
+ // // ---------------------------------------------------------------------
83
+
84
+ (function (root, factory) {
85
+ // @ts-ignore - cannot find name define
86
+ if (typeof define === 'function' && define.amd) {
87
+ // AMD. Register as an anonymous module.
88
+ // @ts-ignore
89
+ define([], factory);
90
+ } else if (typeof module === 'object' && module.exports) {
91
+ // Node.js/CommonJS
92
+
93
+ // In Node (in versions earlier than 11, released 2018), TextEn/Decoder
94
+ // are only available through the "util" package, however bundlers
95
+ // building for browser will see that and always try to include it.
96
+
97
+ // Therefore, this will assume TextEncoder/TextDecoder are in globalThis (not root).
98
+ module.exports = factory(globalThis.TextEncoder, globalThis.TextDecoder);
99
+ // NOTE: Uncomment below if you are using versions of Node earlier than 11.
100
+ // module.exports = factory(require('util').TextEncoder, require('util').TextDecoder);
101
+ } else {
102
+ // Browser globals (root is window)
103
+
104
+ // Assume TextEncoder/TextDecoder are either defined or undefined in window.
105
+ /** @type {*} */ (root)._ = factory(/** @type {*} */ (root).TextEncoder, /** @type {*} */ (root).TextDecoder);
106
+ }
107
+ }(typeof self !== 'undefined' ? self : this,
108
+ // It seems that TypeScript is predicting a type for the whole
109
+ // namespace that includes all functions, and defining any custom
110
+ // returns type ruins that, leaving the .d.ts empty and dry of functions.
111
+ /* eslint-disable jsdoc/require-returns-type */
112
+ /**
113
+ * @param {typeof TextEncoder} _TextEncoder
114
+ * @param {typeof TextDecoder} _TextDecoder
115
+ * @returns Returns the exported namespace.
116
+ */
117
+ function (_TextEncoder, _TextDecoder) {
118
+ /* eslint-enable jsdoc/require-returns-type */
119
+ 'use strict';
120
+
121
+ /**
122
+ * A library for defining structs to convert between JSON and binary.
123
+ * Supports numbers, bytes, strings, and bitfields.
124
+ * Compatible with browsers down to Safari 5.1.
125
+ * @namespace _
126
+ */
127
+ var _ = {};
128
+
129
+ // Add ECMA262-5 method binding if not supported natively
130
+ // https://github.com/ReactNativeNews/react-native-newsletter/blob/93016f62af32d97cc009f991d4f7c3c7155a4f26/ie.js#L8
131
+ if (!('bind' in Function.prototype)) {
132
+ /**
133
+ * @param {Object|null|undefined} owner
134
+ * @returns {Function}
135
+ * @this {Function}
136
+ */
137
+ // @ts-ignore - Property bind does not exist on never?
138
+ Function.prototype.bind = function (owner) {
139
+ var that = this;
140
+ if (arguments.length <= 1) {
141
+ return function () {
142
+ return that.apply(owner, arguments);
143
+ };
144
+ }
145
+ var args = Array.prototype.slice.call(arguments, 1);
146
+ return function () {
147
+ return that.apply(
148
+ owner,
149
+ arguments.length === 0 ? args : args.concat(Array.prototype.slice.call(arguments))
150
+ );
151
+ };
152
+ };
153
+ }
154
+
155
+ // // ---------------------------------------------------------------------
156
+ // // Helper Methods
157
+ // // ---------------------------------------------------------------------
158
+
159
+ /**
160
+ * Creates a new Uint8Array of given size backed by an ArrayBuffer.
161
+ * @param {number} size - The size of the buffer in bytes.
162
+ * @returns {Uint8Array} A new Uint8Array of the specified size.
163
+ */
164
+ function newBuffer(size) {
165
+ return new Uint8Array(new ArrayBuffer(size));
166
+ }
167
+
168
+ /**
169
+ * Extends an object with properties from subsequent objects.
170
+ * @param {Object<string, *>} obj - The target object to extend.
171
+ * @returns {Object} The extended target object.
172
+ */
173
+ function extend(obj) {
174
+ var args = /** @type {Array<Object<string, *>>} */ (Array.prototype.slice.call(arguments, 1));
175
+ args.forEach(function (ext) {
176
+ Object.keys(ext).forEach(function (key) {
177
+ obj[key] = ext[key];
178
+ });
179
+ });
180
+ return obj;
181
+ }
182
+
183
+ /**
184
+ * Adds a field's size to the current offset cursor (bytes/bits).
185
+ * @param {Offset} ctr - The current offset (bytes, bits).
186
+ * @param {Field} f - The field whose size to add.
187
+ * @returns {Offset} The updated offset cursor.
188
+ * @throws {Error} Improperly aligned bitfield before field
189
+ */
190
+ function addField(ctr, f) {
191
+ if ('width' in f && typeof f.width === 'number') {
192
+ ctr.bits = (ctr.bits || 0) + f.width;
193
+ while ((ctr.bits || 0) > 7) {
194
+ ctr.bytes += 1;
195
+ ctr.bits -= 8;
196
+ }
197
+ } else if (!ctr.bits) {
198
+ // Not a bitfield, add full size in bytes.
199
+ ctr.bytes += f.size || 0;
200
+ } else {
201
+ // We have leftover bits that aren't aligned, can't add a normal field.
202
+ throw Error('Improperly aligned bitfield before field: ' + (f.name || ''));
203
+ }
204
+ return ctr;
205
+ }
206
+
207
+ /**
208
+ * Converts a field into an array field if a count is provided.
209
+ * @param {Field} f - The field to arrayize.
210
+ * @param {number} [count] - The number of elements in the array, if needed.
211
+ * @returns {Field} The arrayized field.
212
+ */
213
+ function arrayizeField(f, count) {
214
+ var field = (typeof count === 'number') ? /** @type {Field} */ (extend({
215
+ name: f.name,
216
+ field: f,
217
+ /**
218
+ * Unpacks an array of values from bytes.
219
+ * @param {BufferSource} buf - The buffer to read from.
220
+ * @param {Offset} off - The offset object with bytes and bits.
221
+ * @returns {Array<*>} The unpacked array of values.
222
+ */
223
+ unpack: function (buf, off) {
224
+ off || (off = { bytes: 0, bits: 0 });
225
+ var arr = new Array(count);
226
+ for (var idx = 0, len = arr.length; idx < len; idx += 1) {
227
+ arr[idx] = f.unpack(buf, off);
228
+ }
229
+ return arr;
230
+ },
231
+ /**
232
+ * Packs an array of values into bytes.
233
+ * @param {Array<*>} arr
234
+ * @param {BufferSource} [buf]
235
+ * @param {Offset} [off]
236
+ * @returns {Uint8Array}
237
+ * @this {Field}
238
+ */
239
+ pack: function (arr, buf, off) {
240
+ arr || (arr = new Array(count));
241
+ buf || (buf = newBuffer(this.size));
242
+ off || (off = { bytes: 0, bits: 0 });
243
+ for (var idx = 0, len = Math.min(arr.length, count); idx < len; idx += 1) {
244
+ f.pack(arr[idx], buf, off);
245
+ }
246
+ while (idx++ < count) addField(off, f);
247
+ return /** @type {Uint8Array} */ (buf);
248
+ }
249
+ }, (f.width !== undefined)
250
+ ? { width: f.width * count }
251
+ : { size: f.size * count })
252
+ ) : f;
253
+ return field;
254
+ }
255
+
256
+ // // ---------------------------------------------------------------------
257
+ // // _.struct Definition
258
+ // // ---------------------------------------------------------------------
259
+
260
+ /**
261
+ * Defines a new structure with the given fields.
262
+ * Overloads:
263
+ * _.struct(fields, count?)
264
+ * _.struct(name, fields, count?)
265
+ * @param {string|Array<Field>} name - The structure name or the array of fields if no name.
266
+ * @param {Array<Field|StructInstance<*>>|number} [fields] - The array of fields OR the count if the first param was fields.
267
+ * @param {number} [count] - The number of array elements if making an array of structs.
268
+ * @returns {StructInstance<*>} The resulting struct definition (and array, if count was provided).
269
+ * @throws {Error} Invalid .padTo..., Improperly aligned bitfield at end of struct
270
+ */
271
+ _.struct = function (name, fields, count) {
272
+ /** @type {string|undefined} */
273
+ var structName;
274
+ /** @type {Array<Field>} */
275
+ var fieldDefs;
276
+
277
+ if (typeof name !== 'string') {
278
+ // Overload 1: (fields[, count])
279
+ count = /** @type {number|undefined} */ (fields);
280
+ fieldDefs = /** @type {Array<Field>} */ (name);
281
+ } else {
282
+ // Overload 2: (name, fields[, count])
283
+ structName = name;
284
+ fieldDefs = /** @type {Array<Field>} */ (fields);
285
+ }
286
+
287
+ var _size = { bytes: 0, bits: 0 };
288
+ var _padsById = Object.create(null);
289
+
290
+ /**
291
+ * @param {Object<string, Field>} obj
292
+ * @param {Field} f
293
+ * @returns {Object<string, Field>}
294
+ * @throws {Error} Invalid .padTo...
295
+ */
296
+ function reduceFunc(obj, f) {
297
+ // Handle _padTo:
298
+ if ('_padTo' in f) {
299
+ var padField = /** @type {PaddingField} */ (f); // Cast to this type.
300
+ // Generate a "pad field" for alignment.
301
+ if (!padField._id) {
302
+ padField._id = 'id' + Math.random().toFixed(20).slice(2); // WORKAROUND: https://github.com/tessel/runtime/issues/716
303
+ }
304
+ var neededPadBytes = padField._padTo - _size.bytes;
305
+ if (_size.bits) {
306
+ // This is a bitfield alignment.
307
+ var bitsNeeded = 8 * neededPadBytes - _size.bits;
308
+ if (bitsNeeded < 0) {
309
+ throw Error(
310
+ 'Invalid .padTo(' + padField._padTo + ') field, struct is already ' +
311
+ _size.bytes + ' byte(s) and ' + _size.bits + ' bits!'
312
+ );
313
+ }
314
+ _padsById[padField._id] = { width: bitsNeeded };
315
+ f.width = bitsNeeded;
316
+ } else {
317
+ if (neededPadBytes < 0) {
318
+ throw Error(
319
+ 'Invalid .padTo(' + padField._padTo + ') field, struct is already ' + _size.bytes + ' bytes!'
320
+ );
321
+ }
322
+ _padsById[padField._id] = { size: neededPadBytes };
323
+ f.size = neededPadBytes;
324
+ }
325
+ }
326
+
327
+ // Handle sub-struct hoisting:
328
+ if (f._hoistFields) {
329
+ var hoistFields = f._hoistFields; // Otherwise it thinks it's null or undefined below
330
+ Object.keys(hoistFields).forEach(function (subName) {
331
+ var _f = Object.create(hoistFields[subName]);
332
+ if ('width' in _f && typeof _f.width === 'number') {
333
+ _f.offset = { bytes: _f.offset.bytes + _size.bytes, bits: _f.offset.bits };
334
+ } else {
335
+ _f.offset = /** @type {number} */ (_f.offset) + _size.bytes;
336
+ }
337
+ obj[subName] = _f;
338
+ });
339
+ } else if (f.name) {
340
+ // Create a local copy, assign offset.
341
+ var localCopy = Object.create(f);
342
+ if ('width' in localCopy && typeof localCopy.width === 'number') {
343
+ localCopy.offset = { bytes: _size.bytes, bits: _size.bits };
344
+ } else {
345
+ localCopy.offset = _size.bytes;
346
+ }
347
+ obj[f.name] = localCopy;
348
+ }
349
+
350
+ addField(_size, f);
351
+ return obj;
352
+ }
353
+ /** @type {Object<string, Field>} */
354
+ var fieldsObj = fieldDefs.reduce(reduceFunc, {});
355
+
356
+
357
+ if (_size.bits) {
358
+ throw Error('Improperly aligned bitfield at end of struct: ' + (structName || ''));
359
+ }
360
+
361
+ // Now build the main field definition for the struct.
362
+ var structField = {
363
+ /**
364
+ * Reads (unpacks) a struct from the buffer at the given offset.
365
+ * @param {BufferSource} buf
366
+ * @param {Offset} [off]
367
+ * @returns {*}
368
+ */
369
+ unpack: function (buf, off) {
370
+ off = off || { bytes: 0, bits: 0 };
371
+ /** @type {Object<string, Field>} */
372
+ var obj = {};
373
+ fieldDefs.forEach(function (f) {
374
+ if ('_padTo' in f && /** @type {PaddingField} */ (f)._id !== undefined) {
375
+ // It's a pad spec; retrieve the pad field from _padsById.
376
+ addField(/** @type {Offset} */ (off), _padsById[/** @type {PaddingField} */ (f)._id]);
377
+ return;
378
+ }
379
+ var value = f.unpack(buf, off);
380
+ if (f.name) {
381
+ obj[f.name] = value;
382
+ } else if (typeof value === 'object') {
383
+ extend(obj, value);
384
+ }
385
+ });
386
+ return obj;
387
+ },
388
+
389
+ /**
390
+ * Writes (packs) the given object into a buffer at the given offset.
391
+ * @param {*} obj
392
+ * @param {BufferSource} [buf]
393
+ * @param {Offset} [off]
394
+ * @returns {Uint8Array}
395
+ */
396
+ pack: function (obj, buf, off) {
397
+ obj = obj || {};
398
+ if (!buf) {
399
+ buf = newBuffer(this.size);
400
+ }
401
+ off = off || { bytes: 0, bits: 0 };
402
+ fieldDefs.forEach(function (f) {
403
+ if ('_padTo' in f && /** @type {PaddingField} */ (f)._id !== undefined) {
404
+ addField(/** @type {Offset} */ (off), _padsById[/** @type {PaddingField} */ (f)._id]);
405
+ return;
406
+ }
407
+ var value = f.name ? obj[f.name] : obj;
408
+ f.pack(value, /** @type {BufferSource} */ (buf), off);
409
+ });
410
+ return /** @type {Uint8Array} */ (buf);
411
+ },
412
+
413
+ _hoistFields: structName ? null : fieldsObj,
414
+ fields: fieldsObj,
415
+ size: _size.bytes,
416
+ name: structName
417
+ };
418
+
419
+ // Cast to StructInstance for type compatibility.
420
+ return /** @type {StructInstance<*>} */ (arrayizeField(structField, count));
421
+ };
422
+
423
+ // // ---------------------------------------------------------------------
424
+ // // Begin Bitfield Helpers
425
+ // // ---------------------------------------------------------------------
426
+
427
+ /**
428
+ * Reads a 32-bit unsigned int from buffer, but handles short lengths by not reading beyond the buffer.
429
+ * Used in valueFromBytes by bitfield logic.
430
+ * @param {BufferSource} buffer - The buffer to read from.
431
+ * @param {number} offset - The byte offset to start reading.
432
+ * @param {boolean} littleEndian - Indicates whether to read little-endian.
433
+ * @returns {number} The read unsigned 32-bit integer.
434
+ */
435
+ function truncatedReadUInt32(buffer, offset, littleEndian) {
436
+ var bytes = (buffer instanceof ArrayBuffer) ? new Uint8Array(buffer) : buffer;
437
+ var availableBytes = bytes.length - offset;
438
+ var view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
439
+
440
+ if (availableBytes >= 4) {
441
+ return view.getUint32(offset, littleEndian);
442
+ } else if (availableBytes === 3) {
443
+ var first = view.getUint16(offset, littleEndian);
444
+ var second = view.getUint8(offset + 2);
445
+ return littleEndian
446
+ ? ((second << 16) + first) >>> 0
447
+ : ((first << 8) + second) << 8 >>> 0;
448
+ } else if (availableBytes === 2) {
449
+ return view.getUint16(offset, littleEndian) << (littleEndian ? 0 : 16) >>> 0;
450
+ } else if (availableBytes === 1) {
451
+ return view.getUint8(offset) << (littleEndian ? 0 : 24) >>> 0;
452
+ }
453
+ return 0x0;
454
+ }
455
+
456
+ /**
457
+ * Writes a 32-bit unsigned int to buffer, but handles short lengths by not writing beyond the buffer.
458
+ * Used in bytesFromValue/pack by bitfield logic.
459
+ * @param {BufferSource} buffer - The buffer to write to.
460
+ * @param {number} offset - The byte offset to start writing.
461
+ * @param {number} data - The unsigned 32-bit integer to write.
462
+ * @param {boolean} littleEndian - Indicates whether to write little-endian.
463
+ */
464
+ function truncatedWriteUInt32(buffer, offset, data, littleEndian) {
465
+ var bytes = (buffer instanceof ArrayBuffer) ? new Uint8Array(buffer) : buffer;
466
+ var availableBytes = bytes.length - offset;
467
+ var view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
468
+
469
+ if (availableBytes >= 4) {
470
+ view.setUint32(offset, data, littleEndian);
471
+ } else if (availableBytes === 3) {
472
+ if (littleEndian) {
473
+ view.setUint8(offset, data & 0xff);
474
+ view.setUint16(offset + 1, data >>> 8, littleEndian);
475
+ } else {
476
+ view.setUint16(offset, data >>> 16, littleEndian);
477
+ view.setUint8(offset + 2, (data >>> 8) & 0xff);
478
+ }
479
+ } else if (availableBytes === 2) {
480
+ view.setUint16(offset, littleEndian ? data & 0xffff : data >>> 16, littleEndian);
481
+ } else if (availableBytes === 1) {
482
+ view.setUint8(offset, littleEndian ? data & 0xff : data >>> 24);
483
+ }
484
+ }
485
+
486
+ /**
487
+ * Defines a padding field up to the specified offset in bytes.
488
+ * @param {number} off - The byte offset to pad to.
489
+ * @returns {Field & {_padTo: number, _id?: string}} The padding field definition.
490
+ */
491
+ _.padTo = function (off) {
492
+ var field = /** @type {Field & {_padTo: number}} */ ({ _padTo: off,
493
+ // Dummy implementations to satisfy Field:
494
+ size: 0,
495
+ unpack: function () { return null; },
496
+ pack: function () { return newBuffer(0); }
497
+ });
498
+ return field;
499
+ };
500
+
501
+
502
+ // NOTE: bitfields must be embedded in a struct (C/C++ share this limitation)
503
+
504
+ var FULL = 0xFFFFFFFF;
505
+
506
+ /**
507
+ * A helper for big and little endian bitfields.
508
+ * @param {string} [name] - The name of the bitfield.
509
+ * @param {number} [width=1] - The width of the bitfield in bits.
510
+ * @param {number} [count] - The number of bitfields in an array.
511
+ * @returns {Field} The defined bitfield.
512
+ * @throws {Error} Bitfields support a maximum width of 24 bits
513
+ * @this {BitTransform}
514
+ */
515
+ function bitfield(name, width, count) {
516
+ if (width === undefined) {
517
+ width = 1;
518
+ }
519
+ // NOTE: width limitation is so all values will align *within* a 4-byte word
520
+ if (width > 24) {
521
+ throw Error('Bitfields support a maximum width of 24 bits.');
522
+ }
523
+ var mask = FULL >>> (32 - width);
524
+ var littleEndian = this.littleEndian;
525
+ if (littleEndian) {
526
+ mask >>>= 0;
527
+ }
528
+
529
+ var impl = this;
530
+ return arrayizeField({
531
+ /**
532
+ * @param {BufferSource} buf
533
+ * @param {Offset} [off]
534
+ * @returns {number|boolean}
535
+ * @this {Field & {width: number}}
536
+ */
537
+ unpack: function (buf, off) {
538
+ off = off || { bytes: 0, bits: 0 };
539
+ var over;
540
+ var end;
541
+ var word;
542
+ if (littleEndian) {
543
+ end = off.bits || 0;
544
+ word = truncatedReadUInt32(buf, off.bytes, true) >>> 0;
545
+ over = (word >>> end);
546
+ } else {
547
+ end = (off.bits || 0) + /** @type {number} */ (width);
548
+ word = truncatedReadUInt32(buf, off.bytes, false) || 0;
549
+ over = word >>> (32 - end);
550
+ }
551
+ addField(off, this);
552
+ return impl.b2v.call(this, over & mask);
553
+ },
554
+ /**
555
+ * @param {number} val
556
+ * @param {BufferSource} [buf]
557
+ * @param {Offset} [off]
558
+ * @returns {Uint8Array}
559
+ * @this {Field & {width: number}}
560
+ */
561
+ pack: function (val, buf, off) {
562
+ val = impl.v2b.call(this, val || 0);
563
+ if (!buf) {
564
+ buf = newBuffer(/** @type {number} */(this.size));
565
+ }
566
+ off = off || { bytes: 0, bits: 0 };
567
+ var word;
568
+ var zero;
569
+ var over;
570
+ if (littleEndian) {
571
+ word = truncatedReadUInt32(buf, off.bytes, true) >>> 0;
572
+ var shift = off.bits || 0;
573
+ zero = (mask << shift) >>> 0;
574
+ word &= ~zero;
575
+ over = (val & mask) << shift;
576
+ word = (word | over) >>> 0; // WORKAROUND: https://github.com/tessel/runtime/issues/644
577
+ truncatedWriteUInt32(buf, off.bytes, word, true);
578
+ } else {
579
+ var end = (off.bits || 0) + /** @type {number} */ (width);
580
+ word = truncatedReadUInt32(buf, off.bytes, false) || 0;
581
+ zero = mask << (32 - end);
582
+ over = (val & mask) << (32 - end);
583
+ word &= ~zero;
584
+ word = (word | over) >>> 0; // WORKAROUND: https://github.com/tessel/runtime/issues/644
585
+ truncatedWriteUInt32(buf, off.bytes, word, false);
586
+ }
587
+ addField(off, this);
588
+ return new Uint8Array(buf);
589
+ },
590
+ width: width,
591
+ size: 0, // Placeholder for bitfields.
592
+ name: name
593
+ }, count);
594
+ }
595
+
596
+ // // ---------------------------------------------------------------------
597
+ // // Begin Bitfield Definitions
598
+ // // ---------------------------------------------------------------------
599
+
600
+ /**
601
+ * Boolean field: 1-bit big-endian bitfield that interprets 1/0 as true/false.
602
+ * @param {string} [name]
603
+ * @param {number} [count]
604
+ * @returns {Field}
605
+ */
606
+ _.bool = function (name, count) {
607
+ return bitfield.call({
608
+ /**
609
+ * Converts a bitfield to a boolean.
610
+ * @param {number} b - The bitfield value.
611
+ * @returns {boolean} The boolean representation.
612
+ */
613
+ b2v: function (b) { return Boolean(b); },
614
+ /**
615
+ * Converts a boolean to a bitfield.
616
+ * @param {number|boolean} v - The boolean value.
617
+ * @returns {number} The bitfield representation.
618
+ */
619
+ v2b: function (v) { return v ? FULL : 0; }
620
+ }, name, 1, count);
621
+ };
622
+
623
+ /**
624
+ * Unsigned bitfield (big-endian).
625
+ */
626
+ _.ubit = bitfield.bind({
627
+ /**
628
+ * Converts a bitfield to a value.
629
+ * @param {number} b - The bitfield value.
630
+ * @returns {number} The numeric value.
631
+ */
632
+ b2v: function (b) { return b; },
633
+ /**
634
+ * Converts a value to a bitfield.
635
+ * @param {number|boolean} v - The numeric value.
636
+ * @returns {number} The bitfield representation.
637
+ */
638
+ v2b: function (v) { return Number(v); }
639
+ });
640
+
641
+ /**
642
+ * Unsigned bitfield (little-endian).
643
+ * @param {string} name - The name of the bitfield.
644
+ * @param {number} [width=1] - The width of the bitfield in bits.
645
+ * @param {number} [count] - The number of bitfields in an array.
646
+ * @returns {Object} The defined little-endian bitfield.
647
+ */
648
+ _.ubitLE = bitfield.bind({
649
+ /**
650
+ * Converts a bitfield to a little-endian value.
651
+ * @param {number} b - The bitfield value.
652
+ * @returns {number} The little-endian numeric value.
653
+ */
654
+ b2v: function (b) { return b; },
655
+ /**
656
+ * Converts a little-endian value to a bitfield.
657
+ * @param {number|boolean} v - The little-endian numeric value.
658
+ * @returns {number} The bitfield representation.
659
+ */
660
+ v2b: function (v) { return Number(v); },
661
+ littleEndian: true
662
+ });
663
+
664
+ /**
665
+ * Signed bitfield (big-endian).
666
+ * @param {string} name - The name of the bitfield.
667
+ * @param {number} [width=1] - The width of the bitfield in bits.
668
+ * @param {number} [count] - The number of bitfields in an array.
669
+ * @returns {Object} The defined signed bitfield.
670
+ */
671
+ _.sbit = bitfield.bind({
672
+ /**
673
+ * Converts a bitfield to a signed value.
674
+ * @param {number} b - The bitfield value.
675
+ * @returns {number} The signed numeric value.
676
+ */
677
+ b2v: function (b) {
678
+ var m = 1 << ((this.width || 1) - 1);
679
+ var s = b & m;
680
+ return (s) ? -(b &= ~m) : b;
681
+ },
682
+ /**
683
+ * Converts a signed value to a bitfield.
684
+ * @param {number|boolean} v - The signed numeric value.
685
+ * @returns {number} The bitfield representation.
686
+ */
687
+ v2b: function (v) {
688
+ v = Number(v);
689
+ var m = 1 << ((this.width || 1) - 1);
690
+ var s = (v < 0);
691
+ return (s) ? (-v | m) : v;
692
+ }
693
+ });
694
+
695
+ /**
696
+ * Creates a simple "byte field" for reading/writing raw bytes.
697
+ * @param {string|number} name - The name of the bytefield or its size if name is omitted.
698
+ * @param {number} [size=1] - The size of the bytefield in bytes.
699
+ * @param {number} [count] - The number of bytefields in an array.
700
+ * @returns {Field & ByteTransform} The defined bytefield.
701
+ * @this {ByteTransform}
702
+ */
703
+ function bytefield(name, size, count) {
704
+ /** @type {string|undefined} */
705
+ var fieldName;
706
+ /** @type {number} */
707
+ var fieldSize = 1;
708
+ if (typeof name === 'string') {
709
+ fieldName = name;
710
+ if (typeof size === 'number') {
711
+ fieldSize = size;
712
+ }
713
+ } else {
714
+ // Shift params.
715
+ fieldSize = name;
716
+ count = size; // pass along
717
+ }
718
+ var impl = this;
719
+
720
+ return /** @type {Field & ByteTransform} */ (arrayizeField(/** @type {Field & ByteTransform} */ ({
721
+ name: fieldName,
722
+ size: fieldSize,
723
+
724
+ /**
725
+ * @param {BufferSource} buf
726
+ * @param {Offset} [off]
727
+ * @returns {Uint8Array}
728
+ */
729
+ unpack: function (buf, off) {
730
+ off = off || { bytes: 0, bits: 0 };
731
+ var bytes = (buf instanceof ArrayBuffer) ? new Uint8Array(buf) : buf;
732
+ var val = bytes.subarray(off.bytes, off.bytes + /** @type {number} */ (this.size));
733
+ addField(off, this);
734
+ return impl.b2v.call(this, val);
735
+ },
736
+
737
+ /**
738
+ * @param {Array<number>|Uint8Array|ArrayBuffer} val
739
+ * @param {BufferSource} [buf]
740
+ * @param {Offset} [off]
741
+ * @returns {Uint8Array}
742
+ */
743
+ pack: function (val, buf, off) {
744
+ if (!buf) {
745
+ buf = newBuffer(/** @type {number} */ (this.size));
746
+ }
747
+ off = off || { bytes: 0, bits: 0 };
748
+ var bytes = (buf instanceof ArrayBuffer) ? new Uint8Array(buf) : buf;
749
+ var blk = bytes.subarray(off.bytes, off.bytes + /** @type {number} */ (this.size));
750
+ impl.vTb.call(this, val, blk);
751
+ addField(off, this);
752
+ return /** @type {Uint8Array} */ (buf);
753
+ },
754
+
755
+ // Default transforms:
756
+ /**
757
+ * @param {Uint8Array} b
758
+ * @returns {Uint8Array}
759
+ */
760
+ b2v: function (b) { return b; },
761
+ /**
762
+ * @param {Array<number>|Uint8Array|ArrayBuffer} v
763
+ * @param {Uint8Array} b
764
+ * @returns {number}
765
+ */
766
+ vTb: function (v, b) {
767
+ if (!v) return 0;
768
+ var src = new Uint8Array(v);
769
+ b.set(src.subarray(0, b.length));
770
+ return b.length;
771
+ }
772
+ }), count));
773
+ }
774
+
775
+ /**
776
+ * Swaps adjacent byte pairs in a buffer, used for big-endian <-> little-endian char16 manipulations.
777
+ * http://stackoverflow.com/a/7460958/72637
778
+ * @param {Uint8Array} fromBuffer - The source buffer.
779
+ * @param {Uint8Array} [toBuffer] - The destination buffer. If not provided, fromBuffer is modified.
780
+ * @returns {Uint8Array} The buffer with swapped byte pairs.
781
+ */
782
+ function swapBytesPairs(fromBuffer, toBuffer) {
783
+ toBuffer = toBuffer || fromBuffer;
784
+ var l = fromBuffer.length;
785
+ for (var i = 1; i < l; i += 2) {
786
+ var a = fromBuffer[i - 1];
787
+ toBuffer[i - 1] = fromBuffer[i];
788
+ toBuffer[i] = a;
789
+ }
790
+ return toBuffer;
791
+ }
792
+
793
+ /** Basic raw byte field. */
794
+ _.byte = bytefield.bind({
795
+ /**
796
+ * Converts bytes to a value.
797
+ * @param {BufferSource} b - The bytes to convert.
798
+ * @returns {Uint8Array} The byte value.
799
+ */
800
+ b2v: function (b) { return new Uint8Array(b); },
801
+ /**
802
+ * Converts a value to bytes.
803
+ * @param {BufferSource} v - The value to convert.
804
+ * @param {Uint8Array} b - The buffer to write to.
805
+ * @returns {number} The number of bytes written.
806
+ */
807
+ vTb: function (v, b) { if (!v) return 0; b.set(new Uint8Array(v)); return v.byteLength; }
808
+ });
809
+
810
+ // // ---------------------------------------------------------------------
811
+ // // Character Field Definitions
812
+ // // ---------------------------------------------------------------------
813
+
814
+ /** Null-terminated UTF-8 string field. */
815
+ _.char = bytefield.bind({
816
+ /**
817
+ * Converts bytes to a UTF-8 string.
818
+ * @param {BufferSource} b - The bytes to convert.
819
+ * @returns {string} The resulting string.
820
+ */
821
+ b2v: function (b) {
822
+ var decoder;
823
+ if (typeof _TextDecoder !== 'undefined' && _TextDecoder) {
824
+ decoder = new _TextDecoder('utf-8');
825
+ } else {
826
+ // Fallback minimal polyfill
827
+ decoder = {
828
+ /**
829
+ * @param {BufferSource} buf
830
+ * @returns {string}
831
+ */
832
+ decode: function (buf) {
833
+ var bytes = new Uint8Array(buf);
834
+ var str = '';
835
+ for (var i = 0; i < bytes.length; i++) {
836
+ str += String.fromCharCode(bytes[i]);
837
+ }
838
+ return str;
839
+ }
840
+ };
841
+ }
842
+ var v = decoder.decode(b);
843
+ var z = v.indexOf('\0');
844
+ return (~z) ? v.slice(0, z) : v;
845
+ },
846
+ /**
847
+ * Converts a string to UTF-8 bytes.
848
+ * @param {string} v - The string to convert.
849
+ * @param {Uint8Array} b - The buffer to write to.
850
+ * @returns {number} The number of bytes written.
851
+ */
852
+ vTb: function (v, b) {
853
+ v || (v = '');
854
+ var encoder;
855
+ if (typeof _TextEncoder !== 'undefined' && _TextEncoder) {
856
+ encoder = new _TextEncoder();
857
+ } else {
858
+ // Fallback minimal polyfill
859
+ encoder = {
860
+ /**
861
+ * @param {string} str
862
+ * @returns {Uint8Array}
863
+ */
864
+ encode: function (str) {
865
+ var arr = new Uint8Array(str.length);
866
+ for (var i = 0; i < str.length; i++) {
867
+ arr[i] = str.charCodeAt(i) & 0xff;
868
+ }
869
+ return arr;
870
+ }
871
+ };
872
+ }
873
+ var encoded = encoder.encode(v);
874
+ for (var i = 0; i < encoded.length && i < b.length; i++) {
875
+ b[i] = encoded[i];
876
+ }
877
+ return encoded.length;
878
+ }
879
+ });
880
+
881
+ /** Null-terminated UTF-16LE string field. */
882
+ _.char16le = bytefield.bind({
883
+ /**
884
+ * Converts bytes to a UTF-16LE string.
885
+ * @param {BufferSource} b - The bytes to convert.
886
+ * @returns {string} The resulting string.
887
+ */
888
+ b2v: function (b) {
889
+ var decoder;
890
+ if (typeof _TextDecoder !== 'undefined' && _TextDecoder) {
891
+ decoder = new _TextDecoder('utf-16le');
892
+ } else {
893
+ // fallback
894
+ decoder = {
895
+ /**
896
+ * @param {BufferSource} buf
897
+ * @returns {string}
898
+ */
899
+ decode: function (buf) {
900
+ var bytes = new Uint8Array(buf);
901
+ var str = '';
902
+ for (var i = 0; i < bytes.length; i += 2) {
903
+ var charCode = bytes[i] | (bytes[i + 1] << 8);
904
+ str += String.fromCharCode(charCode);
905
+ }
906
+ return str;
907
+ }
908
+ };
909
+ }
910
+ var v = decoder.decode(b);
911
+ var z = v.indexOf('\0');
912
+ return (~z) ? v.slice(0, z) : v;
913
+ },
914
+ /**
915
+ * Converts a string to UTF-16LE bytes.
916
+ * @param {string} v - The string to convert.
917
+ * @param {Uint8Array} b - The buffer to write to.
918
+ * @returns {number} The number of bytes written.
919
+ */
920
+ vTb: function (v, b) {
921
+ v || (v = '');
922
+ var bytesWritten = 0;
923
+ for (var i = 0; i < v.length && bytesWritten + 1 < b.length; i++) {
924
+ var charCode = v.charCodeAt(i);
925
+ b[bytesWritten++] = charCode & 0xFF;
926
+ b[bytesWritten++] = (charCode >> 8) & 0xFF;
927
+ }
928
+ return bytesWritten;
929
+ }
930
+ });
931
+
932
+ /** Null-terminated UTF-16BE string field. */
933
+ _.char16be = bytefield.bind({
934
+ /**
935
+ * Converts bytes to a UTF-16BE string.
936
+ * @param {BufferSource} b - The bytes to convert.
937
+ * @returns {string} The resulting string.
938
+ */
939
+ b2v: function (b) {
940
+ var temp = new Uint8Array(b);
941
+ swapBytesPairs(temp);
942
+ var decoder;
943
+ if (typeof _TextDecoder !== 'undefined' && _TextDecoder) {
944
+ decoder = new _TextDecoder('utf-16le');
945
+ } else {
946
+ // fallback
947
+ decoder = {
948
+ /**
949
+ * @param {BufferSource} buf
950
+ * @returns {string}
951
+ */
952
+ decode: function (buf) {
953
+ var bytes = new Uint8Array(buf);
954
+ var str = '';
955
+ for (var i = 0; i < bytes.length; i += 2) {
956
+ var charCode = bytes[i] | (bytes[i + 1] << 8);
957
+ str += String.fromCharCode(charCode);
958
+ }
959
+ return str;
960
+ }
961
+ };
962
+ }
963
+ var v = decoder.decode(/** @type {ArrayBuffer} */ (temp.buffer));
964
+ var z = v.indexOf('\0');
965
+ return (~z) ? v.slice(0, z) : v;
966
+ },
967
+ /**
968
+ * Converts a string to UTF-16BE bytes.
969
+ * @param {string} v - The string to convert.
970
+ * @param {Uint8Array} b - The buffer to write to.
971
+ * @returns {number} The number of bytes written.
972
+ */
973
+ vTb: function (v, b) {
974
+ v || (v = '');
975
+ var temp = new Uint8Array(b.length);
976
+ var bytesWritten = 0;
977
+ for (var i = 0; i < v.length && bytesWritten + 1 < temp.length; i++) {
978
+ var charCode = v.charCodeAt(i);
979
+ temp[bytesWritten++] = charCode & 0xFF;
980
+ temp[bytesWritten++] = (charCode >> 8) & 0xFF;
981
+ }
982
+ swapBytesPairs(temp, b);
983
+ return bytesWritten;
984
+ }
985
+ });
986
+
987
+ // // ---------------------------------------------------------------------
988
+ // // dataViewField Definition
989
+ // // ---------------------------------------------------------------------
990
+
991
+ /**
992
+ * Defines a standard field based on read/write methods for a DataView.
993
+ * @param {function(this:DataView, number, boolean):number} getFn - The DataView getter.
994
+ * @param {function(this:DataView, number, number, boolean):void} setFn - The DataView setter.
995
+ * @param {number} size - The size of the field in bytes (4 for Uint32, etc.).
996
+ * @param {boolean} littleEndian - Indicates whether or not the field is little endian.
997
+ * @returns {function((string|number), number=): Field} A function to create the field.
998
+ */
999
+ function dataViewField(getFn, setFn, size, littleEndian) {
1000
+ /**
1001
+ * @param {string|number} name
1002
+ * @param {number} [count]
1003
+ * @returns {Field}
1004
+ */
1005
+ var func = function (name, count) {
1006
+ /** @type {string|undefined} */
1007
+ var fieldName;
1008
+ if (typeof name === 'string') {
1009
+ fieldName = name;
1010
+ } else {
1011
+ // shift
1012
+ count = name;
1013
+ }
1014
+
1015
+ return arrayizeField({
1016
+ name: fieldName,
1017
+ size: size,
1018
+
1019
+ /**
1020
+ * @param {BufferSource} buf
1021
+ * @param {Offset} [off]
1022
+ * @returns {number}
1023
+ */
1024
+ unpack: function (buf, off) {
1025
+ off = off || { bytes: 0 };
1026
+ var bytes = (buf instanceof ArrayBuffer) ? new Uint8Array(buf) : buf;
1027
+ var view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
1028
+ var val = getFn.call(view, off.bytes, littleEndian);
1029
+ addField(off, this);
1030
+ return val;
1031
+ },
1032
+ /**
1033
+ * @param {number} val
1034
+ * @param {BufferSource} [buf]
1035
+ * @param {Offset} [off]
1036
+ * @returns {Uint8Array}
1037
+ */
1038
+ pack: function (val, buf, off) {
1039
+ if (val === undefined || val === null) {
1040
+ val = 0;
1041
+ }
1042
+ if (!buf) {
1043
+ buf = newBuffer(this.size);
1044
+ }
1045
+ off = off || { bytes: 0 };
1046
+ var bytes = (buf instanceof ArrayBuffer) ? new Uint8Array(buf) : buf;
1047
+ var view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
1048
+ setFn.call(view, off.bytes, val, littleEndian);
1049
+ addField(off, this);
1050
+ return new Uint8Array(buf);
1051
+ }
1052
+ }, count);
1053
+ };
1054
+ return func;
1055
+ }
1056
+
1057
+ // // ---------------------------------------------------------------------
1058
+ // // DataView Field Definitions
1059
+ // // ---------------------------------------------------------------------
1060
+
1061
+ var DV = DataView.prototype; // Alias for brevity.
1062
+
1063
+ // Unsigned integers
1064
+ _.uint8 = dataViewField(DV.getUint8, DV.setUint8, 1, false);
1065
+ _.uint16 = dataViewField(DV.getUint16, DV.setUint16, 2, false);
1066
+ _.uint32 = dataViewField(DV.getUint32, DV.setUint32, 4, false);
1067
+ _.uint16le = dataViewField(DV.getUint16, DV.setUint16, 2, true);
1068
+ _.uint32le = dataViewField(DV.getUint32, DV.setUint32, 4, true);
1069
+
1070
+ // Signed integers
1071
+ _.int8 = dataViewField(DV.getInt8, DV.setInt8, 1, false);
1072
+ _.int16 = dataViewField(DV.getInt16, DV.setInt16, 2, false);
1073
+ _.int32 = dataViewField(DV.getInt32, DV.setInt32, 4, false);
1074
+ _.int16le = dataViewField(DV.getInt16, DV.setInt16, 2, true);
1075
+ _.int32le = dataViewField(DV.getInt32, DV.setInt32, 4, true);
1076
+
1077
+ // Floating-point numbers
1078
+ _.float32 = dataViewField(DV.getFloat32, DV.setFloat32, 4, false);
1079
+ _.float64 = dataViewField(DV.getFloat64, DV.setFloat64, 8, false);
1080
+ _.float32le = dataViewField(DV.getFloat32, DV.setFloat32, 4, true);
1081
+ _.float64le = dataViewField(DV.getFloat64, DV.setFloat64, 8, true);
1082
+
1083
+ /**
1084
+ * Derives a new field based on an existing one with custom pack and unpack functions.
1085
+ * The types are intentionally any.
1086
+ * @param {Field} orig - The original field to derive from.
1087
+ * @param {function(*): *} pack - The function to pack the derived value.
1088
+ * @param {function(*): *} unpack - The function to unpack the derived value.
1089
+ * @returns {function((string|number)=, number=): Field} A function to create the derived field.
1090
+ */
1091
+ _.derive = function (orig, pack, unpack) {
1092
+ /**
1093
+ * @param {string|number} [name]
1094
+ * @param {number} [count]
1095
+ * @returns {Field}
1096
+ */
1097
+ var func = function (name, count) {
1098
+ /** @type {string|null} */
1099
+ var fieldName = null;
1100
+ if (typeof name === 'string') {
1101
+ fieldName = name;
1102
+ } else {
1103
+ count = name;
1104
+ }
1105
+
1106
+ var derived = /** @type {Field} */ (extend({
1107
+ /**
1108
+ * @param {BufferSource} buf
1109
+ * @param {Offset} [off]
1110
+ * @returns {*}
1111
+ */
1112
+ unpack: function (buf, off) {
1113
+ var rawVal = orig.unpack(buf, off);
1114
+ return unpack(rawVal);
1115
+ },
1116
+ /**
1117
+ * @param {*} val
1118
+ * @param {BufferSource} buf
1119
+ * @param {Offset} [off]
1120
+ * @returns {Uint8Array}
1121
+ */
1122
+ pack: function (val, buf, off) {
1123
+ var packed = pack(val);
1124
+ return orig.pack(packed, buf, off);
1125
+ },
1126
+ name: fieldName
1127
+ },
1128
+ ('width' in orig)
1129
+ ? { width: orig.width }
1130
+ : { size: orig.size }
1131
+ ));
1132
+
1133
+ return arrayizeField(derived, count);
1134
+ };
1135
+ return func;
1136
+ };
1137
+
1138
+ // Return the `_` object for whichever loader environment we're in.
1139
+ return _;
1140
+ }));