From 28ced92378312050aeef7f5ae61ce275a639f8c6 Mon Sep 17 00:00:00 2001 From: Adrian Vogelsgesang Date: Wed, 13 May 2020 01:43:43 +0200 Subject: [PATCH 01/20] Port JavaScript runtime to TypeScript This commit ports the JavaScript runtime to TypeScript. The main work of this commit is to port the KaitaiStream.js to KaitaiStream.ts. This was pretty much straight search-and-replace task to map the previously prototype-based class definition to a TypeScript class definition. Only in one case, the mapping was not straigtforward: The deprecated `readBitsInt` was previously set to the exact same function instance as `readBitsIntBE`. This is not possible in TypeScript. Instead, this `readBitsInt` is now implemented as a separate function which simply forwards all calls to `readBitsIntBE`. This commit does not yet add types to the class member and function arguments. I am planning to add strict types in a follow-up commit. To compile the TypeScript file to a JavaScript file, this commit uses rollup.js. I would have prefered to directly use the TypeScript compiler, however the TypeScript compiler's UMD loader is missing the "global" fallback in case no module loader is available. Usually, I would put the "Kaitaistream.js" into the .gitignore because it is a generated file. However, doing so would break the existing super-project structure, because the `run-javascript` script inside the `tests` repository does not actually build the JavaScript runtime but only links it --- .gitignore | 4 + .npmignore | 2 + KaitaiStream.js | 1654 ++++++++++++++++++++++----------------------- KaitaiStream.ts | 850 +++++++++++++++++++++++ index.d.ts | 5 + package-lock.json | 93 ++- package.json | 14 +- rollup.config.js | 11 + tsconfig.json | 68 ++ 9 files changed, 1859 insertions(+), 842 deletions(-) create mode 100644 .gitignore create mode 100644 KaitaiStream.ts create mode 100644 index.d.ts create mode 100644 rollup.config.js create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3398fbc --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +node_modules + +# Generated by Typescript compiler +KaitaiStream.d.ts diff --git a/.npmignore b/.npmignore index 2cf6a27..0361ed9 100644 --- a/.npmignore +++ b/.npmignore @@ -1,2 +1,4 @@ .npmignore .travis.yml +rollup.config.js +KaitaiStream.ts diff --git a/KaitaiStream.js b/KaitaiStream.js index 1af93e1..9c75462 100644 --- a/KaitaiStream.js +++ b/KaitaiStream.js @@ -1,842 +1,816 @@ -// -*- mode: js; js-indent-level: 2; -*- -'use strict'; -(function (root, factory) { - if (typeof define === 'function' && define.amd) { - define([], factory); - } else if (typeof module === 'object' && module.exports) { - module.exports = factory(); - } else { - root.KaitaiStream = factory(); - } -}(this, function () { - -/** - KaitaiStream is an implementation of Kaitai Struct API for JavaScript. - Based on DataStream - https://github.com/kig/DataStream.js - - @param {ArrayBuffer} arrayBuffer ArrayBuffer to read from. - @param {?Number} byteOffset Offset from arrayBuffer beginning for the KaitaiStream. - */ -var KaitaiStream = function(arrayBuffer, byteOffset) { - this._byteOffset = byteOffset || 0; - if (arrayBuffer instanceof ArrayBuffer) { - this.buffer = arrayBuffer; - } else if (typeof arrayBuffer == "object") { - this.dataView = arrayBuffer; - if (byteOffset) { - this._byteOffset += byteOffset; - } - } else { - this.buffer = new ArrayBuffer(arrayBuffer || 1); - } - this.pos = 0; - this.alignToByte(); -}; - - -KaitaiStream.prototype = {}; - -/** - Dependency configuration data. Holds urls for (optional) dynamic loading - of code dependencies from a remote server. For use by (static) processing functions. - - Caller should the supported keys to the asset urls as needed. - NOTE: `depUrls` is a static property of KaitaiStream (the factory),like the various - processing functions. It is NOT part of the prototype of instances. - @type {Object} - */ -KaitaiStream.depUrls = { - // processZlib uses this and expected a link to a copy of pako. - // specifically the pako_inflate.min.js script at: - // https://raw.githubusercontent.com/nodeca/pako/master/dist/pako_inflate.min.js - zlib: undefined -}; - -/** - Virtual byte length of the KaitaiStream backing buffer. - Updated to be max of original buffer size and last written size. - If dynamicSize is false is set to buffer size. - @type {number} - */ -KaitaiStream.prototype._byteLength = 0; - -/** - Set/get the backing ArrayBuffer of the KaitaiStream object. - The setter updates the DataView to point to the new buffer. - @type {Object} - */ -Object.defineProperty(KaitaiStream.prototype, 'buffer', - { get: function() { - this._trimAlloc(); - return this._buffer; - }, - set: function(v) { - this._buffer = v; - this._dataView = new DataView(this._buffer, this._byteOffset); - this._byteLength = this._buffer.byteLength; - } }); - -/** - Set/get the byteOffset of the KaitaiStream object. - The setter updates the DataView to point to the new byteOffset. - @type {number} - */ -Object.defineProperty(KaitaiStream.prototype, 'byteOffset', - { get: function() { - return this._byteOffset; - }, - set: function(v) { - this._byteOffset = v; - this._dataView = new DataView(this._buffer, this._byteOffset); - this._byteLength = this._buffer.byteLength; - } }); - -/** - Set/get the backing DataView of the KaitaiStream object. - The setter updates the buffer and byteOffset to point to the DataView values. - @type {Object} - */ -Object.defineProperty(KaitaiStream.prototype, 'dataView', - { get: function() { - return this._dataView; - }, - set: function(v) { - this._byteOffset = v.byteOffset; - this._buffer = v.buffer; - this._dataView = new DataView(this._buffer, this._byteOffset); - this._byteLength = this._byteOffset + v.byteLength; - } }); - -/** - Internal function to trim the KaitaiStream buffer when required. - Used for stripping out the extra bytes from the backing buffer when - the virtual byteLength is smaller than the buffer byteLength (happens after - growing the buffer with writes and not filling the extra space completely). - - @return {null} - */ -KaitaiStream.prototype._trimAlloc = function() { - if (this._byteLength === this._buffer.byteLength) { - return; - } - var buf = new ArrayBuffer(this._byteLength); - var dst = new Uint8Array(buf); - var src = new Uint8Array(this._buffer, 0, dst.length); - dst.set(src); - this.buffer = buf; -}; - -// ======================================================================== -// Stream positioning -// ======================================================================== - -/** - Returns true if the KaitaiStream seek pointer is at the end of buffer and - there's no more data to read. - - @return {boolean} True if the seek pointer is at the end of the buffer. - */ -KaitaiStream.prototype.isEof = function() { - return this.pos >= this.size && this.bitsLeft === 0; -}; - -/** - Sets the KaitaiStream read/write position to given position. - Clamps between 0 and KaitaiStream length. - - @param {number} pos Position to seek to. - @return {null} - */ -KaitaiStream.prototype.seek = function(pos) { - var npos = Math.max(0, Math.min(this.size, pos)); - this.pos = (isNaN(npos) || !isFinite(npos)) ? 0 : npos; -}; - -/** - Returns the byte length of the KaitaiStream object. - @type {number} - */ -Object.defineProperty(KaitaiStream.prototype, 'size', - { get: function() { - return this._byteLength - this._byteOffset; - }}); - -// ======================================================================== -// Integer numbers -// ======================================================================== - -// ------------------------------------------------------------------------ -// Signed -// ------------------------------------------------------------------------ - -/** - Reads an 8-bit signed int from the stream. - @return {number} The read number. - */ -KaitaiStream.prototype.readS1 = function() { - this.ensureBytesLeft(1); - var v = this._dataView.getInt8(this.pos); - this.pos += 1; - return v; -}; - -// ........................................................................ -// Big-endian -// ........................................................................ - -/** - Reads a 16-bit big-endian signed int from the stream. - @return {number} The read number. - */ -KaitaiStream.prototype.readS2be = function() { - this.ensureBytesLeft(2); - var v = this._dataView.getInt16(this.pos); - this.pos += 2; - return v; -}; - -/** - Reads a 32-bit big-endian signed int from the stream. - @return {number} The read number. - */ -KaitaiStream.prototype.readS4be = function() { - this.ensureBytesLeft(4); - var v = this._dataView.getInt32(this.pos); - this.pos += 4; - return v; -}; - -/** - Reads a 64-bit big-endian unsigned int from the stream. Note that - JavaScript does not support 64-bit integers natively, so it will - automatically upgrade internal representation to use IEEE 754 - double precision float. - @return {number} The read number. - */ -KaitaiStream.prototype.readS8be = function() { - this.ensureBytesLeft(8); - var v1 = this.readU4be(); - var v2 = this.readU4be(); - - if ((v1 & 0x80000000) !== 0) { - // negative number - return -(0x100000000 * (v1 ^ 0xffffffff) + (v2 ^ 0xffffffff)) - 1; - } else { - return 0x100000000 * v1 + v2; - } -}; - -// ........................................................................ -// Little-endian -// ........................................................................ - -/** - Reads a 16-bit little-endian signed int from the stream. - @return {number} The read number. - */ -KaitaiStream.prototype.readS2le = function() { - this.ensureBytesLeft(2); - var v = this._dataView.getInt16(this.pos, true); - this.pos += 2; - return v; -}; - -/** - Reads a 32-bit little-endian signed int from the stream. - @return {number} The read number. - */ -KaitaiStream.prototype.readS4le = function() { - this.ensureBytesLeft(4); - var v = this._dataView.getInt32(this.pos, true); - this.pos += 4; - return v; -}; - -/** - Reads a 64-bit little-endian unsigned int from the stream. Note that - JavaScript does not support 64-bit integers natively, so it will - automatically upgrade internal representation to use IEEE 754 - double precision float. - @return {number} The read number. - */ -KaitaiStream.prototype.readS8le = function() { - this.ensureBytesLeft(8); - var v1 = this.readU4le(); - var v2 = this.readU4le(); - - if ((v2 & 0x80000000) !== 0) { - // negative number - return -(0x100000000 * (v2 ^ 0xffffffff) + (v1 ^ 0xffffffff)) - 1; - } else { - return 0x100000000 * v2 + v1; - } -}; - -// ------------------------------------------------------------------------ -// Unsigned -// ------------------------------------------------------------------------ - -/** - Reads an 8-bit unsigned int from the stream. - @return {number} The read number. - */ -KaitaiStream.prototype.readU1 = function() { - this.ensureBytesLeft(1); - var v = this._dataView.getUint8(this.pos); - this.pos += 1; - return v; -}; - -// ........................................................................ -// Big-endian -// ........................................................................ - -/** - Reads a 16-bit big-endian unsigned int from the stream. - @return {number} The read number. - */ -KaitaiStream.prototype.readU2be = function() { - this.ensureBytesLeft(2); - var v = this._dataView.getUint16(this.pos); - this.pos += 2; - return v; -}; - -/** - Reads a 32-bit big-endian unsigned int from the stream. - @return {number} The read number. - */ -KaitaiStream.prototype.readU4be = function() { - this.ensureBytesLeft(4); - var v = this._dataView.getUint32(this.pos); - this.pos += 4; - return v; -}; - -/** - Reads a 64-bit big-endian unsigned int from the stream. Note that - JavaScript does not support 64-bit integers natively, so it will - automatically upgrade internal representation to use IEEE 754 - double precision float. - @return {number} The read number. - */ -KaitaiStream.prototype.readU8be = function() { - this.ensureBytesLeft(8); - var v1 = this.readU4be(); - var v2 = this.readU4be(); - return 0x100000000 * v1 + v2; -}; - -// ........................................................................ -// Little-endian -// ........................................................................ - -/** - Reads a 16-bit little-endian unsigned int from the stream. - @return {number} The read number. - */ -KaitaiStream.prototype.readU2le = function() { - this.ensureBytesLeft(2); - var v = this._dataView.getUint16(this.pos, true); - this.pos += 2; - return v; -}; - -/** - Reads a 32-bit little-endian unsigned int from the stream. - @return {number} The read number. - */ -KaitaiStream.prototype.readU4le = function() { - this.ensureBytesLeft(4); - var v = this._dataView.getUint32(this.pos, true); - this.pos += 4; - return v; -}; - -/** - Reads a 64-bit little-endian unsigned int from the stream. Note that - JavaScript does not support 64-bit integers natively, so it will - automatically upgrade internal representation to use IEEE 754 - double precision float. - @return {number} The read number. - */ -KaitaiStream.prototype.readU8le = function() { - this.ensureBytesLeft(8); - var v1 = this.readU4le(); - var v2 = this.readU4le(); - return 0x100000000 * v2 + v1; -}; - -// ======================================================================== -// Floating point numbers -// ======================================================================== - -// ------------------------------------------------------------------------ -// Big endian -// ------------------------------------------------------------------------ - -KaitaiStream.prototype.readF4be = function() { - this.ensureBytesLeft(4); - var v = this._dataView.getFloat32(this.pos); - this.pos += 4; - return v; -}; - -KaitaiStream.prototype.readF8be = function() { - this.ensureBytesLeft(8); - var v = this._dataView.getFloat64(this.pos); - this.pos += 8; - return v; -}; - -// ------------------------------------------------------------------------ -// Little endian -// ------------------------------------------------------------------------ - -KaitaiStream.prototype.readF4le = function() { - this.ensureBytesLeft(4); - var v = this._dataView.getFloat32(this.pos, true); - this.pos += 4; - return v; -}; - -KaitaiStream.prototype.readF8le = function() { - this.ensureBytesLeft(8); - var v = this._dataView.getFloat64(this.pos, true); - this.pos += 8; - return v; -}; - -// ------------------------------------------------------------------------ -// Unaligned bit values -// ------------------------------------------------------------------------ - -KaitaiStream.prototype.alignToByte = function() { - this.bits = 0; - this.bitsLeft = 0; -}; - -KaitaiStream.prototype.readBitsIntBe = function(n) { - // JS only supports bit operations on 32 bits - if (n > 32) { - throw new Error(`readBitsIntBe: the maximum supported bit length is 32 (tried to read ${n} bits)`); - } - var bitsNeeded = n - this.bitsLeft; - if (bitsNeeded > 0) { - // 1 bit => 1 byte - // 8 bits => 1 byte - // 9 bits => 2 bytes - var bytesNeeded = Math.ceil(bitsNeeded / 8); - var buf = this.readBytes(bytesNeeded); - for (var i = 0; i < bytesNeeded; i++) { - this.bits <<= 8; - this.bits |= buf[i]; - this.bitsLeft += 8; +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, global.KaitaiStream = factory()); +}(this, (function () { 'use strict'; + + /*! ***************************************************************************** + Copyright (c) Microsoft Corporation. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + PERFORMANCE OF THIS SOFTWARE. + ***************************************************************************** */ + /* global Reflect, Promise */ + + var extendStatics = function(d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + + function __extends(d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); } - } - - // raw mask with required number of 1s, starting from lowest bit - var mask = n === 32 ? 0xffffffff : (1 << n) - 1; - // shift this.bits to align the highest bits with the mask & derive reading result - var shiftBits = this.bitsLeft - n; - var res = (this.bits >>> shiftBits) & mask; - // clear top bits that we've just read => AND with 1s - this.bitsLeft -= n; - mask = (1 << this.bitsLeft) - 1; - this.bits &= mask; - - return res; -}; - -/** - * Unused since Kaitai Struct Compiler v0.9+ - compatibility with older versions - * - * @deprecated use {@link readBitsIntBe} instead - */ -KaitaiStream.prototype.readBitsInt = KaitaiStream.prototype.readBitsIntBe; - -KaitaiStream.prototype.readBitsIntLe = function(n) { - // JS only supports bit operations on 32 bits - if (n > 32) { - throw new Error(`readBitsIntLe: the maximum supported bit length is 32 (tried to read ${n} bits)`); - } - var bitsNeeded = n - this.bitsLeft; - if (bitsNeeded > 0) { - // 1 bit => 1 byte - // 8 bits => 1 byte - // 9 bits => 2 bytes - var bytesNeeded = Math.ceil(bitsNeeded / 8); - var buf = this.readBytes(bytesNeeded); - for (var i = 0; i < bytesNeeded; i++) { - this.bits |= (buf[i] << this.bitsLeft); - this.bitsLeft += 8; - } - } - - // raw mask with required number of 1s, starting from lowest bit - var mask = n === 32 ? 0xffffffff : (1 << n) - 1; - // derive reading result - var res = this.bits & mask; - // remove bottom bits that we've just read by shifting - this.bits >>= n; - this.bitsLeft -= n; - - return res; -}; - -/** - Native endianness. Either KaitaiStream.BIG_ENDIAN or KaitaiStream.LITTLE_ENDIAN - depending on the platform endianness. - - @type {boolean} - */ -KaitaiStream.endianness = new Int8Array(new Int16Array([1]).buffer)[0] > 0; - -// ======================================================================== -// Byte arrays -// ======================================================================== - -KaitaiStream.prototype.readBytes = function(len) { - return this.mapUint8Array(len); -}; - -KaitaiStream.prototype.readBytesFull = function() { - return this.mapUint8Array(this.size - this.pos); -}; - -KaitaiStream.prototype.readBytesTerm = function(terminator, include, consume, eosError) { - var blen = this.size - this.pos; - var u8 = new Uint8Array(this._buffer, this._byteOffset + this.pos); - for (var i = 0; i < blen && u8[i] !== terminator; i++); // find first zero byte - if (i === blen) { - // we've read all the buffer and haven't found the terminator - if (eosError) { - throw "End of stream reached, but no terminator " + terminator + " found"; - } else { - return this.mapUint8Array(i); - } - } else { - var arr; - if (include) { - arr = this.mapUint8Array(i + 1); - } else { - arr = this.mapUint8Array(i); - } - if (consume) { - this.pos += 1; - } - return arr; - } -}; - -// Unused since Kaitai Struct Compiler v0.9+ - compatibility with older versions -KaitaiStream.prototype.ensureFixedContents = function(expected) { - var actual = this.readBytes(expected.length); - if (actual.length !== expected.length) { - throw new UnexpectedDataError(expected, actual); - } - var actLen = actual.length; - for (var i = 0; i < actLen; i++) { - if (actual[i] !== expected[i]) { - throw new UnexpectedDataError(expected, actual); - } - } - return actual; -}; - -KaitaiStream.bytesStripRight = function(data, padByte) { - var newLen = data.length; - while (data[newLen - 1] === padByte) - newLen--; - return data.slice(0, newLen); -}; - -KaitaiStream.bytesTerminate = function(data, term, include) { - var newLen = 0; - var maxLen = data.length; - while (newLen < maxLen && data[newLen] !== term) - newLen++; - if (include && newLen < maxLen) - newLen++; - return data.slice(0, newLen); -}; - -KaitaiStream.bytesToStr = function(arr, encoding) { - if (encoding == null || encoding.toLowerCase() === "ascii") { - return KaitaiStream.createStringFromArray(arr); - } else { - if (typeof TextDecoder === 'function') { - // we're in the browser that supports TextDecoder - return (new TextDecoder(encoding)).decode(arr); - } else { - // probably we're in node.js - - // check if it's supported natively by node.js Buffer - // see https://github.com/nodejs/node/blob/master/lib/buffer.js#L187 for details - switch (encoding.toLowerCase()) { - case 'utf8': - case 'utf-8': - case 'ucs2': - case 'ucs-2': - case 'utf16le': - case 'utf-16le': - return new Buffer(arr).toString(encoding); - break; - default: - // unsupported encoding, we'll have to resort to iconv-lite - if (typeof KaitaiStream.iconvlite === 'undefined') - KaitaiStream.iconvlite = require('iconv-lite'); - - return KaitaiStream.iconvlite.decode(arr, encoding); - } - } - } -}; - -// ======================================================================== -// Byte array processing -// ======================================================================== - -KaitaiStream.processXorOne = function(data, key) { - var r = new Uint8Array(data.length); - var dl = data.length; - for (var i = 0; i < dl; i++) - r[i] = data[i] ^ key; - return r; -}; - -KaitaiStream.processXorMany = function(data, key) { - var dl = data.length; - var r = new Uint8Array(dl); - var kl = key.length; - var ki = 0; - for (var i = 0; i < dl; i++) { - r[i] = data[i] ^ key[ki]; - ki++; - if (ki >= kl) - ki = 0; - } - return r; -}; - -KaitaiStream.processRotateLeft = function(data, amount, groupSize) { - if (groupSize !== 1) - throw("unable to rotate group of " + groupSize + " bytes yet"); - - var mask = groupSize * 8 - 1; - var antiAmount = -amount & mask; - - var r = new Uint8Array(data.length); - for (var i = 0; i < data.length; i++) - r[i] = (data[i] << amount) & 0xff | (data[i] >> antiAmount); - - return r; -}; - -KaitaiStream.processZlib = function(buf) { - if (typeof require !== 'undefined') { - // require is available - we're running under node - if (typeof KaitaiStream.zlib === 'undefined') - KaitaiStream.zlib = require('zlib'); - // use node's zlib module API - var r = KaitaiStream.zlib.inflateSync( - Buffer.from(buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength)) - ); - return r; - } else { - // no require() - assume we're running as a web worker in browser. - // user should have configured KaitaiStream.depUrls.zlib, if not - // we'll throw. - if (typeof KaitaiStream.zlib === 'undefined' - && typeof KaitaiStream.depUrls.zlib !== 'undefined') { - importScripts(KaitaiStream.depUrls.zlib); - KaitaiStream.zlib = pako; - } - // use pako API - r = KaitaiStream.zlib.inflate(buf); - return r; - } -}; - -// ======================================================================== -// Misc runtime operations -// ======================================================================== - -KaitaiStream.mod = function(a, b) { - if (b <= 0) - throw "mod divisor <= 0"; - var r = a % b; - if (r < 0) - r += b; - return r; -}; - -KaitaiStream.arrayMin = function(arr) { - var min = arr[0]; - var x; - for (var i = 1, n = arr.length; i < n; ++i) { - x = arr[i]; - if (x < min) min = x; - } - return min; -}; - -KaitaiStream.arrayMax = function(arr) { - var max = arr[0]; - var x; - for (var i = 1, n = arr.length; i < n; ++i) { - x = arr[i]; - if (x > max) max = x; - } - return max; -}; - -KaitaiStream.byteArrayCompare = function(a, b) { - if (a === b) - return 0; - var al = a.length; - var bl = b.length; - var minLen = al < bl ? al : bl; - for (var i = 0; i < minLen; i++) { - var cmp = a[i] - b[i]; - if (cmp !== 0) - return cmp; - } - - // Reached the end of at least one of the arrays - if (al === bl) { - return 0; - } else { - return al - bl; - } -}; - -// ======================================================================== -// Internal implementation details -// ======================================================================== - -var EOFError = KaitaiStream.EOFError = function(bytesReq, bytesAvail) { - this.name = "EOFError"; - this.message = "requested " + bytesReq + " bytes, but only " + bytesAvail + " bytes available"; - this.bytesReq = bytesReq; - this.bytesAvail = bytesAvail; - this.stack = (new Error()).stack; -}; - -EOFError.prototype = Object.create(Error.prototype); -EOFError.prototype.constructor = EOFError; - -// Unused since Kaitai Struct Compiler v0.9+ - compatibility with older versions -var UnexpectedDataError = KaitaiStream.UnexpectedDataError = function(expected, actual) { - this.name = "UnexpectedDataError"; - this.message = "expected [" + expected + "], but got [" + actual + "]"; - this.expected = expected; - this.actual = actual; - this.stack = (new Error()).stack; -}; - -UnexpectedDataError.prototype = Object.create(Error.prototype); -UnexpectedDataError.prototype.constructor = UnexpectedDataError; - -var UndecidedEndiannessError = KaitaiStream.UndecidedEndiannessError = function() { - this.name = "UndecidedEndiannessError"; - this.stack = (new Error()).stack; -}; - -UndecidedEndiannessError.prototype = Object.create(Error.prototype); -UndecidedEndiannessError.prototype.constructor = UndecidedEndiannessError; - -var ValidationNotEqualError = KaitaiStream.ValidationNotEqualError = function(expected, actual) { - this.name = "ValidationNotEqualError"; - this.message = "not equal, expected [" + expected + "], but got [" + actual + "]"; - this.expected = expected; - this.actual = actual; - this.stack = (new Error()).stack; -}; - -ValidationNotEqualError.prototype = Object.create(Error.prototype); -ValidationNotEqualError.prototype.constructor = ValidationNotEqualError; - -var ValidationLessThanError = KaitaiStream.ValidationLessThanError = function(min, actual) { - this.name = "ValidationLessThanError"; - this.message = "not in range, min [" + min + "], but got [" + actual + "]"; - this.min = min; - this.actual = actual; - this.stack = (new Error()).stack; -}; - -ValidationLessThanError.prototype = Object.create(Error.prototype); -ValidationLessThanError.prototype.constructor = ValidationLessThanError; - -var ValidationGreaterThanError = KaitaiStream.ValidationGreaterThanError = function(max, actual) { - this.name = "ValidationGreaterThanError"; - this.message = "not in range, max [" + max + "], but got [" + actual + "]"; - this.max = max; - this.actual = actual; - this.stack = (new Error()).stack; -}; - -ValidationGreaterThanError.prototype = Object.create(Error.prototype); -ValidationGreaterThanError.prototype.constructor = ValidationGreaterThanError; - -var ValidationNotAnyOfError = KaitaiStream.ValidationNotAnyOfError = function(actual, io, srcPath) { - this.name = "ValidationNotAnyOfError"; - this.message = "not any of the list, got [" + actual + "]"; - this.actual = actual; - this.stack = (new Error()).stack; -}; - -ValidationNotAnyOfError.prototype = Object.create(Error.prototype); -ValidationNotAnyOfError.prototype.constructor = ValidationNotAnyOfError; - -/** - Ensures that we have an least `length` bytes left in the stream. - If that's not true, throws an EOFError. - - @param {number} length Number of bytes to require - */ -KaitaiStream.prototype.ensureBytesLeft = function(length) { - if (this.pos + length > this.size) { - throw new EOFError(length, this.size - this.pos); - } -}; - -/** - Maps a Uint8Array into the KaitaiStream buffer. - - Nice for quickly reading in data. - - @param {number} length Number of elements to map. - @return {Object} Uint8Array to the KaitaiStream backing buffer. - */ -KaitaiStream.prototype.mapUint8Array = function(length) { - length |= 0; - - this.ensureBytesLeft(length); - - var arr = new Uint8Array(this._buffer, this.byteOffset + this.pos, length); - this.pos += length; - return arr; -}; - -/** - Creates an array from an array of character codes. - Uses String.fromCharCode in chunks for memory efficiency and then concatenates - the resulting string chunks. - - @param {array|Uint8Array} array Array of character codes. - @return {string} String created from the character codes. -**/ -KaitaiStream.createStringFromArray = function(array) { - var chunk_size = 0x8000; - var chunks = []; - var useSubarray = typeof array.subarray === 'function'; - for (var i=0; i < array.length; i += chunk_size) { - chunks.push(String.fromCharCode.apply(null, useSubarray ? array.subarray(i, i + chunk_size) : array.slice(i, i + chunk_size))); - } - return chunks.join(""); -}; - -return KaitaiStream; -})); + // -*- mode: js; js-indent-level: 2; -*- + /** + KaitaiStream is an implementation of Kaitai Struct API for JavaScript. + Based on DataStream - https://github.com/kig/DataStream.js + + @param {ArrayBuffer} arrayBuffer ArrayBuffer to read from. + @param {?Number} byteOffset Offset from arrayBuffer beginning for the KaitaiStream. + */ + var KaitaiStream = /** @class */ (function () { + function KaitaiStream(arrayBuffer, byteOffset) { + /** + Virtual byte length of the KaitaiStream backing buffer. + Updated to be max of original buffer size and last written size. + If dynamicSize is false is set to buffer size. + */ + this._byteLength = 0; + this._byteOffset = byteOffset || 0; + if (arrayBuffer instanceof ArrayBuffer) { + this.buffer = arrayBuffer; + } + else if (typeof arrayBuffer == "object") { + this.dataView = arrayBuffer; + if (byteOffset) { + this._byteOffset += byteOffset; + } + } + else { + this.buffer = new ArrayBuffer(arrayBuffer || 1); + } + this.pos = 0; + this.alignToByte(); + } + Object.defineProperty(KaitaiStream.prototype, "buffer", { + /** + Set/get the backing ArrayBuffer of the KaitaiStream object. + The setter updates the DataView to point to the new buffer. + */ + get: function () { + this._trimAlloc(); + return this._buffer; + }, + set: function (v) { + this._buffer = v; + this._dataView = new DataView(this._buffer, this._byteOffset); + this._byteLength = this._buffer.byteLength; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(KaitaiStream.prototype, "byteOffset", { + /** + Set/get the byteOffset of the KaitaiStream object. + The setter updates the DataView to point to the new byteOffset. + */ + get: function () { + return this._byteOffset; + }, + set: function (v) { + this._byteOffset = v; + this._dataView = new DataView(this._buffer, this._byteOffset); + this._byteLength = this._buffer.byteLength; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(KaitaiStream.prototype, "dataView", { + /** + Set/get the backing DataView of the KaitaiStream object. + The setter updates the buffer and byteOffset to point to the DataView values. + */ + get: function () { + return this._dataView; + }, + set: function (v) { + this._byteOffset = v.byteOffset; + this._buffer = v.buffer; + this._dataView = new DataView(this._buffer, this._byteOffset); + this._byteLength = this._byteOffset + v.byteLength; + }, + enumerable: false, + configurable: true + }); + /** + Internal function to trim the KaitaiStream buffer when required. + Used for stripping out the extra bytes from the backing buffer when + the virtual byteLength is smaller than the buffer byteLength (happens after + growing the buffer with writes and not filling the extra space completely). + */ + KaitaiStream.prototype._trimAlloc = function () { + if (this._byteLength === this._buffer.byteLength) { + return; + } + var buf = new ArrayBuffer(this._byteLength); + var dst = new Uint8Array(buf); + var src = new Uint8Array(this._buffer, 0, dst.length); + dst.set(src); + this.buffer = buf; + }; + // ======================================================================== + // Stream positioning + // ======================================================================== + /** + Returns true if the KaitaiStream seek pointer is at the end of buffer and + there's no more data to read. + + @return {boolean} True if the seek pointer is at the end of the buffer. + */ + KaitaiStream.prototype.isEof = function () { + return this.pos >= this.size && this.bitsLeft === 0; + }; + /** + Sets the KaitaiStream read/write position to given position. + Clamps between 0 and KaitaiStream length. + + @param {number} pos Position to seek to. + */ + KaitaiStream.prototype.seek = function (pos) { + var npos = Math.max(0, Math.min(this.size, pos)); + this.pos = (isNaN(npos) || !isFinite(npos)) ? 0 : npos; + }; + Object.defineProperty(KaitaiStream.prototype, "size", { + /** + Returns the byte length of the KaitaiStream object. + */ + get: function () { + return this._byteLength - this._byteOffset; + }, + enumerable: false, + configurable: true + }); + // ======================================================================== + // Integer numbers + // ======================================================================== + // ------------------------------------------------------------------------ + // Signed + // ------------------------------------------------------------------------ + /** + Reads an 8-bit signed int from the stream. + @return {number} The read number. + */ + KaitaiStream.prototype.readS1 = function () { + this.ensureBytesLeft(1); + var v = this._dataView.getInt8(this.pos); + this.pos += 1; + return v; + }; + // ........................................................................ + // Big-endian + // ........................................................................ + /** + Reads a 16-bit big-endian signed int from the stream. + @return {number} The read number. + */ + KaitaiStream.prototype.readS2be = function () { + this.ensureBytesLeft(2); + var v = this._dataView.getInt16(this.pos); + this.pos += 2; + return v; + }; + /** + Reads a 32-bit big-endian signed int from the stream. + @return {number} The read number. + */ + KaitaiStream.prototype.readS4be = function () { + this.ensureBytesLeft(4); + var v = this._dataView.getInt32(this.pos); + this.pos += 4; + return v; + }; + /** + Reads a 64-bit big-endian unsigned int from the stream. Note that + JavaScript does not support 64-bit integers natively, so it will + automatically upgrade internal representation to use IEEE 754 + double precision float. + @return {number} The read number. + */ + KaitaiStream.prototype.readS8be = function () { + this.ensureBytesLeft(8); + var v1 = this.readU4be(); + var v2 = this.readU4be(); + if ((v1 & 0x80000000) !== 0) { + // negative number + return -(0x100000000 * (v1 ^ 0xffffffff) + (v2 ^ 0xffffffff)) - 1; + } + else { + return 0x100000000 * v1 + v2; + } + }; + // ........................................................................ + // Little-endian + // ........................................................................ + /** + Reads a 16-bit little-endian signed int from the stream. + @return {number} The read number. + */ + KaitaiStream.prototype.readS2le = function () { + this.ensureBytesLeft(2); + var v = this._dataView.getInt16(this.pos, true); + this.pos += 2; + return v; + }; + /** + Reads a 32-bit little-endian signed int from the stream. + @return {number} The read number. + */ + KaitaiStream.prototype.readS4le = function () { + this.ensureBytesLeft(4); + var v = this._dataView.getInt32(this.pos, true); + this.pos += 4; + return v; + }; + /** + Reads a 64-bit little-endian unsigned int from the stream. Note that + JavaScript does not support 64-bit integers natively, so it will + automatically upgrade internal representation to use IEEE 754 + double precision float. + @return {number} The read number. + */ + KaitaiStream.prototype.readS8le = function () { + this.ensureBytesLeft(8); + var v1 = this.readU4le(); + var v2 = this.readU4le(); + if ((v2 & 0x80000000) !== 0) { + // negative number + return -(0x100000000 * (v2 ^ 0xffffffff) + (v1 ^ 0xffffffff)) - 1; + } + else { + return 0x100000000 * v2 + v1; + } + }; + // ------------------------------------------------------------------------ + // Unsigned + // ------------------------------------------------------------------------ + /** + Reads an 8-bit unsigned int from the stream. + @return {number} The read number. + */ + KaitaiStream.prototype.readU1 = function () { + this.ensureBytesLeft(1); + var v = this._dataView.getUint8(this.pos); + this.pos += 1; + return v; + }; + // ........................................................................ + // Big-endian + // ........................................................................ + /** + Reads a 16-bit big-endian unsigned int from the stream. + @return {number} The read number. + */ + KaitaiStream.prototype.readU2be = function () { + this.ensureBytesLeft(2); + var v = this._dataView.getUint16(this.pos); + this.pos += 2; + return v; + }; + /** + Reads a 32-bit big-endian unsigned int from the stream. + @return {number} The read number. + */ + KaitaiStream.prototype.readU4be = function () { + this.ensureBytesLeft(4); + var v = this._dataView.getUint32(this.pos); + this.pos += 4; + return v; + }; + /** + Reads a 64-bit big-endian unsigned int from the stream. Note that + JavaScript does not support 64-bit integers natively, so it will + automatically upgrade internal representation to use IEEE 754 + double precision float. + @return {number} The read number. + */ + KaitaiStream.prototype.readU8be = function () { + this.ensureBytesLeft(8); + var v1 = this.readU4be(); + var v2 = this.readU4be(); + return 0x100000000 * v1 + v2; + }; + // ........................................................................ + // Little-endian + // ........................................................................ + /** + Reads a 16-bit little-endian unsigned int from the stream. + @return {number} The read number. + */ + KaitaiStream.prototype.readU2le = function () { + this.ensureBytesLeft(2); + var v = this._dataView.getUint16(this.pos, true); + this.pos += 2; + return v; + }; + /** + Reads a 32-bit little-endian unsigned int from the stream. + @return {number} The read number. + */ + KaitaiStream.prototype.readU4le = function () { + this.ensureBytesLeft(4); + var v = this._dataView.getUint32(this.pos, true); + this.pos += 4; + return v; + }; + /** + Reads a 64-bit little-endian unsigned int from the stream. Note that + JavaScript does not support 64-bit integers natively, so it will + automatically upgrade internal representation to use IEEE 754 + double precision float. + @return {number} The read number. + */ + KaitaiStream.prototype.readU8le = function () { + this.ensureBytesLeft(8); + var v1 = this.readU4le(); + var v2 = this.readU4le(); + return 0x100000000 * v2 + v1; + }; + // ======================================================================== + // Floating point numbers + // ======================================================================== + // ------------------------------------------------------------------------ + // Big endian + // ------------------------------------------------------------------------ + KaitaiStream.prototype.readF4be = function () { + this.ensureBytesLeft(4); + var v = this._dataView.getFloat32(this.pos); + this.pos += 4; + return v; + }; + KaitaiStream.prototype.readF8be = function () { + this.ensureBytesLeft(8); + var v = this._dataView.getFloat64(this.pos); + this.pos += 8; + return v; + }; + // ------------------------------------------------------------------------ + // Little endian + // ------------------------------------------------------------------------ + KaitaiStream.prototype.readF4le = function () { + this.ensureBytesLeft(4); + var v = this._dataView.getFloat32(this.pos, true); + this.pos += 4; + return v; + }; + KaitaiStream.prototype.readF8le = function () { + this.ensureBytesLeft(8); + var v = this._dataView.getFloat64(this.pos, true); + this.pos += 8; + return v; + }; + // ------------------------------------------------------------------------ + // Unaligned bit values + // ------------------------------------------------------------------------ + KaitaiStream.prototype.alignToByte = function () { + this.bits = 0; + this.bitsLeft = 0; + }; + KaitaiStream.prototype.readBitsIntBe = function (n) { + // JS only supports bit operations on 32 bits + if (n > 32) { + throw new Error("readBitsIntBe: the maximum supported bit length is 32 (tried to read " + n + " bits)"); + } + var bitsNeeded = n - this.bitsLeft; + if (bitsNeeded > 0) { + // 1 bit => 1 byte + // 8 bits => 1 byte + // 9 bits => 2 bytes + var bytesNeeded = Math.ceil(bitsNeeded / 8); + var buf = this.readBytes(bytesNeeded); + for (var i = 0; i < bytesNeeded; i++) { + this.bits <<= 8; + this.bits |= buf[i]; + this.bitsLeft += 8; + } + } + // raw mask with required number of 1s, starting from lowest bit + var mask = n === 32 ? 0xffffffff : (1 << n) - 1; + // shift this.bits to align the highest bits with the mask & derive reading result + var shiftBits = this.bitsLeft - n; + var res = (this.bits >>> shiftBits) & mask; + // clear top bits that we've just read => AND with 1s + this.bitsLeft -= n; + mask = (1 << this.bitsLeft) - 1; + this.bits &= mask; + return res; + }; + /** + * Unused since Kaitai Struct Compiler v0.9+ - compatibility with older versions + * + * @deprecated use {@link readBitsIntBe} instead + */ + KaitaiStream.prototype.readBitsInt = function (n) { + return this.readBitsIntBe(n); + }; + KaitaiStream.prototype.readBitsIntLe = function (n) { + // JS only supports bit operations on 32 bits + if (n > 32) { + throw new Error("readBitsIntLe: the maximum supported bit length is 32 (tried to read " + n + " bits)"); + } + var bitsNeeded = n - this.bitsLeft; + if (bitsNeeded > 0) { + // 1 bit => 1 byte + // 8 bits => 1 byte + // 9 bits => 2 bytes + var bytesNeeded = Math.ceil(bitsNeeded / 8); + var buf = this.readBytes(bytesNeeded); + for (var i = 0; i < bytesNeeded; i++) { + this.bits |= (buf[i] << this.bitsLeft); + this.bitsLeft += 8; + } + } + // raw mask with required number of 1s, starting from lowest bit + var mask = n === 32 ? 0xffffffff : (1 << n) - 1; + // derive reading result + var res = this.bits & mask; + // remove bottom bits that we've just read by shifting + this.bits >>= n; + this.bitsLeft -= n; + return res; + }; + // ======================================================================== + // Byte arrays + // ======================================================================== + KaitaiStream.prototype.readBytes = function (len) { + return this.mapUint8Array(len); + }; + KaitaiStream.prototype.readBytesFull = function () { + return this.mapUint8Array(this.size - this.pos); + }; + KaitaiStream.prototype.readBytesTerm = function (terminator, include, consume, eosError) { + var blen = this.size - this.pos; + var u8 = new Uint8Array(this._buffer, this._byteOffset + this.pos); + for (var i = 0; i < blen && u8[i] !== terminator; i++) + ; // find first zero byte + if (i === blen) { + // we've read all the buffer and haven't found the terminator + if (eosError) { + throw "End of stream reached, but no terminator " + terminator + " found"; + } + else { + return this.mapUint8Array(i); + } + } + else { + var arr; + if (include) { + arr = this.mapUint8Array(i + 1); + } + else { + arr = this.mapUint8Array(i); + } + if (consume) { + this.pos += 1; + } + return arr; + } + }; + // Unused since Kaitai Struct Compiler v0.9+ - compatibility with older versions + KaitaiStream.prototype.ensureFixedContents = function (expected) { + var actual = this.readBytes(expected.length); + if (actual.length !== expected.length) { + throw new KaitaiStream.UnexpectedDataError(expected, actual); + } + var actLen = actual.length; + for (var i = 0; i < actLen; i++) { + if (actual[i] !== expected[i]) { + throw new KaitaiStream.UnexpectedDataError(expected, actual); + } + } + return actual; + }; + KaitaiStream.bytesStripRight = function (data, padByte) { + var newLen = data.length; + while (data[newLen - 1] === padByte) + newLen--; + return data.slice(0, newLen); + }; + KaitaiStream.bytesTerminate = function (data, term, include) { + var newLen = 0; + var maxLen = data.length; + while (newLen < maxLen && data[newLen] !== term) + newLen++; + if (include && newLen < maxLen) + newLen++; + return data.slice(0, newLen); + }; + KaitaiStream.bytesToStr = function (arr, encoding) { + if (encoding == null || encoding.toLowerCase() === "ascii") { + return KaitaiStream.createStringFromArray(arr); + } + else { + if (typeof TextDecoder === 'function') { + // we're in the browser that supports TextDecoder + return (new TextDecoder(encoding)).decode(arr); + } + else { + // probably we're in node.js + // check if it's supported natively by node.js Buffer + // see https://github.com/nodejs/node/blob/master/lib/buffer.js#L187 for details + switch (encoding.toLowerCase()) { + case 'utf8': + case 'utf-8': + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return new Buffer(arr).toString(encoding); + default: + // unsupported encoding, we'll have to resort to iconv-lite + if (typeof KaitaiStream.iconvlite === 'undefined') + KaitaiStream.iconvlite = require('iconv-lite'); + return KaitaiStream.iconvlite.decode(arr, encoding); + } + } + } + }; + // ======================================================================== + // Byte array processing + // ======================================================================== + KaitaiStream.processXorOne = function (data, key) { + var r = new Uint8Array(data.length); + var dl = data.length; + for (var i = 0; i < dl; i++) + r[i] = data[i] ^ key; + return r; + }; + KaitaiStream.processXorMany = function (data, key) { + var dl = data.length; + var r = new Uint8Array(dl); + var kl = key.length; + var ki = 0; + for (var i = 0; i < dl; i++) { + r[i] = data[i] ^ key[ki]; + ki++; + if (ki >= kl) + ki = 0; + } + return r; + }; + KaitaiStream.processRotateLeft = function (data, amount, groupSize) { + if (groupSize !== 1) + throw ("unable to rotate group of " + groupSize + " bytes yet"); + var mask = groupSize * 8 - 1; + var antiAmount = -amount & mask; + var r = new Uint8Array(data.length); + for (var i = 0; i < data.length; i++) + r[i] = (data[i] << amount) & 0xff | (data[i] >> antiAmount); + return r; + }; + KaitaiStream.processZlib = function (buf) { + if (typeof require !== 'undefined') { + // require is available - we're running under node + if (typeof KaitaiStream.zlib === 'undefined') + KaitaiStream.zlib = require('zlib'); + // use node's zlib module API + var r = KaitaiStream.zlib.inflateSync(Buffer.from(buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength))); + return r; + } + else { + // no require() - assume we're running as a web worker in browser. + // user should have configured KaitaiStream.depUrls.zlib, if not + // we'll throw. + if (typeof KaitaiStream.zlib === 'undefined' + && typeof KaitaiStream.depUrls.zlib !== 'undefined') { + importScripts(KaitaiStream.depUrls.zlib); + KaitaiStream.zlib = self.pako; + } + // use pako API + r = KaitaiStream.zlib.inflate(buf); + return r; + } + }; + // ======================================================================== + // Misc runtime operations + // ======================================================================== + KaitaiStream.mod = function (a, b) { + if (b <= 0) + throw "mod divisor <= 0"; + var r = a % b; + if (r < 0) + r += b; + return r; + }; + KaitaiStream.arrayMin = function (arr) { + var min = arr[0]; + var x; + for (var i = 1, n = arr.length; i < n; ++i) { + x = arr[i]; + if (x < min) + min = x; + } + return min; + }; + KaitaiStream.arrayMax = function (arr) { + var max = arr[0]; + var x; + for (var i = 1, n = arr.length; i < n; ++i) { + x = arr[i]; + if (x > max) + max = x; + } + return max; + }; + KaitaiStream.byteArrayCompare = function (a, b) { + if (a === b) + return 0; + var al = a.length; + var bl = b.length; + var minLen = al < bl ? al : bl; + for (var i = 0; i < minLen; i++) { + var cmp = a[i] - b[i]; + if (cmp !== 0) + return cmp; + } + // Reached the end of at least one of the arrays + if (al === bl) { + return 0; + } + else { + return al - bl; + } + }; + /** + Ensures that we have an least `length` bytes left in the stream. + If that's not true, throws an EOFError. + + @param {number} length Number of bytes to require + */ + KaitaiStream.prototype.ensureBytesLeft = function (length) { + if (this.pos + length > this.size) { + throw new KaitaiStream.EOFError(length, this.size - this.pos); + } + }; + /** + Maps a Uint8Array into the KaitaiStream buffer. + + Nice for quickly reading in data. + + @param {number} length Number of elements to map. + @return {Object} Uint8Array to the KaitaiStream backing buffer. + */ + KaitaiStream.prototype.mapUint8Array = function (length) { + length |= 0; + this.ensureBytesLeft(length); + var arr = new Uint8Array(this._buffer, this.byteOffset + this.pos, length); + this.pos += length; + return arr; + }; + /** + Dependency configuration data. Holds urls for (optional) dynamic loading + of code dependencies from a remote server. For use by (static) processing functions. + + Caller should the supported keys to the asset urls as needed. + NOTE: `depUrls` is a static property of KaitaiStream (the factory),like the various + processing functions. It is NOT part of the prototype of instances. + */ + KaitaiStream.depUrls = { + // processZlib uses this and expected a link to a copy of pako. + // specifically the pako_inflate.min.js script at: + // https://raw.githubusercontent.com/nodeca/pako/master/dist/pako_inflate.min.js + zlib: undefined + }; + /** + Native endianness. Either KaitaiStream.BIG_ENDIAN or KaitaiStream.LITTLE_ENDIAN + depending on the platform endianness. + + @type {boolean} + */ + KaitaiStream.endianness = new Int8Array(new Int16Array([1]).buffer)[0] > 0; + // ======================================================================== + // Internal implementation details + // ======================================================================== + KaitaiStream.EOFError = /** @class */ (function (_super) { + __extends(class_1, _super); + function class_1(bytesReq, bytesAvail) { + var _this = _super.call(this) || this; + Object.setPrototypeOf(_this, KaitaiStream.EOFError.prototype); + _this.name = "EOFError"; + _this.message = "requested " + bytesReq + " bytes, but only " + bytesAvail + " bytes available"; + _this.bytesReq = bytesReq; + _this.bytesAvail = bytesAvail; + return _this; + } + return class_1; + }(Error)); + // Unused since Kaitai Struct Compiler v0.9+ - compatibility with older versions + KaitaiStream.UnexpectedDataError = /** @class */ (function (_super) { + __extends(class_2, _super); + function class_2(expected, actual) { + var _this = _super.call(this) || this; + Object.setPrototypeOf(_this, KaitaiStream.UnexpectedDataError.prototype); + _this.name = "UnexpectedDataError"; + _this.message = "expected [" + expected + "], but got [" + actual + "]"; + _this.expected = expected; + _this.actual = actual; + return _this; + } + return class_2; + }(Error)); + KaitaiStream.UndecidedEndiannessError = /** @class */ (function (_super) { + __extends(class_3, _super); + function class_3() { + var _this = _super.call(this) || this; + Object.setPrototypeOf(_this, KaitaiStream.UndecidedEndiannessError.prototype); + _this.name = "UndecidedEndiannessError"; + return _this; + } + return class_3; + }(Error)); + KaitaiStream.ValidationNotEqualError = /** @class */ (function (_super) { + __extends(class_4, _super); + function class_4(expected, actual) { + var _this = _super.call(this) || this; + Object.setPrototypeOf(_this, KaitaiStream.ValidationNotEqualError.prototype); + _this.name = "ValidationNotEqualError"; + _this.message = "not equal, expected [" + expected + "], but got [" + actual + "]"; + _this.expected = expected; + _this.actual = actual; + return _this; + } + return class_4; + }(Error)); + KaitaiStream.ValidationLessThanError = /** @class */ (function (_super) { + __extends(class_5, _super); + function class_5(min, actual) { + var _this = _super.call(this) || this; + Object.setPrototypeOf(_this, KaitaiStream.ValidationLessThanError.prototype); + _this.name = "ValidationLessThanError"; + _this.message = "not in range, min [" + min + "], but got [" + actual + "]"; + _this.min = min; + _this.actual = actual; + return _this; + } + return class_5; + }(Error)); + KaitaiStream.ValidationGreaterThanError = /** @class */ (function (_super) { + __extends(class_6, _super); + function class_6(max, actual) { + var _this = _super.call(this) || this; + Object.setPrototypeOf(_this, KaitaiStream.ValidationGreaterThanError.prototype); + _this.name = "ValidationGreaterThanError"; + _this.message = "not in range, max [" + max + "], but got [" + actual + "]"; + _this.max = max; + _this.actual = actual; + return _this; + } + return class_6; + }(Error)); + KaitaiStream.ValidationNotAnyOfError = /** @class */ (function (_super) { + __extends(class_7, _super); + function class_7(actual, io, srcPath) { + var _this = _super.call(this) || this; + Object.setPrototypeOf(_this, KaitaiStream.ValidationNotAnyOfError.prototype); + _this.name = "ValidationNotAnyOfError"; + _this.message = "not any of the list, got [" + actual + "]"; + _this.actual = actual; + return _this; + } + return class_7; + }(Error)); + /** + Creates an array from an array of character codes. + Uses String.fromCharCode in chunks for memory efficiency and then concatenates + the resulting string chunks. + + @param {array|Uint8Array} array Array of character codes. + @return {string} String created from the character codes. + */ + KaitaiStream.createStringFromArray = function (array) { + var chunk_size = 0x8000; + var chunks = []; + var useSubarray = typeof array.subarray === 'function'; + for (var i = 0; i < array.length; i += chunk_size) { + chunks.push(String.fromCharCode.apply(null, useSubarray ? array.subarray(i, i + chunk_size) : array.slice(i, i + chunk_size))); + } + return chunks.join(""); + }; + return KaitaiStream; + }()); + + return KaitaiStream; + +}))); diff --git a/KaitaiStream.ts b/KaitaiStream.ts new file mode 100644 index 0000000..346469b --- /dev/null +++ b/KaitaiStream.ts @@ -0,0 +1,850 @@ +// -*- mode: js; js-indent-level: 2; -*- + +/** + KaitaiStream is an implementation of Kaitai Struct API for JavaScript. + Based on DataStream - https://github.com/kig/DataStream.js + + @param {ArrayBuffer} arrayBuffer ArrayBuffer to read from. + @param {?Number} byteOffset Offset from arrayBuffer beginning for the KaitaiStream. + */ +class KaitaiStream { + // Temporary hack since I don't want to declare all members, yet + [key: string]: any; + + constructor(arrayBuffer, byteOffset) { + this._byteOffset = byteOffset || 0; + if (arrayBuffer instanceof ArrayBuffer) { + this.buffer = arrayBuffer; + } else if (typeof arrayBuffer == "object") { + this.dataView = arrayBuffer; + if (byteOffset) { + this._byteOffset += byteOffset; + } + } else { + this.buffer = new ArrayBuffer(arrayBuffer || 1); + } + this.pos = 0; + this.alignToByte(); + } + + /** + Virtual byte length of the KaitaiStream backing buffer. + Updated to be max of original buffer size and last written size. + If dynamicSize is false is set to buffer size. + */ + _byteLength: number = 0; + + /** + Dependency configuration data. Holds urls for (optional) dynamic loading + of code dependencies from a remote server. For use by (static) processing functions. + + Caller should the supported keys to the asset urls as needed. + NOTE: `depUrls` is a static property of KaitaiStream (the factory),like the various + processing functions. It is NOT part of the prototype of instances. + */ + static depUrls = { + // processZlib uses this and expected a link to a copy of pako. + // specifically the pako_inflate.min.js script at: + // https://raw.githubusercontent.com/nodeca/pako/master/dist/pako_inflate.min.js + zlib: undefined + }; + + static zlib: any; + static iconvlite: any; + + /** + Set/get the backing ArrayBuffer of the KaitaiStream object. + The setter updates the DataView to point to the new buffer. + */ + get buffer() { + this._trimAlloc(); + return this._buffer; + } + set buffer(v) { + this._buffer = v; + this._dataView = new DataView(this._buffer, this._byteOffset); + this._byteLength = this._buffer.byteLength; + } + + /** + Set/get the byteOffset of the KaitaiStream object. + The setter updates the DataView to point to the new byteOffset. + */ + get byteOffset() { + return this._byteOffset; + } + set byteOffset(v) { + this._byteOffset = v; + this._dataView = new DataView(this._buffer, this._byteOffset); + this._byteLength = this._buffer.byteLength; + } + + /** + Set/get the backing DataView of the KaitaiStream object. + The setter updates the buffer and byteOffset to point to the DataView values. + */ + get dataView() { + return this._dataView; + } + set dataView(v) { + this._byteOffset = v.byteOffset; + this._buffer = v.buffer; + this._dataView = new DataView(this._buffer, this._byteOffset); + this._byteLength = this._byteOffset + v.byteLength; + } + + /** + Internal function to trim the KaitaiStream buffer when required. + Used for stripping out the extra bytes from the backing buffer when + the virtual byteLength is smaller than the buffer byteLength (happens after + growing the buffer with writes and not filling the extra space completely). + */ + _trimAlloc() { + if (this._byteLength === this._buffer.byteLength) { + return; + } + var buf = new ArrayBuffer(this._byteLength); + var dst = new Uint8Array(buf); + var src = new Uint8Array(this._buffer, 0, dst.length); + dst.set(src); + this.buffer = buf; + } + + + // ======================================================================== + // Stream positioning + // ======================================================================== + + /** + Returns true if the KaitaiStream seek pointer is at the end of buffer and + there's no more data to read. + + @return {boolean} True if the seek pointer is at the end of the buffer. + */ + isEof() { + return this.pos >= this.size && this.bitsLeft === 0; + }; + + /** + Sets the KaitaiStream read/write position to given position. + Clamps between 0 and KaitaiStream length. + + @param {number} pos Position to seek to. + */ + seek(pos) { + var npos = Math.max(0, Math.min(this.size, pos)); + this.pos = (isNaN(npos) || !isFinite(npos)) ? 0 : npos; + }; + + /** + Returns the byte length of the KaitaiStream object. + */ + get size() { + return this._byteLength - this._byteOffset; + } + + // ======================================================================== + // Integer numbers + // ======================================================================== + + // ------------------------------------------------------------------------ + // Signed + // ------------------------------------------------------------------------ + + /** + Reads an 8-bit signed int from the stream. + @return {number} The read number. + */ + readS1() { + this.ensureBytesLeft(1); + var v = this._dataView.getInt8(this.pos); + this.pos += 1; + return v; + }; + + // ........................................................................ + // Big-endian + // ........................................................................ + + /** + Reads a 16-bit big-endian signed int from the stream. + @return {number} The read number. + */ + readS2be() { + this.ensureBytesLeft(2); + var v = this._dataView.getInt16(this.pos); + this.pos += 2; + return v; + }; + + /** + Reads a 32-bit big-endian signed int from the stream. + @return {number} The read number. + */ + readS4be() { + this.ensureBytesLeft(4); + var v = this._dataView.getInt32(this.pos); + this.pos += 4; + return v; + }; + + /** + Reads a 64-bit big-endian unsigned int from the stream. Note that + JavaScript does not support 64-bit integers natively, so it will + automatically upgrade internal representation to use IEEE 754 + double precision float. + @return {number} The read number. + */ + readS8be() { + this.ensureBytesLeft(8); + var v1 = this.readU4be(); + var v2 = this.readU4be(); + + if ((v1 & 0x80000000) !== 0) { + // negative number + return -(0x100000000 * (v1 ^ 0xffffffff) + (v2 ^ 0xffffffff)) - 1; + } else { + return 0x100000000 * v1 + v2; + } + }; + + // ........................................................................ + // Little-endian + // ........................................................................ + + /** + Reads a 16-bit little-endian signed int from the stream. + @return {number} The read number. + */ + readS2le() { + this.ensureBytesLeft(2); + var v = this._dataView.getInt16(this.pos, true); + this.pos += 2; + return v; + }; + + /** + Reads a 32-bit little-endian signed int from the stream. + @return {number} The read number. + */ + readS4le() { + this.ensureBytesLeft(4); + var v = this._dataView.getInt32(this.pos, true); + this.pos += 4; + return v; + }; + + /** + Reads a 64-bit little-endian unsigned int from the stream. Note that + JavaScript does not support 64-bit integers natively, so it will + automatically upgrade internal representation to use IEEE 754 + double precision float. + @return {number} The read number. + */ + readS8le() { + this.ensureBytesLeft(8); + var v1 = this.readU4le(); + var v2 = this.readU4le(); + + if ((v2 & 0x80000000) !== 0) { + // negative number + return -(0x100000000 * (v2 ^ 0xffffffff) + (v1 ^ 0xffffffff)) - 1; + } else { + return 0x100000000 * v2 + v1; + } + }; + + // ------------------------------------------------------------------------ + // Unsigned + // ------------------------------------------------------------------------ + + /** + Reads an 8-bit unsigned int from the stream. + @return {number} The read number. + */ + readU1() { + this.ensureBytesLeft(1); + var v = this._dataView.getUint8(this.pos); + this.pos += 1; + return v; + }; + + // ........................................................................ + // Big-endian + // ........................................................................ + + /** + Reads a 16-bit big-endian unsigned int from the stream. + @return {number} The read number. + */ + readU2be() { + this.ensureBytesLeft(2); + var v = this._dataView.getUint16(this.pos); + this.pos += 2; + return v; + }; + + /** + Reads a 32-bit big-endian unsigned int from the stream. + @return {number} The read number. + */ + readU4be() { + this.ensureBytesLeft(4); + var v = this._dataView.getUint32(this.pos); + this.pos += 4; + return v; + }; + + /** + Reads a 64-bit big-endian unsigned int from the stream. Note that + JavaScript does not support 64-bit integers natively, so it will + automatically upgrade internal representation to use IEEE 754 + double precision float. + @return {number} The read number. + */ + readU8be() { + this.ensureBytesLeft(8); + var v1 = this.readU4be(); + var v2 = this.readU4be(); + return 0x100000000 * v1 + v2; + } + + // ........................................................................ + // Little-endian + // ........................................................................ + + /** + Reads a 16-bit little-endian unsigned int from the stream. + @return {number} The read number. + */ + readU2le() { + this.ensureBytesLeft(2); + var v = this._dataView.getUint16(this.pos, true); + this.pos += 2; + return v; + } + + /** + Reads a 32-bit little-endian unsigned int from the stream. + @return {number} The read number. + */ + readU4le() { + this.ensureBytesLeft(4); + var v = this._dataView.getUint32(this.pos, true); + this.pos += 4; + return v; + } + + /** + Reads a 64-bit little-endian unsigned int from the stream. Note that + JavaScript does not support 64-bit integers natively, so it will + automatically upgrade internal representation to use IEEE 754 + double precision float. + @return {number} The read number. + */ + readU8le() { + this.ensureBytesLeft(8); + var v1 = this.readU4le(); + var v2 = this.readU4le(); + return 0x100000000 * v2 + v1; + }; + + + // ======================================================================== + // Floating point numbers + // ======================================================================== + + // ------------------------------------------------------------------------ + // Big endian + // ------------------------------------------------------------------------ + + readF4be() { + this.ensureBytesLeft(4); + var v = this._dataView.getFloat32(this.pos); + this.pos += 4; + return v; + }; + + readF8be() { + this.ensureBytesLeft(8); + var v = this._dataView.getFloat64(this.pos); + this.pos += 8; + return v; + }; + + // ------------------------------------------------------------------------ + // Little endian + // ------------------------------------------------------------------------ + + readF4le() { + this.ensureBytesLeft(4); + var v = this._dataView.getFloat32(this.pos, true); + this.pos += 4; + return v; + }; + + readF8le() { + this.ensureBytesLeft(8); + var v = this._dataView.getFloat64(this.pos, true); + this.pos += 8; + return v; + }; + + // ------------------------------------------------------------------------ + // Unaligned bit values + // ------------------------------------------------------------------------ + + alignToByte() { + this.bits = 0; + this.bitsLeft = 0; + }; + + readBitsIntBe(n) { + // JS only supports bit operations on 32 bits + if (n > 32) { + throw new Error(`readBitsIntBe: the maximum supported bit length is 32 (tried to read ${n} bits)`); + } + var bitsNeeded = n - this.bitsLeft; + if (bitsNeeded > 0) { + // 1 bit => 1 byte + // 8 bits => 1 byte + // 9 bits => 2 bytes + var bytesNeeded = Math.ceil(bitsNeeded / 8); + var buf = this.readBytes(bytesNeeded); + for (var i = 0; i < bytesNeeded; i++) { + this.bits <<= 8; + this.bits |= buf[i]; + this.bitsLeft += 8; + } + } + + // raw mask with required number of 1s, starting from lowest bit + var mask = n === 32 ? 0xffffffff : (1 << n) - 1; + // shift this.bits to align the highest bits with the mask & derive reading result + var shiftBits = this.bitsLeft - n; + var res = (this.bits >>> shiftBits) & mask; + // clear top bits that we've just read => AND with 1s + this.bitsLeft -= n; + mask = (1 << this.bitsLeft) - 1; + this.bits &= mask; + + return res; + }; + + /** + * Unused since Kaitai Struct Compiler v0.9+ - compatibility with older versions + * + * @deprecated use {@link readBitsIntBe} instead + */ + readBitsInt(n) { + return this.readBitsIntBe(n); + } + + readBitsIntLe(n) { + // JS only supports bit operations on 32 bits + if (n > 32) { + throw new Error(`readBitsIntLe: the maximum supported bit length is 32 (tried to read ${n} bits)`); + } + var bitsNeeded = n - this.bitsLeft; + if (bitsNeeded > 0) { + // 1 bit => 1 byte + // 8 bits => 1 byte + // 9 bits => 2 bytes + var bytesNeeded = Math.ceil(bitsNeeded / 8); + var buf = this.readBytes(bytesNeeded); + for (var i = 0; i < bytesNeeded; i++) { + this.bits |= (buf[i] << this.bitsLeft); + this.bitsLeft += 8; + } + } + + // raw mask with required number of 1s, starting from lowest bit + var mask = n === 32 ? 0xffffffff : (1 << n) - 1; + // derive reading result + var res = this.bits & mask; + // remove bottom bits that we've just read by shifting + this.bits >>= n; + this.bitsLeft -= n; + + return res; + }; + + /** + Native endianness. Either KaitaiStream.BIG_ENDIAN or KaitaiStream.LITTLE_ENDIAN + depending on the platform endianness. + + @type {boolean} + */ + static endianness = new Int8Array(new Int16Array([1]).buffer)[0] > 0; + + // ======================================================================== + // Byte arrays + // ======================================================================== + + readBytes(len) { + return this.mapUint8Array(len); + }; + + readBytesFull() { + return this.mapUint8Array(this.size - this.pos); + }; + + readBytesTerm(terminator, include, consume, eosError) { + var blen = this.size - this.pos; + var u8 = new Uint8Array(this._buffer, this._byteOffset + this.pos); + for (var i = 0; i < blen && u8[i] !== terminator; i++); // find first zero byte + if (i === blen) { + // we've read all the buffer and haven't found the terminator + if (eosError) { + throw "End of stream reached, but no terminator " + terminator + " found"; + } else { + return this.mapUint8Array(i); + } + } else { + var arr; + if (include) { + arr = this.mapUint8Array(i + 1); + } else { + arr = this.mapUint8Array(i); + } + if (consume) { + this.pos += 1; + } + return arr; + } + }; + + // Unused since Kaitai Struct Compiler v0.9+ - compatibility with older versions + ensureFixedContents(expected) { + var actual = this.readBytes(expected.length); + if (actual.length !== expected.length) { + throw new KaitaiStream.UnexpectedDataError(expected, actual); + } + var actLen = actual.length; + for (var i = 0; i < actLen; i++) { + if (actual[i] !== expected[i]) { + throw new KaitaiStream.UnexpectedDataError(expected, actual); + } + } + return actual; + }; + + static bytesStripRight(data, padByte) { + var newLen = data.length; + while (data[newLen - 1] === padByte) + newLen--; + return data.slice(0, newLen); + }; + + static bytesTerminate(data, term, include) { + var newLen = 0; + var maxLen = data.length; + while (newLen < maxLen && data[newLen] !== term) + newLen++; + if (include && newLen < maxLen) + newLen++; + return data.slice(0, newLen); + }; + + static bytesToStr(arr, encoding) { + if (encoding == null || encoding.toLowerCase() === "ascii") { + return KaitaiStream.createStringFromArray(arr); + } else { + if (typeof TextDecoder === 'function') { + // we're in the browser that supports TextDecoder + return (new TextDecoder(encoding)).decode(arr); + } else { + // probably we're in node.js + + // check if it's supported natively by node.js Buffer + // see https://github.com/nodejs/node/blob/master/lib/buffer.js#L187 for details + switch (encoding.toLowerCase()) { + case 'utf8': + case 'utf-8': + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return new Buffer(arr).toString(encoding); + break; + default: + // unsupported encoding, we'll have to resort to iconv-lite + if (typeof KaitaiStream.iconvlite === 'undefined') + KaitaiStream.iconvlite = require('iconv-lite'); + + return KaitaiStream.iconvlite.decode(arr, encoding); + } + } + } + }; + + // ======================================================================== + // Byte array processing + // ======================================================================== + + static processXorOne(data, key) { + var r = new Uint8Array(data.length); + var dl = data.length; + for (var i = 0; i < dl; i++) + r[i] = data[i] ^ key; + return r; + }; + + static processXorMany(data, key) { + var dl = data.length; + var r = new Uint8Array(dl); + var kl = key.length; + var ki = 0; + for (var i = 0; i < dl; i++) { + r[i] = data[i] ^ key[ki]; + ki++; + if (ki >= kl) + ki = 0; + } + return r; + }; + + static processRotateLeft(data, amount, groupSize) { + if (groupSize !== 1) + throw ("unable to rotate group of " + groupSize + " bytes yet"); + + var mask = groupSize * 8 - 1; + var antiAmount = -amount & mask; + + var r = new Uint8Array(data.length); + for (var i = 0; i < data.length; i++) + r[i] = (data[i] << amount) & 0xff | (data[i] >> antiAmount); + + return r; + }; + + static processZlib(buf) { + if (typeof require !== 'undefined') { + // require is available - we're running under node + if (typeof KaitaiStream.zlib === 'undefined') + KaitaiStream.zlib = require('zlib'); + // use node's zlib module API + var r = KaitaiStream.zlib.inflateSync( + Buffer.from(buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength)) + ); + return r; + } else { + // no require() - assume we're running as a web worker in browser. + // user should have configured KaitaiStream.depUrls.zlib, if not + // we'll throw. + if (typeof KaitaiStream.zlib === 'undefined' + && typeof KaitaiStream.depUrls.zlib !== 'undefined') { + importScripts(KaitaiStream.depUrls.zlib); + KaitaiStream.zlib = (self as any).pako; + } + // use pako API + r = KaitaiStream.zlib.inflate(buf); + return r; + } + }; + + // ======================================================================== + // Misc runtime operations + // ======================================================================== + + static mod(a, b) { + if (b <= 0) + throw "mod divisor <= 0"; + var r = a % b; + if (r < 0) + r += b; + return r; + }; + + static arrayMin(arr) { + var min = arr[0]; + var x; + for (var i = 1, n = arr.length; i < n; ++i) { + x = arr[i]; + if (x < min) min = x; + } + return min; + }; + + static arrayMax(arr) { + var max = arr[0]; + var x; + for (var i = 1, n = arr.length; i < n; ++i) { + x = arr[i]; + if (x > max) max = x; + } + return max; + }; + + static byteArrayCompare(a, b) { + if (a === b) + return 0; + var al = a.length; + var bl = b.length; + var minLen = al < bl ? al : bl; + for (var i = 0; i < minLen; i++) { + var cmp = a[i] - b[i]; + if (cmp !== 0) + return cmp; + } + + // Reached the end of at least one of the arrays + if (al === bl) { + return 0; + } else { + return al - bl; + } + }; + + // ======================================================================== + // Internal implementation details + // ======================================================================== + + static EOFError = class extends Error { + bytesReq: number; + bytesAvail: number; + + constructor(bytesReq, bytesAvail) { + super(); + // Workaround https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work + Object.setPrototypeOf(this, KaitaiStream.EOFError.prototype); + this.name = "EOFError"; + this.message = "requested " + bytesReq + " bytes, but only " + bytesAvail + " bytes available"; + this.bytesReq = bytesReq; + this.bytesAvail = bytesAvail; + } + } + + // Unused since Kaitai Struct Compiler v0.9+ - compatibility with older versions + static UnexpectedDataError = class extends Error { + expected: any; + actual: any; + + constructor(expected, actual) { + super(); + // Workaround https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work + Object.setPrototypeOf(this, KaitaiStream.UnexpectedDataError.prototype); + this.name = "UnexpectedDataError"; + this.message = "expected [" + expected + "], but got [" + actual + "]"; + this.expected = expected; + this.actual = actual; + } + }; + + static UndecidedEndiannessError = class extends Error { + constructor() { + super(); + // Workaround https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work + Object.setPrototypeOf(this, KaitaiStream.UndecidedEndiannessError.prototype); + this.name = "UndecidedEndiannessError"; + } + }; + + static ValidationNotEqualError = class extends Error { + expected: any; + actual: any; + + constructor(expected, actual) { + super(); + // Workaround https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work + Object.setPrototypeOf(this, KaitaiStream.ValidationNotEqualError.prototype); + this.name = "ValidationNotEqualError"; + this.message = "not equal, expected [" + expected + "], but got [" + actual + "]"; + this.expected = expected; + this.actual = actual; + } + }; + + static ValidationLessThanError = class extends Error { + min: any; + actual: any; + + constructor(min, actual) { + super(); + // Workaround https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work + Object.setPrototypeOf(this, KaitaiStream.ValidationLessThanError.prototype); + this.name = "ValidationLessThanError"; + this.message = "not in range, min [" + min + "], but got [" + actual + "]"; + this.min = min; + this.actual = actual; + } + }; + + static ValidationGreaterThanError = class extends Error { + max: any; + actual: any; + + constructor(max, actual) { + super(); + // Workaround https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work + Object.setPrototypeOf(this, KaitaiStream.ValidationGreaterThanError.prototype); + this.name = "ValidationGreaterThanError"; + this.message = "not in range, max [" + max + "], but got [" + actual + "]"; + this.max = max; + this.actual = actual; + } + }; + + static ValidationNotAnyOfError = class extends Error { + actual: any; + + constructor(actual, io, srcPath) { + super(); + // Workaround https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work + Object.setPrototypeOf(this, KaitaiStream.ValidationNotAnyOfError.prototype); + this.name = "ValidationNotAnyOfError"; + this.message = "not any of the list, got [" + actual + "]"; + this.actual = actual; + } + }; + + /** + Ensures that we have an least `length` bytes left in the stream. + If that's not true, throws an EOFError. + + @param {number} length Number of bytes to require + */ + ensureBytesLeft(length) { + if (this.pos + length > this.size) { + throw new KaitaiStream.EOFError(length, this.size - this.pos); + } + }; + + /** + Maps a Uint8Array into the KaitaiStream buffer. + + Nice for quickly reading in data. + + @param {number} length Number of elements to map. + @return {Object} Uint8Array to the KaitaiStream backing buffer. + */ + mapUint8Array(length) { + length |= 0; + + this.ensureBytesLeft(length); + + var arr = new Uint8Array(this._buffer, this.byteOffset + this.pos, length); + this.pos += length; + return arr; + }; + + /** + Creates an array from an array of character codes. + Uses String.fromCharCode in chunks for memory efficiency and then concatenates + the resulting string chunks. + + @param {array|Uint8Array} array Array of character codes. + @return {string} String created from the character codes. + */ + static createStringFromArray = function (array) { + var chunk_size = 0x8000; + var chunks = []; + var useSubarray = typeof array.subarray === 'function'; + for (var i = 0; i < array.length; i += chunk_size) { + chunks.push(String.fromCharCode.apply(null, useSubarray ? array.subarray(i, i + chunk_size) : array.slice(i, i + chunk_size))); + } + return chunks.join(""); + }; +} + +export default KaitaiStream; diff --git a/index.d.ts b/index.d.ts new file mode 100644 index 0000000..a31ec9a --- /dev/null +++ b/index.d.ts @@ -0,0 +1,5 @@ +import KaitaiStream = require("./KaitaiStream"); +declare const _default: { + KaitaiStream: typeof KaitaiStream; +}; +export = _default; diff --git a/package-lock.json b/package-lock.json index 0a2a1c2..4f7fc79 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,96 @@ { "name": "kaitai-struct", "version": "0.9.0-SNAPSHOT.3", - "lockfileVersion": 1 + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@rollup/plugin-typescript": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-4.1.1.tgz", + "integrity": "sha512-KYZCn1Iw9hZWkeEPqPs5YjlmvSjR7UdezVca8z0e8rm/29wU24UD9Y4IZHhnc9tm749hzsgBTiOUxA85gfShEQ==", + "dev": true, + "requires": { + "@rollup/pluginutils": "^3.0.1", + "resolve": "^1.14.1" + } + }, + "@rollup/pluginutils": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.0.10.tgz", + "integrity": "sha512-d44M7t+PjmMrASHbhgpSbVgtL6EFyX7J4mYxwQ/c5eoaE6N2VgCgEcWVzNnwycIloti+/MpwFr8qfw+nRw00sw==", + "dev": true, + "requires": { + "@types/estree": "0.0.39", + "estree-walker": "^1.0.1", + "picomatch": "^2.2.2" + } + }, + "@types/estree": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", + "dev": true + }, + "@types/node": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.1.tgz", + "integrity": "sha512-FAYBGwC+W6F9+huFIDtn43cpy7+SzG+atzRiTfdp3inUKL2hXnd4rG8hylJLIh4+hqrQy1P17kvJByE/z825hA==", + "dev": true + }, + "estree-walker": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", + "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", + "dev": true + }, + "fsevents": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", + "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "dev": true, + "optional": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "picomatch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", + "dev": true + }, + "resolve": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, + "rollup": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.10.0.tgz", + "integrity": "sha512-7BmpEfUN9P6esJzWIn3DmR//90mW6YwYB1t3y48LpF8ITpYtL8s1kEirMKqUu44dVH/6a/rs0EuwYVL3FuRDoA==", + "dev": true, + "requires": { + "fsevents": "~2.1.2" + } + }, + "tslib": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", + "dev": true + }, + "typescript": { + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.2.tgz", + "integrity": "sha512-q2ktq4n/uLuNNShyayit+DTobV2ApPEo/6so68JaD5ojvc/6GClBipedB9zNWYxRSAlZXAe405Rlijzl6qDiSw==", + "dev": true + } + } } diff --git a/package.json b/package.json index 58d7d33..a0eaf4c 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "version": "0.9.0-SNAPSHOT.3", "description": "Kaitai Struct: runtime library for Javascript", "main": "index.js", + "types": "index.d.ts", "repository": { "type": "git", "url": "git+https://github.com/kaitai-io/kaitai_struct_javascript_runtime.git" @@ -26,5 +27,16 @@ "bugs": { "url": "https://github.com/kaitai-io/kaitai_struct_javascript_runtime/issues" }, - "homepage": "https://github.com/kaitai-io/kaitai_struct_javascript_runtime#readme" + "homepage": "https://github.com/kaitai-io/kaitai_struct_javascript_runtime#readme", + "scripts": { + "build": "rollup -c", + "prepublish": "npm run build" + }, + "devDependencies": { + "@rollup/plugin-typescript": "^4.1.1", + "@types/node": "^14.0.1", + "rollup": "^2.10.0", + "tslib": "^1.12.0", + "typescript": "^3.9.2" + } } diff --git a/rollup.config.js b/rollup.config.js new file mode 100644 index 0000000..b6ad679 --- /dev/null +++ b/rollup.config.js @@ -0,0 +1,11 @@ +import typescript from '@rollup/plugin-typescript'; + +export default [{ + input: 'KaitaiStream.ts', + output: { + dir: '.', + format: 'umd', + name: 'KaitaiStream' + }, + plugins: [typescript()] +}]; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..7909ea5 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,68 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig.json to read more about this file */ + + /* Basic Options */ + // "incremental": true, /* Enable incremental compilation */ + "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ + "module": "es2015", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ + // "checkJs": true, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ + "declaration": true, /* Generates corresponding '.d.ts' file. */ + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + // "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + "outDir": "./", /* Redirect output structure to the directory. */ + // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ + // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + + /* Strict Type-Checking Options */ + // "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + + /* Module Resolution Options */ + // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + // "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + + /* Experimental Options */ + // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + + /* Advanced Options */ + "skipLibCheck": true, /* Skip type checking of declaration files. */ + "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ + }, + "exclude": [] +} From 628219faa07712615b427ad9879671c0bcd4aa6b Mon Sep 17 00:00:00 2001 From: Tyler Schrock Date: Fri, 3 Dec 2021 01:55:41 -0500 Subject: [PATCH 02/20] Fixup jsdoc comments --- KaitaiStream.ts | 430 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 304 insertions(+), 126 deletions(-) diff --git a/KaitaiStream.ts b/KaitaiStream.ts index 047f795..7e99f28 100644 --- a/KaitaiStream.ts +++ b/KaitaiStream.ts @@ -1,16 +1,17 @@ // -*- mode: js; js-indent-level: 2; -*- /** - KaitaiStream is an implementation of Kaitai Struct API for JavaScript. - Based on DataStream - https://github.com/kig/DataStream.js - - @param {ArrayBuffer} arrayBuffer ArrayBuffer to read from. - @param {?Number} byteOffset Offset from arrayBuffer beginning for the KaitaiStream. - */ + * KaitaiStream is an implementation of Kaitai Struct API for JavaScript. + * Based on DataStream - https://github.com/kig/DataStream.js . + */ class KaitaiStream { // Temporary hack since I don't want to declare all members, yet [key: string]: any; + /** + * @param arrayBuffer ArrayBuffer to read from. + * @param byteOffset Offset from arrayBuffer beginning for the KaitaiStream. + */ constructor(arrayBuffer, byteOffset) { this._byteOffset = byteOffset || 0; if (arrayBuffer instanceof ArrayBuffer) { @@ -28,20 +29,20 @@ class KaitaiStream { } /** - Virtual byte length of the KaitaiStream backing buffer. - Updated to be max of original buffer size and last written size. - If dynamicSize is false is set to buffer size. - */ + * Virtual byte length of the KaitaiStream backing buffer. + * Updated to be max of original buffer size and last written size. + * If dynamicSize is false is set to buffer size. + */ _byteLength: number = 0; /** - Dependency configuration data. Holds urls for (optional) dynamic loading - of code dependencies from a remote server. For use by (static) processing functions. - - Caller should the supported keys to the asset urls as needed. - NOTE: `depUrls` is a static property of KaitaiStream (the factory),like the various - processing functions. It is NOT part of the prototype of instances. - */ + * Dependency configuration data. Holds urls for (optional) dynamic loading + * of code dependencies from a remote server. For use by (static) processing functions. + * + * Caller should the supported keys to the asset urls as needed. + * NOTE: `depUrls` is a static property of KaitaiStream (the factory), like the various + * processing functions. It is NOT part of the prototype of instances. + */ static depUrls = { // processZlib uses this and expected a link to a copy of pako. // specifically the pako_inflate.min.js script at: @@ -53,13 +54,18 @@ class KaitaiStream { static iconvlite: any; /** - Set/get the backing ArrayBuffer of the KaitaiStream object. - The setter updates the DataView to point to the new buffer. - */ + * Gets the backing ArrayBuffer of the KaitaiStream object. + * + * @returns The backing ArrayBuffer. + */ get buffer() { this._trimAlloc(); return this._buffer; } + /** + * Sets the backing ArrayBuffer of the KaitaiStream object and updates the + * DataView to point to the new buffer. + */ set buffer(v) { this._buffer = v; this._dataView = new DataView(this._buffer, this._byteOffset); @@ -67,12 +73,17 @@ class KaitaiStream { } /** - Set/get the byteOffset of the KaitaiStream object. - The setter updates the DataView to point to the new byteOffset. - */ + * Gets the byteOffset of the KaitaiStream object. + * + * @returns The byteOffset. + */ get byteOffset() { return this._byteOffset; } + /** + * Sets the byteOffset of the KaitaiStream object and updates the DataView to + * point to the new byteOffset. + */ set byteOffset(v) { this._byteOffset = v; this._dataView = new DataView(this._buffer, this._byteOffset); @@ -80,12 +91,17 @@ class KaitaiStream { } /** - Set/get the backing DataView of the KaitaiStream object. - The setter updates the buffer and byteOffset to point to the DataView values. - */ + * Gets the backing DataView of the KaitaiStream object. + * + * @returns The backing DataView. + */ get dataView() { return this._dataView; } + /** + * Sets the backing DataView of the KaitaiStream object and updates the buffer + * and byteOffset to point to the DataView values. + */ set dataView(v) { this._byteOffset = v.byteOffset; this._buffer = v.buffer; @@ -94,11 +110,11 @@ class KaitaiStream { } /** - Internal function to trim the KaitaiStream buffer when required. - Used for stripping out the extra bytes from the backing buffer when - the virtual byteLength is smaller than the buffer byteLength (happens after - growing the buffer with writes and not filling the extra space completely). - */ + * Internal function to trim the KaitaiStream buffer when required. + * Used for stripping out the extra bytes from the backing buffer when + * the virtual byteLength is smaller than the buffer byteLength (happens after + * growing the buffer with writes and not filling the extra space completely). + */ _trimAlloc() { if (this._byteLength === this._buffer.byteLength) { return; @@ -116,29 +132,31 @@ class KaitaiStream { // ======================================================================== /** - Returns true if the KaitaiStream seek pointer is at the end of buffer and - there's no more data to read. - - @return {boolean} True if the seek pointer is at the end of the buffer. + * Returns true if the KaitaiStream seek pointer is at the end of buffer and + * there's no more data to read. + * + * @returns True if the seek pointer is at the end of the buffer. */ isEof() { return this.pos >= this.size && this.bitsLeft === 0; }; /** - Sets the KaitaiStream read/write position to given position. - Clamps between 0 and KaitaiStream length. - - @param {number} pos Position to seek to. - */ + * Sets the KaitaiStream read/write position to given position. + * Clamps between 0 and KaitaiStream length. + * + * @param pos Position to seek to. + */ seek(pos) { var npos = Math.max(0, Math.min(this.size, pos)); this.pos = (isNaN(npos) || !isFinite(npos)) ? 0 : npos; }; /** - Returns the byte length of the KaitaiStream object. - */ + * Returns the byte length of the KaitaiStream object. + * + * @returns The byte length. + */ get size() { return this._byteLength - this._byteOffset; } @@ -152,9 +170,10 @@ class KaitaiStream { // ------------------------------------------------------------------------ /** - Reads an 8-bit signed int from the stream. - @return {number} The read number. - */ + * Reads an 8-bit signed int from the stream. + * + * @returns The read number. + */ readS1() { this.ensureBytesLeft(1); var v = this._dataView.getInt8(this.pos); @@ -167,9 +186,10 @@ class KaitaiStream { // ........................................................................ /** - Reads a 16-bit big-endian signed int from the stream. - @return {number} The read number. - */ + * Reads a 16-bit big-endian signed int from the stream. + * + * @returns The read number. + */ readS2be() { this.ensureBytesLeft(2); var v = this._dataView.getInt16(this.pos); @@ -178,9 +198,10 @@ class KaitaiStream { }; /** - Reads a 32-bit big-endian signed int from the stream. - @return {number} The read number. - */ + * Reads a 32-bit big-endian signed int from the stream. + * + * @returns The read number. + */ readS4be() { this.ensureBytesLeft(4); var v = this._dataView.getInt32(this.pos); @@ -189,12 +210,13 @@ class KaitaiStream { }; /** - Reads a 64-bit big-endian unsigned int from the stream. Note that - JavaScript does not support 64-bit integers natively, so it will - automatically upgrade internal representation to use IEEE 754 - double precision float. - @return {number} The read number. - */ + * Reads a 64-bit big-endian unsigned int from the stream. Note that + * JavaScript does not support 64-bit integers natively, so it will + * automatically upgrade internal representation to use IEEE 754 + * double precision float. + * + * @returns The read number. + */ readS8be() { this.ensureBytesLeft(8); var v1 = this.readU4be(); @@ -213,9 +235,10 @@ class KaitaiStream { // ........................................................................ /** - Reads a 16-bit little-endian signed int from the stream. - @return {number} The read number. - */ + * Reads a 16-bit little-endian signed int from the stream. + * + * @returns The read number. + */ readS2le() { this.ensureBytesLeft(2); var v = this._dataView.getInt16(this.pos, true); @@ -224,9 +247,10 @@ class KaitaiStream { }; /** - Reads a 32-bit little-endian signed int from the stream. - @return {number} The read number. - */ + * Reads a 32-bit little-endian signed int from the stream. + * + * @returns The read number. + */ readS4le() { this.ensureBytesLeft(4); var v = this._dataView.getInt32(this.pos, true); @@ -235,12 +259,13 @@ class KaitaiStream { }; /** - Reads a 64-bit little-endian unsigned int from the stream. Note that - JavaScript does not support 64-bit integers natively, so it will - automatically upgrade internal representation to use IEEE 754 - double precision float. - @return {number} The read number. - */ + * Reads a 64-bit little-endian unsigned int from the stream. Note that + * JavaScript does not support 64-bit integers natively, so it will + * automatically upgrade internal representation to use IEEE 754 + * double precision float. + * + * @returns The read number. + */ readS8le() { this.ensureBytesLeft(8); var v1 = this.readU4le(); @@ -259,9 +284,10 @@ class KaitaiStream { // ------------------------------------------------------------------------ /** - Reads an 8-bit unsigned int from the stream. - @return {number} The read number. - */ + * Reads an 8-bit unsigned int from the stream. + * + * @returns The read number. + */ readU1() { this.ensureBytesLeft(1); var v = this._dataView.getUint8(this.pos); @@ -274,9 +300,10 @@ class KaitaiStream { // ........................................................................ /** - Reads a 16-bit big-endian unsigned int from the stream. - @return {number} The read number. - */ + * Reads a 16-bit big-endian unsigned int from the stream. + * + * @returns The read number. + */ readU2be() { this.ensureBytesLeft(2); var v = this._dataView.getUint16(this.pos); @@ -285,9 +312,10 @@ class KaitaiStream { }; /** - Reads a 32-bit big-endian unsigned int from the stream. - @return {number} The read number. - */ + * Reads a 32-bit big-endian unsigned int from the stream. + * + * @returns The read number. + */ readU4be() { this.ensureBytesLeft(4); var v = this._dataView.getUint32(this.pos); @@ -296,12 +324,13 @@ class KaitaiStream { }; /** - Reads a 64-bit big-endian unsigned int from the stream. Note that - JavaScript does not support 64-bit integers natively, so it will - automatically upgrade internal representation to use IEEE 754 - double precision float. - @return {number} The read number. - */ + * Reads a 64-bit big-endian unsigned int from the stream. Note that + * JavaScript does not support 64-bit integers natively, so it will + * automatically upgrade internal representation to use IEEE 754 + * double precision float. + * + * @returns The read number. + */ readU8be() { this.ensureBytesLeft(8); var v1 = this.readU4be(); @@ -314,9 +343,10 @@ class KaitaiStream { // ........................................................................ /** - Reads a 16-bit little-endian unsigned int from the stream. - @return {number} The read number. - */ + * Reads a 16-bit little-endian unsigned int from the stream. + * + * @returns The read number. + */ readU2le() { this.ensureBytesLeft(2); var v = this._dataView.getUint16(this.pos, true); @@ -325,9 +355,10 @@ class KaitaiStream { } /** - Reads a 32-bit little-endian unsigned int from the stream. - @return {number} The read number. - */ + * Reads a 32-bit little-endian unsigned int from the stream. + * + * @returns The read number. + */ readU4le() { this.ensureBytesLeft(4); var v = this._dataView.getUint32(this.pos, true); @@ -336,12 +367,13 @@ class KaitaiStream { } /** - Reads a 64-bit little-endian unsigned int from the stream. Note that - JavaScript does not support 64-bit integers natively, so it will - automatically upgrade internal representation to use IEEE 754 - double precision float. - @return {number} The read number. - */ + * Reads a 64-bit little-endian unsigned int from the stream. Note that + * JavaScript does not support 64-bit integers natively, so it will + * automatically upgrade internal representation to use IEEE 754 + * double precision float. + * + * @returns The read number. + */ readU8le() { this.ensureBytesLeft(8); var v1 = this.readU4le(); @@ -358,6 +390,11 @@ class KaitaiStream { // Big endian // ------------------------------------------------------------------------ + /** + * Reads a 32-bit big-endian float from the stream. + * + * @returns The read number. + */ readF4be() { this.ensureBytesLeft(4); var v = this._dataView.getFloat32(this.pos); @@ -365,6 +402,11 @@ class KaitaiStream { return v; }; + /** + * Reads a 64-bit big-endian float from the stream. + * + * @returns The read number. + */ readF8be() { this.ensureBytesLeft(8); var v = this._dataView.getFloat64(this.pos); @@ -376,6 +418,11 @@ class KaitaiStream { // Little endian // ------------------------------------------------------------------------ + /** + * Reads a 32-bit little-endian float from the stream. + * + * @returns The read number. + */ readF4le() { this.ensureBytesLeft(4); var v = this._dataView.getFloat32(this.pos, true); @@ -383,6 +430,11 @@ class KaitaiStream { return v; }; + /** + * Reads a 64-bit little-endian float from the stream. + * + * @returns The read number. + */ readF8le() { this.ensureBytesLeft(8); var v = this._dataView.getFloat64(this.pos, true); @@ -394,11 +446,19 @@ class KaitaiStream { // Unaligned bit values // ------------------------------------------------------------------------ + /** + * Aligns bit reading to the byte boundry. + */ alignToByte() { this.bits = 0; this.bitsLeft = 0; }; + /** + * @param n The number of bits to read. + * @returns The read bits. + * @throws {Error} + */ readBitsIntBe(n) { // JS only supports bit operations on 32 bits if (n > 32) { @@ -432,14 +492,21 @@ class KaitaiStream { }; /** - * Unused since Kaitai Struct Compiler v0.9+ - compatibility with older versions + * Unused since Kaitai Struct Compiler v0.9+ - compatibility with older versions. * - * @deprecated use {@link readBitsIntBe} instead + * @deprecated Use {@link readBitsIntBe} instead. + * @param n The number of bits to read. + * @returns The read bits. */ readBitsInt(n) { return this.readBitsIntBe(n); } + /** + * @param n The number of bits to read. + * @returns The read bits. + * @throws {Error} + */ readBitsIntLe(n) { // JS only supports bit operations on 32 bits if (n > 32) { @@ -470,25 +537,40 @@ class KaitaiStream { }; /** - Native endianness. Either KaitaiStream.BIG_ENDIAN or KaitaiStream.LITTLE_ENDIAN - depending on the platform endianness. - - @type {boolean} - */ + * Native endianness. Either KaitaiStream.BIG_ENDIAN or KaitaiStream.LITTLE_ENDIAN + * depending on the platform endianness. + */ static endianness = new Int8Array(new Int16Array([1]).buffer)[0] > 0; // ======================================================================== // Byte arrays // ======================================================================== + /** + * @param len The number of bytes to read. + * @returns The read bytes. + */ readBytes(len) { return this.mapUint8Array(len); }; + /** + * @returns The read bytes. + */ readBytesFull() { return this.mapUint8Array(this.size - this.pos); }; + /** + * Reads bytes until the terminator byte is found. + * + * @param terminator The terminator byte. + * @param include True if the terminator should be included with the returned bytes. + * @param consume True if the terminator should be consumed from the input stream. + * @param eosError True to throw an error if the end of stream is reached. + * @returns The read bytes. + * @throws {string} + */ readBytesTerm(terminator, include, consume, eosError) { var blen = this.size - this.pos; var u8 = new Uint8Array(this._buffer, this._byteOffset + this.pos); @@ -514,7 +596,13 @@ class KaitaiStream { } }; - // Unused since Kaitai Struct Compiler v0.9+ - compatibility with older versions + /** + * Unused since Kaitai Struct Compiler v0.9+ - compatibility with older versions. + * + * @param expected The expected bytes. + * @returns The read bytes. + * @throws {KaitaiStream.UnexpectedDataError} + */ ensureFixedContents(expected) { var actual = this.readBytes(expected.length); if (actual.length !== expected.length) { @@ -529,6 +617,11 @@ class KaitaiStream { return actual; }; + /** + * @param data The data. + * @param padByte The byte to strip. + * @returns The stripped data. + */ static bytesStripRight(data, padByte) { var newLen = data.length; while (data[newLen - 1] === padByte) { @@ -537,6 +630,12 @@ class KaitaiStream { return data.slice(0, newLen); }; + /** + * @param data The data. + * @param term The terminator. + * @param include True if the returned bytes should include the terminator. + * @returns The terminated bytes. + */ static bytesTerminate(data, term, include) { var newLen = 0; var maxLen = data.length; @@ -548,6 +647,11 @@ class KaitaiStream { return data.slice(0, newLen); }; + /** + * @param arr The bytes. + * @param encoding The character encoding. + * @returns The decoded string. + */ static bytesToStr(arr, encoding) { if (encoding == null || encoding.toLowerCase() === "ascii") { return KaitaiStream.createStringFromArray(arr); @@ -584,6 +688,11 @@ class KaitaiStream { // Byte array processing // ======================================================================== + /** + * @param data The input bytes. + * @param key The key byte. + * @returns The Xor'd bytes. + */ static processXorOne(data, key) { var r = new Uint8Array(data.length); var dl = data.length; @@ -592,6 +701,11 @@ class KaitaiStream { return r; }; + /** + * @param data The input bytes. + * @param key The key bytes. + * @returns The Xor'd bytes. + */ static processXorMany(data, key) { var dl = data.length; var r = new Uint8Array(dl); @@ -606,6 +720,13 @@ class KaitaiStream { return r; }; + /** + * @param data The input bytes. + * @param amount The number of bytes to rotate. + * @param groupSize The number of bytes in each group. + * @returns The rotated bytes. + * @throws {string} + */ static processRotateLeft(data, amount, groupSize) { if (groupSize !== 1) throw ("unable to rotate group of " + groupSize + " bytes yet"); @@ -620,6 +741,10 @@ class KaitaiStream { return r; }; + /** + * @param buf The input bytes. + * @returns The uncompressed bytes. + */ static processZlib(buf) { if (typeof require !== 'undefined') { // require is available - we're running under node @@ -649,6 +774,12 @@ class KaitaiStream { // Misc runtime operations // ======================================================================== + /** + * @param a The dividend. + * @param b The divisor. + * @returns The result of `a` mod `b`. + * @throws {string} + */ static mod(a, b) { if (b <= 0) throw "mod divisor <= 0"; @@ -658,6 +789,12 @@ class KaitaiStream { return r; }; + /** + * Gets the smallest value in an array. + * + * @param arr The input array. + * @returns The smallest value. + */ static arrayMin(arr) { var min = arr[0]; var x; @@ -668,6 +805,12 @@ class KaitaiStream { return min; }; + /** + * Gets the largest value in an array. + * + * @param arr The input array. + * @returns The largest value. + */ static arrayMax(arr) { var max = arr[0]; var x; @@ -678,6 +821,13 @@ class KaitaiStream { return max; }; + /** + * Compares two arrays of bytes from left to right. + * + * @param a The first array. + * @param b The second array. + * @returns `0` if the arrays are the equal, a positive number if `a` is greater than `b`, or a negative number if `a` is less than `b`. + */ static byteArrayCompare(a, b) { if (a === b) return 0; @@ -706,6 +856,10 @@ class KaitaiStream { bytesReq: number; bytesAvail: number; + /** + * @param bytesReq The number of bytes requested. + * @param bytesAvail The number of bytes available. + */ constructor(bytesReq, bytesAvail) { super(); // Workaround https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work @@ -717,11 +871,17 @@ class KaitaiStream { } } - // Unused since Kaitai Struct Compiler v0.9+ - compatibility with older versions + /** + * Unused since Kaitai Struct Compiler v0.9+ - compatibility with older versions. + */ static UnexpectedDataError = class extends Error { expected: any; actual: any; + /** + * @param expected The expected value. + * @param actual The actual value. + */ constructor(expected, actual) { super(); // Workaround https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work @@ -746,6 +906,10 @@ class KaitaiStream { expected: any; actual: any; + /** + * @param expected The expected value. + * @param actual The actual value. + */ constructor(expected, actual) { super(); // Workaround https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work @@ -761,6 +925,10 @@ class KaitaiStream { min: any; actual: any; + /** + * @param min The minimum allowed value. + * @param actual The actual value. + */ constructor(min, actual) { super(); // Workaround https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work @@ -776,6 +944,10 @@ class KaitaiStream { max: any; actual: any; + /** + * @param max The maximum allowed value. + * @param actual The actual value. + */ constructor(max, actual) { super(); // Workaround https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work @@ -790,7 +962,10 @@ class KaitaiStream { static ValidationNotAnyOfError = class extends Error { actual: any; - constructor(actual, io, srcPath) { + /** + * @param actual The actual value. + */ + constructor(actual) { super(); // Workaround https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work Object.setPrototypeOf(this, KaitaiStream.ValidationNotAnyOfError.prototype); @@ -803,7 +978,10 @@ class KaitaiStream { static ValidationExprError = class extends Error { actual: any; - constructor(actual, io, srcPath) { + /** + * @param actual The actual value. + */ + constructor(actual) { super(); // Workaround https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work Object.setPrototypeOf(this, KaitaiStream.ValidationExprError.prototype); @@ -814,11 +992,12 @@ class KaitaiStream { }; /** - Ensures that we have an least `length` bytes left in the stream. - If that's not true, throws an EOFError. - - @param {number} length Number of bytes to require - */ + * Ensures that we have an least `length` bytes left in the stream. + * If that's not true, throws an EOFError. + * + * @param length Number of bytes to require. + * @throws {KaitaiStream.EOFError} + */ ensureBytesLeft(length) { if (this.pos + length > this.size) { throw new KaitaiStream.EOFError(length, this.size - this.pos); @@ -826,13 +1005,12 @@ class KaitaiStream { }; /** - Maps a Uint8Array into the KaitaiStream buffer. - - Nice for quickly reading in data. - - @param {number} length Number of elements to map. - @return {Object} Uint8Array to the KaitaiStream backing buffer. - */ + * Maps a Uint8Array into the KaitaiStream buffer. + * Nice for quickly reading in data. + * + * @param length Number of elements to map. + * @returns A Uint8Array to the KaitaiStream backing buffer. + */ mapUint8Array(length) { length |= 0; @@ -844,13 +1022,13 @@ class KaitaiStream { }; /** - Creates an array from an array of character codes. - Uses String.fromCharCode in chunks for memory efficiency and then concatenates - the resulting string chunks. - - @param {array|Uint8Array} array Array of character codes. - @return {string} String created from the character codes. - */ + * Creates an array from an array of character codes. + * Uses String.fromCharCode in chunks for memory efficiency and then concatenates + * the resulting string chunks. + * + * @param array Array of character codes. + * @returns String created from the character codes. + */ static createStringFromArray = function (array) { var chunk_size = 0x8000; var chunks = []; From ecf644a4300140be7631327f03152fa063a5022c Mon Sep 17 00:00:00 2001 From: Tyler Schrock Date: Fri, 3 Dec 2021 06:01:40 -0500 Subject: [PATCH 03/20] Update typescript dependencies --- .gitignore | 5 +- package-lock.json | 91 ++++++++++++++++++----------- package.json | 10 ++-- tsconfig.json | 144 ++++++++++++++++++++++++++++------------------ 4 files changed, 155 insertions(+), 95 deletions(-) diff --git a/.gitignore b/.gitignore index 3398fbc..86df457 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ -node_modules +/node_modules/ # Generated by Typescript compiler -KaitaiStream.d.ts +/KaitaiStream.d.ts +/KaitaiStream.js diff --git a/package-lock.json b/package-lock.json index f561584..3080c05 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,19 +5,19 @@ "requires": true, "dependencies": { "@rollup/plugin-typescript": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-4.1.1.tgz", - "integrity": "sha512-KYZCn1Iw9hZWkeEPqPs5YjlmvSjR7UdezVca8z0e8rm/29wU24UD9Y4IZHhnc9tm749hzsgBTiOUxA85gfShEQ==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-8.3.0.tgz", + "integrity": "sha512-I5FpSvLbtAdwJ+naznv+B4sjXZUcIvLLceYpITAn7wAP8W0wqc5noLdGIp9HGVntNhRWXctwPYrSSFQxtl0FPA==", "dev": true, "requires": { - "@rollup/pluginutils": "^3.0.1", - "resolve": "^1.14.1" + "@rollup/pluginutils": "^3.1.0", + "resolve": "^1.17.0" } }, "@rollup/pluginutils": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.0.10.tgz", - "integrity": "sha512-d44M7t+PjmMrASHbhgpSbVgtL6EFyX7J4mYxwQ/c5eoaE6N2VgCgEcWVzNnwycIloti+/MpwFr8qfw+nRw00sw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", + "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", "dev": true, "requires": { "@types/estree": "0.0.39", @@ -32,9 +32,9 @@ "dev": true }, "@types/node": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.1.tgz", - "integrity": "sha512-FAYBGwC+W6F9+huFIDtn43cpy7+SzG+atzRiTfdp3inUKL2hXnd4rG8hylJLIh4+hqrQy1P17kvJByE/z825hA==", + "version": "16.11.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.11.tgz", + "integrity": "sha512-KB0sixD67CeecHC33MYn+eYARkqTheIRNuu97y2XMjR7Wu3XibO1vaY6VBV6O/a89SPI81cEUIYT87UqUWlZNw==", "dev": true }, "estree-walker": { @@ -44,52 +44,77 @@ "dev": true }, "fsevents": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", - "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "dev": true, "optional": true }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "is-core-module": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", + "integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, "picomatch": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", - "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", "dev": true }, "resolve": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", - "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", "dev": true, "requires": { + "is-core-module": "^2.2.0", "path-parse": "^1.0.6" } }, "rollup": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.10.0.tgz", - "integrity": "sha512-7BmpEfUN9P6esJzWIn3DmR//90mW6YwYB1t3y48LpF8ITpYtL8s1kEirMKqUu44dVH/6a/rs0EuwYVL3FuRDoA==", + "version": "2.60.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.60.2.tgz", + "integrity": "sha512-1Bgjpq61sPjgoZzuiDSGvbI1tD91giZABgjCQBKM5aYLnzjq52GoDuWVwT/cm/MCxCMPU8gqQvkj8doQ5C8Oqw==", "dev": true, "requires": { - "fsevents": "~2.1.2" + "fsevents": "~2.3.2" } }, "tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", "dev": true }, "typescript": { - "version": "3.9.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.2.tgz", - "integrity": "sha512-q2ktq4n/uLuNNShyayit+DTobV2ApPEo/6so68JaD5ojvc/6GClBipedB9zNWYxRSAlZXAe405Rlijzl6qDiSw==", + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.2.tgz", + "integrity": "sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw==", "dev": true } } diff --git a/package.json b/package.json index 6ba29c4..09bafab 100644 --- a/package.json +++ b/package.json @@ -33,10 +33,10 @@ "prepublish": "npm run build" }, "devDependencies": { - "@rollup/plugin-typescript": "^4.1.1", - "@types/node": "^14.0.1", - "rollup": "^2.10.0", - "tslib": "^1.12.0", - "typescript": "^3.9.2" + "@rollup/plugin-typescript": "^8.3.0", + "@types/node": "^16.11.11", + "rollup": "^2.60.2", + "tslib": "^2.3.1", + "typescript": "^4.5.2" } } diff --git a/tsconfig.json b/tsconfig.json index 7909ea5..99a44ad 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,67 +2,101 @@ "compilerOptions": { /* Visit https://aka.ms/tsconfig.json to read more about this file */ - /* Basic Options */ - // "incremental": true, /* Enable incremental compilation */ - "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ - "module": "es2015", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ - // "checkJs": true, /* Report errors in .js files. */ - // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ - "declaration": true, /* Generates corresponding '.d.ts' file. */ - // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ - // "sourceMap": true, /* Generates corresponding '.map' file. */ - // "outFile": "./", /* Concatenate and emit output to single file. */ - "outDir": "./", /* Redirect output structure to the directory. */ - // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ - // "composite": true, /* Enable project compilation */ - // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ - // "removeComments": true, /* Do not emit comments to output. */ - // "noEmit": true, /* Do not emit outputs. */ - // "importHelpers": true, /* Import emit helpers from 'tslib'. */ - // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ - // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + /* Projects */ + // "incremental": true, /* Enable incremental compilation */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ - /* Strict Type-Checking Options */ - // "strict": true, /* Enable all strict type-checking options. */ - // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ - // "strictNullChecks": true, /* Enable strict null checks. */ - // "strictFunctionTypes": true, /* Enable strict checking of function types. */ - // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ - // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ - // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ - // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + /* Language and Environment */ + "target": "es5", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */ + // "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ - /* Additional Checks */ - // "noUnusedLocals": true, /* Report errors on unused locals. */ - // "noUnusedParameters": true, /* Report errors on unused parameters. */ - // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ - // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + /* Modules */ + "module": "es2015", /* Specify what module code is generated. */ + // "rootDir": "./", /* Specify the root folder within your source files. */ + // "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "resolveJsonModule": true, /* Enable importing .json files */ + // "noResolve": true, /* Disallow `import`s, `require`s or ``s from expanding the number of files TypeScript should add to a project. */ - /* Module Resolution Options */ - // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ - // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ - // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ - // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ - // "typeRoots": [], /* List of folders to include type definitions from. */ - // "types": [], /* Type declaration files to be included in compilation. */ - // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ - // "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ - // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ - // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + /* JavaScript Support */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */ - /* Source Map Options */ - // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ - // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + /* Emit */ + "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */ + "outDir": "./", /* Specify an output folder for all emitted files. */ + // "removeComments": true, /* Disable emitting comments. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ - /* Experimental Options */ - // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ - // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */ + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ - /* Advanced Options */ - "skipLibCheck": true, /* Skip type checking of declaration files. */ - "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ + /* Type Checking */ + // "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */ + // "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */ + // "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ }, "exclude": [] } From 3dc2785326fd809579bc7067d4c10b811aaf38d0 Mon Sep 17 00:00:00 2001 From: Tyler Schrock Date: Fri, 3 Dec 2021 07:16:38 -0500 Subject: [PATCH 04/20] Add more types and enable strict type checking --- KaitaiStream.ts | 118 +++++++++++++++++++++++++++++------------------- tsconfig.json | 2 +- 2 files changed, 72 insertions(+), 48 deletions(-) diff --git a/KaitaiStream.ts b/KaitaiStream.ts index 7e99f28..1ff6a1e 100644 --- a/KaitaiStream.ts +++ b/KaitaiStream.ts @@ -1,18 +1,38 @@ // -*- mode: js; js-indent-level: 2; -*- +// Interfaces for optional dependencies +interface IconvLite { + decode(buffer: Buffer | Uint8Array, encoding: string): string +} + +interface Zlib { + inflateSync(buf: ArrayBuffer | NodeJS.ArrayBufferView): Buffer; +} + +interface Pako { + inflate(data: Uint8Array | number[]): Uint8Array; +} + +// Workaround for https://github.com/microsoft/TypeScript/issues/36470 +declare global { + interface CallableFunction { + apply(this: (this: T, ...args: A[]) => R, thisArg: T, args: ArrayLike): R; + } +} + +// When loaded into a web worker, pako gets added to the global scope +declare const pako: Pako; + /** * KaitaiStream is an implementation of Kaitai Struct API for JavaScript. * Based on DataStream - https://github.com/kig/DataStream.js . */ class KaitaiStream { - // Temporary hack since I don't want to declare all members, yet - [key: string]: any; - /** * @param arrayBuffer ArrayBuffer to read from. * @param byteOffset Offset from arrayBuffer beginning for the KaitaiStream. */ - constructor(arrayBuffer, byteOffset) { + constructor(arrayBuffer: ArrayBuffer | DataView | number, byteOffset?: number) { this._byteOffset = byteOffset || 0; if (arrayBuffer instanceof ArrayBuffer) { this.buffer = arrayBuffer; @@ -33,7 +53,14 @@ class KaitaiStream { * Updated to be max of original buffer size and last written size. * If dynamicSize is false is set to buffer size. */ - _byteLength: number = 0; + private _byteLength = 0; + private _byteOffset = 0; + private _buffer!: ArrayBuffer; + private _dataView!: DataView; + + public pos: number; + public bits = 0; + public bitsLeft = 0; /** * Dependency configuration data. Holds urls for (optional) dynamic loading @@ -43,15 +70,15 @@ class KaitaiStream { * NOTE: `depUrls` is a static property of KaitaiStream (the factory), like the various * processing functions. It is NOT part of the prototype of instances. */ - static depUrls = { + static depUrls: Record = { // processZlib uses this and expected a link to a copy of pako. // specifically the pako_inflate.min.js script at: // https://raw.githubusercontent.com/nodeca/pako/master/dist/pako_inflate.min.js zlib: undefined }; - static zlib: any; - static iconvlite: any; + public static iconvlite?: IconvLite; + public static zlib?: Pako | Zlib; /** * Gets the backing ArrayBuffer of the KaitaiStream object. @@ -147,7 +174,7 @@ class KaitaiStream { * * @param pos Position to seek to. */ - seek(pos) { + seek(pos: number) { var npos = Math.max(0, Math.min(this.size, pos)); this.pos = (isNaN(npos) || !isFinite(npos)) ? 0 : npos; }; @@ -459,7 +486,7 @@ class KaitaiStream { * @returns The read bits. * @throws {Error} */ - readBitsIntBe(n) { + readBitsIntBe(n: number) { // JS only supports bit operations on 32 bits if (n > 32) { throw new Error(`readBitsIntBe: the maximum supported bit length is 32 (tried to read ${n} bits)`); @@ -498,7 +525,7 @@ class KaitaiStream { * @param n The number of bits to read. * @returns The read bits. */ - readBitsInt(n) { + readBitsInt(n: number) { return this.readBitsIntBe(n); } @@ -507,7 +534,7 @@ class KaitaiStream { * @returns The read bits. * @throws {Error} */ - readBitsIntLe(n) { + readBitsIntLe(n: number) { // JS only supports bit operations on 32 bits if (n > 32) { throw new Error(`readBitsIntLe: the maximum supported bit length is 32 (tried to read ${n} bits)`); @@ -550,7 +577,7 @@ class KaitaiStream { * @param len The number of bytes to read. * @returns The read bytes. */ - readBytes(len) { + readBytes(len: number) { return this.mapUint8Array(len); }; @@ -571,7 +598,7 @@ class KaitaiStream { * @returns The read bytes. * @throws {string} */ - readBytesTerm(terminator, include, consume, eosError) { + readBytesTerm(terminator: number, include: boolean, consume: boolean, eosError: boolean) { var blen = this.size - this.pos; var u8 = new Uint8Array(this._buffer, this._byteOffset + this.pos); for (var i = 0; i < blen && u8[i] !== terminator; i++); // find first zero byte @@ -603,7 +630,7 @@ class KaitaiStream { * @returns The read bytes. * @throws {KaitaiStream.UnexpectedDataError} */ - ensureFixedContents(expected) { + ensureFixedContents(expected: ArrayLike) { var actual = this.readBytes(expected.length); if (actual.length !== expected.length) { throw new KaitaiStream.UnexpectedDataError(expected, actual); @@ -622,7 +649,7 @@ class KaitaiStream { * @param padByte The byte to strip. * @returns The stripped data. */ - static bytesStripRight(data, padByte) { + static bytesStripRight(data: number[] | NodeJS.TypedArray, padByte: number) { var newLen = data.length; while (data[newLen - 1] === padByte) { newLen--; @@ -636,7 +663,7 @@ class KaitaiStream { * @param include True if the returned bytes should include the terminator. * @returns The terminated bytes. */ - static bytesTerminate(data, term, include) { + static bytesTerminate(data: number[] | NodeJS.TypedArray, term: number, include: boolean) { var newLen = 0; var maxLen = data.length; while (newLen < maxLen && data[newLen] !== term) { @@ -652,7 +679,7 @@ class KaitaiStream { * @param encoding The character encoding. * @returns The decoded string. */ - static bytesToStr(arr, encoding) { + static bytesToStr(arr: Uint8Array, encoding: BufferEncoding) { if (encoding == null || encoding.toLowerCase() === "ascii") { return KaitaiStream.createStringFromArray(arr); } else { @@ -671,12 +698,11 @@ class KaitaiStream { case 'ucs-2': case 'utf16le': case 'utf-16le': - return new Buffer(arr).toString(encoding); - break; + return Buffer.from(arr).toString(encoding); default: // unsupported encoding, we'll have to resort to iconv-lite if (typeof KaitaiStream.iconvlite === 'undefined') - KaitaiStream.iconvlite = require('iconv-lite'); + KaitaiStream.iconvlite = require('iconv-lite') as IconvLite; return KaitaiStream.iconvlite.decode(arr, encoding); } @@ -693,7 +719,7 @@ class KaitaiStream { * @param key The key byte. * @returns The Xor'd bytes. */ - static processXorOne(data, key) { + static processXorOne(data: ArrayLike, key: number) { var r = new Uint8Array(data.length); var dl = data.length; for (var i = 0; i < dl; i++) @@ -706,7 +732,7 @@ class KaitaiStream { * @param key The key bytes. * @returns The Xor'd bytes. */ - static processXorMany(data, key) { + static processXorMany(data: ArrayLike, key: ArrayLike) { var dl = data.length; var r = new Uint8Array(dl); var kl = key.length; @@ -727,7 +753,7 @@ class KaitaiStream { * @returns The rotated bytes. * @throws {string} */ - static processRotateLeft(data, amount, groupSize) { + static processRotateLeft(data: ArrayLike, amount: number, groupSize: number) { if (groupSize !== 1) throw ("unable to rotate group of " + groupSize + " bytes yet"); @@ -745,16 +771,15 @@ class KaitaiStream { * @param buf The input bytes. * @returns The uncompressed bytes. */ - static processZlib(buf) { + static processZlib(buf: Uint8Array) { if (typeof require !== 'undefined') { // require is available - we're running under node if (typeof KaitaiStream.zlib === 'undefined') - KaitaiStream.zlib = require('zlib'); + KaitaiStream.zlib = require('zlib') as Zlib; // use node's zlib module API - var r = KaitaiStream.zlib.inflateSync( + return (KaitaiStream.zlib as Zlib).inflateSync( Buffer.from(buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength)) ); - return r; } else { // no require() - assume we're running as a web worker in browser. // user should have configured KaitaiStream.depUrls.zlib, if not @@ -762,11 +787,10 @@ class KaitaiStream { if (typeof KaitaiStream.zlib === 'undefined' && typeof KaitaiStream.depUrls.zlib !== 'undefined') { importScripts(KaitaiStream.depUrls.zlib); - KaitaiStream.zlib = (self as any).pako; + KaitaiStream.zlib = pako; } // use pako API - r = KaitaiStream.zlib.inflate(buf); - return r; + return (KaitaiStream.zlib as Pako).inflate(buf); } }; @@ -780,7 +804,7 @@ class KaitaiStream { * @returns The result of `a` mod `b`. * @throws {string} */ - static mod(a, b) { + static mod(a: number, b: number) { if (b <= 0) throw "mod divisor <= 0"; var r = a % b; @@ -795,7 +819,7 @@ class KaitaiStream { * @param arr The input array. * @returns The smallest value. */ - static arrayMin(arr) { + static arrayMin(arr: ArrayLike) { var min = arr[0]; var x; for (var i = 1, n = arr.length; i < n; ++i) { @@ -811,7 +835,7 @@ class KaitaiStream { * @param arr The input array. * @returns The largest value. */ - static arrayMax(arr) { + static arrayMax(arr: ArrayLike) { var max = arr[0]; var x; for (var i = 1, n = arr.length; i < n; ++i) { @@ -828,7 +852,7 @@ class KaitaiStream { * @param b The second array. * @returns `0` if the arrays are the equal, a positive number if `a` is greater than `b`, or a negative number if `a` is less than `b`. */ - static byteArrayCompare(a, b) { + static byteArrayCompare(a: ArrayLike, b: ArrayLike) { if (a === b) return 0; var al = a.length; @@ -860,7 +884,7 @@ class KaitaiStream { * @param bytesReq The number of bytes requested. * @param bytesAvail The number of bytes available. */ - constructor(bytesReq, bytesAvail) { + constructor(bytesReq: number, bytesAvail: number) { super(); // Workaround https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work Object.setPrototypeOf(this, KaitaiStream.EOFError.prototype); @@ -882,7 +906,7 @@ class KaitaiStream { * @param expected The expected value. * @param actual The actual value. */ - constructor(expected, actual) { + constructor(expected: any, actual: any) { super(); // Workaround https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work Object.setPrototypeOf(this, KaitaiStream.UnexpectedDataError.prototype); @@ -910,7 +934,7 @@ class KaitaiStream { * @param expected The expected value. * @param actual The actual value. */ - constructor(expected, actual) { + constructor(expected: any, actual: any) { super(); // Workaround https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work Object.setPrototypeOf(this, KaitaiStream.ValidationNotEqualError.prototype); @@ -929,7 +953,7 @@ class KaitaiStream { * @param min The minimum allowed value. * @param actual The actual value. */ - constructor(min, actual) { + constructor(min: any, actual: any) { super(); // Workaround https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work Object.setPrototypeOf(this, KaitaiStream.ValidationLessThanError.prototype); @@ -948,7 +972,7 @@ class KaitaiStream { * @param max The maximum allowed value. * @param actual The actual value. */ - constructor(max, actual) { + constructor(max: any, actual: any) { super(); // Workaround https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work Object.setPrototypeOf(this, KaitaiStream.ValidationGreaterThanError.prototype); @@ -965,7 +989,7 @@ class KaitaiStream { /** * @param actual The actual value. */ - constructor(actual) { + constructor(actual: any) { super(); // Workaround https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work Object.setPrototypeOf(this, KaitaiStream.ValidationNotAnyOfError.prototype); @@ -981,7 +1005,7 @@ class KaitaiStream { /** * @param actual The actual value. */ - constructor(actual) { + constructor(actual: any) { super(); // Workaround https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work Object.setPrototypeOf(this, KaitaiStream.ValidationExprError.prototype); @@ -998,7 +1022,7 @@ class KaitaiStream { * @param length Number of bytes to require. * @throws {KaitaiStream.EOFError} */ - ensureBytesLeft(length) { + ensureBytesLeft(length: number) { if (this.pos + length > this.size) { throw new KaitaiStream.EOFError(length, this.size - this.pos); } @@ -1011,7 +1035,7 @@ class KaitaiStream { * @param length Number of elements to map. * @returns A Uint8Array to the KaitaiStream backing buffer. */ - mapUint8Array(length) { + mapUint8Array(length: number) { length |= 0; this.ensureBytesLeft(length); @@ -1029,12 +1053,12 @@ class KaitaiStream { * @param array Array of character codes. * @returns String created from the character codes. */ - static createStringFromArray = function (array) { + static createStringFromArray = function (array: Array | Uint8Array) { var chunk_size = 0x8000; var chunks = []; - var useSubarray = typeof array.subarray === 'function'; for (var i = 0; i < array.length; i += chunk_size) { - chunks.push(String.fromCharCode.apply(null, useSubarray ? array.subarray(i, i + chunk_size) : array.slice(i, i + chunk_size))); + const chunk = 'subarray' in array ? array.subarray(i, i + chunk_size) : array.slice(i, i + chunk_size) + chunks.push(String.fromCharCode.apply(null, chunk)); } return chunks.join(""); }; diff --git a/tsconfig.json b/tsconfig.json index 99a44ad..86e0333 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -74,7 +74,7 @@ "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ /* Type Checking */ - // "strict": true, /* Enable all strict type-checking options. */ + "strict": true, /* Enable all strict type-checking options. */ // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */ // "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */ // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ From 5d15c52c81b3653f7d232f5e4eff2fbfdee024c9 Mon Sep 17 00:00:00 2001 From: Tyler Schrock Date: Fri, 3 Dec 2021 07:22:53 -0500 Subject: [PATCH 05/20] Clean up error classes --- KaitaiStream.ts | 54 ++++++++++++++++++++++--------------------------- 1 file changed, 24 insertions(+), 30 deletions(-) diff --git a/KaitaiStream.ts b/KaitaiStream.ts index 1ff6a1e..e674664 100644 --- a/KaitaiStream.ts +++ b/KaitaiStream.ts @@ -877,6 +877,7 @@ class KaitaiStream { // ======================================================================== static EOFError = class extends Error { + name = "EOFError"; bytesReq: number; bytesAvail: number; @@ -885,11 +886,9 @@ class KaitaiStream { * @param bytesAvail The number of bytes available. */ constructor(bytesReq: number, bytesAvail: number) { - super(); - // Workaround https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work + super("requested " + bytesReq + " bytes, but only " + bytesAvail + " bytes available"); + // Workaround https://www.typescriptlang.org/docs/handbook/2/classes.html#inheriting-built-in-types Object.setPrototypeOf(this, KaitaiStream.EOFError.prototype); - this.name = "EOFError"; - this.message = "requested " + bytesReq + " bytes, but only " + bytesAvail + " bytes available"; this.bytesReq = bytesReq; this.bytesAvail = bytesAvail; } @@ -899,6 +898,7 @@ class KaitaiStream { * Unused since Kaitai Struct Compiler v0.9+ - compatibility with older versions. */ static UnexpectedDataError = class extends Error { + name = "UnexpectedDataError"; expected: any; actual: any; @@ -907,26 +907,26 @@ class KaitaiStream { * @param actual The actual value. */ constructor(expected: any, actual: any) { - super(); - // Workaround https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work + super("expected [" + expected + "], but got [" + actual + "]"); + // Workaround https://www.typescriptlang.org/docs/handbook/2/classes.html#inheriting-built-in-types Object.setPrototypeOf(this, KaitaiStream.UnexpectedDataError.prototype); - this.name = "UnexpectedDataError"; - this.message = "expected [" + expected + "], but got [" + actual + "]"; this.expected = expected; this.actual = actual; } }; static UndecidedEndiannessError = class extends Error { + name = "UndecidedEndiannessError"; + constructor() { super(); - // Workaround https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work + // Workaround https://www.typescriptlang.org/docs/handbook/2/classes.html#inheriting-built-in-types Object.setPrototypeOf(this, KaitaiStream.UndecidedEndiannessError.prototype); - this.name = "UndecidedEndiannessError"; } }; static ValidationNotEqualError = class extends Error { + name = "ValidationNotEqualError"; expected: any; actual: any; @@ -935,17 +935,16 @@ class KaitaiStream { * @param actual The actual value. */ constructor(expected: any, actual: any) { - super(); - // Workaround https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work + super("not equal, expected [" + expected + "], but got [" + actual + "]"); + // Workaround https://www.typescriptlang.org/docs/handbook/2/classes.html#inheriting-built-in-types Object.setPrototypeOf(this, KaitaiStream.ValidationNotEqualError.prototype); - this.name = "ValidationNotEqualError"; - this.message = "not equal, expected [" + expected + "], but got [" + actual + "]"; this.expected = expected; this.actual = actual; } }; static ValidationLessThanError = class extends Error { + name = "ValidationLessThanError"; min: any; actual: any; @@ -954,17 +953,16 @@ class KaitaiStream { * @param actual The actual value. */ constructor(min: any, actual: any) { - super(); - // Workaround https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work + super("not in range, min [" + min + "], but got [" + actual + "]"); + // Workaround https://www.typescriptlang.org/docs/handbook/2/classes.html#inheriting-built-in-types Object.setPrototypeOf(this, KaitaiStream.ValidationLessThanError.prototype); - this.name = "ValidationLessThanError"; - this.message = "not in range, min [" + min + "], but got [" + actual + "]"; this.min = min; this.actual = actual; } }; static ValidationGreaterThanError = class extends Error { + name = "ValidationGreaterThanError"; max: any; actual: any; @@ -973,44 +971,40 @@ class KaitaiStream { * @param actual The actual value. */ constructor(max: any, actual: any) { - super(); - // Workaround https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work + super("not in range, max [" + max + "], but got [" + actual + "]"); + // Workaround https://www.typescriptlang.org/docs/handbook/2/classes.html#inheriting-built-in-types Object.setPrototypeOf(this, KaitaiStream.ValidationGreaterThanError.prototype); - this.name = "ValidationGreaterThanError"; - this.message = "not in range, max [" + max + "], but got [" + actual + "]"; this.max = max; this.actual = actual; } }; static ValidationNotAnyOfError = class extends Error { + name = "ValidationNotAnyOfError"; actual: any; /** * @param actual The actual value. */ constructor(actual: any) { - super(); - // Workaround https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work + super("not any of the list, got [" + actual + "]"); + // Workaround https://www.typescriptlang.org/docs/handbook/2/classes.html#inheriting-built-in-types Object.setPrototypeOf(this, KaitaiStream.ValidationNotAnyOfError.prototype); - this.name = "ValidationNotAnyOfError"; - this.message = "not any of the list, got [" + actual + "]"; this.actual = actual; } }; static ValidationExprError = class extends Error { + name = "ValidationExprError"; actual: any; /** * @param actual The actual value. */ constructor(actual: any) { - super(); - // Workaround https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work + super("not matching the expression, got [" + actual + "]"); + // Workaround https://www.typescriptlang.org/docs/handbook/2/classes.html#inheriting-built-in-types Object.setPrototypeOf(this, KaitaiStream.ValidationExprError.prototype); - this.name = "ValidationExprError"; - this.message = "not matching the expression, got [" + actual + "]"; this.actual = actual; } }; From 6f7bc8f7a6a473fcd1374a479ec2571920ac491a Mon Sep 17 00:00:00 2001 From: Tyler Schrock Date: Fri, 3 Dec 2021 07:40:12 -0500 Subject: [PATCH 06/20] Change var to const/let and add return types --- KaitaiStream.ts | 329 ++++++++++++++++++++++++------------------------ 1 file changed, 165 insertions(+), 164 deletions(-) diff --git a/KaitaiStream.ts b/KaitaiStream.ts index e674664..3051373 100644 --- a/KaitaiStream.ts +++ b/KaitaiStream.ts @@ -85,7 +85,7 @@ class KaitaiStream { * * @returns The backing ArrayBuffer. */ - get buffer() { + get buffer(): ArrayBuffer { this._trimAlloc(); return this._buffer; } @@ -104,7 +104,7 @@ class KaitaiStream { * * @returns The byteOffset. */ - get byteOffset() { + get byteOffset(): number { return this._byteOffset; } /** @@ -122,7 +122,7 @@ class KaitaiStream { * * @returns The backing DataView. */ - get dataView() { + get dataView(): DataView { return this._dataView; } /** @@ -142,13 +142,13 @@ class KaitaiStream { * the virtual byteLength is smaller than the buffer byteLength (happens after * growing the buffer with writes and not filling the extra space completely). */ - _trimAlloc() { + _trimAlloc(): void { if (this._byteLength === this._buffer.byteLength) { return; } - var buf = new ArrayBuffer(this._byteLength); - var dst = new Uint8Array(buf); - var src = new Uint8Array(this._buffer, 0, dst.length); + const buf = new ArrayBuffer(this._byteLength); + const dst = new Uint8Array(buf); + const src = new Uint8Array(this._buffer, 0, dst.length); dst.set(src); this.buffer = buf; } @@ -164,9 +164,9 @@ class KaitaiStream { * * @returns True if the seek pointer is at the end of the buffer. */ - isEof() { + isEof(): boolean { return this.pos >= this.size && this.bitsLeft === 0; - }; + } /** * Sets the KaitaiStream read/write position to given position. @@ -174,17 +174,17 @@ class KaitaiStream { * * @param pos Position to seek to. */ - seek(pos: number) { - var npos = Math.max(0, Math.min(this.size, pos)); + seek(pos: number): void { + const npos = Math.max(0, Math.min(this.size, pos)); this.pos = (isNaN(npos) || !isFinite(npos)) ? 0 : npos; - }; + } /** * Returns the byte length of the KaitaiStream object. * * @returns The byte length. */ - get size() { + get size(): number { return this._byteLength - this._byteOffset; } @@ -201,12 +201,12 @@ class KaitaiStream { * * @returns The read number. */ - readS1() { + readS1(): number { this.ensureBytesLeft(1); - var v = this._dataView.getInt8(this.pos); + const v = this._dataView.getInt8(this.pos); this.pos += 1; return v; - }; + } // ........................................................................ // Big-endian @@ -217,24 +217,24 @@ class KaitaiStream { * * @returns The read number. */ - readS2be() { + readS2be(): number { this.ensureBytesLeft(2); - var v = this._dataView.getInt16(this.pos); + const v = this._dataView.getInt16(this.pos); this.pos += 2; return v; - }; + } /** * Reads a 32-bit big-endian signed int from the stream. * * @returns The read number. */ - readS4be() { + readS4be(): number { this.ensureBytesLeft(4); - var v = this._dataView.getInt32(this.pos); + const v = this._dataView.getInt32(this.pos); this.pos += 4; return v; - }; + } /** * Reads a 64-bit big-endian unsigned int from the stream. Note that @@ -244,10 +244,10 @@ class KaitaiStream { * * @returns The read number. */ - readS8be() { + readS8be(): number { this.ensureBytesLeft(8); - var v1 = this.readU4be(); - var v2 = this.readU4be(); + const v1 = this.readU4be(); + const v2 = this.readU4be(); if ((v1 & 0x80000000) !== 0) { // negative number @@ -255,7 +255,7 @@ class KaitaiStream { } else { return 0x100000000 * v1 + v2; } - }; + } // ........................................................................ // Little-endian @@ -266,24 +266,24 @@ class KaitaiStream { * * @returns The read number. */ - readS2le() { + readS2le(): number { this.ensureBytesLeft(2); - var v = this._dataView.getInt16(this.pos, true); + const v = this._dataView.getInt16(this.pos, true); this.pos += 2; return v; - }; + } /** * Reads a 32-bit little-endian signed int from the stream. * * @returns The read number. */ - readS4le() { + readS4le(): number { this.ensureBytesLeft(4); - var v = this._dataView.getInt32(this.pos, true); + const v = this._dataView.getInt32(this.pos, true); this.pos += 4; return v; - }; + } /** * Reads a 64-bit little-endian unsigned int from the stream. Note that @@ -293,10 +293,10 @@ class KaitaiStream { * * @returns The read number. */ - readS8le() { + readS8le(): number { this.ensureBytesLeft(8); - var v1 = this.readU4le(); - var v2 = this.readU4le(); + const v1 = this.readU4le(); + const v2 = this.readU4le(); if ((v2 & 0x80000000) !== 0) { // negative number @@ -304,7 +304,7 @@ class KaitaiStream { } else { return 0x100000000 * v2 + v1; } - }; + } // ------------------------------------------------------------------------ // Unsigned @@ -315,12 +315,12 @@ class KaitaiStream { * * @returns The read number. */ - readU1() { + readU1(): number { this.ensureBytesLeft(1); - var v = this._dataView.getUint8(this.pos); + const v = this._dataView.getUint8(this.pos); this.pos += 1; return v; - }; + } // ........................................................................ // Big-endian @@ -331,24 +331,24 @@ class KaitaiStream { * * @returns The read number. */ - readU2be() { + readU2be(): number { this.ensureBytesLeft(2); - var v = this._dataView.getUint16(this.pos); + const v = this._dataView.getUint16(this.pos); this.pos += 2; return v; - }; + } /** * Reads a 32-bit big-endian unsigned int from the stream. * * @returns The read number. */ - readU4be() { + readU4be(): number { this.ensureBytesLeft(4); - var v = this._dataView.getUint32(this.pos); + const v = this._dataView.getUint32(this.pos); this.pos += 4; return v; - }; + } /** * Reads a 64-bit big-endian unsigned int from the stream. Note that @@ -358,10 +358,10 @@ class KaitaiStream { * * @returns The read number. */ - readU8be() { + readU8be(): number { this.ensureBytesLeft(8); - var v1 = this.readU4be(); - var v2 = this.readU4be(); + const v1 = this.readU4be(); + const v2 = this.readU4be(); return 0x100000000 * v1 + v2; } @@ -374,9 +374,9 @@ class KaitaiStream { * * @returns The read number. */ - readU2le() { + readU2le(): number { this.ensureBytesLeft(2); - var v = this._dataView.getUint16(this.pos, true); + const v = this._dataView.getUint16(this.pos, true); this.pos += 2; return v; } @@ -386,9 +386,9 @@ class KaitaiStream { * * @returns The read number. */ - readU4le() { + readU4le(): number { this.ensureBytesLeft(4); - var v = this._dataView.getUint32(this.pos, true); + const v = this._dataView.getUint32(this.pos, true); this.pos += 4; return v; } @@ -401,12 +401,12 @@ class KaitaiStream { * * @returns The read number. */ - readU8le() { + readU8le(): number { this.ensureBytesLeft(8); - var v1 = this.readU4le(); - var v2 = this.readU4le(); + const v1 = this.readU4le(); + const v2 = this.readU4le(); return 0x100000000 * v2 + v1; - }; + } // ======================================================================== @@ -422,24 +422,24 @@ class KaitaiStream { * * @returns The read number. */ - readF4be() { + readF4be(): number { this.ensureBytesLeft(4); - var v = this._dataView.getFloat32(this.pos); + const v = this._dataView.getFloat32(this.pos); this.pos += 4; return v; - }; + } /** * Reads a 64-bit big-endian float from the stream. * * @returns The read number. */ - readF8be() { + readF8be(): number { this.ensureBytesLeft(8); - var v = this._dataView.getFloat64(this.pos); + const v = this._dataView.getFloat64(this.pos); this.pos += 8; return v; - }; + } // ------------------------------------------------------------------------ // Little endian @@ -450,24 +450,24 @@ class KaitaiStream { * * @returns The read number. */ - readF4le() { + readF4le(): number { this.ensureBytesLeft(4); - var v = this._dataView.getFloat32(this.pos, true); + const v = this._dataView.getFloat32(this.pos, true); this.pos += 4; return v; - }; + } /** * Reads a 64-bit little-endian float from the stream. * * @returns The read number. */ - readF8le() { + readF8le(): number { this.ensureBytesLeft(8); - var v = this._dataView.getFloat64(this.pos, true); + const v = this._dataView.getFloat64(this.pos, true); this.pos += 8; return v; - }; + } // ------------------------------------------------------------------------ // Unaligned bit values @@ -476,29 +476,29 @@ class KaitaiStream { /** * Aligns bit reading to the byte boundry. */ - alignToByte() { + alignToByte(): void { this.bits = 0; this.bitsLeft = 0; - }; + } /** * @param n The number of bits to read. * @returns The read bits. * @throws {Error} */ - readBitsIntBe(n: number) { + readBitsIntBe(n: number): number { // JS only supports bit operations on 32 bits if (n > 32) { throw new Error(`readBitsIntBe: the maximum supported bit length is 32 (tried to read ${n} bits)`); } - var bitsNeeded = n - this.bitsLeft; + const bitsNeeded = n - this.bitsLeft; if (bitsNeeded > 0) { // 1 bit => 1 byte // 8 bits => 1 byte // 9 bits => 2 bytes - var bytesNeeded = Math.ceil(bitsNeeded / 8); - var buf = this.readBytes(bytesNeeded); - for (var i = 0; i < bytesNeeded; i++) { + const bytesNeeded = Math.ceil(bitsNeeded / 8); + const buf = this.readBytes(bytesNeeded); + for (let i = 0; i < bytesNeeded; i++) { this.bits <<= 8; this.bits |= buf[i]; this.bitsLeft += 8; @@ -506,17 +506,17 @@ class KaitaiStream { } // raw mask with required number of 1s, starting from lowest bit - var mask = n === 32 ? 0xffffffff : (1 << n) - 1; + let mask = n === 32 ? 0xffffffff : (1 << n) - 1; // shift this.bits to align the highest bits with the mask & derive reading result - var shiftBits = this.bitsLeft - n; - var res = (this.bits >>> shiftBits) & mask; + const shiftBits = this.bitsLeft - n; + const res = (this.bits >>> shiftBits) & mask; // clear top bits that we've just read => AND with 1s this.bitsLeft -= n; mask = (1 << this.bitsLeft) - 1; this.bits &= mask; return res; - }; + } /** * Unused since Kaitai Struct Compiler v0.9+ - compatibility with older versions. @@ -525,7 +525,7 @@ class KaitaiStream { * @param n The number of bits to read. * @returns The read bits. */ - readBitsInt(n: number) { + readBitsInt(n: number): number { return this.readBitsIntBe(n); } @@ -534,34 +534,34 @@ class KaitaiStream { * @returns The read bits. * @throws {Error} */ - readBitsIntLe(n: number) { + readBitsIntLe(n: number): number { // JS only supports bit operations on 32 bits if (n > 32) { throw new Error(`readBitsIntLe: the maximum supported bit length is 32 (tried to read ${n} bits)`); } - var bitsNeeded = n - this.bitsLeft; + const bitsNeeded = n - this.bitsLeft; if (bitsNeeded > 0) { // 1 bit => 1 byte // 8 bits => 1 byte // 9 bits => 2 bytes - var bytesNeeded = Math.ceil(bitsNeeded / 8); - var buf = this.readBytes(bytesNeeded); - for (var i = 0; i < bytesNeeded; i++) { + const bytesNeeded = Math.ceil(bitsNeeded / 8); + const buf = this.readBytes(bytesNeeded); + for (let i = 0; i < bytesNeeded; i++) { this.bits |= (buf[i] << this.bitsLeft); this.bitsLeft += 8; } } // raw mask with required number of 1s, starting from lowest bit - var mask = n === 32 ? 0xffffffff : (1 << n) - 1; + const mask = n === 32 ? 0xffffffff : (1 << n) - 1; // derive reading result - var res = this.bits & mask; + const res = this.bits & mask; // remove bottom bits that we've just read by shifting this.bits >>>= n; this.bitsLeft -= n; return res; - }; + } /** * Native endianness. Either KaitaiStream.BIG_ENDIAN or KaitaiStream.LITTLE_ENDIAN @@ -577,16 +577,16 @@ class KaitaiStream { * @param len The number of bytes to read. * @returns The read bytes. */ - readBytes(len: number) { + readBytes(len: number): Uint8Array { return this.mapUint8Array(len); - }; + } /** * @returns The read bytes. */ - readBytesFull() { + readBytesFull(): Uint8Array { return this.mapUint8Array(this.size - this.pos); - }; + } /** * Reads bytes until the terminator byte is found. @@ -598,10 +598,11 @@ class KaitaiStream { * @returns The read bytes. * @throws {string} */ - readBytesTerm(terminator: number, include: boolean, consume: boolean, eosError: boolean) { - var blen = this.size - this.pos; - var u8 = new Uint8Array(this._buffer, this._byteOffset + this.pos); - for (var i = 0; i < blen && u8[i] !== terminator; i++); // find first zero byte + readBytesTerm(terminator: number, include: boolean, consume: boolean, eosError: boolean): Uint8Array { + const blen = this.size - this.pos; + const u8 = new Uint8Array(this._buffer, this._byteOffset + this.pos); + let i; + for (i = 0; i < blen && u8[i] !== terminator; i++); // find first zero byte if (i === blen) { // we've read all the buffer and haven't found the terminator if (eosError) { @@ -610,7 +611,7 @@ class KaitaiStream { return this.mapUint8Array(i); } } else { - var arr; + let arr; if (include) { arr = this.mapUint8Array(i + 1); } else { @@ -621,7 +622,7 @@ class KaitaiStream { } return arr; } - }; + } /** * Unused since Kaitai Struct Compiler v0.9+ - compatibility with older versions. @@ -630,32 +631,32 @@ class KaitaiStream { * @returns The read bytes. * @throws {KaitaiStream.UnexpectedDataError} */ - ensureFixedContents(expected: ArrayLike) { - var actual = this.readBytes(expected.length); + ensureFixedContents(expected: ArrayLike): Uint8Array { + const actual = this.readBytes(expected.length); if (actual.length !== expected.length) { throw new KaitaiStream.UnexpectedDataError(expected, actual); } - var actLen = actual.length; - for (var i = 0; i < actLen; i++) { + const actLen = actual.length; + for (let i = 0; i < actLen; i++) { if (actual[i] !== expected[i]) { throw new KaitaiStream.UnexpectedDataError(expected, actual); } } return actual; - }; + } /** * @param data The data. * @param padByte The byte to strip. * @returns The stripped data. */ - static bytesStripRight(data: number[] | NodeJS.TypedArray, padByte: number) { - var newLen = data.length; + static bytesStripRight(data: number[] | NodeJS.TypedArray, padByte: number): number[] | NodeJS.TypedArray { + let newLen = data.length; while (data[newLen - 1] === padByte) { newLen--; } return data.slice(0, newLen); - }; + } /** * @param data The data. @@ -663,23 +664,23 @@ class KaitaiStream { * @param include True if the returned bytes should include the terminator. * @returns The terminated bytes. */ - static bytesTerminate(data: number[] | NodeJS.TypedArray, term: number, include: boolean) { - var newLen = 0; - var maxLen = data.length; + static bytesTerminate(data: number[] | NodeJS.TypedArray, term: number, include: boolean): number[] | NodeJS.TypedArray { + let newLen = 0; + const maxLen = data.length; while (newLen < maxLen && data[newLen] !== term) { newLen++; } if (include && newLen < maxLen) newLen++; return data.slice(0, newLen); - }; + } /** * @param arr The bytes. * @param encoding The character encoding. * @returns The decoded string. */ - static bytesToStr(arr: Uint8Array, encoding: BufferEncoding) { + static bytesToStr(arr: Uint8Array, encoding: BufferEncoding): string { if (encoding == null || encoding.toLowerCase() === "ascii") { return KaitaiStream.createStringFromArray(arr); } else { @@ -708,7 +709,7 @@ class KaitaiStream { } } } - }; + } // ======================================================================== // Byte array processing @@ -719,32 +720,32 @@ class KaitaiStream { * @param key The key byte. * @returns The Xor'd bytes. */ - static processXorOne(data: ArrayLike, key: number) { - var r = new Uint8Array(data.length); - var dl = data.length; - for (var i = 0; i < dl; i++) + static processXorOne(data: ArrayLike, key: number): Uint8Array { + const r = new Uint8Array(data.length); + const dl = data.length; + for (let i = 0; i < dl; i++) r[i] = data[i] ^ key; return r; - }; + } /** * @param data The input bytes. * @param key The key bytes. * @returns The Xor'd bytes. */ - static processXorMany(data: ArrayLike, key: ArrayLike) { - var dl = data.length; - var r = new Uint8Array(dl); - var kl = key.length; - var ki = 0; - for (var i = 0; i < dl; i++) { + static processXorMany(data: ArrayLike, key: ArrayLike): Uint8Array { + const dl = data.length; + const r = new Uint8Array(dl); + const kl = key.length; + let ki = 0; + for (let i = 0; i < dl; i++) { r[i] = data[i] ^ key[ki]; ki++; if (ki >= kl) ki = 0; } return r; - }; + } /** * @param data The input bytes. @@ -753,25 +754,25 @@ class KaitaiStream { * @returns The rotated bytes. * @throws {string} */ - static processRotateLeft(data: ArrayLike, amount: number, groupSize: number) { + static processRotateLeft(data: ArrayLike, amount: number, groupSize: number): Uint8Array { if (groupSize !== 1) throw ("unable to rotate group of " + groupSize + " bytes yet"); - var mask = groupSize * 8 - 1; - var antiAmount = -amount & mask; + const mask = groupSize * 8 - 1; + const antiAmount = -amount & mask; - var r = new Uint8Array(data.length); - for (var i = 0; i < data.length; i++) + const r = new Uint8Array(data.length); + for (let i = 0; i < data.length; i++) r[i] = (data[i] << amount) & 0xff | (data[i] >> antiAmount); return r; - }; + } /** * @param buf The input bytes. * @returns The uncompressed bytes. */ - static processZlib(buf: Uint8Array) { + static processZlib(buf: Uint8Array): Uint8Array | Buffer { if (typeof require !== 'undefined') { // require is available - we're running under node if (typeof KaitaiStream.zlib === 'undefined') @@ -792,7 +793,7 @@ class KaitaiStream { // use pako API return (KaitaiStream.zlib as Pako).inflate(buf); } - }; + } // ======================================================================== // Misc runtime operations @@ -804,14 +805,14 @@ class KaitaiStream { * @returns The result of `a` mod `b`. * @throws {string} */ - static mod(a: number, b: number) { + static mod(a: number, b: number): number { if (b <= 0) throw "mod divisor <= 0"; - var r = a % b; + let r = a % b; if (r < 0) r += b; return r; - }; + } /** * Gets the smallest value in an array. @@ -819,15 +820,15 @@ class KaitaiStream { * @param arr The input array. * @returns The smallest value. */ - static arrayMin(arr: ArrayLike) { - var min = arr[0]; - var x; - for (var i = 1, n = arr.length; i < n; ++i) { + static arrayMin(arr: ArrayLike): number { + let min = arr[0]; + let x; + for (let i = 1, n = arr.length; i < n; ++i) { x = arr[i]; if (x < min) min = x; } return min; - }; + } /** * Gets the largest value in an array. @@ -835,15 +836,15 @@ class KaitaiStream { * @param arr The input array. * @returns The largest value. */ - static arrayMax(arr: ArrayLike) { - var max = arr[0]; - var x; - for (var i = 1, n = arr.length; i < n; ++i) { + static arrayMax(arr: ArrayLike): number { + let max = arr[0]; + let x; + for (let i = 1, n = arr.length; i < n; ++i) { x = arr[i]; if (x > max) max = x; } return max; - }; + } /** * Compares two arrays of bytes from left to right. @@ -852,14 +853,14 @@ class KaitaiStream { * @param b The second array. * @returns `0` if the arrays are the equal, a positive number if `a` is greater than `b`, or a negative number if `a` is less than `b`. */ - static byteArrayCompare(a: ArrayLike, b: ArrayLike) { + static byteArrayCompare(a: ArrayLike, b: ArrayLike): number { if (a === b) return 0; - var al = a.length; - var bl = b.length; - var minLen = al < bl ? al : bl; - for (var i = 0; i < minLen; i++) { - var cmp = a[i] - b[i]; + const al = a.length; + const bl = b.length; + const minLen = al < bl ? al : bl; + for (let i = 0; i < minLen; i++) { + const cmp = a[i] - b[i]; if (cmp !== 0) return cmp; } @@ -870,7 +871,7 @@ class KaitaiStream { } else { return al - bl; } - }; + } // ======================================================================== // Internal implementation details @@ -892,7 +893,7 @@ class KaitaiStream { this.bytesReq = bytesReq; this.bytesAvail = bytesAvail; } - } + }; /** * Unused since Kaitai Struct Compiler v0.9+ - compatibility with older versions. @@ -1016,11 +1017,11 @@ class KaitaiStream { * @param length Number of bytes to require. * @throws {KaitaiStream.EOFError} */ - ensureBytesLeft(length: number) { + ensureBytesLeft(length: number): void { if (this.pos + length > this.size) { throw new KaitaiStream.EOFError(length, this.size - this.pos); } - }; + } /** * Maps a Uint8Array into the KaitaiStream buffer. @@ -1029,15 +1030,15 @@ class KaitaiStream { * @param length Number of elements to map. * @returns A Uint8Array to the KaitaiStream backing buffer. */ - mapUint8Array(length: number) { + mapUint8Array(length: number): Uint8Array { length |= 0; this.ensureBytesLeft(length); - var arr = new Uint8Array(this._buffer, this.byteOffset + this.pos, length); + const arr = new Uint8Array(this._buffer, this.byteOffset + this.pos, length); this.pos += length; return arr; - }; + } /** * Creates an array from an array of character codes. @@ -1047,15 +1048,15 @@ class KaitaiStream { * @param array Array of character codes. * @returns String created from the character codes. */ - static createStringFromArray = function (array: Array | Uint8Array) { - var chunk_size = 0x8000; - var chunks = []; - for (var i = 0; i < array.length; i += chunk_size) { - const chunk = 'subarray' in array ? array.subarray(i, i + chunk_size) : array.slice(i, i + chunk_size) + static createStringFromArray(array: number[] | Uint8Array): string { + const chunk_size = 0x8000; + const chunks = []; + for (let i = 0; i < array.length; i += chunk_size) { + const chunk = 'subarray' in array ? array.subarray(i, i + chunk_size) : array.slice(i, i + chunk_size); chunks.push(String.fromCharCode.apply(null, chunk)); } return chunks.join(""); - }; + } } export default KaitaiStream; From a3eadc7243aa286a6baf46a9c42bfb0d7dac5d4b Mon Sep 17 00:00:00 2001 From: Tyler Schrock Date: Fri, 3 Dec 2021 07:54:38 -0500 Subject: [PATCH 07/20] Add accessibility modifiers --- KaitaiStream.ts | 180 ++++++++++++++++++++++++------------------------ 1 file changed, 91 insertions(+), 89 deletions(-) diff --git a/KaitaiStream.ts b/KaitaiStream.ts index 3051373..4a41e9c 100644 --- a/KaitaiStream.ts +++ b/KaitaiStream.ts @@ -32,7 +32,7 @@ class KaitaiStream { * @param arrayBuffer ArrayBuffer to read from. * @param byteOffset Offset from arrayBuffer beginning for the KaitaiStream. */ - constructor(arrayBuffer: ArrayBuffer | DataView | number, byteOffset?: number) { + public constructor(arrayBuffer: ArrayBuffer | DataView | number, byteOffset?: number) { this._byteOffset = byteOffset || 0; if (arrayBuffer instanceof ArrayBuffer) { this.buffer = arrayBuffer; @@ -70,7 +70,7 @@ class KaitaiStream { * NOTE: `depUrls` is a static property of KaitaiStream (the factory), like the various * processing functions. It is NOT part of the prototype of instances. */ - static depUrls: Record = { + public static depUrls: Record = { // processZlib uses this and expected a link to a copy of pako. // specifically the pako_inflate.min.js script at: // https://raw.githubusercontent.com/nodeca/pako/master/dist/pako_inflate.min.js @@ -85,15 +85,16 @@ class KaitaiStream { * * @returns The backing ArrayBuffer. */ - get buffer(): ArrayBuffer { + public get buffer(): ArrayBuffer { this._trimAlloc(); return this._buffer; } + /** * Sets the backing ArrayBuffer of the KaitaiStream object and updates the * DataView to point to the new buffer. */ - set buffer(v) { + public set buffer(v) { this._buffer = v; this._dataView = new DataView(this._buffer, this._byteOffset); this._byteLength = this._buffer.byteLength; @@ -104,14 +105,15 @@ class KaitaiStream { * * @returns The byteOffset. */ - get byteOffset(): number { + public get byteOffset(): number { return this._byteOffset; } + /** * Sets the byteOffset of the KaitaiStream object and updates the DataView to * point to the new byteOffset. */ - set byteOffset(v) { + public set byteOffset(v) { this._byteOffset = v; this._dataView = new DataView(this._buffer, this._byteOffset); this._byteLength = this._buffer.byteLength; @@ -122,14 +124,14 @@ class KaitaiStream { * * @returns The backing DataView. */ - get dataView(): DataView { + public get dataView(): DataView { return this._dataView; } /** * Sets the backing DataView of the KaitaiStream object and updates the buffer * and byteOffset to point to the DataView values. */ - set dataView(v) { + public set dataView(v) { this._byteOffset = v.byteOffset; this._buffer = v.buffer; this._dataView = new DataView(this._buffer, this._byteOffset); @@ -142,7 +144,7 @@ class KaitaiStream { * the virtual byteLength is smaller than the buffer byteLength (happens after * growing the buffer with writes and not filling the extra space completely). */ - _trimAlloc(): void { + private _trimAlloc(): void { if (this._byteLength === this._buffer.byteLength) { return; } @@ -164,7 +166,7 @@ class KaitaiStream { * * @returns True if the seek pointer is at the end of the buffer. */ - isEof(): boolean { + public isEof(): boolean { return this.pos >= this.size && this.bitsLeft === 0; } @@ -174,7 +176,7 @@ class KaitaiStream { * * @param pos Position to seek to. */ - seek(pos: number): void { + public seek(pos: number): void { const npos = Math.max(0, Math.min(this.size, pos)); this.pos = (isNaN(npos) || !isFinite(npos)) ? 0 : npos; } @@ -184,7 +186,7 @@ class KaitaiStream { * * @returns The byte length. */ - get size(): number { + public get size(): number { return this._byteLength - this._byteOffset; } @@ -201,7 +203,7 @@ class KaitaiStream { * * @returns The read number. */ - readS1(): number { + public readS1(): number { this.ensureBytesLeft(1); const v = this._dataView.getInt8(this.pos); this.pos += 1; @@ -217,7 +219,7 @@ class KaitaiStream { * * @returns The read number. */ - readS2be(): number { + public readS2be(): number { this.ensureBytesLeft(2); const v = this._dataView.getInt16(this.pos); this.pos += 2; @@ -229,7 +231,7 @@ class KaitaiStream { * * @returns The read number. */ - readS4be(): number { + public readS4be(): number { this.ensureBytesLeft(4); const v = this._dataView.getInt32(this.pos); this.pos += 4; @@ -244,7 +246,7 @@ class KaitaiStream { * * @returns The read number. */ - readS8be(): number { + public readS8be(): number { this.ensureBytesLeft(8); const v1 = this.readU4be(); const v2 = this.readU4be(); @@ -266,7 +268,7 @@ class KaitaiStream { * * @returns The read number. */ - readS2le(): number { + public readS2le(): number { this.ensureBytesLeft(2); const v = this._dataView.getInt16(this.pos, true); this.pos += 2; @@ -278,7 +280,7 @@ class KaitaiStream { * * @returns The read number. */ - readS4le(): number { + public readS4le(): number { this.ensureBytesLeft(4); const v = this._dataView.getInt32(this.pos, true); this.pos += 4; @@ -293,7 +295,7 @@ class KaitaiStream { * * @returns The read number. */ - readS8le(): number { + public readS8le(): number { this.ensureBytesLeft(8); const v1 = this.readU4le(); const v2 = this.readU4le(); @@ -315,7 +317,7 @@ class KaitaiStream { * * @returns The read number. */ - readU1(): number { + public readU1(): number { this.ensureBytesLeft(1); const v = this._dataView.getUint8(this.pos); this.pos += 1; @@ -331,7 +333,7 @@ class KaitaiStream { * * @returns The read number. */ - readU2be(): number { + public readU2be(): number { this.ensureBytesLeft(2); const v = this._dataView.getUint16(this.pos); this.pos += 2; @@ -343,7 +345,7 @@ class KaitaiStream { * * @returns The read number. */ - readU4be(): number { + public readU4be(): number { this.ensureBytesLeft(4); const v = this._dataView.getUint32(this.pos); this.pos += 4; @@ -358,7 +360,7 @@ class KaitaiStream { * * @returns The read number. */ - readU8be(): number { + public readU8be(): number { this.ensureBytesLeft(8); const v1 = this.readU4be(); const v2 = this.readU4be(); @@ -374,7 +376,7 @@ class KaitaiStream { * * @returns The read number. */ - readU2le(): number { + public readU2le(): number { this.ensureBytesLeft(2); const v = this._dataView.getUint16(this.pos, true); this.pos += 2; @@ -386,7 +388,7 @@ class KaitaiStream { * * @returns The read number. */ - readU4le(): number { + public readU4le(): number { this.ensureBytesLeft(4); const v = this._dataView.getUint32(this.pos, true); this.pos += 4; @@ -401,7 +403,7 @@ class KaitaiStream { * * @returns The read number. */ - readU8le(): number { + public readU8le(): number { this.ensureBytesLeft(8); const v1 = this.readU4le(); const v2 = this.readU4le(); @@ -422,7 +424,7 @@ class KaitaiStream { * * @returns The read number. */ - readF4be(): number { + public readF4be(): number { this.ensureBytesLeft(4); const v = this._dataView.getFloat32(this.pos); this.pos += 4; @@ -434,7 +436,7 @@ class KaitaiStream { * * @returns The read number. */ - readF8be(): number { + public readF8be(): number { this.ensureBytesLeft(8); const v = this._dataView.getFloat64(this.pos); this.pos += 8; @@ -450,7 +452,7 @@ class KaitaiStream { * * @returns The read number. */ - readF4le(): number { + public readF4le(): number { this.ensureBytesLeft(4); const v = this._dataView.getFloat32(this.pos, true); this.pos += 4; @@ -462,7 +464,7 @@ class KaitaiStream { * * @returns The read number. */ - readF8le(): number { + public readF8le(): number { this.ensureBytesLeft(8); const v = this._dataView.getFloat64(this.pos, true); this.pos += 8; @@ -476,7 +478,7 @@ class KaitaiStream { /** * Aligns bit reading to the byte boundry. */ - alignToByte(): void { + public alignToByte(): void { this.bits = 0; this.bitsLeft = 0; } @@ -486,7 +488,7 @@ class KaitaiStream { * @returns The read bits. * @throws {Error} */ - readBitsIntBe(n: number): number { + public readBitsIntBe(n: number): number { // JS only supports bit operations on 32 bits if (n > 32) { throw new Error(`readBitsIntBe: the maximum supported bit length is 32 (tried to read ${n} bits)`); @@ -525,7 +527,7 @@ class KaitaiStream { * @param n The number of bits to read. * @returns The read bits. */ - readBitsInt(n: number): number { + public readBitsInt(n: number): number { return this.readBitsIntBe(n); } @@ -534,7 +536,7 @@ class KaitaiStream { * @returns The read bits. * @throws {Error} */ - readBitsIntLe(n: number): number { + public readBitsIntLe(n: number): number { // JS only supports bit operations on 32 bits if (n > 32) { throw new Error(`readBitsIntLe: the maximum supported bit length is 32 (tried to read ${n} bits)`); @@ -567,7 +569,7 @@ class KaitaiStream { * Native endianness. Either KaitaiStream.BIG_ENDIAN or KaitaiStream.LITTLE_ENDIAN * depending on the platform endianness. */ - static endianness = new Int8Array(new Int16Array([1]).buffer)[0] > 0; + public static endianness = new Int8Array(new Int16Array([1]).buffer)[0] > 0; // ======================================================================== // Byte arrays @@ -577,14 +579,14 @@ class KaitaiStream { * @param len The number of bytes to read. * @returns The read bytes. */ - readBytes(len: number): Uint8Array { + public readBytes(len: number): Uint8Array { return this.mapUint8Array(len); } /** * @returns The read bytes. */ - readBytesFull(): Uint8Array { + public readBytesFull(): Uint8Array { return this.mapUint8Array(this.size - this.pos); } @@ -598,7 +600,7 @@ class KaitaiStream { * @returns The read bytes. * @throws {string} */ - readBytesTerm(terminator: number, include: boolean, consume: boolean, eosError: boolean): Uint8Array { + public readBytesTerm(terminator: number, include: boolean, consume: boolean, eosError: boolean): Uint8Array { const blen = this.size - this.pos; const u8 = new Uint8Array(this._buffer, this._byteOffset + this.pos); let i; @@ -631,7 +633,7 @@ class KaitaiStream { * @returns The read bytes. * @throws {KaitaiStream.UnexpectedDataError} */ - ensureFixedContents(expected: ArrayLike): Uint8Array { + public ensureFixedContents(expected: ArrayLike): Uint8Array { const actual = this.readBytes(expected.length); if (actual.length !== expected.length) { throw new KaitaiStream.UnexpectedDataError(expected, actual); @@ -650,7 +652,7 @@ class KaitaiStream { * @param padByte The byte to strip. * @returns The stripped data. */ - static bytesStripRight(data: number[] | NodeJS.TypedArray, padByte: number): number[] | NodeJS.TypedArray { + public static bytesStripRight(data: number[] | NodeJS.TypedArray, padByte: number): number[] | NodeJS.TypedArray { let newLen = data.length; while (data[newLen - 1] === padByte) { newLen--; @@ -664,7 +666,7 @@ class KaitaiStream { * @param include True if the returned bytes should include the terminator. * @returns The terminated bytes. */ - static bytesTerminate(data: number[] | NodeJS.TypedArray, term: number, include: boolean): number[] | NodeJS.TypedArray { + public static bytesTerminate(data: number[] | NodeJS.TypedArray, term: number, include: boolean): number[] | NodeJS.TypedArray { let newLen = 0; const maxLen = data.length; while (newLen < maxLen && data[newLen] !== term) { @@ -680,7 +682,7 @@ class KaitaiStream { * @param encoding The character encoding. * @returns The decoded string. */ - static bytesToStr(arr: Uint8Array, encoding: BufferEncoding): string { + public static bytesToStr(arr: Uint8Array, encoding: BufferEncoding): string { if (encoding == null || encoding.toLowerCase() === "ascii") { return KaitaiStream.createStringFromArray(arr); } else { @@ -720,7 +722,7 @@ class KaitaiStream { * @param key The key byte. * @returns The Xor'd bytes. */ - static processXorOne(data: ArrayLike, key: number): Uint8Array { + public static processXorOne(data: ArrayLike, key: number): Uint8Array { const r = new Uint8Array(data.length); const dl = data.length; for (let i = 0; i < dl; i++) @@ -733,7 +735,7 @@ class KaitaiStream { * @param key The key bytes. * @returns The Xor'd bytes. */ - static processXorMany(data: ArrayLike, key: ArrayLike): Uint8Array { + public static processXorMany(data: ArrayLike, key: ArrayLike): Uint8Array { const dl = data.length; const r = new Uint8Array(dl); const kl = key.length; @@ -754,7 +756,7 @@ class KaitaiStream { * @returns The rotated bytes. * @throws {string} */ - static processRotateLeft(data: ArrayLike, amount: number, groupSize: number): Uint8Array { + public static processRotateLeft(data: ArrayLike, amount: number, groupSize: number): Uint8Array { if (groupSize !== 1) throw ("unable to rotate group of " + groupSize + " bytes yet"); @@ -772,7 +774,7 @@ class KaitaiStream { * @param buf The input bytes. * @returns The uncompressed bytes. */ - static processZlib(buf: Uint8Array): Uint8Array | Buffer { + public static processZlib(buf: Uint8Array): Uint8Array | Buffer { if (typeof require !== 'undefined') { // require is available - we're running under node if (typeof KaitaiStream.zlib === 'undefined') @@ -805,7 +807,7 @@ class KaitaiStream { * @returns The result of `a` mod `b`. * @throws {string} */ - static mod(a: number, b: number): number { + public static mod(a: number, b: number): number { if (b <= 0) throw "mod divisor <= 0"; let r = a % b; @@ -820,7 +822,7 @@ class KaitaiStream { * @param arr The input array. * @returns The smallest value. */ - static arrayMin(arr: ArrayLike): number { + public static arrayMin(arr: ArrayLike): number { let min = arr[0]; let x; for (let i = 1, n = arr.length; i < n; ++i) { @@ -836,7 +838,7 @@ class KaitaiStream { * @param arr The input array. * @returns The largest value. */ - static arrayMax(arr: ArrayLike): number { + public static arrayMax(arr: ArrayLike): number { let max = arr[0]; let x; for (let i = 1, n = arr.length; i < n; ++i) { @@ -853,7 +855,7 @@ class KaitaiStream { * @param b The second array. * @returns `0` if the arrays are the equal, a positive number if `a` is greater than `b`, or a negative number if `a` is less than `b`. */ - static byteArrayCompare(a: ArrayLike, b: ArrayLike): number { + public static byteArrayCompare(a: ArrayLike, b: ArrayLike): number { if (a === b) return 0; const al = a.length; @@ -877,16 +879,16 @@ class KaitaiStream { // Internal implementation details // ======================================================================== - static EOFError = class extends Error { - name = "EOFError"; - bytesReq: number; - bytesAvail: number; + public static EOFError = class extends Error { + public name = "EOFError"; + public bytesReq: number; + public bytesAvail: number; /** * @param bytesReq The number of bytes requested. * @param bytesAvail The number of bytes available. */ - constructor(bytesReq: number, bytesAvail: number) { + public constructor(bytesReq: number, bytesAvail: number) { super("requested " + bytesReq + " bytes, but only " + bytesAvail + " bytes available"); // Workaround https://www.typescriptlang.org/docs/handbook/2/classes.html#inheriting-built-in-types Object.setPrototypeOf(this, KaitaiStream.EOFError.prototype); @@ -898,16 +900,16 @@ class KaitaiStream { /** * Unused since Kaitai Struct Compiler v0.9+ - compatibility with older versions. */ - static UnexpectedDataError = class extends Error { - name = "UnexpectedDataError"; - expected: any; - actual: any; + public static UnexpectedDataError = class extends Error { + public name = "UnexpectedDataError"; + public expected: any; + public actual: any; /** * @param expected The expected value. * @param actual The actual value. */ - constructor(expected: any, actual: any) { + public constructor(expected: any, actual: any) { super("expected [" + expected + "], but got [" + actual + "]"); // Workaround https://www.typescriptlang.org/docs/handbook/2/classes.html#inheriting-built-in-types Object.setPrototypeOf(this, KaitaiStream.UnexpectedDataError.prototype); @@ -916,26 +918,26 @@ class KaitaiStream { } }; - static UndecidedEndiannessError = class extends Error { - name = "UndecidedEndiannessError"; + public static UndecidedEndiannessError = class extends Error { + public name = "UndecidedEndiannessError"; - constructor() { + public constructor() { super(); // Workaround https://www.typescriptlang.org/docs/handbook/2/classes.html#inheriting-built-in-types Object.setPrototypeOf(this, KaitaiStream.UndecidedEndiannessError.prototype); } }; - static ValidationNotEqualError = class extends Error { - name = "ValidationNotEqualError"; - expected: any; - actual: any; + public static ValidationNotEqualError = class extends Error { + public name = "ValidationNotEqualError"; + public expected: any; + public actual: any; /** * @param expected The expected value. * @param actual The actual value. */ - constructor(expected: any, actual: any) { + public constructor(expected: any, actual: any) { super("not equal, expected [" + expected + "], but got [" + actual + "]"); // Workaround https://www.typescriptlang.org/docs/handbook/2/classes.html#inheriting-built-in-types Object.setPrototypeOf(this, KaitaiStream.ValidationNotEqualError.prototype); @@ -944,16 +946,16 @@ class KaitaiStream { } }; - static ValidationLessThanError = class extends Error { - name = "ValidationLessThanError"; - min: any; - actual: any; + public static ValidationLessThanError = class extends Error { + public name = "ValidationLessThanError"; + public min: any; + public actual: any; /** * @param min The minimum allowed value. * @param actual The actual value. */ - constructor(min: any, actual: any) { + public constructor(min: any, actual: any) { super("not in range, min [" + min + "], but got [" + actual + "]"); // Workaround https://www.typescriptlang.org/docs/handbook/2/classes.html#inheriting-built-in-types Object.setPrototypeOf(this, KaitaiStream.ValidationLessThanError.prototype); @@ -962,16 +964,16 @@ class KaitaiStream { } }; - static ValidationGreaterThanError = class extends Error { - name = "ValidationGreaterThanError"; - max: any; - actual: any; + public static ValidationGreaterThanError = class extends Error { + public name = "ValidationGreaterThanError"; + public max: any; + public actual: any; /** * @param max The maximum allowed value. * @param actual The actual value. */ - constructor(max: any, actual: any) { + public constructor(max: any, actual: any) { super("not in range, max [" + max + "], but got [" + actual + "]"); // Workaround https://www.typescriptlang.org/docs/handbook/2/classes.html#inheriting-built-in-types Object.setPrototypeOf(this, KaitaiStream.ValidationGreaterThanError.prototype); @@ -980,14 +982,14 @@ class KaitaiStream { } }; - static ValidationNotAnyOfError = class extends Error { - name = "ValidationNotAnyOfError"; - actual: any; + public static ValidationNotAnyOfError = class extends Error { + public name = "ValidationNotAnyOfError"; + public actual: any; /** * @param actual The actual value. */ - constructor(actual: any) { + public constructor(actual: any) { super("not any of the list, got [" + actual + "]"); // Workaround https://www.typescriptlang.org/docs/handbook/2/classes.html#inheriting-built-in-types Object.setPrototypeOf(this, KaitaiStream.ValidationNotAnyOfError.prototype); @@ -995,14 +997,14 @@ class KaitaiStream { } }; - static ValidationExprError = class extends Error { - name = "ValidationExprError"; - actual: any; + public static ValidationExprError = class extends Error { + public name = "ValidationExprError"; + public actual: any; /** * @param actual The actual value. */ - constructor(actual: any) { + public constructor(actual: any) { super("not matching the expression, got [" + actual + "]"); // Workaround https://www.typescriptlang.org/docs/handbook/2/classes.html#inheriting-built-in-types Object.setPrototypeOf(this, KaitaiStream.ValidationExprError.prototype); @@ -1017,7 +1019,7 @@ class KaitaiStream { * @param length Number of bytes to require. * @throws {KaitaiStream.EOFError} */ - ensureBytesLeft(length: number): void { + public ensureBytesLeft(length: number): void { if (this.pos + length > this.size) { throw new KaitaiStream.EOFError(length, this.size - this.pos); } @@ -1030,7 +1032,7 @@ class KaitaiStream { * @param length Number of elements to map. * @returns A Uint8Array to the KaitaiStream backing buffer. */ - mapUint8Array(length: number): Uint8Array { + public mapUint8Array(length: number): Uint8Array { length |= 0; this.ensureBytesLeft(length); @@ -1048,7 +1050,7 @@ class KaitaiStream { * @param array Array of character codes. * @returns String created from the character codes. */ - static createStringFromArray(array: number[] | Uint8Array): string { + public static createStringFromArray(array: number[] | Uint8Array): string { const chunk_size = 0x8000; const chunks = []; for (let i = 0; i < array.length; i += chunk_size) { From ae54b143cf6356255db751ed98556c80008c49c0 Mon Sep 17 00:00:00 2001 From: Tyler Schrock Date: Fri, 3 Dec 2021 08:00:07 -0500 Subject: [PATCH 08/20] A few more types --- KaitaiStream.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/KaitaiStream.ts b/KaitaiStream.ts index 4a41e9c..6b9b614 100644 --- a/KaitaiStream.ts +++ b/KaitaiStream.ts @@ -94,7 +94,7 @@ class KaitaiStream { * Sets the backing ArrayBuffer of the KaitaiStream object and updates the * DataView to point to the new buffer. */ - public set buffer(v) { + public set buffer(v: ArrayBuffer) { this._buffer = v; this._dataView = new DataView(this._buffer, this._byteOffset); this._byteLength = this._buffer.byteLength; @@ -113,7 +113,7 @@ class KaitaiStream { * Sets the byteOffset of the KaitaiStream object and updates the DataView to * point to the new byteOffset. */ - public set byteOffset(v) { + public set byteOffset(v: number) { this._byteOffset = v; this._dataView = new DataView(this._buffer, this._byteOffset); this._byteLength = this._buffer.byteLength; @@ -131,7 +131,7 @@ class KaitaiStream { * Sets the backing DataView of the KaitaiStream object and updates the buffer * and byteOffset to point to the DataView values. */ - public set dataView(v) { + public set dataView(v: DataView) { this._byteOffset = v.byteOffset; this._buffer = v.buffer; this._dataView = new DataView(this._buffer, this._byteOffset); @@ -155,7 +155,6 @@ class KaitaiStream { this.buffer = buf; } - // ======================================================================== // Stream positioning // ======================================================================== @@ -410,7 +409,6 @@ class KaitaiStream { return 0x100000000 * v2 + v1; } - // ======================================================================== // Floating point numbers // ======================================================================== @@ -569,7 +567,7 @@ class KaitaiStream { * Native endianness. Either KaitaiStream.BIG_ENDIAN or KaitaiStream.LITTLE_ENDIAN * depending on the platform endianness. */ - public static endianness = new Int8Array(new Int16Array([1]).buffer)[0] > 0; + public static endianness: boolean = new Int8Array(new Int16Array([1]).buffer)[0] > 0; // ======================================================================== // Byte arrays From 0bc2a1151c9f9d5e6818cc30d2f570ce8c814709 Mon Sep 17 00:00:00 2001 From: Tyler Schrock Date: Fri, 3 Dec 2021 08:18:05 -0500 Subject: [PATCH 09/20] Update npmignore --- .npmignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.npmignore b/.npmignore index 0361ed9..04235e6 100644 --- a/.npmignore +++ b/.npmignore @@ -2,3 +2,6 @@ .travis.yml rollup.config.js KaitaiStream.ts +tsconfig.json +*.tgz +.github From bdfc43488b6d30d9286a4eb0055ddad0309b3d60 Mon Sep 17 00:00:00 2001 From: Petr Pucil Date: Sun, 29 Sep 2024 22:13:00 +0200 Subject: [PATCH 10/20] Accept `Uint8Array` as the only type for byte arrays This is now possible thanks to https://github.com/kaitai-io/kaitai_struct_compiler/commit/ec064e3930e03f74df50359dc89836e778a48b2d --- KaitaiStream.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/KaitaiStream.ts b/KaitaiStream.ts index 3943e64..2aabadb 100644 --- a/KaitaiStream.ts +++ b/KaitaiStream.ts @@ -650,7 +650,7 @@ class KaitaiStream { * @returns The read bytes. * @throws {string} */ - public readBytesTermMulti(terminator: number[] | Uint8Array, include: boolean, consume: boolean, eosError: boolean): Uint8Array { + public readBytesTermMulti(terminator: Uint8Array, include: boolean, consume: boolean, eosError: boolean): Uint8Array { var unitSize = terminator.length; var data = new Uint8Array(this._buffer, this._byteOffset + this.pos, this.size - this.pos); var res = KaitaiStream.bytesTerminateMulti(data, terminator, true); @@ -698,7 +698,7 @@ class KaitaiStream { * @param padByte The byte to strip. * @returns The stripped data. */ - public static bytesStripRight(data: number[] | NodeJS.TypedArray, padByte: number): number[] | NodeJS.TypedArray { + public static bytesStripRight(data: Uint8Array, padByte: number): Uint8Array { let newLen = data.length; while (data[newLen - 1] === padByte) { newLen--; @@ -712,7 +712,7 @@ class KaitaiStream { * @param include True if the returned bytes should include the terminator. * @returns The terminated bytes. */ - public static bytesTerminate(data: number[] | NodeJS.TypedArray, term: number, include: boolean): number[] | NodeJS.TypedArray { + public static bytesTerminate(data: Uint8Array, term: number, include: boolean): Uint8Array { let newLen = 0; const maxLen = data.length; while (newLen < maxLen && data[newLen] !== term) { @@ -729,7 +729,7 @@ class KaitaiStream { * @param include True if the returned bytes should include the terminator. * @returns The terminated bytes. */ - public static bytesTerminateMulti(data: Uint8Array, term: number[] | NodeJS.TypedArray, include: boolean): Uint8Array { + public static bytesTerminateMulti(data: Uint8Array, term: Uint8Array, include: boolean): Uint8Array { var unitSize = term.length; if (unitSize === 0) { return new Uint8Array(); @@ -796,7 +796,7 @@ class KaitaiStream { * @param key The key byte. * @returns The Xor'd bytes. */ - public static processXorOne(data: ArrayLike, key: number): Uint8Array { + public static processXorOne(data: Uint8Array, key: number): Uint8Array { const r = new Uint8Array(data.length); const dl = data.length; for (let i = 0; i < dl; i++) @@ -809,7 +809,7 @@ class KaitaiStream { * @param key The key bytes. * @returns The Xor'd bytes. */ - public static processXorMany(data: ArrayLike, key: ArrayLike): Uint8Array { + public static processXorMany(data: Uint8Array, key: Uint8Array): Uint8Array { const dl = data.length; const r = new Uint8Array(dl); const kl = key.length; @@ -830,7 +830,7 @@ class KaitaiStream { * @returns The rotated bytes. * @throws {string} */ - public static processRotateLeft(data: ArrayLike, amount: number, groupSize: number): Uint8Array { + public static processRotateLeft(data: Uint8Array, amount: number, groupSize: number): Uint8Array { if (groupSize !== 1) throw ("unable to rotate group of " + groupSize + " bytes yet"); @@ -929,7 +929,7 @@ class KaitaiStream { * @param b The second array. * @returns `0` if the arrays are the equal, a positive number if `a` is greater than `b`, or a negative number if `a` is less than `b`. */ - public static byteArrayCompare(a: number[] | Uint8Array, b: number[] | Uint8Array): number { + public static byteArrayCompare(a: Uint8Array, b: Uint8Array): number { if (a === b) return 0; const al = a.length; @@ -1139,11 +1139,11 @@ class KaitaiStream { * @param array Array of character codes. * @returns String created from the character codes. */ - public static createStringFromArray(array: number[] | Uint8Array): string { + public static createStringFromArray(array: Uint8Array): string { const chunk_size = 0x8000; const chunks = []; for (let i = 0; i < array.length; i += chunk_size) { - const chunk = 'subarray' in array ? array.subarray(i, i + chunk_size) : array.slice(i, i + chunk_size); + const chunk = array.subarray(i, i + chunk_size); chunks.push(String.fromCharCode.apply(null, chunk)); } return chunks.join(""); From c3621b13aa6cc4f186c229b8743412d22a740b95 Mon Sep 17 00:00:00 2001 From: Petr Pucil Date: Sun, 29 Sep 2024 22:58:13 +0200 Subject: [PATCH 11/20] Fix some incorrect comments --- KaitaiStream.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/KaitaiStream.ts b/KaitaiStream.ts index 2aabadb..4091e16 100644 --- a/KaitaiStream.ts +++ b/KaitaiStream.ts @@ -474,7 +474,7 @@ class KaitaiStream { // ------------------------------------------------------------------------ /** - * Aligns bit reading to the byte boundry. + * Aligns the stream position to the next byte boundary. */ public alignToByte(): void { this.bitsLeft = 0; @@ -761,13 +761,13 @@ class KaitaiStream { return KaitaiStream.createStringFromArray(arr); } else { if (typeof TextDecoder === 'function') { - // we're in the browser that supports TextDecoder + // we're in a browser that supports TextDecoder, or in Node.js 11 or later return (new TextDecoder(encoding)).decode(arr); } else { - // probably we're in node.js + // probably we're in Node.js < 11 - // check if it's supported natively by node.js Buffer - // see https://github.com/nodejs/node/blob/master/lib/buffer.js#L187 for details + // check if it's supported natively by Node.js Buffer + // see https://nodejs.org/docs/latest-v10.x/api/buffer.html#buffer_buffers_and_character_encodings switch (encoding.toLowerCase()) { case 'utf8': case 'utf-8': @@ -825,7 +825,7 @@ class KaitaiStream { /** * @param data The input bytes. - * @param amount The number of bytes to rotate. + * @param amount The shift amount in bits. * @param groupSize The number of bytes in each group. * @returns The rotated bytes. * @throws {string} @@ -1102,8 +1102,8 @@ class KaitaiStream { }; /** - * Ensures that we have an least `length` bytes left in the stream. - * If that's not true, throws an EOFError. + * Ensures that we have at least `length` bytes left in the stream. + * If not, throws an EOFError. * * @param length Number of bytes to require. * @throws {KaitaiStream.EOFError} From 9cbb4a0ac34f718357d9a000f003c51afdd03f2b Mon Sep 17 00:00:00 2001 From: Petr Pucil Date: Sun, 29 Sep 2024 23:00:57 +0200 Subject: [PATCH 12/20] bytesToStr(): change `encoding` parameter type to `string` --- KaitaiStream.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/KaitaiStream.ts b/KaitaiStream.ts index 4091e16..2c02fc8 100644 --- a/KaitaiStream.ts +++ b/KaitaiStream.ts @@ -756,7 +756,7 @@ class KaitaiStream { * @param encoding The character encoding. * @returns The decoded string. */ - public static bytesToStr(arr: Uint8Array, encoding: BufferEncoding): string { + public static bytesToStr(arr: Uint8Array, encoding: string): string { if (encoding == null || encoding.toLowerCase() === "ascii") { return KaitaiStream.createStringFromArray(arr); } else { @@ -775,7 +775,7 @@ class KaitaiStream { case 'ucs-2': case 'utf16le': case 'utf-16le': - return Buffer.from(arr).toString(encoding); + return Buffer.from(arr).toString(encoding as BufferEncoding); default: // unsupported encoding, we'll have to resort to iconv-lite if (typeof KaitaiStream.iconvlite === 'undefined') From 8a30b2a44869507a9e3b512b33ca1e8e1d1a2e46 Mon Sep 17 00:00:00 2001 From: Petr Pucil Date: Sun, 29 Sep 2024 23:11:10 +0200 Subject: [PATCH 13/20] processZlib(): return `Uint8Array` even on Node.js --- KaitaiStream.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/KaitaiStream.ts b/KaitaiStream.ts index 2c02fc8..78775a3 100644 --- a/KaitaiStream.ts +++ b/KaitaiStream.ts @@ -848,15 +848,16 @@ class KaitaiStream { * @param buf The input bytes. * @returns The uncompressed bytes. */ - public static processZlib(buf: Uint8Array): Uint8Array | Buffer { + public static processZlib(buf: Uint8Array): Uint8Array { if (typeof require !== 'undefined') { // require is available - we're running under node if (typeof KaitaiStream.zlib === 'undefined') KaitaiStream.zlib = require('zlib') as Zlib; // use node's zlib module API - return (KaitaiStream.zlib as Zlib).inflateSync( - Buffer.from(buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength)) + const r = (KaitaiStream.zlib as Zlib).inflateSync( + Buffer.from(buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength)) ); + return new Uint8Array(r.buffer, r.byteOffset, r.length); } else { // no require() - assume we're running as a web worker in browser. // user should have configured KaitaiStream.depUrls.zlib, if not From 59e604c4b6221a2797025fc2be4fad17f530a76d Mon Sep 17 00:00:00 2001 From: Petr Pucil Date: Sun, 29 Sep 2024 23:14:02 +0200 Subject: [PATCH 14/20] Use `const` and `let` instead of `var` everywhere --- KaitaiStream.ts | 50 ++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/KaitaiStream.ts b/KaitaiStream.ts index 78775a3..b0cd080 100644 --- a/KaitaiStream.ts +++ b/KaitaiStream.ts @@ -491,22 +491,22 @@ class KaitaiStream { if (n > 32) { throw new RangeError("readBitsIntBe: the maximum supported bit length is 32 (tried to read " + n + " bits)"); } - var res = 0; + let res = 0; - var bitsNeeded = n - this.bitsLeft; + const bitsNeeded = n - this.bitsLeft; this.bitsLeft = -bitsNeeded & 7; // `-bitsNeeded mod 8` if (bitsNeeded > 0) { // 1 bit => 1 byte // 8 bits => 1 byte // 9 bits => 2 bytes - var bytesNeeded = ((bitsNeeded - 1) >> 3) + 1; // `ceil(bitsNeeded / 8)` (NB: `x >> 3` is `floor(x / 8)`) - var buf = this.readBytes(bytesNeeded); - for (var i = 0; i < bytesNeeded; i++) { + const bytesNeeded = ((bitsNeeded - 1) >> 3) + 1; // `ceil(bitsNeeded / 8)` (NB: `x >> 3` is `floor(x / 8)`) + const buf = this.readBytes(bytesNeeded); + for (let i = 0; i < bytesNeeded; i++) { res = res << 8 | buf[i]; } - var newBits = res; + const newBits = res; res = res >>> this.bitsLeft | this.bits << bitsNeeded; // `x << 32` is defined as `x << 0` in JS, but only `0 << 32` // can occur here (`n = 32` and `bitsLeft = 0`, this implies // `bits = 0` unless changed externally) @@ -515,7 +515,7 @@ class KaitaiStream { res = this.bits >>> -bitsNeeded; // shift unneeded bits out } - var mask = (1 << this.bitsLeft) - 1; // `bitsLeft` is in range 0..7, so `(1 << 32)` does not have to be considered + const mask = (1 << this.bitsLeft) - 1; // `bitsLeft` is in range 0..7, so `(1 << 32)` does not have to be considered this.bits &= mask; // always return an unsigned 32-bit integer @@ -543,23 +543,23 @@ class KaitaiStream { if (n > 32) { throw new RangeError("readBitsIntLe: the maximum supported bit length is 32 (tried to read " + n + " bits)"); } - var res = 0; - var bitsNeeded = n - this.bitsLeft; + let res = 0; + const bitsNeeded = n - this.bitsLeft; if (bitsNeeded > 0) { // 1 bit => 1 byte // 8 bits => 1 byte // 9 bits => 2 bytes - var bytesNeeded = ((bitsNeeded - 1) >> 3) + 1; // `ceil(bitsNeeded / 8)` (NB: `x >> 3` is `floor(x / 8)`) - var buf = this.readBytes(bytesNeeded); - for (var i = 0; i < bytesNeeded; i++) { + const bytesNeeded = ((bitsNeeded - 1) >> 3) + 1; // `ceil(bitsNeeded / 8)` (NB: `x >> 3` is `floor(x / 8)`) + const buf = this.readBytes(bytesNeeded); + for (let i = 0; i < bytesNeeded; i++) { res |= buf[i] << (i * 8); } // NB: in JavaScript, bit shift operators always shift by modulo 32 of the right-hand operand (see // https://tc39.es/ecma262/multipage/ecmascript-data-types-and-values.html#sec-numeric-types-number-unsignedRightShift), // so `res >>> 32` is equivalent to `res >>> 0` (but we don't want that) - var newBits = bitsNeeded < 32 ? res >>> bitsNeeded : 0; + const newBits = bitsNeeded < 32 ? res >>> bitsNeeded : 0; res = res << this.bitsLeft | this.bits; this.bits = newBits; } else { @@ -571,7 +571,7 @@ class KaitaiStream { // always return an unsigned 32-bit integer if (n < 32) { - var mask = (1 << n) - 1; + const mask = (1 << n) - 1; res &= mask; // this produces a signed 32-bit int, but the sign bit is cleared } else { res >>>= 0; @@ -651,14 +651,14 @@ class KaitaiStream { * @throws {string} */ public readBytesTermMulti(terminator: Uint8Array, include: boolean, consume: boolean, eosError: boolean): Uint8Array { - var unitSize = terminator.length; - var data = new Uint8Array(this._buffer, this._byteOffset + this.pos, this.size - this.pos); - var res = KaitaiStream.bytesTerminateMulti(data, terminator, true); + const unitSize = terminator.length; + const data = new Uint8Array(this._buffer, this._byteOffset + this.pos, this.size - this.pos); + let res = KaitaiStream.bytesTerminateMulti(data, terminator, true); this.pos += res.length; - var termFound = - res.length !== 0 && - res.length % unitSize === 0 && - KaitaiStream.byteArrayCompare(new Uint8Array(res.buffer, res.length - unitSize), terminator) === 0; + const termFound = + res.length !== 0 && + res.length % unitSize === 0 && + KaitaiStream.byteArrayCompare(new Uint8Array(res.buffer, res.length - unitSize), terminator) === 0; if (termFound) { if (!include) { res = new Uint8Array(res.buffer, res.byteOffset, res.length - unitSize); @@ -730,13 +730,13 @@ class KaitaiStream { * @returns The terminated bytes. */ public static bytesTerminateMulti(data: Uint8Array, term: Uint8Array, include: boolean): Uint8Array { - var unitSize = term.length; + const unitSize = term.length; if (unitSize === 0) { return new Uint8Array(); } - var len = data.length; - var iTerm = 0; - for (var iData = 0; iData < len;) { + const len = data.length; + let iTerm = 0; + for (let iData = 0; iData < len;) { if (data[iData] !== term[iTerm]) { iData += unitSize - iTerm; iTerm = 0; From 313c29fdd31251064d85fd9f763bbfc90c4b67a8 Mon Sep 17 00:00:00 2001 From: Petr Pucil Date: Sun, 29 Sep 2024 23:23:22 +0200 Subject: [PATCH 15/20] Update npm dependencies --- package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 68aa27f..b02e9bc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -66,9 +66,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "16.18.108", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.108.tgz", - "integrity": "sha512-fj42LD82fSv6yN9C6Q4dzS+hujHj+pTv0IpRR3kI20fnYeS0ytBpjFO9OjmDowSPPt4lNKN46JLaKbCyP+BW2A==", + "version": "16.18.112", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.112.tgz", + "integrity": "sha512-EKrbKUGJROm17+dY/gMi31aJlGLJ75e1IkTojt9n6u+hnaTBDs+M1bIdOawpk2m6YUAXq/R2W0SxCng1tndHCg==", "dev": true, "license": "MIT" }, @@ -172,9 +172,9 @@ } }, "node_modules/rollup": { - "version": "2.79.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", - "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", + "version": "2.79.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.2.tgz", + "integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==", "dev": true, "license": "MIT", "bin": { From 02a66a4bdfe8a2f04605478fe67ba7810421c784 Mon Sep 17 00:00:00 2001 From: Petr Pucil Date: Mon, 30 Sep 2024 00:02:24 +0200 Subject: [PATCH 16/20] package.json: include *.d.ts files in the package --- package.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 890f731..493ac0f 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,9 @@ "main": "index.js", "types": "index.d.ts", "files": [ - "KaitaiStream.js" + "KaitaiStream.js", + "KaitaiStream.d.ts", + "index.d.ts" ], "scripts": { "build": "rollup -c", From 66f2af0f3aef700cff7e5ff63e1d30f8945a1a87 Mon Sep 17 00:00:00 2001 From: Petr Pucil Date: Wed, 2 Oct 2024 17:45:22 +0200 Subject: [PATCH 17/20] Use named (not anonymous) classes for custom error types This fixes the `name` property of class constructors. For example, `KaitaiStream.ValidationNotEqualError.name` now returns `'ValidationNotEqualError'`. This is consistent with how the built-in error types work - for example, `RangeError.name` is `'RangeError'`, as you would expect. Previously, when the classes were anonymous, `KaitaiStream.ValidationNotEqualError.name` returned `'class_4'` (which was an automatically generated name that the TypeScript compiler used in the output ES5 code). It's worth noting that error constructors were anonymous also in our handwritten `KaitaiStream.js` before we migrated to TypeScript ( `KaitaiStream.ValidationNotEqualError.name` used to be the empty string `''`), so keeping them anonymous wouldn't really be a regression, but it's nice to improve it now. I noticed this issue because we use [`assert.throws`](https://nodejs.org/docs/latest-v20.x/api/assert.html#assertthrowsfn-error-message) in KST with arguments as in the "Validate instanceof using constructor:" example. If this assertion fails, Node.js includes the `name` property of the constructor in the error message if it's not empty. As a result, the error message was the following when a `ValidationNotEqualError` was expected but not thrown: ``` Missing expected exception (class_4). AssertionError [ERR_ASSERTION]: Missing expected exception (class_4). at /tests/helpers/javascript/testHelperThrows.js:12:16 at FSReqCallback.readFileAfterClose [as oncomplete] (node:internal/fs/read/context:68:3) ``` Now it is `Missing expected exception (ValidationNotEqualError)`. --- KaitaiStream.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/KaitaiStream.ts b/KaitaiStream.ts index b0cd080..a53a11e 100644 --- a/KaitaiStream.ts +++ b/KaitaiStream.ts @@ -954,7 +954,7 @@ class KaitaiStream { // Internal implementation details // ======================================================================== - public static EOFError = class extends Error { + public static EOFError = class EOFError extends Error { public name = "EOFError"; public bytesReq: number; public bytesAvail: number; @@ -975,7 +975,7 @@ class KaitaiStream { /** * Unused since Kaitai Struct Compiler v0.9+ - compatibility with older versions. */ - public static UnexpectedDataError = class extends Error { + public static UnexpectedDataError = class UnexpectedDataError extends Error { public name = "UnexpectedDataError"; public expected: any; public actual: any; @@ -993,7 +993,7 @@ class KaitaiStream { } }; - public static UndecidedEndiannessError = class extends Error { + public static UndecidedEndiannessError = class UndecidedEndiannessError extends Error { public name = "UndecidedEndiannessError"; public constructor() { @@ -1003,7 +1003,7 @@ class KaitaiStream { } }; - public static ValidationNotEqualError = class extends Error { + public static ValidationNotEqualError = class ValidationNotEqualError extends Error { public name = "ValidationNotEqualError"; public expected: any; public actual: any; @@ -1021,7 +1021,7 @@ class KaitaiStream { } }; - public static ValidationLessThanError = class extends Error { + public static ValidationLessThanError = class ValidationLessThanError extends Error { public name = "ValidationLessThanError"; public min: any; public actual: any; @@ -1039,7 +1039,7 @@ class KaitaiStream { } }; - public static ValidationGreaterThanError = class extends Error { + public static ValidationGreaterThanError = class ValidationGreaterThanError extends Error { public name = "ValidationGreaterThanError"; public max: any; public actual: any; @@ -1057,7 +1057,7 @@ class KaitaiStream { } }; - public static ValidationNotAnyOfError = class extends Error { + public static ValidationNotAnyOfError = class ValidationNotAnyOfError extends Error { public name = "ValidationNotAnyOfError"; public actual: any; @@ -1072,7 +1072,7 @@ class KaitaiStream { } }; - public static ValidationNotInEnumError = class extends Error { + public static ValidationNotInEnumError = class ValidationNotInEnumError extends Error { public name = "ValidationNotInEnumError"; public actual: any; @@ -1087,7 +1087,7 @@ class KaitaiStream { } }; - public static ValidationExprError = class extends Error { + public static ValidationExprError = class ValidationExprError extends Error { public name = "ValidationExprError"; public actual: any; From 127e4334886802921c26fbc7dc99ece4e9128195 Mon Sep 17 00:00:00 2001 From: Petr Pucil Date: Wed, 2 Oct 2024 23:58:53 +0200 Subject: [PATCH 18/20] package.json: remove deprecated `prepublish` script The status of `prepublish` scripts is still unclear and confusing to me. https://docs.npmjs.com/cli/v10/using-npm/scripts#life-cycle-scripts states "prepublish (DEPRECATED)", then https://docs.npmjs.com/cli/v10/using-npm/scripts#prepare-and-prepublish explains how `prepublish` is a confusing name (because it doesn't actually run with `publish` at all), but then it still recommends "use a `prepublish` script" for tasks such as "Compiling CoffeeScript source code into JavaScript". That doesn't sound like it's deprecated to me if it's still recommended in some situations, so maybe the documentation is wrong. I suppose the least surprising option is not to rely on any automatic "hook" and always require running `npm run build` manually. --- package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json b/package.json index 493ac0f..c125fce 100644 --- a/package.json +++ b/package.json @@ -34,8 +34,7 @@ "index.d.ts" ], "scripts": { - "build": "rollup -c", - "prepublish": "npm run build" + "build": "rollup -c" }, "devDependencies": { "@rollup/plugin-typescript": "^8.3.0", From 779daa6ed16726ccc0dca8c22ea48adfc5d48aee Mon Sep 17 00:00:00 2001 From: Petr Pucil Date: Thu, 3 Oct 2024 00:11:43 +0200 Subject: [PATCH 19/20] GH Actions: run `npm run build` before publishing --- .github/workflows/build.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2e508d4..c7f6fc7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -15,6 +15,10 @@ jobs: node-version: '20' registry-url: https://registry.npmjs.org/ - run: npm ci + - run: npm run build + - name: Check that KaitaiStream.js will be included + run: | + npm publish --tag next --dry-run --json | jq --exit-status '.files | map(.path) | any(. == "KaitaiStream.js")' - name: Publish to npm run: npm publish --tag next env: From 239aabe10290e927b757b14a24a0ef888955339f Mon Sep 17 00:00:00 2001 From: Petr Pucil Date: Thu, 3 Oct 2024 00:25:59 +0200 Subject: [PATCH 20/20] README: add "Development" section --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index d3a2afe..0141140 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,20 @@ Further reading: * [About API implemented in this library](http://doc.kaitai.io/stream_api.html) * [JavaScript-specific notes](http://doc.kaitai.io/lang_javascript.html) - also includes Quick start guide +## Development + +After cloning this repository, you must run these commands to compile the +TypeScript source file [`KaitaiStream.ts`](./KaitaiStream.ts) into the +`KaitaiStream.js` file (ignored by Git) with JavaScript code: + +```shell +npm install +npm run build +``` + +`npm run build` also needs to be run after each change to +[`KaitaiStream.ts`](./KaitaiStream.ts). + ## Licensing Copyright 2012-2016 Ilmari Heikkinen\