Spaces:
Sleeping
Sleeping
| /* The MIT License (MIT) | |
| * | |
| * Copyright 2015-2018 Peter A. Bigot | |
| * | |
| * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| * of this software and associated documentation files (the "Software"), to deal | |
| * in the Software without restriction, including without limitation the rights | |
| * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| * copies of the Software, and to permit persons to whom the Software is | |
| * furnished to do so, subject to the following conditions: | |
| * | |
| * The above copyright notice and this permission notice shall be included in | |
| * all copies or substantial portions of the Software. | |
| * | |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| * THE SOFTWARE. | |
| */ | |
| /** | |
| * Support for translating between Uint8Array instances and JavaScript | |
| * native types. | |
| * | |
| * {@link module:Layout~Layout|Layout} is the basis of a class | |
| * hierarchy that associates property names with sequences of encoded | |
| * bytes. | |
| * | |
| * Layouts are supported for these scalar (numeric) types: | |
| * * {@link module:Layout~UInt|Unsigned integers in little-endian | |
| * format} with {@link module:Layout.u8|8-bit}, {@link | |
| * module:Layout.u16|16-bit}, {@link module:Layout.u24|24-bit}, | |
| * {@link module:Layout.u32|32-bit}, {@link | |
| * module:Layout.u40|40-bit}, and {@link module:Layout.u48|48-bit} | |
| * representation ranges; | |
| * * {@link module:Layout~UIntBE|Unsigned integers in big-endian | |
| * format} with {@link module:Layout.u16be|16-bit}, {@link | |
| * module:Layout.u24be|24-bit}, {@link module:Layout.u32be|32-bit}, | |
| * {@link module:Layout.u40be|40-bit}, and {@link | |
| * module:Layout.u48be|48-bit} representation ranges; | |
| * * {@link module:Layout~Int|Signed integers in little-endian | |
| * format} with {@link module:Layout.s8|8-bit}, {@link | |
| * module:Layout.s16|16-bit}, {@link module:Layout.s24|24-bit}, | |
| * {@link module:Layout.s32|32-bit}, {@link | |
| * module:Layout.s40|40-bit}, and {@link module:Layout.s48|48-bit} | |
| * representation ranges; | |
| * * {@link module:Layout~IntBE|Signed integers in big-endian format} | |
| * with {@link module:Layout.s16be|16-bit}, {@link | |
| * module:Layout.s24be|24-bit}, {@link module:Layout.s32be|32-bit}, | |
| * {@link module:Layout.s40be|40-bit}, and {@link | |
| * module:Layout.s48be|48-bit} representation ranges; | |
| * * 64-bit integral values that decode to an exact (if magnitude is | |
| * less than 2^53) or nearby integral Number in {@link | |
| * module:Layout.nu64|unsigned little-endian}, {@link | |
| * module:Layout.nu64be|unsigned big-endian}, {@link | |
| * module:Layout.ns64|signed little-endian}, and {@link | |
| * module:Layout.ns64be|unsigned big-endian} encodings; | |
| * * 32-bit floating point values with {@link | |
| * module:Layout.f32|little-endian} and {@link | |
| * module:Layout.f32be|big-endian} representations; | |
| * * 64-bit floating point values with {@link | |
| * module:Layout.f64|little-endian} and {@link | |
| * module:Layout.f64be|big-endian} representations; | |
| * * {@link module:Layout.const|Constants} that take no space in the | |
| * encoded expression. | |
| * | |
| * and for these aggregate types: | |
| * * {@link module:Layout.seq|Sequence}s of instances of a {@link | |
| * module:Layout~Layout|Layout}, with JavaScript representation as | |
| * an Array and constant or data-dependent {@link | |
| * module:Layout~Sequence#count|length}; | |
| * * {@link module:Layout.struct|Structure}s that aggregate a | |
| * heterogeneous sequence of {@link module:Layout~Layout|Layout} | |
| * instances, with JavaScript representation as an Object; | |
| * * {@link module:Layout.union|Union}s that support multiple {@link | |
| * module:Layout~VariantLayout|variant layouts} over a fixed | |
| * (padded) or variable (not padded) span of bytes, using an | |
| * unsigned integer at the start of the data or a separate {@link | |
| * module:Layout.unionLayoutDiscriminator|layout element} to | |
| * determine which layout to use when interpreting the buffer | |
| * contents; | |
| * * {@link module:Layout.bits|BitStructure}s that contain a sequence | |
| * of individual {@link | |
| * module:Layout~BitStructure#addField|BitField}s packed into an 8, | |
| * 16, 24, or 32-bit unsigned integer starting at the least- or | |
| * most-significant bit; | |
| * * {@link module:Layout.cstr|C strings} of varying length; | |
| * * {@link module:Layout.blob|Blobs} of fixed- or variable-{@link | |
| * module:Layout~Blob#length|length} raw data. | |
| * | |
| * All {@link module:Layout~Layout|Layout} instances are immutable | |
| * after construction, to prevent internal state from becoming | |
| * inconsistent. | |
| * | |
| * @local Layout | |
| * @local ExternalLayout | |
| * @local GreedyCount | |
| * @local OffsetLayout | |
| * @local UInt | |
| * @local UIntBE | |
| * @local Int | |
| * @local IntBE | |
| * @local NearUInt64 | |
| * @local NearUInt64BE | |
| * @local NearInt64 | |
| * @local NearInt64BE | |
| * @local Float | |
| * @local FloatBE | |
| * @local Double | |
| * @local DoubleBE | |
| * @local Sequence | |
| * @local Structure | |
| * @local UnionDiscriminator | |
| * @local UnionLayoutDiscriminator | |
| * @local Union | |
| * @local VariantLayout | |
| * @local BitStructure | |
| * @local BitField | |
| * @local Boolean | |
| * @local Blob | |
| * @local CString | |
| * @local Constant | |
| * @local bindConstructorLayout | |
| * @module Layout | |
| * @license MIT | |
| * @author Peter A. Bigot | |
| * @see {@link https://github.com/pabigot/buffer-layout|buffer-layout on GitHub} | |
| */ | |
| ; | |
| Object.defineProperty(exports, "__esModule", { value: true }); | |
| exports.s16 = exports.s8 = exports.nu64be = exports.u48be = exports.u40be = exports.u32be = exports.u24be = exports.u16be = exports.nu64 = exports.u48 = exports.u40 = exports.u32 = exports.u24 = exports.u16 = exports.u8 = exports.offset = exports.greedy = exports.Constant = exports.UTF8 = exports.CString = exports.Blob = exports.Boolean = exports.BitField = exports.BitStructure = exports.VariantLayout = exports.Union = exports.UnionLayoutDiscriminator = exports.UnionDiscriminator = exports.Structure = exports.Sequence = exports.DoubleBE = exports.Double = exports.FloatBE = exports.Float = exports.NearInt64BE = exports.NearInt64 = exports.NearUInt64BE = exports.NearUInt64 = exports.IntBE = exports.Int = exports.UIntBE = exports.UInt = exports.OffsetLayout = exports.GreedyCount = exports.ExternalLayout = exports.bindConstructorLayout = exports.nameWithProperty = exports.Layout = exports.uint8ArrayToBuffer = exports.checkUint8Array = void 0; | |
| exports.constant = exports.utf8 = exports.cstr = exports.blob = exports.unionLayoutDiscriminator = exports.union = exports.seq = exports.bits = exports.struct = exports.f64be = exports.f64 = exports.f32be = exports.f32 = exports.ns64be = exports.s48be = exports.s40be = exports.s32be = exports.s24be = exports.s16be = exports.ns64 = exports.s48 = exports.s40 = exports.s32 = exports.s24 = void 0; | |
| const buffer_1 = require("buffer"); | |
| /* Check if a value is a Uint8Array. | |
| * | |
| * @ignore */ | |
| function checkUint8Array(b) { | |
| if (!(b instanceof Uint8Array)) { | |
| throw new TypeError('b must be a Uint8Array'); | |
| } | |
| } | |
| exports.checkUint8Array = checkUint8Array; | |
| /* Create a Buffer instance from a Uint8Array. | |
| * | |
| * @ignore */ | |
| function uint8ArrayToBuffer(b) { | |
| checkUint8Array(b); | |
| return buffer_1.Buffer.from(b.buffer, b.byteOffset, b.length); | |
| } | |
| exports.uint8ArrayToBuffer = uint8ArrayToBuffer; | |
| /** | |
| * Base class for layout objects. | |
| * | |
| * **NOTE** This is an abstract base class; you can create instances | |
| * if it amuses you, but they won't support the {@link | |
| * Layout#encode|encode} or {@link Layout#decode|decode} functions. | |
| * | |
| * @param {Number} span - Initializer for {@link Layout#span|span}. The | |
| * parameter must be an integer; a negative value signifies that the | |
| * span is {@link Layout#getSpan|value-specific}. | |
| * | |
| * @param {string} [property] - Initializer for {@link | |
| * Layout#property|property}. | |
| * | |
| * @abstract | |
| */ | |
| class Layout { | |
| constructor(span, property) { | |
| if (!Number.isInteger(span)) { | |
| throw new TypeError('span must be an integer'); | |
| } | |
| /** The span of the layout in bytes. | |
| * | |
| * Positive values are generally expected. | |
| * | |
| * Zero will only appear in {@link Constant}s and in {@link | |
| * Sequence}s where the {@link Sequence#count|count} is zero. | |
| * | |
| * A negative value indicates that the span is value-specific, and | |
| * must be obtained using {@link Layout#getSpan|getSpan}. */ | |
| this.span = span; | |
| /** The property name used when this layout is represented in an | |
| * Object. | |
| * | |
| * Used only for layouts that {@link Layout#decode|decode} to Object | |
| * instances. If left undefined the span of the unnamed layout will | |
| * be treated as padding: it will not be mutated by {@link | |
| * Layout#encode|encode} nor represented as a property in the | |
| * decoded Object. */ | |
| this.property = property; | |
| } | |
| /** Function to create an Object into which decoded properties will | |
| * be written. | |
| * | |
| * Used only for layouts that {@link Layout#decode|decode} to Object | |
| * instances, which means: | |
| * * {@link Structure} | |
| * * {@link Union} | |
| * * {@link VariantLayout} | |
| * * {@link BitStructure} | |
| * | |
| * If left undefined the JavaScript representation of these layouts | |
| * will be Object instances. | |
| * | |
| * See {@link bindConstructorLayout}. | |
| */ | |
| makeDestinationObject() { | |
| return {}; | |
| } | |
| /** | |
| * Calculate the span of a specific instance of a layout. | |
| * | |
| * @param {Uint8Array} b - the buffer that contains an encoded instance. | |
| * | |
| * @param {Number} [offset] - the offset at which the encoded instance | |
| * starts. If absent a zero offset is inferred. | |
| * | |
| * @return {Number} - the number of bytes covered by the layout | |
| * instance. If this method is not overridden in a subclass the | |
| * definition-time constant {@link Layout#span|span} will be | |
| * returned. | |
| * | |
| * @throws {RangeError} - if the length of the value cannot be | |
| * determined. | |
| */ | |
| getSpan(b, offset) { | |
| if (0 > this.span) { | |
| throw new RangeError('indeterminate span'); | |
| } | |
| return this.span; | |
| } | |
| /** | |
| * Replicate the layout using a new property. | |
| * | |
| * This function must be used to get a structurally-equivalent layout | |
| * with a different name since all {@link Layout} instances are | |
| * immutable. | |
| * | |
| * **NOTE** This is a shallow copy. All fields except {@link | |
| * Layout#property|property} are strictly equal to the origin layout. | |
| * | |
| * @param {String} property - the value for {@link | |
| * Layout#property|property} in the replica. | |
| * | |
| * @returns {Layout} - the copy with {@link Layout#property|property} | |
| * set to `property`. | |
| */ | |
| replicate(property) { | |
| const rv = Object.create(this.constructor.prototype); | |
| Object.assign(rv, this); | |
| rv.property = property; | |
| return rv; | |
| } | |
| /** | |
| * Create an object from layout properties and an array of values. | |
| * | |
| * **NOTE** This function returns `undefined` if invoked on a layout | |
| * that does not return its value as an Object. Objects are | |
| * returned for things that are a {@link Structure}, which includes | |
| * {@link VariantLayout|variant layouts} if they are structures, and | |
| * excludes {@link Union}s. If you want this feature for a union | |
| * you must use {@link Union.getVariant|getVariant} to select the | |
| * desired layout. | |
| * | |
| * @param {Array} values - an array of values that correspond to the | |
| * default order for properties. As with {@link Layout#decode|decode} | |
| * layout elements that have no property name are skipped when | |
| * iterating over the array values. Only the top-level properties are | |
| * assigned; arguments are not assigned to properties of contained | |
| * layouts. Any unused values are ignored. | |
| * | |
| * @return {(Object|undefined)} | |
| */ | |
| fromArray(values) { | |
| return undefined; | |
| } | |
| } | |
| exports.Layout = Layout; | |
| /* Provide text that carries a name (such as for a function that will | |
| * be throwing an error) annotated with the property of a given layout | |
| * (such as one for which the value was unacceptable). | |
| * | |
| * @ignore */ | |
| function nameWithProperty(name, lo) { | |
| if (lo.property) { | |
| return name + '[' + lo.property + ']'; | |
| } | |
| return name; | |
| } | |
| exports.nameWithProperty = nameWithProperty; | |
| /** | |
| * Augment a class so that instances can be encoded/decoded using a | |
| * given layout. | |
| * | |
| * Calling this function couples `Class` with `layout` in several ways: | |
| * | |
| * * `Class.layout_` becomes a static member property equal to `layout`; | |
| * * `layout.boundConstructor_` becomes a static member property equal | |
| * to `Class`; | |
| * * The {@link Layout#makeDestinationObject|makeDestinationObject()} | |
| * property of `layout` is set to a function that returns a `new | |
| * Class()`; | |
| * * `Class.decode(b, offset)` becomes a static member function that | |
| * delegates to {@link Layout#decode|layout.decode}. The | |
| * synthesized function may be captured and extended. | |
| * * `Class.prototype.encode(b, offset)` provides an instance member | |
| * function that delegates to {@link Layout#encode|layout.encode} | |
| * with `src` set to `this`. The synthesized function may be | |
| * captured and extended, but when the extension is invoked `this` | |
| * must be explicitly bound to the instance. | |
| * | |
| * @param {class} Class - a JavaScript class with a nullary | |
| * constructor. | |
| * | |
| * @param {Layout} layout - the {@link Layout} instance used to encode | |
| * instances of `Class`. | |
| */ | |
| // `Class` must be a constructor Function, but the assignment of a `layout_` property to it makes it difficult to type | |
| // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types | |
| function bindConstructorLayout(Class, layout) { | |
| if ('function' !== typeof Class) { | |
| throw new TypeError('Class must be constructor'); | |
| } | |
| if (Object.prototype.hasOwnProperty.call(Class, 'layout_')) { | |
| throw new Error('Class is already bound to a layout'); | |
| } | |
| if (!(layout && (layout instanceof Layout))) { | |
| throw new TypeError('layout must be a Layout'); | |
| } | |
| if (Object.prototype.hasOwnProperty.call(layout, 'boundConstructor_')) { | |
| throw new Error('layout is already bound to a constructor'); | |
| } | |
| Class.layout_ = layout; | |
| layout.boundConstructor_ = Class; | |
| layout.makeDestinationObject = (() => new Class()); | |
| Object.defineProperty(Class.prototype, 'encode', { | |
| value(b, offset) { | |
| return layout.encode(this, b, offset); | |
| }, | |
| writable: true, | |
| }); | |
| Object.defineProperty(Class, 'decode', { | |
| value(b, offset) { | |
| return layout.decode(b, offset); | |
| }, | |
| writable: true, | |
| }); | |
| } | |
| exports.bindConstructorLayout = bindConstructorLayout; | |
| /** | |
| * An object that behaves like a layout but does not consume space | |
| * within its containing layout. | |
| * | |
| * This is primarily used to obtain metadata about a member, such as a | |
| * {@link OffsetLayout} that can provide data about a {@link | |
| * Layout#getSpan|value-specific span}. | |
| * | |
| * **NOTE** This is an abstract base class; you can create instances | |
| * if it amuses you, but they won't support {@link | |
| * ExternalLayout#isCount|isCount} or other {@link Layout} functions. | |
| * | |
| * @param {Number} span - initializer for {@link Layout#span|span}. | |
| * The parameter can range from 1 through 6. | |
| * | |
| * @param {string} [property] - initializer for {@link | |
| * Layout#property|property}. | |
| * | |
| * @abstract | |
| * @augments {Layout} | |
| */ | |
| class ExternalLayout extends Layout { | |
| /** | |
| * Return `true` iff the external layout decodes to an unsigned | |
| * integer layout. | |
| * | |
| * In that case it can be used as the source of {@link | |
| * Sequence#count|Sequence counts}, {@link Blob#length|Blob lengths}, | |
| * or as {@link UnionLayoutDiscriminator#layout|external union | |
| * discriminators}. | |
| * | |
| * @abstract | |
| */ | |
| isCount() { | |
| throw new Error('ExternalLayout is abstract'); | |
| } | |
| } | |
| exports.ExternalLayout = ExternalLayout; | |
| /** | |
| * An {@link ExternalLayout} that determines its {@link | |
| * Layout#decode|value} based on offset into and length of the buffer | |
| * on which it is invoked. | |
| * | |
| * *Factory*: {@link module:Layout.greedy|greedy} | |
| * | |
| * @param {Number} [elementSpan] - initializer for {@link | |
| * GreedyCount#elementSpan|elementSpan}. | |
| * | |
| * @param {string} [property] - initializer for {@link | |
| * Layout#property|property}. | |
| * | |
| * @augments {ExternalLayout} | |
| */ | |
| class GreedyCount extends ExternalLayout { | |
| constructor(elementSpan = 1, property) { | |
| if ((!Number.isInteger(elementSpan)) || (0 >= elementSpan)) { | |
| throw new TypeError('elementSpan must be a (positive) integer'); | |
| } | |
| super(-1, property); | |
| /** The layout for individual elements of the sequence. The value | |
| * must be a positive integer. If not provided, the value will be | |
| * 1. */ | |
| this.elementSpan = elementSpan; | |
| } | |
| /** @override */ | |
| isCount() { | |
| return true; | |
| } | |
| /** @override */ | |
| decode(b, offset = 0) { | |
| checkUint8Array(b); | |
| const rem = b.length - offset; | |
| return Math.floor(rem / this.elementSpan); | |
| } | |
| /** @override */ | |
| encode(src, b, offset) { | |
| return 0; | |
| } | |
| } | |
| exports.GreedyCount = GreedyCount; | |
| /** | |
| * An {@link ExternalLayout} that supports accessing a {@link Layout} | |
| * at a fixed offset from the start of another Layout. The offset may | |
| * be before, within, or after the base layout. | |
| * | |
| * *Factory*: {@link module:Layout.offset|offset} | |
| * | |
| * @param {Layout} layout - initializer for {@link | |
| * OffsetLayout#layout|layout}, modulo `property`. | |
| * | |
| * @param {Number} [offset] - Initializes {@link | |
| * OffsetLayout#offset|offset}. Defaults to zero. | |
| * | |
| * @param {string} [property] - Optional new property name for a | |
| * {@link Layout#replicate| replica} of `layout` to be used as {@link | |
| * OffsetLayout#layout|layout}. If not provided the `layout` is used | |
| * unchanged. | |
| * | |
| * @augments {Layout} | |
| */ | |
| class OffsetLayout extends ExternalLayout { | |
| constructor(layout, offset = 0, property) { | |
| if (!(layout instanceof Layout)) { | |
| throw new TypeError('layout must be a Layout'); | |
| } | |
| if (!Number.isInteger(offset)) { | |
| throw new TypeError('offset must be integer or undefined'); | |
| } | |
| super(layout.span, property || layout.property); | |
| /** The subordinated layout. */ | |
| this.layout = layout; | |
| /** The location of {@link OffsetLayout#layout} relative to the | |
| * start of another layout. | |
| * | |
| * The value may be positive or negative, but an error will thrown | |
| * if at the point of use it goes outside the span of the Uint8Array | |
| * being accessed. */ | |
| this.offset = offset; | |
| } | |
| /** @override */ | |
| isCount() { | |
| return ((this.layout instanceof UInt) | |
| || (this.layout instanceof UIntBE)); | |
| } | |
| /** @override */ | |
| decode(b, offset = 0) { | |
| return this.layout.decode(b, offset + this.offset); | |
| } | |
| /** @override */ | |
| encode(src, b, offset = 0) { | |
| return this.layout.encode(src, b, offset + this.offset); | |
| } | |
| } | |
| exports.OffsetLayout = OffsetLayout; | |
| /** | |
| * Represent an unsigned integer in little-endian format. | |
| * | |
| * *Factory*: {@link module:Layout.u8|u8}, {@link | |
| * module:Layout.u16|u16}, {@link module:Layout.u24|u24}, {@link | |
| * module:Layout.u32|u32}, {@link module:Layout.u40|u40}, {@link | |
| * module:Layout.u48|u48} | |
| * | |
| * @param {Number} span - initializer for {@link Layout#span|span}. | |
| * The parameter can range from 1 through 6. | |
| * | |
| * @param {string} [property] - initializer for {@link | |
| * Layout#property|property}. | |
| * | |
| * @augments {Layout} | |
| */ | |
| class UInt extends Layout { | |
| constructor(span, property) { | |
| super(span, property); | |
| if (6 < this.span) { | |
| throw new RangeError('span must not exceed 6 bytes'); | |
| } | |
| } | |
| /** @override */ | |
| decode(b, offset = 0) { | |
| return uint8ArrayToBuffer(b).readUIntLE(offset, this.span); | |
| } | |
| /** @override */ | |
| encode(src, b, offset = 0) { | |
| uint8ArrayToBuffer(b).writeUIntLE(src, offset, this.span); | |
| return this.span; | |
| } | |
| } | |
| exports.UInt = UInt; | |
| /** | |
| * Represent an unsigned integer in big-endian format. | |
| * | |
| * *Factory*: {@link module:Layout.u8be|u8be}, {@link | |
| * module:Layout.u16be|u16be}, {@link module:Layout.u24be|u24be}, | |
| * {@link module:Layout.u32be|u32be}, {@link | |
| * module:Layout.u40be|u40be}, {@link module:Layout.u48be|u48be} | |
| * | |
| * @param {Number} span - initializer for {@link Layout#span|span}. | |
| * The parameter can range from 1 through 6. | |
| * | |
| * @param {string} [property] - initializer for {@link | |
| * Layout#property|property}. | |
| * | |
| * @augments {Layout} | |
| */ | |
| class UIntBE extends Layout { | |
| constructor(span, property) { | |
| super(span, property); | |
| if (6 < this.span) { | |
| throw new RangeError('span must not exceed 6 bytes'); | |
| } | |
| } | |
| /** @override */ | |
| decode(b, offset = 0) { | |
| return uint8ArrayToBuffer(b).readUIntBE(offset, this.span); | |
| } | |
| /** @override */ | |
| encode(src, b, offset = 0) { | |
| uint8ArrayToBuffer(b).writeUIntBE(src, offset, this.span); | |
| return this.span; | |
| } | |
| } | |
| exports.UIntBE = UIntBE; | |
| /** | |
| * Represent a signed integer in little-endian format. | |
| * | |
| * *Factory*: {@link module:Layout.s8|s8}, {@link | |
| * module:Layout.s16|s16}, {@link module:Layout.s24|s24}, {@link | |
| * module:Layout.s32|s32}, {@link module:Layout.s40|s40}, {@link | |
| * module:Layout.s48|s48} | |
| * | |
| * @param {Number} span - initializer for {@link Layout#span|span}. | |
| * The parameter can range from 1 through 6. | |
| * | |
| * @param {string} [property] - initializer for {@link | |
| * Layout#property|property}. | |
| * | |
| * @augments {Layout} | |
| */ | |
| class Int extends Layout { | |
| constructor(span, property) { | |
| super(span, property); | |
| if (6 < this.span) { | |
| throw new RangeError('span must not exceed 6 bytes'); | |
| } | |
| } | |
| /** @override */ | |
| decode(b, offset = 0) { | |
| return uint8ArrayToBuffer(b).readIntLE(offset, this.span); | |
| } | |
| /** @override */ | |
| encode(src, b, offset = 0) { | |
| uint8ArrayToBuffer(b).writeIntLE(src, offset, this.span); | |
| return this.span; | |
| } | |
| } | |
| exports.Int = Int; | |
| /** | |
| * Represent a signed integer in big-endian format. | |
| * | |
| * *Factory*: {@link module:Layout.s8be|s8be}, {@link | |
| * module:Layout.s16be|s16be}, {@link module:Layout.s24be|s24be}, | |
| * {@link module:Layout.s32be|s32be}, {@link | |
| * module:Layout.s40be|s40be}, {@link module:Layout.s48be|s48be} | |
| * | |
| * @param {Number} span - initializer for {@link Layout#span|span}. | |
| * The parameter can range from 1 through 6. | |
| * | |
| * @param {string} [property] - initializer for {@link | |
| * Layout#property|property}. | |
| * | |
| * @augments {Layout} | |
| */ | |
| class IntBE extends Layout { | |
| constructor(span, property) { | |
| super(span, property); | |
| if (6 < this.span) { | |
| throw new RangeError('span must not exceed 6 bytes'); | |
| } | |
| } | |
| /** @override */ | |
| decode(b, offset = 0) { | |
| return uint8ArrayToBuffer(b).readIntBE(offset, this.span); | |
| } | |
| /** @override */ | |
| encode(src, b, offset = 0) { | |
| uint8ArrayToBuffer(b).writeIntBE(src, offset, this.span); | |
| return this.span; | |
| } | |
| } | |
| exports.IntBE = IntBE; | |
| const V2E32 = Math.pow(2, 32); | |
| /* True modulus high and low 32-bit words, where low word is always | |
| * non-negative. */ | |
| function divmodInt64(src) { | |
| const hi32 = Math.floor(src / V2E32); | |
| const lo32 = src - (hi32 * V2E32); | |
| return { hi32, lo32 }; | |
| } | |
| /* Reconstruct Number from quotient and non-negative remainder */ | |
| function roundedInt64(hi32, lo32) { | |
| return hi32 * V2E32 + lo32; | |
| } | |
| /** | |
| * Represent an unsigned 64-bit integer in little-endian format when | |
| * encoded and as a near integral JavaScript Number when decoded. | |
| * | |
| * *Factory*: {@link module:Layout.nu64|nu64} | |
| * | |
| * **NOTE** Values with magnitude greater than 2^52 may not decode to | |
| * the exact value of the encoded representation. | |
| * | |
| * @augments {Layout} | |
| */ | |
| class NearUInt64 extends Layout { | |
| constructor(property) { | |
| super(8, property); | |
| } | |
| /** @override */ | |
| decode(b, offset = 0) { | |
| const buffer = uint8ArrayToBuffer(b); | |
| const lo32 = buffer.readUInt32LE(offset); | |
| const hi32 = buffer.readUInt32LE(offset + 4); | |
| return roundedInt64(hi32, lo32); | |
| } | |
| /** @override */ | |
| encode(src, b, offset = 0) { | |
| const split = divmodInt64(src); | |
| const buffer = uint8ArrayToBuffer(b); | |
| buffer.writeUInt32LE(split.lo32, offset); | |
| buffer.writeUInt32LE(split.hi32, offset + 4); | |
| return 8; | |
| } | |
| } | |
| exports.NearUInt64 = NearUInt64; | |
| /** | |
| * Represent an unsigned 64-bit integer in big-endian format when | |
| * encoded and as a near integral JavaScript Number when decoded. | |
| * | |
| * *Factory*: {@link module:Layout.nu64be|nu64be} | |
| * | |
| * **NOTE** Values with magnitude greater than 2^52 may not decode to | |
| * the exact value of the encoded representation. | |
| * | |
| * @augments {Layout} | |
| */ | |
| class NearUInt64BE extends Layout { | |
| constructor(property) { | |
| super(8, property); | |
| } | |
| /** @override */ | |
| decode(b, offset = 0) { | |
| const buffer = uint8ArrayToBuffer(b); | |
| const hi32 = buffer.readUInt32BE(offset); | |
| const lo32 = buffer.readUInt32BE(offset + 4); | |
| return roundedInt64(hi32, lo32); | |
| } | |
| /** @override */ | |
| encode(src, b, offset = 0) { | |
| const split = divmodInt64(src); | |
| const buffer = uint8ArrayToBuffer(b); | |
| buffer.writeUInt32BE(split.hi32, offset); | |
| buffer.writeUInt32BE(split.lo32, offset + 4); | |
| return 8; | |
| } | |
| } | |
| exports.NearUInt64BE = NearUInt64BE; | |
| /** | |
| * Represent a signed 64-bit integer in little-endian format when | |
| * encoded and as a near integral JavaScript Number when decoded. | |
| * | |
| * *Factory*: {@link module:Layout.ns64|ns64} | |
| * | |
| * **NOTE** Values with magnitude greater than 2^52 may not decode to | |
| * the exact value of the encoded representation. | |
| * | |
| * @augments {Layout} | |
| */ | |
| class NearInt64 extends Layout { | |
| constructor(property) { | |
| super(8, property); | |
| } | |
| /** @override */ | |
| decode(b, offset = 0) { | |
| const buffer = uint8ArrayToBuffer(b); | |
| const lo32 = buffer.readUInt32LE(offset); | |
| const hi32 = buffer.readInt32LE(offset + 4); | |
| return roundedInt64(hi32, lo32); | |
| } | |
| /** @override */ | |
| encode(src, b, offset = 0) { | |
| const split = divmodInt64(src); | |
| const buffer = uint8ArrayToBuffer(b); | |
| buffer.writeUInt32LE(split.lo32, offset); | |
| buffer.writeInt32LE(split.hi32, offset + 4); | |
| return 8; | |
| } | |
| } | |
| exports.NearInt64 = NearInt64; | |
| /** | |
| * Represent a signed 64-bit integer in big-endian format when | |
| * encoded and as a near integral JavaScript Number when decoded. | |
| * | |
| * *Factory*: {@link module:Layout.ns64be|ns64be} | |
| * | |
| * **NOTE** Values with magnitude greater than 2^52 may not decode to | |
| * the exact value of the encoded representation. | |
| * | |
| * @augments {Layout} | |
| */ | |
| class NearInt64BE extends Layout { | |
| constructor(property) { | |
| super(8, property); | |
| } | |
| /** @override */ | |
| decode(b, offset = 0) { | |
| const buffer = uint8ArrayToBuffer(b); | |
| const hi32 = buffer.readInt32BE(offset); | |
| const lo32 = buffer.readUInt32BE(offset + 4); | |
| return roundedInt64(hi32, lo32); | |
| } | |
| /** @override */ | |
| encode(src, b, offset = 0) { | |
| const split = divmodInt64(src); | |
| const buffer = uint8ArrayToBuffer(b); | |
| buffer.writeInt32BE(split.hi32, offset); | |
| buffer.writeUInt32BE(split.lo32, offset + 4); | |
| return 8; | |
| } | |
| } | |
| exports.NearInt64BE = NearInt64BE; | |
| /** | |
| * Represent a 32-bit floating point number in little-endian format. | |
| * | |
| * *Factory*: {@link module:Layout.f32|f32} | |
| * | |
| * @param {string} [property] - initializer for {@link | |
| * Layout#property|property}. | |
| * | |
| * @augments {Layout} | |
| */ | |
| class Float extends Layout { | |
| constructor(property) { | |
| super(4, property); | |
| } | |
| /** @override */ | |
| decode(b, offset = 0) { | |
| return uint8ArrayToBuffer(b).readFloatLE(offset); | |
| } | |
| /** @override */ | |
| encode(src, b, offset = 0) { | |
| uint8ArrayToBuffer(b).writeFloatLE(src, offset); | |
| return 4; | |
| } | |
| } | |
| exports.Float = Float; | |
| /** | |
| * Represent a 32-bit floating point number in big-endian format. | |
| * | |
| * *Factory*: {@link module:Layout.f32be|f32be} | |
| * | |
| * @param {string} [property] - initializer for {@link | |
| * Layout#property|property}. | |
| * | |
| * @augments {Layout} | |
| */ | |
| class FloatBE extends Layout { | |
| constructor(property) { | |
| super(4, property); | |
| } | |
| /** @override */ | |
| decode(b, offset = 0) { | |
| return uint8ArrayToBuffer(b).readFloatBE(offset); | |
| } | |
| /** @override */ | |
| encode(src, b, offset = 0) { | |
| uint8ArrayToBuffer(b).writeFloatBE(src, offset); | |
| return 4; | |
| } | |
| } | |
| exports.FloatBE = FloatBE; | |
| /** | |
| * Represent a 64-bit floating point number in little-endian format. | |
| * | |
| * *Factory*: {@link module:Layout.f64|f64} | |
| * | |
| * @param {string} [property] - initializer for {@link | |
| * Layout#property|property}. | |
| * | |
| * @augments {Layout} | |
| */ | |
| class Double extends Layout { | |
| constructor(property) { | |
| super(8, property); | |
| } | |
| /** @override */ | |
| decode(b, offset = 0) { | |
| return uint8ArrayToBuffer(b).readDoubleLE(offset); | |
| } | |
| /** @override */ | |
| encode(src, b, offset = 0) { | |
| uint8ArrayToBuffer(b).writeDoubleLE(src, offset); | |
| return 8; | |
| } | |
| } | |
| exports.Double = Double; | |
| /** | |
| * Represent a 64-bit floating point number in big-endian format. | |
| * | |
| * *Factory*: {@link module:Layout.f64be|f64be} | |
| * | |
| * @param {string} [property] - initializer for {@link | |
| * Layout#property|property}. | |
| * | |
| * @augments {Layout} | |
| */ | |
| class DoubleBE extends Layout { | |
| constructor(property) { | |
| super(8, property); | |
| } | |
| /** @override */ | |
| decode(b, offset = 0) { | |
| return uint8ArrayToBuffer(b).readDoubleBE(offset); | |
| } | |
| /** @override */ | |
| encode(src, b, offset = 0) { | |
| uint8ArrayToBuffer(b).writeDoubleBE(src, offset); | |
| return 8; | |
| } | |
| } | |
| exports.DoubleBE = DoubleBE; | |
| /** | |
| * Represent a contiguous sequence of a specific layout as an Array. | |
| * | |
| * *Factory*: {@link module:Layout.seq|seq} | |
| * | |
| * @param {Layout} elementLayout - initializer for {@link | |
| * Sequence#elementLayout|elementLayout}. | |
| * | |
| * @param {(Number|ExternalLayout)} count - initializer for {@link | |
| * Sequence#count|count}. The parameter must be either a positive | |
| * integer or an instance of {@link ExternalLayout}. | |
| * | |
| * @param {string} [property] - initializer for {@link | |
| * Layout#property|property}. | |
| * | |
| * @augments {Layout} | |
| */ | |
| class Sequence extends Layout { | |
| constructor(elementLayout, count, property) { | |
| if (!(elementLayout instanceof Layout)) { | |
| throw new TypeError('elementLayout must be a Layout'); | |
| } | |
| if (!(((count instanceof ExternalLayout) && count.isCount()) | |
| || (Number.isInteger(count) && (0 <= count)))) { | |
| throw new TypeError('count must be non-negative integer ' | |
| + 'or an unsigned integer ExternalLayout'); | |
| } | |
| let span = -1; | |
| if ((!(count instanceof ExternalLayout)) | |
| && (0 < elementLayout.span)) { | |
| span = count * elementLayout.span; | |
| } | |
| super(span, property); | |
| /** The layout for individual elements of the sequence. */ | |
| this.elementLayout = elementLayout; | |
| /** The number of elements in the sequence. | |
| * | |
| * This will be either a non-negative integer or an instance of | |
| * {@link ExternalLayout} for which {@link | |
| * ExternalLayout#isCount|isCount()} is `true`. */ | |
| this.count = count; | |
| } | |
| /** @override */ | |
| getSpan(b, offset = 0) { | |
| if (0 <= this.span) { | |
| return this.span; | |
| } | |
| let span = 0; | |
| let count = this.count; | |
| if (count instanceof ExternalLayout) { | |
| count = count.decode(b, offset); | |
| } | |
| if (0 < this.elementLayout.span) { | |
| span = count * this.elementLayout.span; | |
| } | |
| else { | |
| let idx = 0; | |
| while (idx < count) { | |
| span += this.elementLayout.getSpan(b, offset + span); | |
| ++idx; | |
| } | |
| } | |
| return span; | |
| } | |
| /** @override */ | |
| decode(b, offset = 0) { | |
| const rv = []; | |
| let i = 0; | |
| let count = this.count; | |
| if (count instanceof ExternalLayout) { | |
| count = count.decode(b, offset); | |
| } | |
| while (i < count) { | |
| rv.push(this.elementLayout.decode(b, offset)); | |
| offset += this.elementLayout.getSpan(b, offset); | |
| i += 1; | |
| } | |
| return rv; | |
| } | |
| /** Implement {@link Layout#encode|encode} for {@link Sequence}. | |
| * | |
| * **NOTE** If `src` is shorter than {@link Sequence#count|count} then | |
| * the unused space in the buffer is left unchanged. If `src` is | |
| * longer than {@link Sequence#count|count} the unneeded elements are | |
| * ignored. | |
| * | |
| * **NOTE** If {@link Layout#count|count} is an instance of {@link | |
| * ExternalLayout} then the length of `src` will be encoded as the | |
| * count after `src` is encoded. */ | |
| encode(src, b, offset = 0) { | |
| const elo = this.elementLayout; | |
| const span = src.reduce((span, v) => { | |
| return span + elo.encode(v, b, offset + span); | |
| }, 0); | |
| if (this.count instanceof ExternalLayout) { | |
| this.count.encode(src.length, b, offset); | |
| } | |
| return span; | |
| } | |
| } | |
| exports.Sequence = Sequence; | |
| /** | |
| * Represent a contiguous sequence of arbitrary layout elements as an | |
| * Object. | |
| * | |
| * *Factory*: {@link module:Layout.struct|struct} | |
| * | |
| * **NOTE** The {@link Layout#span|span} of the structure is variable | |
| * if any layout in {@link Structure#fields|fields} has a variable | |
| * span. When {@link Layout#encode|encoding} we must have a value for | |
| * all variable-length fields, or we wouldn't be able to figure out | |
| * how much space to use for storage. We can only identify the value | |
| * for a field when it has a {@link Layout#property|property}. As | |
| * such, although a structure may contain both unnamed fields and | |
| * variable-length fields, it cannot contain an unnamed | |
| * variable-length field. | |
| * | |
| * @param {Layout[]} fields - initializer for {@link | |
| * Structure#fields|fields}. An error is raised if this contains a | |
| * variable-length field for which a {@link Layout#property|property} | |
| * is not defined. | |
| * | |
| * @param {string} [property] - initializer for {@link | |
| * Layout#property|property}. | |
| * | |
| * @param {Boolean} [decodePrefixes] - initializer for {@link | |
| * Structure#decodePrefixes|property}. | |
| * | |
| * @throws {Error} - if `fields` contains an unnamed variable-length | |
| * layout. | |
| * | |
| * @augments {Layout} | |
| */ | |
| class Structure extends Layout { | |
| constructor(fields, property, decodePrefixes) { | |
| if (!(Array.isArray(fields) | |
| && fields.reduce((acc, v) => acc && (v instanceof Layout), true))) { | |
| throw new TypeError('fields must be array of Layout instances'); | |
| } | |
| if (('boolean' === typeof property) | |
| && (undefined === decodePrefixes)) { | |
| decodePrefixes = property; | |
| property = undefined; | |
| } | |
| /* Verify absence of unnamed variable-length fields. */ | |
| for (const fd of fields) { | |
| if ((0 > fd.span) | |
| && (undefined === fd.property)) { | |
| throw new Error('fields cannot contain unnamed variable-length layout'); | |
| } | |
| } | |
| let span = -1; | |
| try { | |
| span = fields.reduce((span, fd) => span + fd.getSpan(), 0); | |
| } | |
| catch (e) { | |
| // ignore error | |
| } | |
| super(span, property); | |
| /** The sequence of {@link Layout} values that comprise the | |
| * structure. | |
| * | |
| * The individual elements need not be the same type, and may be | |
| * either scalar or aggregate layouts. If a member layout leaves | |
| * its {@link Layout#property|property} undefined the | |
| * corresponding region of the buffer associated with the element | |
| * will not be mutated. | |
| * | |
| * @type {Layout[]} */ | |
| this.fields = fields; | |
| /** Control behavior of {@link Layout#decode|decode()} given short | |
| * buffers. | |
| * | |
| * In some situations a structure many be extended with additional | |
| * fields over time, with older installations providing only a | |
| * prefix of the full structure. If this property is `true` | |
| * decoding will accept those buffers and leave subsequent fields | |
| * undefined, as long as the buffer ends at a field boundary. | |
| * Defaults to `false`. */ | |
| this.decodePrefixes = !!decodePrefixes; | |
| } | |
| /** @override */ | |
| getSpan(b, offset = 0) { | |
| if (0 <= this.span) { | |
| return this.span; | |
| } | |
| let span = 0; | |
| try { | |
| span = this.fields.reduce((span, fd) => { | |
| const fsp = fd.getSpan(b, offset); | |
| offset += fsp; | |
| return span + fsp; | |
| }, 0); | |
| } | |
| catch (e) { | |
| throw new RangeError('indeterminate span'); | |
| } | |
| return span; | |
| } | |
| /** @override */ | |
| decode(b, offset = 0) { | |
| checkUint8Array(b); | |
| const dest = this.makeDestinationObject(); | |
| for (const fd of this.fields) { | |
| if (undefined !== fd.property) { | |
| dest[fd.property] = fd.decode(b, offset); | |
| } | |
| offset += fd.getSpan(b, offset); | |
| if (this.decodePrefixes | |
| && (b.length === offset)) { | |
| break; | |
| } | |
| } | |
| return dest; | |
| } | |
| /** Implement {@link Layout#encode|encode} for {@link Structure}. | |
| * | |
| * If `src` is missing a property for a member with a defined {@link | |
| * Layout#property|property} the corresponding region of the buffer is | |
| * left unmodified. */ | |
| encode(src, b, offset = 0) { | |
| const firstOffset = offset; | |
| let lastOffset = 0; | |
| let lastWrote = 0; | |
| for (const fd of this.fields) { | |
| let span = fd.span; | |
| lastWrote = (0 < span) ? span : 0; | |
| if (undefined !== fd.property) { | |
| const fv = src[fd.property]; | |
| if (undefined !== fv) { | |
| lastWrote = fd.encode(fv, b, offset); | |
| if (0 > span) { | |
| /* Read the as-encoded span, which is not necessarily the | |
| * same as what we wrote. */ | |
| span = fd.getSpan(b, offset); | |
| } | |
| } | |
| } | |
| lastOffset = offset; | |
| offset += span; | |
| } | |
| /* Use (lastOffset + lastWrote) instead of offset because the last | |
| * item may have had a dynamic length and we don't want to include | |
| * the padding between it and the end of the space reserved for | |
| * it. */ | |
| return (lastOffset + lastWrote) - firstOffset; | |
| } | |
| /** @override */ | |
| fromArray(values) { | |
| const dest = this.makeDestinationObject(); | |
| for (const fd of this.fields) { | |
| if ((undefined !== fd.property) | |
| && (0 < values.length)) { | |
| dest[fd.property] = values.shift(); | |
| } | |
| } | |
| return dest; | |
| } | |
| /** | |
| * Get access to the layout of a given property. | |
| * | |
| * @param {String} property - the structure member of interest. | |
| * | |
| * @return {Layout} - the layout associated with `property`, or | |
| * undefined if there is no such property. | |
| */ | |
| layoutFor(property) { | |
| if ('string' !== typeof property) { | |
| throw new TypeError('property must be string'); | |
| } | |
| for (const fd of this.fields) { | |
| if (fd.property === property) { | |
| return fd; | |
| } | |
| } | |
| return undefined; | |
| } | |
| /** | |
| * Get the offset of a structure member. | |
| * | |
| * @param {String} property - the structure member of interest. | |
| * | |
| * @return {Number} - the offset in bytes to the start of `property` | |
| * within the structure, or undefined if `property` is not a field | |
| * within the structure. If the property is a member but follows a | |
| * variable-length structure member a negative number will be | |
| * returned. | |
| */ | |
| offsetOf(property) { | |
| if ('string' !== typeof property) { | |
| throw new TypeError('property must be string'); | |
| } | |
| let offset = 0; | |
| for (const fd of this.fields) { | |
| if (fd.property === property) { | |
| return offset; | |
| } | |
| if (0 > fd.span) { | |
| offset = -1; | |
| } | |
| else if (0 <= offset) { | |
| offset += fd.span; | |
| } | |
| } | |
| return undefined; | |
| } | |
| } | |
| exports.Structure = Structure; | |
| /** | |
| * An object that can provide a {@link | |
| * Union#discriminator|discriminator} API for {@link Union}. | |
| * | |
| * **NOTE** This is an abstract base class; you can create instances | |
| * if it amuses you, but they won't support the {@link | |
| * UnionDiscriminator#encode|encode} or {@link | |
| * UnionDiscriminator#decode|decode} functions. | |
| * | |
| * @param {string} [property] - Default for {@link | |
| * UnionDiscriminator#property|property}. | |
| * | |
| * @abstract | |
| */ | |
| class UnionDiscriminator { | |
| constructor(property) { | |
| /** The {@link Layout#property|property} to be used when the | |
| * discriminator is referenced in isolation (generally when {@link | |
| * Union#decode|Union decode} cannot delegate to a specific | |
| * variant). */ | |
| this.property = property; | |
| } | |
| /** Analog to {@link Layout#decode|Layout decode} for union discriminators. | |
| * | |
| * The implementation of this method need not reference the buffer if | |
| * variant information is available through other means. */ | |
| decode(b, offset) { | |
| throw new Error('UnionDiscriminator is abstract'); | |
| } | |
| /** Analog to {@link Layout#decode|Layout encode} for union discriminators. | |
| * | |
| * The implementation of this method need not store the value if | |
| * variant information is maintained through other means. */ | |
| encode(src, b, offset) { | |
| throw new Error('UnionDiscriminator is abstract'); | |
| } | |
| } | |
| exports.UnionDiscriminator = UnionDiscriminator; | |
| /** | |
| * An object that can provide a {@link | |
| * UnionDiscriminator|discriminator API} for {@link Union} using an | |
| * unsigned integral {@link Layout} instance located either inside or | |
| * outside the union. | |
| * | |
| * @param {ExternalLayout} layout - initializes {@link | |
| * UnionLayoutDiscriminator#layout|layout}. Must satisfy {@link | |
| * ExternalLayout#isCount|isCount()}. | |
| * | |
| * @param {string} [property] - Default for {@link | |
| * UnionDiscriminator#property|property}, superseding the property | |
| * from `layout`, but defaulting to `variant` if neither `property` | |
| * nor layout provide a property name. | |
| * | |
| * @augments {UnionDiscriminator} | |
| */ | |
| class UnionLayoutDiscriminator extends UnionDiscriminator { | |
| constructor(layout, property) { | |
| if (!((layout instanceof ExternalLayout) | |
| && layout.isCount())) { | |
| throw new TypeError('layout must be an unsigned integer ExternalLayout'); | |
| } | |
| super(property || layout.property || 'variant'); | |
| /** The {@link ExternalLayout} used to access the discriminator | |
| * value. */ | |
| this.layout = layout; | |
| } | |
| /** Delegate decoding to {@link UnionLayoutDiscriminator#layout|layout}. */ | |
| decode(b, offset) { | |
| return this.layout.decode(b, offset); | |
| } | |
| /** Delegate encoding to {@link UnionLayoutDiscriminator#layout|layout}. */ | |
| encode(src, b, offset) { | |
| return this.layout.encode(src, b, offset); | |
| } | |
| } | |
| exports.UnionLayoutDiscriminator = UnionLayoutDiscriminator; | |
| /** | |
| * Represent any number of span-compatible layouts. | |
| * | |
| * *Factory*: {@link module:Layout.union|union} | |
| * | |
| * If the union has a {@link Union#defaultLayout|default layout} that | |
| * layout must have a non-negative {@link Layout#span|span}. The span | |
| * of a fixed-span union includes its {@link | |
| * Union#discriminator|discriminator} if the variant is a {@link | |
| * Union#usesPrefixDiscriminator|prefix of the union}, plus the span | |
| * of its {@link Union#defaultLayout|default layout}. | |
| * | |
| * If the union does not have a default layout then the encoded span | |
| * of the union depends on the encoded span of its variant (which may | |
| * be fixed or variable). | |
| * | |
| * {@link VariantLayout#layout|Variant layout}s are added through | |
| * {@link Union#addVariant|addVariant}. If the union has a default | |
| * layout, the span of the {@link VariantLayout#layout|layout | |
| * contained by the variant} must not exceed the span of the {@link | |
| * Union#defaultLayout|default layout} (minus the span of a {@link | |
| * Union#usesPrefixDiscriminator|prefix disriminator}, if used). The | |
| * span of the variant will equal the span of the union itself. | |
| * | |
| * The variant for a buffer can only be identified from the {@link | |
| * Union#discriminator|discriminator} {@link | |
| * UnionDiscriminator#property|property} (in the case of the {@link | |
| * Union#defaultLayout|default layout}), or by using {@link | |
| * Union#getVariant|getVariant} and examining the resulting {@link | |
| * VariantLayout} instance. | |
| * | |
| * A variant compatible with a JavaScript object can be identified | |
| * using {@link Union#getSourceVariant|getSourceVariant}. | |
| * | |
| * @param {(UnionDiscriminator|ExternalLayout|Layout)} discr - How to | |
| * identify the layout used to interpret the union contents. The | |
| * parameter must be an instance of {@link UnionDiscriminator}, an | |
| * {@link ExternalLayout} that satisfies {@link | |
| * ExternalLayout#isCount|isCount()}, or {@link UInt} (or {@link | |
| * UIntBE}). When a non-external layout element is passed the layout | |
| * appears at the start of the union. In all cases the (synthesized) | |
| * {@link UnionDiscriminator} instance is recorded as {@link | |
| * Union#discriminator|discriminator}. | |
| * | |
| * @param {(Layout|null)} defaultLayout - initializer for {@link | |
| * Union#defaultLayout|defaultLayout}. If absent defaults to `null`. | |
| * If `null` there is no default layout: the union has data-dependent | |
| * length and attempts to decode or encode unrecognized variants will | |
| * throw an exception. A {@link Layout} instance must have a | |
| * non-negative {@link Layout#span|span}, and if it lacks a {@link | |
| * Layout#property|property} the {@link | |
| * Union#defaultLayout|defaultLayout} will be a {@link | |
| * Layout#replicate|replica} with property `content`. | |
| * | |
| * @param {string} [property] - initializer for {@link | |
| * Layout#property|property}. | |
| * | |
| * @augments {Layout} | |
| */ | |
| class Union extends Layout { | |
| constructor(discr, defaultLayout, property) { | |
| let discriminator; | |
| if ((discr instanceof UInt) | |
| || (discr instanceof UIntBE)) { | |
| discriminator = new UnionLayoutDiscriminator(new OffsetLayout(discr)); | |
| } | |
| else if ((discr instanceof ExternalLayout) | |
| && discr.isCount()) { | |
| discriminator = new UnionLayoutDiscriminator(discr); | |
| } | |
| else if (!(discr instanceof UnionDiscriminator)) { | |
| throw new TypeError('discr must be a UnionDiscriminator ' | |
| + 'or an unsigned integer layout'); | |
| } | |
| else { | |
| discriminator = discr; | |
| } | |
| if (undefined === defaultLayout) { | |
| defaultLayout = null; | |
| } | |
| if (!((null === defaultLayout) | |
| || (defaultLayout instanceof Layout))) { | |
| throw new TypeError('defaultLayout must be null or a Layout'); | |
| } | |
| if (null !== defaultLayout) { | |
| if (0 > defaultLayout.span) { | |
| throw new Error('defaultLayout must have constant span'); | |
| } | |
| if (undefined === defaultLayout.property) { | |
| defaultLayout = defaultLayout.replicate('content'); | |
| } | |
| } | |
| /* The union span can be estimated only if there's a default | |
| * layout. The union spans its default layout, plus any prefix | |
| * variant layout. By construction both layouts, if present, have | |
| * non-negative span. */ | |
| let span = -1; | |
| if (defaultLayout) { | |
| span = defaultLayout.span; | |
| if ((0 <= span) && ((discr instanceof UInt) | |
| || (discr instanceof UIntBE))) { | |
| span += discriminator.layout.span; | |
| } | |
| } | |
| super(span, property); | |
| /** The interface for the discriminator value in isolation. | |
| * | |
| * This a {@link UnionDiscriminator} either passed to the | |
| * constructor or synthesized from the `discr` constructor | |
| * argument. {@link | |
| * Union#usesPrefixDiscriminator|usesPrefixDiscriminator} will be | |
| * `true` iff the `discr` parameter was a non-offset {@link | |
| * Layout} instance. */ | |
| this.discriminator = discriminator; | |
| /** `true` if the {@link Union#discriminator|discriminator} is the | |
| * first field in the union. | |
| * | |
| * If `false` the discriminator is obtained from somewhere | |
| * else. */ | |
| this.usesPrefixDiscriminator = (discr instanceof UInt) | |
| || (discr instanceof UIntBE); | |
| /** The layout for non-discriminator content when the value of the | |
| * discriminator is not recognized. | |
| * | |
| * This is the value passed to the constructor. It is | |
| * structurally equivalent to the second component of {@link | |
| * Union#layout|layout} but may have a different property | |
| * name. */ | |
| this.defaultLayout = defaultLayout; | |
| /** A registry of allowed variants. | |
| * | |
| * The keys are unsigned integers which should be compatible with | |
| * {@link Union.discriminator|discriminator}. The property value | |
| * is the corresponding {@link VariantLayout} instances assigned | |
| * to this union by {@link Union#addVariant|addVariant}. | |
| * | |
| * **NOTE** The registry remains mutable so that variants can be | |
| * {@link Union#addVariant|added} at any time. Users should not | |
| * manipulate the content of this property. */ | |
| this.registry = {}; | |
| /* Private variable used when invoking getSourceVariant */ | |
| let boundGetSourceVariant = this.defaultGetSourceVariant.bind(this); | |
| /** Function to infer the variant selected by a source object. | |
| * | |
| * Defaults to {@link | |
| * Union#defaultGetSourceVariant|defaultGetSourceVariant} but may | |
| * be overridden using {@link | |
| * Union#configGetSourceVariant|configGetSourceVariant}. | |
| * | |
| * @param {Object} src - as with {@link | |
| * Union#defaultGetSourceVariant|defaultGetSourceVariant}. | |
| * | |
| * @returns {(undefined|VariantLayout)} The default variant | |
| * (`undefined`) or first registered variant that uses a property | |
| * available in `src`. */ | |
| this.getSourceVariant = function (src) { | |
| return boundGetSourceVariant(src); | |
| }; | |
| /** Function to override the implementation of {@link | |
| * Union#getSourceVariant|getSourceVariant}. | |
| * | |
| * Use this if the desired variant cannot be identified using the | |
| * algorithm of {@link | |
| * Union#defaultGetSourceVariant|defaultGetSourceVariant}. | |
| * | |
| * **NOTE** The provided function will be invoked bound to this | |
| * Union instance, providing local access to {@link | |
| * Union#registry|registry}. | |
| * | |
| * @param {Function} gsv - a function that follows the API of | |
| * {@link Union#defaultGetSourceVariant|defaultGetSourceVariant}. */ | |
| this.configGetSourceVariant = function (gsv) { | |
| boundGetSourceVariant = gsv.bind(this); | |
| }; | |
| } | |
| /** @override */ | |
| getSpan(b, offset = 0) { | |
| if (0 <= this.span) { | |
| return this.span; | |
| } | |
| /* Default layouts always have non-negative span, so we don't have | |
| * one and we have to recognize the variant which will in turn | |
| * determine the span. */ | |
| const vlo = this.getVariant(b, offset); | |
| if (!vlo) { | |
| throw new Error('unable to determine span for unrecognized variant'); | |
| } | |
| return vlo.getSpan(b, offset); | |
| } | |
| /** | |
| * Method to infer a registered Union variant compatible with `src`. | |
| * | |
| * The first satisfied rule in the following sequence defines the | |
| * return value: | |
| * * If `src` has properties matching the Union discriminator and | |
| * the default layout, `undefined` is returned regardless of the | |
| * value of the discriminator property (this ensures the default | |
| * layout will be used); | |
| * * If `src` has a property matching the Union discriminator, the | |
| * value of the discriminator identifies a registered variant, and | |
| * either (a) the variant has no layout, or (b) `src` has the | |
| * variant's property, then the variant is returned (because the | |
| * source satisfies the constraints of the variant it identifies); | |
| * * If `src` does not have a property matching the Union | |
| * discriminator, but does have a property matching a registered | |
| * variant, then the variant is returned (because the source | |
| * matches a variant without an explicit conflict); | |
| * * An error is thrown (because we either can't identify a variant, | |
| * or we were explicitly told the variant but can't satisfy it). | |
| * | |
| * @param {Object} src - an object presumed to be compatible with | |
| * the content of the Union. | |
| * | |
| * @return {(undefined|VariantLayout)} - as described above. | |
| * | |
| * @throws {Error} - if `src` cannot be associated with a default or | |
| * registered variant. | |
| */ | |
| defaultGetSourceVariant(src) { | |
| if (Object.prototype.hasOwnProperty.call(src, this.discriminator.property)) { | |
| if (this.defaultLayout && this.defaultLayout.property | |
| && Object.prototype.hasOwnProperty.call(src, this.defaultLayout.property)) { | |
| return undefined; | |
| } | |
| const vlo = this.registry[src[this.discriminator.property]]; | |
| if (vlo | |
| && ((!vlo.layout) | |
| || (vlo.property && Object.prototype.hasOwnProperty.call(src, vlo.property)))) { | |
| return vlo; | |
| } | |
| } | |
| else { | |
| for (const tag in this.registry) { | |
| const vlo = this.registry[tag]; | |
| if (vlo.property && Object.prototype.hasOwnProperty.call(src, vlo.property)) { | |
| return vlo; | |
| } | |
| } | |
| } | |
| throw new Error('unable to infer src variant'); | |
| } | |
| /** Implement {@link Layout#decode|decode} for {@link Union}. | |
| * | |
| * If the variant is {@link Union#addVariant|registered} the return | |
| * value is an instance of that variant, with no explicit | |
| * discriminator. Otherwise the {@link Union#defaultLayout|default | |
| * layout} is used to decode the content. */ | |
| decode(b, offset = 0) { | |
| let dest; | |
| const dlo = this.discriminator; | |
| const discr = dlo.decode(b, offset); | |
| const clo = this.registry[discr]; | |
| if (undefined === clo) { | |
| const defaultLayout = this.defaultLayout; | |
| let contentOffset = 0; | |
| if (this.usesPrefixDiscriminator) { | |
| contentOffset = dlo.layout.span; | |
| } | |
| dest = this.makeDestinationObject(); | |
| dest[dlo.property] = discr; | |
| // defaultLayout.property can be undefined, but this is allowed by buffer-layout | |
| // eslint-disable-next-line @typescript-eslint/no-non-null-assertion | |
| dest[defaultLayout.property] = defaultLayout.decode(b, offset + contentOffset); | |
| } | |
| else { | |
| dest = clo.decode(b, offset); | |
| } | |
| return dest; | |
| } | |
| /** Implement {@link Layout#encode|encode} for {@link Union}. | |
| * | |
| * This API assumes the `src` object is consistent with the union's | |
| * {@link Union#defaultLayout|default layout}. To encode variants | |
| * use the appropriate variant-specific {@link VariantLayout#encode} | |
| * method. */ | |
| encode(src, b, offset = 0) { | |
| const vlo = this.getSourceVariant(src); | |
| if (undefined === vlo) { | |
| const dlo = this.discriminator; | |
| // this.defaultLayout is not undefined when vlo is undefined | |
| // eslint-disable-next-line @typescript-eslint/no-non-null-assertion | |
| const clo = this.defaultLayout; | |
| let contentOffset = 0; | |
| if (this.usesPrefixDiscriminator) { | |
| contentOffset = dlo.layout.span; | |
| } | |
| dlo.encode(src[dlo.property], b, offset); | |
| // clo.property is not undefined when vlo is undefined | |
| // eslint-disable-next-line @typescript-eslint/no-non-null-assertion | |
| return contentOffset + clo.encode(src[clo.property], b, offset + contentOffset); | |
| } | |
| return vlo.encode(src, b, offset); | |
| } | |
| /** Register a new variant structure within a union. The newly | |
| * created variant is returned. | |
| * | |
| * @param {Number} variant - initializer for {@link | |
| * VariantLayout#variant|variant}. | |
| * | |
| * @param {Layout} layout - initializer for {@link | |
| * VariantLayout#layout|layout}. | |
| * | |
| * @param {String} property - initializer for {@link | |
| * Layout#property|property}. | |
| * | |
| * @return {VariantLayout} */ | |
| addVariant(variant, layout, property) { | |
| const rv = new VariantLayout(this, variant, layout, property); | |
| this.registry[variant] = rv; | |
| return rv; | |
| } | |
| /** | |
| * Get the layout associated with a registered variant. | |
| * | |
| * If `vb` does not produce a registered variant the function returns | |
| * `undefined`. | |
| * | |
| * @param {(Number|Uint8Array)} vb - either the variant number, or a | |
| * buffer from which the discriminator is to be read. | |
| * | |
| * @param {Number} offset - offset into `vb` for the start of the | |
| * union. Used only when `vb` is an instance of {Uint8Array}. | |
| * | |
| * @return {({VariantLayout}|undefined)} | |
| */ | |
| getVariant(vb, offset = 0) { | |
| let variant; | |
| if (vb instanceof Uint8Array) { | |
| variant = this.discriminator.decode(vb, offset); | |
| } | |
| else { | |
| variant = vb; | |
| } | |
| return this.registry[variant]; | |
| } | |
| } | |
| exports.Union = Union; | |
| /** | |
| * Represent a specific variant within a containing union. | |
| * | |
| * **NOTE** The {@link Layout#span|span} of the variant may include | |
| * the span of the {@link Union#discriminator|discriminator} used to | |
| * identify it, but values read and written using the variant strictly | |
| * conform to the content of {@link VariantLayout#layout|layout}. | |
| * | |
| * **NOTE** User code should not invoke this constructor directly. Use | |
| * the union {@link Union#addVariant|addVariant} helper method. | |
| * | |
| * @param {Union} union - initializer for {@link | |
| * VariantLayout#union|union}. | |
| * | |
| * @param {Number} variant - initializer for {@link | |
| * VariantLayout#variant|variant}. | |
| * | |
| * @param {Layout} [layout] - initializer for {@link | |
| * VariantLayout#layout|layout}. If absent the variant carries no | |
| * data. | |
| * | |
| * @param {String} [property] - initializer for {@link | |
| * Layout#property|property}. Unlike many other layouts, variant | |
| * layouts normally include a property name so they can be identified | |
| * within their containing {@link Union}. The property identifier may | |
| * be absent only if `layout` is is absent. | |
| * | |
| * @augments {Layout} | |
| */ | |
| class VariantLayout extends Layout { | |
| constructor(union, variant, layout, property) { | |
| if (!(union instanceof Union)) { | |
| throw new TypeError('union must be a Union'); | |
| } | |
| if ((!Number.isInteger(variant)) || (0 > variant)) { | |
| throw new TypeError('variant must be a (non-negative) integer'); | |
| } | |
| if (('string' === typeof layout) | |
| && (undefined === property)) { | |
| property = layout; | |
| layout = null; | |
| } | |
| if (layout) { | |
| if (!(layout instanceof Layout)) { | |
| throw new TypeError('layout must be a Layout'); | |
| } | |
| if ((null !== union.defaultLayout) | |
| && (0 <= layout.span) | |
| && (layout.span > union.defaultLayout.span)) { | |
| throw new Error('variant span exceeds span of containing union'); | |
| } | |
| if ('string' !== typeof property) { | |
| throw new TypeError('variant must have a String property'); | |
| } | |
| } | |
| let span = union.span; | |
| if (0 > union.span) { | |
| span = layout ? layout.span : 0; | |
| if ((0 <= span) && union.usesPrefixDiscriminator) { | |
| span += union.discriminator.layout.span; | |
| } | |
| } | |
| super(span, property); | |
| /** The {@link Union} to which this variant belongs. */ | |
| this.union = union; | |
| /** The unsigned integral value identifying this variant within | |
| * the {@link Union#discriminator|discriminator} of the containing | |
| * union. */ | |
| this.variant = variant; | |
| /** The {@link Layout} to be used when reading/writing the | |
| * non-discriminator part of the {@link | |
| * VariantLayout#union|union}. If `null` the variant carries no | |
| * data. */ | |
| this.layout = layout || null; | |
| } | |
| /** @override */ | |
| getSpan(b, offset = 0) { | |
| if (0 <= this.span) { | |
| /* Will be equal to the containing union span if that is not | |
| * variable. */ | |
| return this.span; | |
| } | |
| let contentOffset = 0; | |
| if (this.union.usesPrefixDiscriminator) { | |
| contentOffset = this.union.discriminator.layout.span; | |
| } | |
| /* Span is defined solely by the variant (and prefix discriminator) */ | |
| let span = 0; | |
| if (this.layout) { | |
| span = this.layout.getSpan(b, offset + contentOffset); | |
| } | |
| return contentOffset + span; | |
| } | |
| /** @override */ | |
| decode(b, offset = 0) { | |
| const dest = this.makeDestinationObject(); | |
| if (this !== this.union.getVariant(b, offset)) { | |
| throw new Error('variant mismatch'); | |
| } | |
| let contentOffset = 0; | |
| if (this.union.usesPrefixDiscriminator) { | |
| contentOffset = this.union.discriminator.layout.span; | |
| } | |
| if (this.layout) { | |
| dest[this.property] = this.layout.decode(b, offset + contentOffset); | |
| } | |
| else if (this.property) { | |
| dest[this.property] = true; | |
| } | |
| else if (this.union.usesPrefixDiscriminator) { | |
| dest[this.union.discriminator.property] = this.variant; | |
| } | |
| return dest; | |
| } | |
| /** @override */ | |
| encode(src, b, offset = 0) { | |
| let contentOffset = 0; | |
| if (this.union.usesPrefixDiscriminator) { | |
| contentOffset = this.union.discriminator.layout.span; | |
| } | |
| if (this.layout | |
| && (!Object.prototype.hasOwnProperty.call(src, this.property))) { | |
| throw new TypeError('variant lacks property ' + this.property); | |
| } | |
| this.union.discriminator.encode(this.variant, b, offset); | |
| let span = contentOffset; | |
| if (this.layout) { | |
| this.layout.encode(src[this.property], b, offset + contentOffset); | |
| span += this.layout.getSpan(b, offset + contentOffset); | |
| if ((0 <= this.union.span) | |
| && (span > this.union.span)) { | |
| throw new Error('encoded variant overruns containing union'); | |
| } | |
| } | |
| return span; | |
| } | |
| /** Delegate {@link Layout#fromArray|fromArray} to {@link | |
| * VariantLayout#layout|layout}. */ | |
| fromArray(values) { | |
| if (this.layout) { | |
| return this.layout.fromArray(values); | |
| } | |
| return undefined; | |
| } | |
| } | |
| exports.VariantLayout = VariantLayout; | |
| /** JavaScript chose to define bitwise operations as operating on | |
| * signed 32-bit values in 2's complement form, meaning any integer | |
| * with bit 31 set is going to look negative. For right shifts that's | |
| * not a problem, because `>>>` is a logical shift, but for every | |
| * other bitwise operator we have to compensate for possible negative | |
| * results. */ | |
| function fixBitwiseResult(v) { | |
| if (0 > v) { | |
| v += 0x100000000; | |
| } | |
| return v; | |
| } | |
| /** | |
| * Contain a sequence of bit fields as an unsigned integer. | |
| * | |
| * *Factory*: {@link module:Layout.bits|bits} | |
| * | |
| * This is a container element; within it there are {@link BitField} | |
| * instances that provide the extracted properties. The container | |
| * simply defines the aggregate representation and its bit ordering. | |
| * The representation is an object containing properties with numeric | |
| * or {@link Boolean} values. | |
| * | |
| * {@link BitField}s are added with the {@link | |
| * BitStructure#addField|addField} and {@link | |
| * BitStructure#addBoolean|addBoolean} methods. | |
| * @param {Layout} word - initializer for {@link | |
| * BitStructure#word|word}. The parameter must be an instance of | |
| * {@link UInt} (or {@link UIntBE}) that is no more than 4 bytes wide. | |
| * | |
| * @param {bool} [msb] - `true` if the bit numbering starts at the | |
| * most significant bit of the containing word; `false` (default) if | |
| * it starts at the least significant bit of the containing word. If | |
| * the parameter at this position is a string and `property` is | |
| * `undefined` the value of this argument will instead be used as the | |
| * value of `property`. | |
| * | |
| * @param {string} [property] - initializer for {@link | |
| * Layout#property|property}. | |
| * | |
| * @augments {Layout} | |
| */ | |
| class BitStructure extends Layout { | |
| constructor(word, msb, property) { | |
| if (!((word instanceof UInt) | |
| || (word instanceof UIntBE))) { | |
| throw new TypeError('word must be a UInt or UIntBE layout'); | |
| } | |
| if (('string' === typeof msb) | |
| && (undefined === property)) { | |
| property = msb; | |
| msb = false; | |
| } | |
| if (4 < word.span) { | |
| throw new RangeError('word cannot exceed 32 bits'); | |
| } | |
| super(word.span, property); | |
| /** The layout used for the packed value. {@link BitField} | |
| * instances are packed sequentially depending on {@link | |
| * BitStructure#msb|msb}. */ | |
| this.word = word; | |
| /** Whether the bit sequences are packed starting at the most | |
| * significant bit growing down (`true`), or the least significant | |
| * bit growing up (`false`). | |
| * | |
| * **NOTE** Regardless of this value, the least significant bit of | |
| * any {@link BitField} value is the least significant bit of the | |
| * corresponding section of the packed value. */ | |
| this.msb = !!msb; | |
| /** The sequence of {@link BitField} layouts that comprise the | |
| * packed structure. | |
| * | |
| * **NOTE** The array remains mutable to allow fields to be {@link | |
| * BitStructure#addField|added} after construction. Users should | |
| * not manipulate the content of this property.*/ | |
| this.fields = []; | |
| /* Storage for the value. Capture a variable instead of using an | |
| * instance property because we don't want anything to change the | |
| * value without going through the mutator. */ | |
| let value = 0; | |
| this._packedSetValue = function (v) { | |
| value = fixBitwiseResult(v); | |
| return this; | |
| }; | |
| this._packedGetValue = function () { | |
| return value; | |
| }; | |
| } | |
| /** @override */ | |
| decode(b, offset = 0) { | |
| const dest = this.makeDestinationObject(); | |
| const value = this.word.decode(b, offset); | |
| this._packedSetValue(value); | |
| for (const fd of this.fields) { | |
| if (undefined !== fd.property) { | |
| dest[fd.property] = fd.decode(b); | |
| } | |
| } | |
| return dest; | |
| } | |
| /** Implement {@link Layout#encode|encode} for {@link BitStructure}. | |
| * | |
| * If `src` is missing a property for a member with a defined {@link | |
| * Layout#property|property} the corresponding region of the packed | |
| * value is left unmodified. Unused bits are also left unmodified. */ | |
| encode(src, b, offset = 0) { | |
| const value = this.word.decode(b, offset); | |
| this._packedSetValue(value); | |
| for (const fd of this.fields) { | |
| if (undefined !== fd.property) { | |
| const fv = src[fd.property]; | |
| if (undefined !== fv) { | |
| fd.encode(fv); | |
| } | |
| } | |
| } | |
| return this.word.encode(this._packedGetValue(), b, offset); | |
| } | |
| /** Register a new bitfield with a containing bit structure. The | |
| * resulting bitfield is returned. | |
| * | |
| * @param {Number} bits - initializer for {@link BitField#bits|bits}. | |
| * | |
| * @param {string} property - initializer for {@link | |
| * Layout#property|property}. | |
| * | |
| * @return {BitField} */ | |
| addField(bits, property) { | |
| const bf = new BitField(this, bits, property); | |
| this.fields.push(bf); | |
| return bf; | |
| } | |
| /** As with {@link BitStructure#addField|addField} for single-bit | |
| * fields with `boolean` value representation. | |
| * | |
| * @param {string} property - initializer for {@link | |
| * Layout#property|property}. | |
| * | |
| * @return {Boolean} */ | |
| // `Boolean` conflicts with the native primitive type | |
| // eslint-disable-next-line @typescript-eslint/ban-types | |
| addBoolean(property) { | |
| // This is my Boolean, not the Javascript one. | |
| const bf = new Boolean(this, property); | |
| this.fields.push(bf); | |
| return bf; | |
| } | |
| /** | |
| * Get access to the bit field for a given property. | |
| * | |
| * @param {String} property - the bit field of interest. | |
| * | |
| * @return {BitField} - the field associated with `property`, or | |
| * undefined if there is no such property. | |
| */ | |
| fieldFor(property) { | |
| if ('string' !== typeof property) { | |
| throw new TypeError('property must be string'); | |
| } | |
| for (const fd of this.fields) { | |
| if (fd.property === property) { | |
| return fd; | |
| } | |
| } | |
| return undefined; | |
| } | |
| } | |
| exports.BitStructure = BitStructure; | |
| /** | |
| * Represent a sequence of bits within a {@link BitStructure}. | |
| * | |
| * All bit field values are represented as unsigned integers. | |
| * | |
| * **NOTE** User code should not invoke this constructor directly. | |
| * Use the container {@link BitStructure#addField|addField} helper | |
| * method. | |
| * | |
| * **NOTE** BitField instances are not instances of {@link Layout} | |
| * since {@link Layout#span|span} measures 8-bit units. | |
| * | |
| * @param {BitStructure} container - initializer for {@link | |
| * BitField#container|container}. | |
| * | |
| * @param {Number} bits - initializer for {@link BitField#bits|bits}. | |
| * | |
| * @param {string} [property] - initializer for {@link | |
| * Layout#property|property}. | |
| */ | |
| class BitField { | |
| constructor(container, bits, property) { | |
| if (!(container instanceof BitStructure)) { | |
| throw new TypeError('container must be a BitStructure'); | |
| } | |
| if ((!Number.isInteger(bits)) || (0 >= bits)) { | |
| throw new TypeError('bits must be positive integer'); | |
| } | |
| const totalBits = 8 * container.span; | |
| const usedBits = container.fields.reduce((sum, fd) => sum + fd.bits, 0); | |
| if ((bits + usedBits) > totalBits) { | |
| throw new Error('bits too long for span remainder (' | |
| + (totalBits - usedBits) + ' of ' | |
| + totalBits + ' remain)'); | |
| } | |
| /** The {@link BitStructure} instance to which this bit field | |
| * belongs. */ | |
| this.container = container; | |
| /** The span of this value in bits. */ | |
| this.bits = bits; | |
| /** A mask of {@link BitField#bits|bits} bits isolating value bits | |
| * that fit within the field. | |
| * | |
| * That is, it masks a value that has not yet been shifted into | |
| * position within its containing packed integer. */ | |
| this.valueMask = (1 << bits) - 1; | |
| if (32 === bits) { // shifted value out of range | |
| this.valueMask = 0xFFFFFFFF; | |
| } | |
| /** The offset of the value within the containing packed unsigned | |
| * integer. The least significant bit of the packed value is at | |
| * offset zero, regardless of bit ordering used. */ | |
| this.start = usedBits; | |
| if (this.container.msb) { | |
| this.start = totalBits - usedBits - bits; | |
| } | |
| /** A mask of {@link BitField#bits|bits} isolating the field value | |
| * within the containing packed unsigned integer. */ | |
| this.wordMask = fixBitwiseResult(this.valueMask << this.start); | |
| /** The property name used when this bitfield is represented in an | |
| * Object. | |
| * | |
| * Intended to be functionally equivalent to {@link | |
| * Layout#property}. | |
| * | |
| * If left undefined the corresponding span of bits will be | |
| * treated as padding: it will not be mutated by {@link | |
| * Layout#encode|encode} nor represented as a property in the | |
| * decoded Object. */ | |
| this.property = property; | |
| } | |
| /** Store a value into the corresponding subsequence of the containing | |
| * bit field. */ | |
| decode(b, offset) { | |
| const word = this.container._packedGetValue(); | |
| const wordValue = fixBitwiseResult(word & this.wordMask); | |
| const value = wordValue >>> this.start; | |
| return value; | |
| } | |
| /** Store a value into the corresponding subsequence of the containing | |
| * bit field. | |
| * | |
| * **NOTE** This is not a specialization of {@link | |
| * Layout#encode|Layout.encode} and there is no return value. */ | |
| encode(value) { | |
| if ('number' !== typeof value | |
| || !Number.isInteger(value) | |
| || (value !== fixBitwiseResult(value & this.valueMask))) { | |
| throw new TypeError(nameWithProperty('BitField.encode', this) | |
| + ' value must be integer not exceeding ' + this.valueMask); | |
| } | |
| const word = this.container._packedGetValue(); | |
| const wordValue = fixBitwiseResult(value << this.start); | |
| this.container._packedSetValue(fixBitwiseResult(word & ~this.wordMask) | |
| | wordValue); | |
| } | |
| } | |
| exports.BitField = BitField; | |
| /** | |
| * Represent a single bit within a {@link BitStructure} as a | |
| * JavaScript boolean. | |
| * | |
| * **NOTE** User code should not invoke this constructor directly. | |
| * Use the container {@link BitStructure#addBoolean|addBoolean} helper | |
| * method. | |
| * | |
| * @param {BitStructure} container - initializer for {@link | |
| * BitField#container|container}. | |
| * | |
| * @param {string} [property] - initializer for {@link | |
| * Layout#property|property}. | |
| * | |
| * @augments {BitField} | |
| */ | |
| /* eslint-disable no-extend-native */ | |
| class Boolean extends BitField { | |
| constructor(container, property) { | |
| super(container, 1, property); | |
| } | |
| /** Override {@link BitField#decode|decode} for {@link Boolean|Boolean}. | |
| * | |
| * @returns {boolean} */ | |
| decode(b, offset) { | |
| return !!super.decode(b, offset); | |
| } | |
| /** @override */ | |
| encode(value) { | |
| if ('boolean' === typeof value) { | |
| // BitField requires integer values | |
| value = +value; | |
| } | |
| super.encode(value); | |
| } | |
| } | |
| exports.Boolean = Boolean; | |
| /* eslint-enable no-extend-native */ | |
| /** | |
| * Contain a fixed-length block of arbitrary data, represented as a | |
| * Uint8Array. | |
| * | |
| * *Factory*: {@link module:Layout.blob|blob} | |
| * | |
| * @param {(Number|ExternalLayout)} length - initializes {@link | |
| * Blob#length|length}. | |
| * | |
| * @param {String} [property] - initializer for {@link | |
| * Layout#property|property}. | |
| * | |
| * @augments {Layout} | |
| */ | |
| class Blob extends Layout { | |
| constructor(length, property) { | |
| if (!(((length instanceof ExternalLayout) && length.isCount()) | |
| || (Number.isInteger(length) && (0 <= length)))) { | |
| throw new TypeError('length must be positive integer ' | |
| + 'or an unsigned integer ExternalLayout'); | |
| } | |
| let span = -1; | |
| if (!(length instanceof ExternalLayout)) { | |
| span = length; | |
| } | |
| super(span, property); | |
| /** The number of bytes in the blob. | |
| * | |
| * This may be a non-negative integer, or an instance of {@link | |
| * ExternalLayout} that satisfies {@link | |
| * ExternalLayout#isCount|isCount()}. */ | |
| this.length = length; | |
| } | |
| /** @override */ | |
| getSpan(b, offset) { | |
| let span = this.span; | |
| if (0 > span) { | |
| span = this.length.decode(b, offset); | |
| } | |
| return span; | |
| } | |
| /** @override */ | |
| decode(b, offset = 0) { | |
| let span = this.span; | |
| if (0 > span) { | |
| span = this.length.decode(b, offset); | |
| } | |
| return uint8ArrayToBuffer(b).slice(offset, offset + span); | |
| } | |
| /** Implement {@link Layout#encode|encode} for {@link Blob}. | |
| * | |
| * **NOTE** If {@link Layout#count|count} is an instance of {@link | |
| * ExternalLayout} then the length of `src` will be encoded as the | |
| * count after `src` is encoded. */ | |
| encode(src, b, offset) { | |
| let span = this.length; | |
| if (this.length instanceof ExternalLayout) { | |
| span = src.length; | |
| } | |
| if (!(src instanceof Uint8Array && span === src.length)) { | |
| throw new TypeError(nameWithProperty('Blob.encode', this) | |
| + ' requires (length ' + span + ') Uint8Array as src'); | |
| } | |
| if ((offset + span) > b.length) { | |
| throw new RangeError('encoding overruns Uint8Array'); | |
| } | |
| const srcBuffer = uint8ArrayToBuffer(src); | |
| uint8ArrayToBuffer(b).write(srcBuffer.toString('hex'), offset, span, 'hex'); | |
| if (this.length instanceof ExternalLayout) { | |
| this.length.encode(span, b, offset); | |
| } | |
| return span; | |
| } | |
| } | |
| exports.Blob = Blob; | |
| /** | |
| * Contain a `NUL`-terminated UTF8 string. | |
| * | |
| * *Factory*: {@link module:Layout.cstr|cstr} | |
| * | |
| * **NOTE** Any UTF8 string that incorporates a zero-valued byte will | |
| * not be correctly decoded by this layout. | |
| * | |
| * @param {String} [property] - initializer for {@link | |
| * Layout#property|property}. | |
| * | |
| * @augments {Layout} | |
| */ | |
| class CString extends Layout { | |
| constructor(property) { | |
| super(-1, property); | |
| } | |
| /** @override */ | |
| getSpan(b, offset = 0) { | |
| checkUint8Array(b); | |
| let idx = offset; | |
| while ((idx < b.length) && (0 !== b[idx])) { | |
| idx += 1; | |
| } | |
| return 1 + idx - offset; | |
| } | |
| /** @override */ | |
| decode(b, offset = 0) { | |
| const span = this.getSpan(b, offset); | |
| return uint8ArrayToBuffer(b).slice(offset, offset + span - 1).toString('utf-8'); | |
| } | |
| /** @override */ | |
| encode(src, b, offset = 0) { | |
| /* Must force this to a string, lest it be a number and the | |
| * "utf8-encoding" below actually allocate a buffer of length | |
| * src */ | |
| if ('string' !== typeof src) { | |
| src = String(src); | |
| } | |
| const srcb = buffer_1.Buffer.from(src, 'utf8'); | |
| const span = srcb.length; | |
| if ((offset + span) > b.length) { | |
| throw new RangeError('encoding overruns Buffer'); | |
| } | |
| const buffer = uint8ArrayToBuffer(b); | |
| srcb.copy(buffer, offset); | |
| buffer[offset + span] = 0; | |
| return span + 1; | |
| } | |
| } | |
| exports.CString = CString; | |
| /** | |
| * Contain a UTF8 string with implicit length. | |
| * | |
| * *Factory*: {@link module:Layout.utf8|utf8} | |
| * | |
| * **NOTE** Because the length is implicit in the size of the buffer | |
| * this layout should be used only in isolation, or in a situation | |
| * where the length can be expressed by operating on a slice of the | |
| * containing buffer. | |
| * | |
| * @param {Number} [maxSpan] - the maximum length allowed for encoded | |
| * string content. If not provided there is no bound on the allowed | |
| * content. | |
| * | |
| * @param {String} [property] - initializer for {@link | |
| * Layout#property|property}. | |
| * | |
| * @augments {Layout} | |
| */ | |
| class UTF8 extends Layout { | |
| constructor(maxSpan, property) { | |
| if (('string' === typeof maxSpan) && (undefined === property)) { | |
| property = maxSpan; | |
| maxSpan = undefined; | |
| } | |
| if (undefined === maxSpan) { | |
| maxSpan = -1; | |
| } | |
| else if (!Number.isInteger(maxSpan)) { | |
| throw new TypeError('maxSpan must be an integer'); | |
| } | |
| super(-1, property); | |
| /** The maximum span of the layout in bytes. | |
| * | |
| * Positive values are generally expected. Zero is abnormal. | |
| * Attempts to encode or decode a value that exceeds this length | |
| * will throw a `RangeError`. | |
| * | |
| * A negative value indicates that there is no bound on the length | |
| * of the content. */ | |
| this.maxSpan = maxSpan; | |
| } | |
| /** @override */ | |
| getSpan(b, offset = 0) { | |
| checkUint8Array(b); | |
| return b.length - offset; | |
| } | |
| /** @override */ | |
| decode(b, offset = 0) { | |
| const span = this.getSpan(b, offset); | |
| if ((0 <= this.maxSpan) | |
| && (this.maxSpan < span)) { | |
| throw new RangeError('text length exceeds maxSpan'); | |
| } | |
| return uint8ArrayToBuffer(b).slice(offset, offset + span).toString('utf-8'); | |
| } | |
| /** @override */ | |
| encode(src, b, offset = 0) { | |
| /* Must force this to a string, lest it be a number and the | |
| * "utf8-encoding" below actually allocate a buffer of length | |
| * src */ | |
| if ('string' !== typeof src) { | |
| src = String(src); | |
| } | |
| const srcb = buffer_1.Buffer.from(src, 'utf8'); | |
| const span = srcb.length; | |
| if ((0 <= this.maxSpan) | |
| && (this.maxSpan < span)) { | |
| throw new RangeError('text length exceeds maxSpan'); | |
| } | |
| if ((offset + span) > b.length) { | |
| throw new RangeError('encoding overruns Buffer'); | |
| } | |
| srcb.copy(uint8ArrayToBuffer(b), offset); | |
| return span; | |
| } | |
| } | |
| exports.UTF8 = UTF8; | |
| /** | |
| * Contain a constant value. | |
| * | |
| * This layout may be used in cases where a JavaScript value can be | |
| * inferred without an expression in the binary encoding. An example | |
| * would be a {@link VariantLayout|variant layout} where the content | |
| * is implied by the union {@link Union#discriminator|discriminator}. | |
| * | |
| * @param {Object|Number|String} value - initializer for {@link | |
| * Constant#value|value}. If the value is an object (or array) and | |
| * the application intends the object to remain unchanged regardless | |
| * of what is done to values decoded by this layout, the value should | |
| * be frozen prior passing it to this constructor. | |
| * | |
| * @param {String} [property] - initializer for {@link | |
| * Layout#property|property}. | |
| * | |
| * @augments {Layout} | |
| */ | |
| class Constant extends Layout { | |
| constructor(value, property) { | |
| super(0, property); | |
| /** The value produced by this constant when the layout is {@link | |
| * Constant#decode|decoded}. | |
| * | |
| * Any JavaScript value including `null` and `undefined` is | |
| * permitted. | |
| * | |
| * **WARNING** If `value` passed in the constructor was not | |
| * frozen, it is possible for users of decoded values to change | |
| * the content of the value. */ | |
| this.value = value; | |
| } | |
| /** @override */ | |
| decode(b, offset) { | |
| return this.value; | |
| } | |
| /** @override */ | |
| encode(src, b, offset) { | |
| /* Constants take no space */ | |
| return 0; | |
| } | |
| } | |
| exports.Constant = Constant; | |
| /** Factory for {@link GreedyCount}. */ | |
| exports.greedy = ((elementSpan, property) => new GreedyCount(elementSpan, property)); | |
| /** Factory for {@link OffsetLayout}. */ | |
| exports.offset = ((layout, offset, property) => new OffsetLayout(layout, offset, property)); | |
| /** Factory for {@link UInt|unsigned int layouts} spanning one | |
| * byte. */ | |
| exports.u8 = ((property) => new UInt(1, property)); | |
| /** Factory for {@link UInt|little-endian unsigned int layouts} | |
| * spanning two bytes. */ | |
| exports.u16 = ((property) => new UInt(2, property)); | |
| /** Factory for {@link UInt|little-endian unsigned int layouts} | |
| * spanning three bytes. */ | |
| exports.u24 = ((property) => new UInt(3, property)); | |
| /** Factory for {@link UInt|little-endian unsigned int layouts} | |
| * spanning four bytes. */ | |
| exports.u32 = ((property) => new UInt(4, property)); | |
| /** Factory for {@link UInt|little-endian unsigned int layouts} | |
| * spanning five bytes. */ | |
| exports.u40 = ((property) => new UInt(5, property)); | |
| /** Factory for {@link UInt|little-endian unsigned int layouts} | |
| * spanning six bytes. */ | |
| exports.u48 = ((property) => new UInt(6, property)); | |
| /** Factory for {@link NearUInt64|little-endian unsigned int | |
| * layouts} interpreted as Numbers. */ | |
| exports.nu64 = ((property) => new NearUInt64(property)); | |
| /** Factory for {@link UInt|big-endian unsigned int layouts} | |
| * spanning two bytes. */ | |
| exports.u16be = ((property) => new UIntBE(2, property)); | |
| /** Factory for {@link UInt|big-endian unsigned int layouts} | |
| * spanning three bytes. */ | |
| exports.u24be = ((property) => new UIntBE(3, property)); | |
| /** Factory for {@link UInt|big-endian unsigned int layouts} | |
| * spanning four bytes. */ | |
| exports.u32be = ((property) => new UIntBE(4, property)); | |
| /** Factory for {@link UInt|big-endian unsigned int layouts} | |
| * spanning five bytes. */ | |
| exports.u40be = ((property) => new UIntBE(5, property)); | |
| /** Factory for {@link UInt|big-endian unsigned int layouts} | |
| * spanning six bytes. */ | |
| exports.u48be = ((property) => new UIntBE(6, property)); | |
| /** Factory for {@link NearUInt64BE|big-endian unsigned int | |
| * layouts} interpreted as Numbers. */ | |
| exports.nu64be = ((property) => new NearUInt64BE(property)); | |
| /** Factory for {@link Int|signed int layouts} spanning one | |
| * byte. */ | |
| exports.s8 = ((property) => new Int(1, property)); | |
| /** Factory for {@link Int|little-endian signed int layouts} | |
| * spanning two bytes. */ | |
| exports.s16 = ((property) => new Int(2, property)); | |
| /** Factory for {@link Int|little-endian signed int layouts} | |
| * spanning three bytes. */ | |
| exports.s24 = ((property) => new Int(3, property)); | |
| /** Factory for {@link Int|little-endian signed int layouts} | |
| * spanning four bytes. */ | |
| exports.s32 = ((property) => new Int(4, property)); | |
| /** Factory for {@link Int|little-endian signed int layouts} | |
| * spanning five bytes. */ | |
| exports.s40 = ((property) => new Int(5, property)); | |
| /** Factory for {@link Int|little-endian signed int layouts} | |
| * spanning six bytes. */ | |
| exports.s48 = ((property) => new Int(6, property)); | |
| /** Factory for {@link NearInt64|little-endian signed int layouts} | |
| * interpreted as Numbers. */ | |
| exports.ns64 = ((property) => new NearInt64(property)); | |
| /** Factory for {@link Int|big-endian signed int layouts} | |
| * spanning two bytes. */ | |
| exports.s16be = ((property) => new IntBE(2, property)); | |
| /** Factory for {@link Int|big-endian signed int layouts} | |
| * spanning three bytes. */ | |
| exports.s24be = ((property) => new IntBE(3, property)); | |
| /** Factory for {@link Int|big-endian signed int layouts} | |
| * spanning four bytes. */ | |
| exports.s32be = ((property) => new IntBE(4, property)); | |
| /** Factory for {@link Int|big-endian signed int layouts} | |
| * spanning five bytes. */ | |
| exports.s40be = ((property) => new IntBE(5, property)); | |
| /** Factory for {@link Int|big-endian signed int layouts} | |
| * spanning six bytes. */ | |
| exports.s48be = ((property) => new IntBE(6, property)); | |
| /** Factory for {@link NearInt64BE|big-endian signed int layouts} | |
| * interpreted as Numbers. */ | |
| exports.ns64be = ((property) => new NearInt64BE(property)); | |
| /** Factory for {@link Float|little-endian 32-bit floating point} values. */ | |
| exports.f32 = ((property) => new Float(property)); | |
| /** Factory for {@link FloatBE|big-endian 32-bit floating point} values. */ | |
| exports.f32be = ((property) => new FloatBE(property)); | |
| /** Factory for {@link Double|little-endian 64-bit floating point} values. */ | |
| exports.f64 = ((property) => new Double(property)); | |
| /** Factory for {@link DoubleBE|big-endian 64-bit floating point} values. */ | |
| exports.f64be = ((property) => new DoubleBE(property)); | |
| /** Factory for {@link Structure} values. */ | |
| exports.struct = ((fields, property, decodePrefixes) => new Structure(fields, property, decodePrefixes)); | |
| /** Factory for {@link BitStructure} values. */ | |
| exports.bits = ((word, msb, property) => new BitStructure(word, msb, property)); | |
| /** Factory for {@link Sequence} values. */ | |
| exports.seq = ((elementLayout, count, property) => new Sequence(elementLayout, count, property)); | |
| /** Factory for {@link Union} values. */ | |
| exports.union = ((discr, defaultLayout, property) => new Union(discr, defaultLayout, property)); | |
| /** Factory for {@link UnionLayoutDiscriminator} values. */ | |
| exports.unionLayoutDiscriminator = ((layout, property) => new UnionLayoutDiscriminator(layout, property)); | |
| /** Factory for {@link Blob} values. */ | |
| exports.blob = ((length, property) => new Blob(length, property)); | |
| /** Factory for {@link CString} values. */ | |
| exports.cstr = ((property) => new CString(property)); | |
| /** Factory for {@link UTF8} values. */ | |
| exports.utf8 = ((maxSpan, property) => new UTF8(maxSpan, property)); | |
| /** Factory for {@link Constant} values. */ | |
| exports.constant = ((value, property) => new Constant(value, property)); | |
| //# sourceMappingURL=Layout.js.map |