diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..8d49016 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,28 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +## [1.1.0] - 2023-05-28 + +### Added + +- Starter thinking around a Media API. + +### Changed + +- Change console.warn to a console.error when importing archive.js. + +### Removed + +- Removed build step for bitjs.io now that all browsers (Firefox 114+) support ES Module Workers. + +## [1.0.11] - 2023-02-15 + +### Added + +- Add sniffer support for the ICO format. +- Add unit test coverage via c8. + +### Fixed + +- Fixes for the audio/flac codec type. diff --git a/archive/archive.js b/archive/archive.js index b6552bc..ea4ef4a 100644 --- a/archive/archive.js +++ b/archive/archive.js @@ -14,6 +14,7 @@ import { UnarchiveAppendEvent, UnarchiveErrorEvent, UnarchiveEvent, UnarchiveEve UnarchiveProgressEvent, UnarchiveStartEvent, Unarchiver, UnrarrerInternal, UntarrerInternal, UnzipperInternal, getUnarchiverInternal } from './decompress-internal.js'; +import { Unzipper, Unrarrer, Untarrer, getUnarchiver } from './decompress.js'; export { UnarchiveAppendEvent, @@ -26,65 +27,7 @@ export { UnarchiveProgressEvent, UnarchiveStartEvent, Unarchiver, + Unzipper, Unrarrer, Untarrer, getUnarchiver } -/** - * All extracted files returned by an Unarchiver will implement - * the following interface: - */ - -/** - * @typedef UnarchivedFile - * @property {string} filename - * @property {Uint8Array} fileData - */ - -/** - * The goal is to make this testable - send getUnarchiver() an array buffer of - * an archive, call start on the unarchiver, expect the returned result. - * - * Problem: It relies on Web Workers, and that won't work in a nodejs context. - * Solution: Make archive.js very thin, have it feed web-specific things into - * an internal module that is isomorphic JavaScript. - * - * TODO: - * - write unit tests for archive-internal.js that use the nodejs Worker - * equivalent. - * - maybe use @pgriess/node-webworker or @audreyt/node-webworker-threads or - * just node's worker_threads ? - */ - -const createWorkerFn = (scriptFilename) => new Worker(scriptFilename); - -function warn() { - console.warn(`Stop using archive.js and use decompress.js instead. This module will be removed.`); -} - -// Thin wrappers of unarchivers for clients who want to construct a specific -// unarchiver themselves rather than use getUnarchiver(). -export class Unzipper extends UnzipperInternal { - constructor(ab, options) { warn(); super(ab, createWorkerFn, options); } -} - -export class Unrarrer extends UnrarrerInternal { - constructor(ab, options) { warn(); super(ab, createWorkerFn, options); } -} - -export class Untarrer extends UntarrerInternal { - constructor(ab, options) { warn(); super(ab, createWorkerFn, options); } -} - -/** - * Factory method that creates an unarchiver based on the byte signature found - * in the arrayBuffer. - * @param {ArrayBuffer} ab The ArrayBuffer to unarchive. Note that this ArrayBuffer - * must not be referenced after calling this method, as the ArrayBuffer is marked - * as Transferable and sent to a Worker thread once start() is called. - * @param {Object|string} options An optional object of options, or a string - * representing where the path to the unarchiver script files. - * @returns {Unarchiver} - */ -export function getUnarchiver(ab, options = {}) { - warn(); - return getUnarchiverInternal(ab, createWorkerFn, options); -} +console.error(`bitjs: Stop importing archive.js, this module will be removed. Import decompress.js instead.`); diff --git a/archive/decompress.js b/archive/decompress.js index 0802975..9894040 100644 --- a/archive/decompress.js +++ b/archive/decompress.js @@ -53,7 +53,7 @@ export { * just node's worker_threads ? */ -const createWorkerFn = (scriptFilename) => new Worker(scriptFilename); +const createWorkerFn = (scriptFilename) => new Worker(scriptFilename, { type: 'module' }); // Thin wrappers of compressors for clients who want to construct a specific // unarchiver themselves rather than use getUnarchiver(). diff --git a/archive/rarvm.js b/archive/rarvm.js index ebc8663..55206ea 100644 --- a/archive/rarvm.js +++ b/archive/rarvm.js @@ -6,6 +6,8 @@ * Copyright(c) 2017 Google Inc. */ +import { BitStream } from '../io/bitstream.js'; + /** * CRC Implementation. */ @@ -104,13 +106,13 @@ function CRC(startCRC, arr) { /** * RarVM Implementation. */ -const VM_MEMSIZE = 0x40000; -const VM_MEMMASK = (VM_MEMSIZE - 1); -const VM_GLOBALMEMADDR = 0x3C000; -const VM_GLOBALMEMSIZE = 0x2000; -const VM_FIXEDGLOBALSIZE = 64; -const MAXWINSIZE = 0x400000; -const MAXWINMASK = (MAXWINSIZE - 1); +export const VM_MEMSIZE = 0x40000; +export const VM_MEMMASK = (VM_MEMSIZE - 1); +export const VM_GLOBALMEMADDR = 0x3C000; +export const VM_GLOBALMEMSIZE = 0x2000; +export const VM_FIXEDGLOBALSIZE = 64; +export const MAXWINSIZE = 0x400000; +export const MAXWINMASK = (MAXWINSIZE - 1); /** */ @@ -448,7 +450,7 @@ const StdList = [ /** * @constructor */ -class RarVM { +export class RarVM { constructor() { /** @private {Uint8Array} */ this.mem_ = null; @@ -486,7 +488,7 @@ class RarVM { /** * @param {VM_PreparedOperand} op * @param {boolean} byteMode - * @param {bitjs.io.BitStream} bstream A rtl bit stream. + * @param {BitStream} bstream A rtl bit stream. */ decodeArg(op, byteMode, bstream) { const data = bstream.peekBits(16); @@ -808,7 +810,7 @@ class RarVM { //InitBitInput(); //memcpy(InBuf,Code,Min(CodeSize,BitInput::MAX_SIZE)); - const bstream = new bitjs.io.BitStream(code.buffer, true /* rtl */); + const bstream = new BitStream(code.buffer, true /* rtl */); // Calculate the single byte XOR checksum to check validity of VM code. let xorSum = 0; @@ -970,7 +972,7 @@ class RarVM { /** * Static function that reads in the next set of bits for the VM * (might return 4, 8, 16 or 32 bits). - * @param {bitjs.io.BitStream} bstream A RTL bit stream. + * @param {BitStream} bstream A RTL bit stream. * @returns {number} The value of the bits read. */ static readData(bstream) { diff --git a/archive/unrar.js b/archive/unrar.js index 28a2659..635fc33 100644 --- a/archive/unrar.js +++ b/archive/unrar.js @@ -12,10 +12,10 @@ // present. // This file expects to be invoked as a Worker (see onmessage below). -importScripts('../io/bitstream-worker.js'); -importScripts('../io/bytestream-worker.js'); -importScripts('../io/bytebuffer-worker.js'); -importScripts('rarvm.js'); +import { BitStream } from '../io/bitstream.js'; +import { ByteStream } from '../io/bytestream.js'; +import { ByteBuffer } from '../io/bytebuffer.js'; +import { VM_GLOBALMEMADDR, VM_GLOBALMEMSIZE, VM_FIXEDGLOBALSIZE, MAXWINMASK } from './rarvm.js'; const UnarchiveState = { NOT_STARTED: 0, @@ -86,7 +86,7 @@ const ENDARC_HEAD = 0x7b; */ class RarVolumeHeader { /** - * @param {bitjs.io.ByteStream} bstream + * @param {ByteStream} bstream */ constructor(bstream) { let headBytesRead = 0; @@ -316,19 +316,19 @@ const RD = { //rep decode }; /** - * @type {Array} + * @type {Array} */ const rOldBuffers = []; /** * The current buffer we are unpacking to. - * @type {bitjs.io.ByteBuffer} + * @type {ByteBuffer} */ let rBuffer; /** * The buffer of the final bytes after filtering (only used in Unpack29). - * @type {bitjs.io.ByteBuffer} + * @type {ByteBuffer} */ let wBuffer; @@ -346,7 +346,7 @@ let wBuffer; /** * Read in Huffman tables for RAR - * @param {bitjs.io.BitStream} bstream + * @param {BitStream} bstream */ function RarReadTables(bstream) { const BitLength = new Array(rBC); @@ -491,7 +491,7 @@ function RarMakeDecodeTables(BitLength, offset, dec, size) { // TODO: implement /** - * @param {bitjs.io.BitStream} bstream + * @param {BitStream} bstream * @param {boolean} Solid */ function Unpack15(bstream, Solid) { @@ -500,7 +500,7 @@ function Unpack15(bstream, Solid) { /** * Unpacks the bit stream into rBuffer using the Unpack20 algorithm. - * @param {bitjs.io.BitStream} bstream + * @param {BitStream} bstream * @param {boolean} Solid */ function Unpack20(bstream, Solid) { @@ -694,7 +694,7 @@ function InitFilters() { */ function RarAddVMCode(firstByte, vmCode) { VM.init(); - const bstream = new bitjs.io.BitStream(vmCode.buffer, true /* rtl */); + const bstream = new BitStream(vmCode.buffer, true /* rtl */); let filtPos; if (firstByte & 0x80) { @@ -864,7 +864,7 @@ function RarAddVMCode(firstByte, vmCode) { /** - * @param {!bitjs.io.BitStream} bstream + * @param {!BitStream} bstream */ function RarReadVMCode(bstream) { const firstByte = bstream.readBits(8); @@ -886,7 +886,7 @@ function RarReadVMCode(bstream) { /** * Unpacks the bit stream into rBuffer using the Unpack29 algorithm. - * @param {bitjs.io.BitStream} bstream + * @param {BitStream} bstream * @param {boolean} Solid */ function Unpack29(bstream, Solid) { @@ -1258,9 +1258,9 @@ function unpack(v) { // TODO: implement what happens when unpVer is < 15 const Ver = v.header.unpVer <= 15 ? 15 : v.header.unpVer; const Solid = v.header.flags.LHD_SOLID; - const bstream = new bitjs.io.BitStream(v.fileData.buffer, true /* rtl */, v.fileData.byteOffset, v.fileData.byteLength); + const bstream = new BitStream(v.fileData.buffer, true /* rtl */, v.fileData.byteOffset, v.fileData.byteLength); - rBuffer = new bitjs.io.ByteBuffer(v.header.unpackedSize); + rBuffer = new ByteBuffer(v.header.unpackedSize); if (logToConsole) { info('Unpacking ' + v.filename + ' RAR v' + Ver); @@ -1276,7 +1276,7 @@ function unpack(v) { break; case 29: // rar 3.x compression case 36: // alternative hash - wBuffer = new bitjs.io.ByteBuffer(rBuffer.data.length); + wBuffer = new ByteBuffer(rBuffer.data.length); Unpack29(bstream, Solid); break; } // switch(method) @@ -1290,7 +1290,7 @@ function unpack(v) { */ class RarLocalFile { /** - * @param {bitjs.io.ByteStream} bstream + * @param {ByteStream} bstream */ constructor(bstream) { this.header = new RarVolumeHeader(bstream); @@ -1325,7 +1325,7 @@ class RarLocalFile { // Create a new buffer and copy it over. const len = this.header.packSize; - const newBuffer = new bitjs.io.ByteBuffer(len); + const newBuffer = new ByteBuffer(len); newBuffer.insertBytes(this.fileData); this.fileData = newBuffer.data; } else { @@ -1402,7 +1402,7 @@ onmessage = function (event) { // This is the very first time we have been called. Initialize the bytestream. if (!bytestream) { - bytestream = new bitjs.io.ByteStream(bytes); + bytestream = new ByteStream(bytes); currentFilename = ''; currentFileNumber = 0; diff --git a/archive/untar.js b/archive/untar.js index ed63a47..1b11041 100644 --- a/archive/untar.js +++ b/archive/untar.js @@ -11,7 +11,7 @@ */ // This file expects to be invoked as a Worker (see onmessage below). -importScripts('../io/bytestream-worker.js'); +import { ByteStream } from '../io/bytestream.js'; const UnarchiveState = { NOT_STARTED: 0, @@ -166,7 +166,7 @@ onmessage = function (event) { // This is the very first time we have been called. Initialize the bytestream. if (!bytestream) { - bytestream = new bitjs.io.ByteStream(bytes); + bytestream = new ByteStream(bytes); } else { bytestream.push(bytes); } diff --git a/archive/unzip.js b/archive/unzip.js index 473de5b..d62d9b1 100644 --- a/archive/unzip.js +++ b/archive/unzip.js @@ -13,9 +13,9 @@ */ // This file expects to be invoked as a Worker (see onmessage below). -importScripts('../io/bitstream-worker.js'); -importScripts('../io/bytebuffer-worker.js'); -importScripts('../io/bytestream-worker.js'); +import { BitStream } from '../io/bitstream.js'; +import { ByteBuffer } from '../io/bytebuffer.js'; +import { ByteStream } from '../io/bytestream.js'; const UnarchiveState = { NOT_STARTED: 0, @@ -74,7 +74,7 @@ const BIT = [0x01, 0x02, 0x04, 0x08, class ZipLocalFile { /** - * @param {bitjs.io.ByteStream} bstream + * @param {ByteStream} bstream */ constructor(bstream) { if (typeof bstream != typeof {} || !bstream.readNumber || typeof bstream.readNumber != typeof function () { }) { @@ -133,7 +133,7 @@ class ZipLocalFile { // Copy all the read bytes into a buffer and examine the last 16 bytes to see if they are the // data descriptor. let bufferedByteArr = savedBstream.peekBytes(numBytesSeeked); - const descriptorStream = new bitjs.io.ByteStream(bufferedByteArr.buffer, numBytesSeeked - 16, 16); + const descriptorStream = new ByteStream(bufferedByteArr.buffer, numBytesSeeked - 16, 16); const maybeDescriptorSig = descriptorStream.readNumber(4); const maybeCrc32 = descriptorStream.readNumber(4); const maybeCompressedSize = descriptorStream.readNumber(4); @@ -327,7 +327,7 @@ function getFixedDistanceTable() { /** * Extract one bit at a time until we find a matching Huffman Code * then return that symbol. - * @param {bitjs.io.BitStream} bstream + * @param {BitStream} bstream * @param {Map} hcTable * @returns {number} */ @@ -417,10 +417,10 @@ const DistLookupTable = [ ]; /** - * @param {bitjs.io.BitStream} bstream + * @param {BitStream} bstream * @param {Map} hcLiteralTable * @param {Map} hcDistanceTable - * @param {bitjs.io.ByteBuffer} buffer + * @param {ByteBuffer} buffer * @returns */ function inflateBlockData(bstream, hcLiteralTable, hcDistanceTable, buffer) { @@ -491,13 +491,13 @@ function inflateBlockData(bstream, hcLiteralTable, hcDistanceTable, buffer) { */ function inflate(compressedData, numDecompressedBytes) { // Bit stream representing the compressed data. - /** @type {bitjs.io.BitStream} */ - const bstream = new bitjs.io.BitStream(compressedData.buffer, + /** @type {BitStream} */ + const bstream = new BitStream(compressedData.buffer, false /* mtl */, compressedData.byteOffset, compressedData.byteLength); - /** @type {bitjs.io.ByteBuffer} */ - const buffer = new bitjs.io.ByteBuffer(numDecompressedBytes); + /** @type {ByteBuffer} */ + const buffer = new ByteBuffer(numDecompressedBytes); let blockSize = 0; // block format: http://tools.ietf.org/html/rfc1951#page-9 @@ -734,7 +734,7 @@ onmessage = function (event) { // This is the very first time we have been called. Initialize the bytestream. if (!bytestream) { - bytestream = new bitjs.io.ByteStream(bytes); + bytestream = new ByteStream(bytes); } else { bytestream.push(bytes); } diff --git a/archive/zip.js b/archive/zip.js index e0c4108..cb47eba 100644 --- a/archive/zip.js +++ b/archive/zip.js @@ -12,9 +12,7 @@ */ // This file expects to be invoked as a Worker (see onmessage below). -importScripts('../io/bitstream-worker.js'); -importScripts('../io/bytebuffer-worker.js'); -importScripts('../io/bytestream-worker.js'); +import { ByteBuffer } from '../io/bytebuffer.js'; /** * The client sends messages to this Worker containing files to archive in order. The client @@ -146,14 +144,14 @@ function dateToDosTime(jsDate) { /** * @param {FileInfo} file - * @returns {bitjs.io.ByteBuffer} + * @returns {ByteBuffer} */ function zipOneFile(file) { // Zip Local File Header has 30 bytes and then the filename and extrafields. const fileHeaderSize = 30 + file.fileName.length; - /** @type {bitjs.io.ByteBuffer} */ - const buffer = new bitjs.io.ByteBuffer(fileHeaderSize + file.fileData.length); + /** @type {ByteBuffer} */ + const buffer = new ByteBuffer(fileHeaderSize + file.fileData.length); buffer.writeNumber(zLocalFileHeaderSignature, 4); // Magic number. buffer.writeNumber(0x0A, 2); // Version. @@ -190,13 +188,13 @@ function zipOneFile(file) { } /** - * @returns {bitjs.io.ByteBuffer} + * @returns {ByteBuffer} */ function writeCentralFileDirectory() { // Each central directory file header is 46 bytes + the filename. let cdsLength = filesCompressed.map(f => f.fileName.length + 46).reduce((a, c) => a + c); // 22 extra bytes for the end-of-central-dir header. - const buffer = new bitjs.io.ByteBuffer(cdsLength + 22); + const buffer = new ByteBuffer(cdsLength + 22); for (const cdInfo of centralDirectoryInfos) { buffer.writeNumber(zCentralFileHeaderSignature, 4); // Magic number. diff --git a/build/Makefile b/build/Makefile index 5555daa..224db92 100644 --- a/build/Makefile +++ b/build/Makefile @@ -1,13 +1,9 @@ -all: bitjs-io bitjs-image-webp-shim +all: bitjs-image-webp-shim clean: - $(MAKE) -C io clean $(MAKE) -C image/webp-shim clean -bitjs-io: - $(MAKE) -C io - # Make webp-shim/ bitjs-image-webp-shim: $(MAKE) -C image/webp-shim diff --git a/build/README.md b/build/README.md index 80c886c..9494ef4 100644 --- a/build/README.md +++ b/build/README.md @@ -1,7 +1,8 @@ # Build This folder contains files needed to build various pieces of the library. You only need to worry -about this if you intend on patching / modifying the library in some way. +about this if you intend on patching / modifying the library in some way. It is only needed for the +WebP Shim parts of bitjs. ## Prerequisites * Install Docker diff --git a/build/io/Makefile b/build/io/Makefile deleted file mode 100644 index dbe4c30..0000000 --- a/build/io/Makefile +++ /dev/null @@ -1,83 +0,0 @@ -OUT_PATH=../../io - -BITSTREAM_MODULE=${OUT_PATH}/bitstream.js -BITSTREAM_WORKER=${OUT_PATH}/bitstream-worker.js - -BYTESTREAM_MODULE=${OUT_PATH}/bytestream.js -BYTESTREAM_WORKER=${OUT_PATH}/bytestream-worker.js - -BITBUFFER_MODULE=${OUT_PATH}/bitbuffer.js -BITBUFFER_WORKER=${OUT_PATH}/bitbuffer-worker.js - -BYTEBUFFER_MODULE=${OUT_PATH}/bytebuffer.js -BYTEBUFFER_WORKER=${OUT_PATH}/bytebuffer-worker.js - -BITSTREAM_DEF=bitstream-def.js -BYTESTREAM_DEF=bytestream-def.js -BITBUFFER_DEF=bitbuffer-def.js -BYTEBUFFER_DEF=bytebuffer-def.js - -DISCLAIMER="// THIS IS A GENERATED FILE! DO NOT EDIT, INSTEAD EDIT THE FILE IN bitjs/build/io." - -all: ${BITSTREAM_MODULE} ${BITSTREAM_WORKER} \ - ${BYTESTREAM_MODULE} ${BYTESTREAM_WORKER} \ - ${BITBUFFER_MODULE} ${BITBUFFER_WORKER} \ - ${BYTEBUFFER_MODULE} ${BYTEBUFFER_WORKER} - -clean: - rm -rf ${BITSTREAM_MODULE} - rm -rf ${BITSTREAM_WORKER} - rm -rf ${BYTESTREAM_MODULE} - rm -rf ${BYTESTREAM_WORKER} - rm -rf ${BITBUFFER_MODULE} - rm -rf ${BITBUFFER_WORKER} - rm -rf ${BYTEBUFFER_MODULE} - rm -rf ${BYTEBUFFER_WORKER} - -${BITSTREAM_MODULE}: Makefile ${BITSTREAM_DEF} - echo ${DISCLAIMER} > ${BITSTREAM_MODULE} - echo "export const BitStream =" >> ${BITSTREAM_MODULE} - cat ${BITSTREAM_DEF} >> ${BITSTREAM_MODULE} - -${BITSTREAM_WORKER}: Makefile ${BITSTREAM_DEF} - echo ${DISCLAIMER} > ${BITSTREAM_WORKER} - echo "var bitjs = bitjs || {};" >> ${BITSTREAM_WORKER} - echo "bitjs.io = bitjs.io || {};" >> ${BITSTREAM_WORKER} - echo "bitjs.io.BitStream =" >> ${BITSTREAM_WORKER} - cat ${BITSTREAM_DEF} >> ${BITSTREAM_WORKER} - -${BYTESTREAM_MODULE}: Makefile ${BYTESTREAM_DEF} - echo ${DISCLAIMER} > ${BYTESTREAM_MODULE} - echo "export const ByteStream =" >> ${BYTESTREAM_MODULE} - cat ${BYTESTREAM_DEF} >> ${BYTESTREAM_MODULE} - -${BYTESTREAM_WORKER}: Makefile ${BYTESTREAM_DEF} - echo ${DISCLAIMER} > ${BYTESTREAM_WORKER} - echo "var bitjs = bitjs || {};" >> ${BYTESTREAM_WORKER} - echo "bitjs.io = bitjs.io || {};" >> ${BYTESTREAM_WORKER} - echo "bitjs.io.ByteStream =" >> ${BYTESTREAM_WORKER} - cat ${BYTESTREAM_DEF} >> ${BYTESTREAM_WORKER} - -${BITBUFFER_MODULE}: Makefile ${BITBUFFER_DEF} - echo ${DISCLAIMER} > ${BITBUFFER_MODULE} - echo "export const BitBuffer =" >> ${BITBUFFER_MODULE} - cat ${BITBUFFER_DEF} >> ${BITBUFFER_MODULE} - -${BITBUFFER_WORKER}: Makefile ${BITBUFFER_DEF} - echo ${DISCLAIMER} > ${BITBUFFER_WORKER} - echo "var bitjs = bitjs || {};" >> ${BITBUFFER_WORKER} - echo "bitjs.io = bitjs.io || {};" >> ${BITBUFFER_WORKER} - echo "bitjs.io.BitBuffer =" >> ${BITBUFFER_WORKER} - cat ${BITBUFFER_DEF} >> ${BITBUFFER_WORKER} - -${BYTEBUFFER_MODULE}: Makefile ${BYTEBUFFER_DEF} - echo ${DISCLAIMER} > ${BYTEBUFFER_MODULE} - echo "export const ByteBuffer =" >> ${BYTEBUFFER_MODULE} - cat ${BYTEBUFFER_DEF} >> ${BYTEBUFFER_MODULE} - -${BYTEBUFFER_WORKER}: Makefile ${BYTEBUFFER_DEF} - echo ${DISCLAIMER} > ${BYTEBUFFER_WORKER} - echo "var bitjs = bitjs || {};" >> ${BYTEBUFFER_WORKER} - echo "bitjs.io = bitjs.io || {};" >> ${BYTEBUFFER_WORKER} - echo "bitjs.io.ByteBuffer =" >> ${BYTEBUFFER_WORKER} - cat ${BYTEBUFFER_DEF} >> ${BYTEBUFFER_WORKER} diff --git a/build/io/bitbuffer-def.js b/build/io/bitbuffer-def.js deleted file mode 100644 index 55c45b3..0000000 --- a/build/io/bitbuffer-def.js +++ /dev/null @@ -1,200 +0,0 @@ -/* - * bytebuffer-def.js - * - * Provides a writer for bits. - * - * Licensed under the MIT License - * - * Copyright(c) 2021 Google Inc. - */ - -(function () { - const BITMASK = [ - 0, - 0b00000001, - 0b00000011, - 0b00000111, - 0b00001111, - 0b00011111, - 0b00111111, - 0b01111111, - 0b11111111, - ] - - /** - * A write-only Bit buffer which uses a Uint8Array as a backing store. - */ - class BitBuffer { - /** - * @param {number} numBytes The number of bytes to allocate. - * @param {boolean} mtl The bit-packing mode. True means pack bits from most-significant (7) to - * least-significant (0). Defaults false: least-significant (0) to most-significant (8). - */ - constructor(numBytes, mtl = false) { - if (typeof numBytes != typeof 1 || numBytes <= 0) { - throw "Error! ByteBuffer initialized with '" + numBytes + "'"; - } - - /** - * @type {Uint8Array} - * @public - */ - this.data = new Uint8Array(numBytes); - - /** - * Whether we pack bits from most-significant-bit to least. Defaults false (least-to-most - * significant bit packing). - * @type {boolean} - * @private - */ - this.mtl = mtl; - - /** - * The current byte we are filling with bits. - * @type {number} - * @public - */ - this.bytePtr = 0; - - /** - * Points at the bit within the current byte where the next bit will go. This number ranges - * from 0 to 7 and the direction of packing is indicated by the mtl property. - * @type {number} - * @public - */ - this.bitPtr = this.mtl ? 7 : 0; - } - - /** @returns {boolean} */ - getPackingDirection() { - return this.mtl; - } - - /** - * Sets the bit-packing direction. Default (false) is least-significant-bit (0) to - * most-significant (7). Changing the bit-packing direction when the bit pointer is in the - * middle of a byte will fill the rest of that byte with 0s using the current bit-packing - * direction and then set the bit pointer to the appropriate bit of the next byte. If there - * are no more bytes left in this buffer, it will throw an error. - */ - setPackingDirection(mtl = false) { - if (this.mtl !== mtl) { - if (this.mtl && this.bitPtr !== 7) { - this.bytePtr++; - if (this.bytePtr >= this.data.byteLength) { - throw `No more bytes left when switching packing direction`; - } - this.bitPtr = 7; - } else if (!this.mtl && this.bitPtr !== 0) { - this.bytePtr++; - if (this.bytePtr >= this.data.byteLength) { - throw `No more bytes left when switching packing direction`; - } - this.bitPtr = 0; - } - } - - this.mtl = mtl; - } - - /** - * writeBits(3, 6) is the same as writeBits(0b000011, 6). - * Will throw an error (without writing) if this would over-flow the buffer. - * @param {number} val The bits to pack into the buffer. Negative values are not allowed. - * @param {number} numBits Must be positive, non-zero and less or equal to than 53, since - * JavaScript can only support 53-bit integers. - */ - writeBits(val, numBits) { - if (val < 0 || typeof val !== typeof 1) { - throw `Trying to write an invalid value into the BitBuffer: ${val}`; - } - if (numBits < 0 || numBits > 53) { - throw `Trying to write ${numBits} bits into the BitBuffer`; - } - - const totalBitsInBuffer = this.data.byteLength * 8; - const writtenBits = this.bytePtr * 8 + this.bitPtr; - const bitsLeftInBuffer = totalBitsInBuffer - writtenBits; - if (numBits > bitsLeftInBuffer) { - throw `Trying to write ${numBits} into the BitBuffer that only has ${bitsLeftInBuffer}`; - } - - // Least-to-most-significant bit packing method (LTM). - if (!this.mtl) { - let numBitsLeftToWrite = numBits; - while (numBitsLeftToWrite > 0) { - /** The number of bits available to fill in this byte. */ - const bitCapacityInThisByte = 8 - this.bitPtr; - /** The number of bits of val we will write into this byte. */ - const numBitsToWriteIntoThisByte = Math.min(numBitsLeftToWrite, bitCapacityInThisByte); - /** The number of bits that fit in subsequent bytes. */ - const numExcessBits = numBitsLeftToWrite - numBitsToWriteIntoThisByte; - if (numExcessBits < 0) { - throw `Error in LTM bit packing, # of excess bits is negative`; - } - /** The actual bits that need to be written into this byte. Starts at LSB. */ - let actualBitsToWrite = (val & BITMASK[numBitsToWriteIntoThisByte]); - // Only adjust and write bits if any are set to 1. - if (actualBitsToWrite > 0) { - actualBitsToWrite <<= this.bitPtr; - // Now write into the buffer. - this.data[this.bytePtr] |= actualBitsToWrite; - } - // Update the bit/byte pointers and remaining bits to write. - this.bitPtr += numBitsToWriteIntoThisByte; - if (this.bitPtr > 7) { - if (this.bitPtr !== 8) { - throw `Error in LTM bit packing. Tried to write more bits than it should have.`; - } - this.bytePtr++; - this.bitPtr = 0; - } - // Remove bits that have been written from LSB end. - val >>= numBitsToWriteIntoThisByte; - numBitsLeftToWrite -= numBitsToWriteIntoThisByte; - } - } - // Most-to-least-significant bit packing method (MTL). - else { - let numBitsLeftToWrite = numBits; - while (numBitsLeftToWrite > 0) { - /** The number of bits available to fill in this byte. */ - const bitCapacityInThisByte = this.bitPtr + 1; - /** The number of bits of val we will write into this byte. */ - const numBitsToWriteIntoThisByte = Math.min(numBitsLeftToWrite, bitCapacityInThisByte); - /** The number of bits that fit in subsequent bytes. */ - const numExcessBits = numBitsLeftToWrite - numBitsToWriteIntoThisByte; - if (numExcessBits < 0) { - throw `Error in MTL bit packing, # of excess bits is negative`; - } - /** The actual bits that need to be written into this byte. Starts at MSB. */ - let actualBitsToWrite = ((val >> numExcessBits) & BITMASK[numBitsToWriteIntoThisByte]); - // Only adjust and write bits if any are set to 1. - if (actualBitsToWrite > 0) { - // If the number of bits left to write do not fill up this byte, we need to shift these - // bits to the left so they are written into the proper place in the buffer. - if (numBitsLeftToWrite < bitCapacityInThisByte) { - actualBitsToWrite <<= (bitCapacityInThisByte - numBitsLeftToWrite); - } - // Now write into the buffer. - this.data[this.bytePtr] |= actualBitsToWrite; - } - // Update the bit/byte pointers and remaining bits to write - this.bitPtr -= numBitsToWriteIntoThisByte; - if (this.bitPtr < 0) { - if (this.bitPtr !== -1) { - throw `Error in MTL bit packing. Tried to write more bits than it should have.`; - } - this.bytePtr++; - this.bitPtr = 7; - } - // Remove bits that have been written from MSB end. - val -= (actualBitsToWrite << numExcessBits); - numBitsLeftToWrite -= numBitsToWriteIntoThisByte; - } - } - } - } - - return BitBuffer; -})(); diff --git a/build/io/bitstream-def.js b/build/io/bitstream-def.js deleted file mode 100644 index f73ec97..0000000 --- a/build/io/bitstream-def.js +++ /dev/null @@ -1,312 +0,0 @@ -/* - * bitstream-def.js - * - * Provides readers for bitstreams. - * - * Licensed under the MIT License - * - * Copyright(c) 2011 Google Inc. - * Copyright(c) 2011 antimatter15 - */ - -(function () { - // mask for getting N number of bits (0-8) - const BITMASK = [0, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF]; - - /** - * This object allows you to peek and consume bits and bytes out of a stream. - * Note that this stream is optimized, and thus, will *NOT* throw an error if - * the end of the stream is reached. Only use this in scenarios where you - * already have all the bits you need. - * - * Bit reading always proceeds from the first byte in the buffer, to the - * second byte, and so on. The MTL flag controls which bit is considered - * first *inside* the byte. - * - * An Example for how Most-To-Least vs Least-to-Most mode works: - * - * If you have an ArrayBuffer with the following two Uint8s: - * 185 (0b10111001) and 66 (0b01000010) - * and you perform a series of readBits: 2 bits, then 3, then 5, then 6. - * - * A BitStream in "mtl" mode will yield the following: - * - readBits(2) => 2 ('10') - * - readBits(3) => 7 ('111') - * - readBits(5) => 5 ('00101') - * - readBits(6) => 2 ('000010') - * - * A BitStream in "ltm" mode will yield the following: - * - readBits(2) => 1 ('01') - * - readBits(3) => 6 ('110') - * - readBits(5) => 21 ('10101') - * - readBits(6) => 16 ('010000') - */ - class BitStream { - /** - * @param {ArrayBuffer} ab An ArrayBuffer object or a Uint8Array. - * @param {boolean} mtl Whether the stream reads bits from the byte starting with the - * most-significant-bit (bit 7) to least-significant (bit 0). False means the direction is - * from least-significant-bit (bit 0) to most-significant (bit 7). - * @param {Number} opt_offset The offset into the ArrayBuffer - * @param {Number} opt_length The length of this BitStream - */ - constructor(ab, mtl, opt_offset, opt_length) { - if (!(ab instanceof ArrayBuffer)) { - throw 'Error! BitArray constructed with an invalid ArrayBuffer object'; - } - - const offset = opt_offset || 0; - const length = opt_length || ab.byteLength; - - /** - * The bytes in the stream. - * @type {Uint8Array} - * @private - */ - this.bytes = new Uint8Array(ab, offset, length); - - /** - * The byte in the stream that we are currently on. - * @type {Number} - * @private - */ - this.bytePtr = 0; - - /** - * The bit in the current byte that we will read next (can have values 0 through 7). - * @type {Number} - * @private - */ - this.bitPtr = 0; // tracks which bit we are on (can have values 0 through 7) - - /** - * An ever-increasing number. - * @type {Number} - * @private - */ - this.bitsRead_ = 0; - - this.peekBits = mtl ? this.peekBits_mtl : this.peekBits_ltm; - } - - /** - * Returns how many bites have been read in the stream since the beginning of time. - * @returns {number} - */ - getNumBitsRead() { - return this.bitsRead_; - } - - /** - * Returns how many bits are currently in the stream left to be read. - * @returns {number} - */ - getNumBitsLeft() { - const bitsLeftInByte = 8 - this.bitPtr; - return (this.bytes.byteLength - this.bytePtr - 1) * 8 + bitsLeftInByte; - } - - /** - * byte0 byte1 byte2 byte3 - * 7......0 | 7......0 | 7......0 | 7......0 - * - * The bit pointer starts at least-significant bit (0) of byte0 and moves left until it reaches - * bit7 of byte0, then jumps to bit0 of byte1, etc. - * @param {number} n The number of bits to peek, must be a positive integer. - * @param {boolean=} movePointers Whether to move the pointer, defaults false. - * @returns {number} The peeked bits, as an unsigned number. - */ - peekBits_ltm(n, opt_movePointers) { - const NUM = parseInt(n, 10); - let num = NUM; - if (n !== num || num <= 0) { - return 0; - } - - const movePointers = opt_movePointers || false; - let bytes = this.bytes; - let bytePtr = this.bytePtr; - let bitPtr = this.bitPtr; - let result = 0; - let bitsIn = 0; - - // keep going until we have no more bits left to peek at - while (num > 0) { - // We overflowed the stream, so just return what we got. - if (bytePtr >= bytes.length) { - break; - } - - const numBitsLeftInThisByte = (8 - bitPtr); - if (num >= numBitsLeftInThisByte) { - const mask = (BITMASK[numBitsLeftInThisByte] << bitPtr); - result |= (((bytes[bytePtr] & mask) >> bitPtr) << bitsIn); - - bytePtr++; - bitPtr = 0; - bitsIn += numBitsLeftInThisByte; - num -= numBitsLeftInThisByte; - } else { - const mask = (BITMASK[num] << bitPtr); - result |= (((bytes[bytePtr] & mask) >> bitPtr) << bitsIn); - - bitPtr += num; - break; - } - } - - if (movePointers) { - this.bitPtr = bitPtr; - this.bytePtr = bytePtr; - this.bitsRead_ += NUM; - } - - return result; - } - - /** - * byte0 byte1 byte2 byte3 - * 7......0 | 7......0 | 7......0 | 7......0 - * - * The bit pointer starts at bit7 of byte0 and moves right until it reaches - * bit0 of byte0, then goes to bit7 of byte1, etc. - * @param {number} n The number of bits to peek. Must be a positive integer. - * @param {boolean=} movePointers Whether to move the pointer, defaults false. - * @returns {number} The peeked bits, as an unsigned number. - */ - peekBits_mtl(n, opt_movePointers) { - const NUM = parseInt(n, 10); - let num = NUM; - if (n !== num || num <= 0) { - return 0; - } - - const movePointers = opt_movePointers || false; - let bytes = this.bytes; - let bytePtr = this.bytePtr; - let bitPtr = this.bitPtr; - let result = 0; - - // keep going until we have no more bits left to peek at - while (num > 0) { - // We overflowed the stream, so just return the bits we got. - if (bytePtr >= bytes.length) { - break; - } - - const numBitsLeftInThisByte = (8 - bitPtr); - if (num >= numBitsLeftInThisByte) { - result <<= numBitsLeftInThisByte; - result |= (BITMASK[numBitsLeftInThisByte] & bytes[bytePtr]); - bytePtr++; - bitPtr = 0; - num -= numBitsLeftInThisByte; - } else { - result <<= num; - const numBits = 8 - num - bitPtr; - result |= ((bytes[bytePtr] & (BITMASK[num] << numBits)) >> numBits); - - bitPtr += num; - break; - } - } - - if (movePointers) { - this.bitPtr = bitPtr; - this.bytePtr = bytePtr; - this.bitsRead_ += NUM; - } - - return result; - } - - /** - * Peek at 16 bits from current position in the buffer. - * Bit at (bytePtr,bitPtr) has the highest position in returning data. - * Taken from getbits.hpp in unrar. - * TODO: Move this out of BitStream and into unrar. - * @returns {number} - */ - getBits() { - return (((((this.bytes[this.bytePtr] & 0xff) << 16) + - ((this.bytes[this.bytePtr + 1] & 0xff) << 8) + - ((this.bytes[this.bytePtr + 2] & 0xff))) >>> (8 - this.bitPtr)) & 0xffff); - } - - /** - * Reads n bits out of the stream, consuming them (moving the bit pointer). - * @param {number} n The number of bits to read. Must be a positive integer. - * @returns {number} The read bits, as an unsigned number. - */ - readBits(n) { - return this.peekBits(n, true); - } - - /** - * This returns n bytes as a sub-array, advancing the pointer if movePointers - * is true. Only use this for uncompressed blocks as this throws away remaining - * bits in the current byte. - * @param {number} n The number of bytes to peek. Must be a positive integer. - * @param {boolean=} movePointers Whether to move the pointer, defaults false. - * @returns {Uint8Array} The subarray. - */ - peekBytes(n, opt_movePointers) { - const num = parseInt(n, 10); - if (n !== num || num < 0) { - throw 'Error! Called peekBytes() with a non-positive integer: ' + n; - } else if (num === 0) { - return new Uint8Array(); - } - - // Flush bits until we are byte-aligned. - // from http://tools.ietf.org/html/rfc1951#page-11 - // "Any bits of input up to the next byte boundary are ignored." - while (this.bitPtr != 0) { - this.readBits(1); - } - - const numBytesLeft = this.getNumBitsLeft() / 8; - if (num > numBytesLeft) { - throw 'Error! Overflowed the bit stream! n=' + num + ', bytePtr=' + this.bytePtr + - ', bytes.length=' + this.bytes.length + ', bitPtr=' + this.bitPtr; - } - - const movePointers = opt_movePointers || false; - const result = new Uint8Array(num); - let bytes = this.bytes; - let ptr = this.bytePtr; - let bytesLeftToCopy = num; - while (bytesLeftToCopy > 0) { - const bytesLeftInStream = bytes.length - ptr; - const sourceLength = Math.min(bytesLeftToCopy, bytesLeftInStream); - - result.set(bytes.subarray(ptr, ptr + sourceLength), num - bytesLeftToCopy); - - ptr += sourceLength; - // Overflowed the stream, just return what we got. - if (ptr >= bytes.length) { - break; - } - - bytesLeftToCopy -= sourceLength; - } - - if (movePointers) { - this.bytePtr += num; - this.bitsRead_ += (num * 8); - } - - return result; - } - - /** - * @param {number} n The number of bytes to read. - * @returns {Uint8Array} The subarray. - */ - readBytes(n) { - return this.peekBytes(n, true); - } - } - - return BitStream; -})(); diff --git a/build/io/bytebuffer-def.js b/build/io/bytebuffer-def.js deleted file mode 100644 index 4f010f8..0000000 --- a/build/io/bytebuffer-def.js +++ /dev/null @@ -1,127 +0,0 @@ -/* - * bytebuffer-def.js - * - * Provides a writer for bytes. - * - * Licensed under the MIT License - * - * Copyright(c) 2011 Google Inc. - * Copyright(c) 2011 antimatter15 - */ - -(function () { - /** - * A write-only Byte buffer which uses a Uint8 Typed Array as a backing store. - */ - class ByteBuffer { - /** - * @param {number} numBytes The number of bytes to allocate. - */ - constructor(numBytes) { - if (typeof numBytes != typeof 1 || numBytes <= 0) { - throw "Error! ByteBuffer initialized with '" + numBytes + "'"; - } - - /** - * @type {Uint8Array} - * @public - */ - this.data = new Uint8Array(numBytes); - - /** - * @type {number} - * @public - */ - this.ptr = 0; - } - - - /** - * @param {number} b The byte to insert. - */ - insertByte(b) { - // TODO: throw if byte is invalid? - this.data[this.ptr++] = b; - } - - /** - * @param {Array.|Uint8Array|Int8Array} bytes The bytes to insert. - */ - insertBytes(bytes) { - // TODO: throw if bytes is invalid? - this.data.set(bytes, this.ptr); - this.ptr += bytes.length; - } - - /** - * Writes an unsigned number into the next n bytes. If the number is too large - * to fit into n bytes or is negative, an error is thrown. - * @param {number} num The unsigned number to write. - * @param {number} numBytes The number of bytes to write the number into. - */ - writeNumber(num, numBytes) { - if (numBytes < 1 || !numBytes) { - throw 'Trying to write into too few bytes: ' + numBytes; - } - if (num < 0) { - throw 'Trying to write a negative number (' + num + - ') as an unsigned number to an ArrayBuffer'; - } - if (num > (Math.pow(2, numBytes * 8) - 1)) { - throw 'Trying to write ' + num + ' into only ' + numBytes + ' bytes'; - } - - // Roll 8-bits at a time into an array of bytes. - const bytes = []; - while (numBytes-- > 0) { - const eightBits = num & 255; - bytes.push(eightBits); - num >>= 8; - } - - this.insertBytes(bytes); - } - - /** - * Writes a signed number into the next n bytes. If the number is too large - * to fit into n bytes, an error is thrown. - * @param {number} num The signed number to write. - * @param {number} numBytes The number of bytes to write the number into. - */ - writeSignedNumber(num, numBytes) { - if (numBytes < 1) { - throw 'Trying to write into too few bytes: ' + numBytes; - } - - const HALF = Math.pow(2, (numBytes * 8) - 1); - if (num >= HALF || num < -HALF) { - throw 'Trying to write ' + num + ' into only ' + numBytes + ' bytes'; - } - - // Roll 8-bits at a time into an array of bytes. - const bytes = []; - while (numBytes-- > 0) { - const eightBits = num & 255; - bytes.push(eightBits); - num >>= 8; - } - - this.insertBytes(bytes); - } - - /** - * @param {string} str The ASCII string to write. - */ - writeASCIIString(str) { - for (let i = 0; i < str.length; ++i) { - const curByte = str.charCodeAt(i); - if (curByte < 0 || curByte > 255) { - throw 'Trying to write a non-ASCII string!'; - } - this.insertByte(curByte); - } - } - } - - return ByteBuffer; -})(); diff --git a/build/io/bytestream-def.js b/build/io/bytestream-def.js deleted file mode 100644 index 888d2cb..0000000 --- a/build/io/bytestream-def.js +++ /dev/null @@ -1,308 +0,0 @@ -/* - * bytestream-def.js - * - * Provides readers for byte streams. - * - * Licensed under the MIT License - * - * Copyright(c) 2011 Google Inc. - * Copyright(c) 2011 antimatter15 - */ - -(function () { - /** - * This object allows you to peek and consume bytes as numbers and strings out - * of a stream. More bytes can be pushed into the back of the stream via the - * push() method. - */ - class ByteStream { - /** - * @param {ArrayBuffer} ab The ArrayBuffer object. - * @param {number=} opt_offset The offset into the ArrayBuffer - * @param {number=} opt_length The length of this ByteStream - */ - constructor(ab, opt_offset, opt_length) { - if (!(ab instanceof ArrayBuffer)) { - throw 'Error! BitArray constructed with an invalid ArrayBuffer object'; - } - - const offset = opt_offset || 0; - const length = opt_length || ab.byteLength; - - /** - * The current page of bytes in the stream. - * @type {Uint8Array} - * @private - */ - this.bytes = new Uint8Array(ab, offset, length); - - /** - * The next pages of bytes in the stream. - * @type {Array} - * @private - */ - this.pages_ = []; - - /** - * The byte in the current page that we will read next. - * @type {Number} - * @private - */ - this.ptr = 0; - - /** - * An ever-increasing number. - * @type {Number} - * @private - */ - this.bytesRead_ = 0; - } - - /** - * Returns how many bytes have been read in the stream since the beginning of time. - */ - getNumBytesRead() { - return this.bytesRead_; - } - - /** - * Returns how many bytes are currently in the stream left to be read. - */ - getNumBytesLeft() { - const bytesInCurrentPage = (this.bytes.byteLength - this.ptr); - return this.pages_.reduce((acc, arr) => acc + arr.length, bytesInCurrentPage); - } - - /** - * Move the pointer ahead n bytes. If the pointer is at the end of the current array - * of bytes and we have another page of bytes, point at the new page. This is a private - * method, no validation is done. - * @param {number} n Number of bytes to increment. - * @private - */ - movePointer_(n) { - this.ptr += n; - this.bytesRead_ += n; - while (this.ptr >= this.bytes.length && this.pages_.length > 0) { - this.ptr -= this.bytes.length; - this.bytes = this.pages_.shift(); - } - } - - /** - * Peeks at the next n bytes as an unsigned number but does not advance the - * pointer. - * @param {number} n The number of bytes to peek at. Must be a positive integer. - * @returns {number} The n bytes interpreted as an unsigned number. - */ - peekNumber(n) { - const num = parseInt(n, 10); - if (n !== num || num < 0) { - throw 'Error! Called peekNumber() with a non-positive integer'; - } else if (num === 0) { - return 0; - } - - if (n > 4) { - throw 'Error! Called peekNumber(' + n + - ') but this method can only reliably read numbers up to 4 bytes long'; - } - - if (this.getNumBytesLeft() < num) { - throw 'Error! Overflowed the byte stream while peekNumber()! n=' + num + - ', ptr=' + this.ptr + ', bytes.length=' + this.getNumBytesLeft(); - } - - let result = 0; - let curPage = this.bytes; - let pageIndex = 0; - let ptr = this.ptr; - for (let i = 0; i < num; ++i) { - result |= (curPage[ptr++] << (i * 8)); - - if (ptr >= curPage.length) { - curPage = this.pages_[pageIndex++]; - ptr = 0; - } - } - - return result; - } - - - /** - * Returns the next n bytes as an unsigned number (or -1 on error) - * and advances the stream pointer n bytes. - * @param {number} n The number of bytes to read. Must be a positive integer. - * @returns {number} The n bytes interpreted as an unsigned number. - */ - readNumber(n) { - const num = this.peekNumber(n); - this.movePointer_(n); - return num; - } - - - /** - * Returns the next n bytes as a signed number but does not advance the - * pointer. - * @param {number} n The number of bytes to read. Must be a positive integer. - * @returns {number} The bytes interpreted as a signed number. - */ - peekSignedNumber(n) { - let num = this.peekNumber(n); - const HALF = Math.pow(2, (n * 8) - 1); - const FULL = HALF * 2; - - if (num >= HALF) num -= FULL; - - return num; - } - - - /** - * Returns the next n bytes as a signed number and advances the stream pointer. - * @param {number} n The number of bytes to read. Must be a positive integer. - * @returns {number} The bytes interpreted as a signed number. - */ - readSignedNumber(n) { - const num = this.peekSignedNumber(n); - this.movePointer_(n); - return num; - } - - - /** - * This returns n bytes as a sub-array, advancing the pointer if movePointers - * is true. - * @param {number} n The number of bytes to read. Must be a positive integer. - * @param {boolean} movePointers Whether to move the pointers. - * @returns {Uint8Array} The subarray. - */ - peekBytes(n, movePointers) { - const num = parseInt(n, 10); - if (n !== num || num < 0) { - throw 'Error! Called peekBytes() with a non-positive integer'; - } else if (num === 0) { - return new Uint8Array(); - } - - const totalBytesLeft = this.getNumBytesLeft(); - if (num > totalBytesLeft) { - throw 'Error! Overflowed the byte stream during peekBytes! n=' + num + - ', ptr=' + this.ptr + ', bytes.length=' + this.getNumBytesLeft(); - } - - const result = new Uint8Array(num); - let curPage = this.bytes; - let ptr = this.ptr; - let bytesLeftToCopy = num; - let pageIndex = 0; - while (bytesLeftToCopy > 0) { - const bytesLeftInPage = curPage.length - ptr; - const sourceLength = Math.min(bytesLeftToCopy, bytesLeftInPage); - - result.set(curPage.subarray(ptr, ptr + sourceLength), num - bytesLeftToCopy); - - ptr += sourceLength; - if (ptr >= curPage.length) { - curPage = this.pages_[pageIndex++]; - ptr = 0; - } - - bytesLeftToCopy -= sourceLength; - } - - if (movePointers) { - this.movePointer_(num); - } - - return result; - } - - /** - * Reads the next n bytes as a sub-array. - * @param {number} n The number of bytes to read. Must be a positive integer. - * @returns {Uint8Array} The subarray. - */ - readBytes(n) { - return this.peekBytes(n, true); - } - - /** - * Peeks at the next n bytes as an ASCII string but does not advance the pointer. - * @param {number} n The number of bytes to peek at. Must be a positive integer. - * @returns {string} The next n bytes as a string. - */ - peekString(n) { - const num = parseInt(n, 10); - if (n !== num || num < 0) { - throw 'Error! Called peekString() with a non-positive integer'; - } else if (num === 0) { - return ''; - } - - const totalBytesLeft = this.getNumBytesLeft(); - if (num > totalBytesLeft) { - throw 'Error! Overflowed the byte stream while peekString()! n=' + num + - ', ptr=' + this.ptr + ', bytes.length=' + this.getNumBytesLeft(); - } - - let result = new Array(num); - let curPage = this.bytes; - let pageIndex = 0; - let ptr = this.ptr; - for (let i = 0; i < num; ++i) { - result[i] = String.fromCharCode(curPage[ptr++]); - if (ptr >= curPage.length) { - curPage = this.pages_[pageIndex++]; - ptr = 0; - } - } - - return result.join(''); - } - - /** - * Returns the next n bytes as an ASCII string and advances the stream pointer - * n bytes. - * @param {number} n The number of bytes to read. Must be a positive integer. - * @returns {string} The next n bytes as a string. - */ - readString(n) { - const strToReturn = this.peekString(n); - this.movePointer_(n); - return strToReturn; - } - - /** - * Feeds more bytes into the back of the stream. - * @param {ArrayBuffer} ab - */ - push(ab) { - if (!(ab instanceof ArrayBuffer)) { - throw 'Error! ByteStream.push() called with an invalid ArrayBuffer object'; - } - - this.pages_.push(new Uint8Array(ab)); - // If the pointer is at the end of the current page of bytes, this will advance - // to the next page. - this.movePointer_(0); - } - - /** - * Creates a new ByteStream from this ByteStream that can be read / peeked. - * @returns {ByteStream} A clone of this ByteStream. - */ - tee() { - const clone = new ByteStream(this.bytes.buffer); - clone.bytes = this.bytes; - clone.ptr = this.ptr; - clone.pages_ = this.pages_.slice(); - clone.bytesRead_ = this.bytesRead_; - return clone; - } - } - - return ByteStream; -})(); diff --git a/index.js b/index.js index e1b106b..2bd7954 100644 --- a/index.js +++ b/index.js @@ -20,10 +20,11 @@ export { UnarchiveEvent, UnarchiveEventType, UnarchiveInfoEvent, UnarchiveErrorEvent, UnarchiveStartEvent, UnarchiveFinishEvent, UnarchiveProgressEvent, UnarchiveExtractEvent, Unarchiver, Unzipper, Unrarrer, Untarrer, getUnarchiver -} from './archive/archive.js'; +} from './archive/decompress.js'; export { getFullMIMEString, getShortMIMEString } from './codecs/codecs.js'; export { findMimeType } from './file/sniffer.js'; export { convertWebPtoPNG, convertWebPtoJPG } from './image/webp-shim/webp-shim.js'; +export { BitBuffer } from './io/bitbuffer.js'; export { BitStream } from './io/bitstream.js'; export { ByteBuffer } from './io/bytebuffer.js'; export { ByteStream } from './io/bytestream.js'; diff --git a/io/README.md b/io/README.md deleted file mode 100644 index 3aab370..0000000 --- a/io/README.md +++ /dev/null @@ -1,2 +0,0 @@ -These generated files exist because Firefox does not support Worker Modules yet. -See https://bugzilla.mozilla.org/show_bug.cgi?id=1247687. diff --git a/io/bitbuffer-worker.js b/io/bitbuffer-worker.js deleted file mode 100644 index 191439d..0000000 --- a/io/bitbuffer-worker.js +++ /dev/null @@ -1,204 +0,0 @@ -// THIS IS A GENERATED FILE! DO NOT EDIT, INSTEAD EDIT THE FILE IN bitjs/build/io. -var bitjs = bitjs || {}; -bitjs.io = bitjs.io || {}; -bitjs.io.BitBuffer = -/* - * bytebuffer-def.js - * - * Provides a writer for bits. - * - * Licensed under the MIT License - * - * Copyright(c) 2021 Google Inc. - */ - -(function () { - const BITMASK = [ - 0, - 0b00000001, - 0b00000011, - 0b00000111, - 0b00001111, - 0b00011111, - 0b00111111, - 0b01111111, - 0b11111111, - ] - - /** - * A write-only Bit buffer which uses a Uint8Array as a backing store. - */ - class BitBuffer { - /** - * @param {number} numBytes The number of bytes to allocate. - * @param {boolean} mtl The bit-packing mode. True means pack bits from most-significant (7) to - * least-significant (0). Defaults false: least-significant (0) to most-significant (8). - */ - constructor(numBytes, mtl = false) { - if (typeof numBytes != typeof 1 || numBytes <= 0) { - throw "Error! ByteBuffer initialized with '" + numBytes + "'"; - } - - /** - * @type {Uint8Array} - * @public - */ - this.data = new Uint8Array(numBytes); - - /** - * Whether we pack bits from most-significant-bit to least. Defaults false (least-to-most - * significant bit packing). - * @type {boolean} - * @private - */ - this.mtl = mtl; - - /** - * The current byte we are filling with bits. - * @type {number} - * @public - */ - this.bytePtr = 0; - - /** - * Points at the bit within the current byte where the next bit will go. This number ranges - * from 0 to 7 and the direction of packing is indicated by the mtl property. - * @type {number} - * @public - */ - this.bitPtr = this.mtl ? 7 : 0; - } - - /** @returns {boolean} */ - getPackingDirection() { - return this.mtl; - } - - /** - * Sets the bit-packing direction. Default (false) is least-significant-bit (0) to - * most-significant (7). Changing the bit-packing direction when the bit pointer is in the - * middle of a byte will fill the rest of that byte with 0s using the current bit-packing - * direction and then set the bit pointer to the appropriate bit of the next byte. If there - * are no more bytes left in this buffer, it will throw an error. - */ - setPackingDirection(mtl = false) { - if (this.mtl !== mtl) { - if (this.mtl && this.bitPtr !== 7) { - this.bytePtr++; - if (this.bytePtr >= this.data.byteLength) { - throw `No more bytes left when switching packing direction`; - } - this.bitPtr = 7; - } else if (!this.mtl && this.bitPtr !== 0) { - this.bytePtr++; - if (this.bytePtr >= this.data.byteLength) { - throw `No more bytes left when switching packing direction`; - } - this.bitPtr = 0; - } - } - - this.mtl = mtl; - } - - /** - * writeBits(3, 6) is the same as writeBits(0b000011, 6). - * Will throw an error (without writing) if this would over-flow the buffer. - * @param {number} val The bits to pack into the buffer. Negative values are not allowed. - * @param {number} numBits Must be positive, non-zero and less or equal to than 53, since - * JavaScript can only support 53-bit integers. - */ - writeBits(val, numBits) { - if (val < 0 || typeof val !== typeof 1) { - throw `Trying to write an invalid value into the BitBuffer: ${val}`; - } - if (numBits < 0 || numBits > 53) { - throw `Trying to write ${numBits} bits into the BitBuffer`; - } - - const totalBitsInBuffer = this.data.byteLength * 8; - const writtenBits = this.bytePtr * 8 + this.bitPtr; - const bitsLeftInBuffer = totalBitsInBuffer - writtenBits; - if (numBits > bitsLeftInBuffer) { - throw `Trying to write ${numBits} into the BitBuffer that only has ${bitsLeftInBuffer}`; - } - - // Least-to-most-significant bit packing method (LTM). - if (!this.mtl) { - let numBitsLeftToWrite = numBits; - while (numBitsLeftToWrite > 0) { - /** The number of bits available to fill in this byte. */ - const bitCapacityInThisByte = 8 - this.bitPtr; - /** The number of bits of val we will write into this byte. */ - const numBitsToWriteIntoThisByte = Math.min(numBitsLeftToWrite, bitCapacityInThisByte); - /** The number of bits that fit in subsequent bytes. */ - const numExcessBits = numBitsLeftToWrite - numBitsToWriteIntoThisByte; - if (numExcessBits < 0) { - throw `Error in LTM bit packing, # of excess bits is negative`; - } - /** The actual bits that need to be written into this byte. Starts at LSB. */ - let actualBitsToWrite = (val & BITMASK[numBitsToWriteIntoThisByte]); - // Only adjust and write bits if any are set to 1. - if (actualBitsToWrite > 0) { - actualBitsToWrite <<= this.bitPtr; - // Now write into the buffer. - this.data[this.bytePtr] |= actualBitsToWrite; - } - // Update the bit/byte pointers and remaining bits to write. - this.bitPtr += numBitsToWriteIntoThisByte; - if (this.bitPtr > 7) { - if (this.bitPtr !== 8) { - throw `Error in LTM bit packing. Tried to write more bits than it should have.`; - } - this.bytePtr++; - this.bitPtr = 0; - } - // Remove bits that have been written from LSB end. - val >>= numBitsToWriteIntoThisByte; - numBitsLeftToWrite -= numBitsToWriteIntoThisByte; - } - } - // Most-to-least-significant bit packing method (MTL). - else { - let numBitsLeftToWrite = numBits; - while (numBitsLeftToWrite > 0) { - /** The number of bits available to fill in this byte. */ - const bitCapacityInThisByte = this.bitPtr + 1; - /** The number of bits of val we will write into this byte. */ - const numBitsToWriteIntoThisByte = Math.min(numBitsLeftToWrite, bitCapacityInThisByte); - /** The number of bits that fit in subsequent bytes. */ - const numExcessBits = numBitsLeftToWrite - numBitsToWriteIntoThisByte; - if (numExcessBits < 0) { - throw `Error in MTL bit packing, # of excess bits is negative`; - } - /** The actual bits that need to be written into this byte. Starts at MSB. */ - let actualBitsToWrite = ((val >> numExcessBits) & BITMASK[numBitsToWriteIntoThisByte]); - // Only adjust and write bits if any are set to 1. - if (actualBitsToWrite > 0) { - // If the number of bits left to write do not fill up this byte, we need to shift these - // bits to the left so they are written into the proper place in the buffer. - if (numBitsLeftToWrite < bitCapacityInThisByte) { - actualBitsToWrite <<= (bitCapacityInThisByte - numBitsLeftToWrite); - } - // Now write into the buffer. - this.data[this.bytePtr] |= actualBitsToWrite; - } - // Update the bit/byte pointers and remaining bits to write - this.bitPtr -= numBitsToWriteIntoThisByte; - if (this.bitPtr < 0) { - if (this.bitPtr !== -1) { - throw `Error in MTL bit packing. Tried to write more bits than it should have.`; - } - this.bytePtr++; - this.bitPtr = 7; - } - // Remove bits that have been written from MSB end. - val -= (actualBitsToWrite << numExcessBits); - numBitsLeftToWrite -= numBitsToWriteIntoThisByte; - } - } - } - } - - return BitBuffer; -})(); diff --git a/io/bitbuffer.js b/io/bitbuffer.js index 836b4d1..0a2ac18 100644 --- a/io/bitbuffer.js +++ b/io/bitbuffer.js @@ -1,202 +1,197 @@ -// THIS IS A GENERATED FILE! DO NOT EDIT, INSTEAD EDIT THE FILE IN bitjs/build/io. -export const BitBuffer = /* - * bytebuffer-def.js + * bitbuffer.js * - * Provides a writer for bits. + * Provides writer for bits. * * Licensed under the MIT License * - * Copyright(c) 2021 Google Inc. + * Copyright(c) 2023 Google Inc. + * Copyright(c) 2011 antimatter15 */ -(function () { - const BITMASK = [ - 0, - 0b00000001, - 0b00000011, - 0b00000111, - 0b00001111, - 0b00011111, - 0b00111111, - 0b01111111, - 0b11111111, - ] +const BITMASK = [ + 0, + 0b00000001, + 0b00000011, + 0b00000111, + 0b00001111, + 0b00011111, + 0b00111111, + 0b01111111, + 0b11111111, +] + +/** + * A write-only Bit buffer which uses a Uint8Array as a backing store. + */ +export class BitBuffer { + /** + * @param {number} numBytes The number of bytes to allocate. + * @param {boolean} mtl The bit-packing mode. True means pack bits from most-significant (7) to + * least-significant (0). Defaults false: least-significant (0) to most-significant (8). + */ + constructor(numBytes, mtl = false) { + if (typeof numBytes != typeof 1 || numBytes <= 0) { + throw "Error! ByteBuffer initialized with '" + numBytes + "'"; + } + + /** + * @type {Uint8Array} + * @public + */ + this.data = new Uint8Array(numBytes); + + /** + * Whether we pack bits from most-significant-bit to least. Defaults false (least-to-most + * significant bit packing). + * @type {boolean} + * @private + */ + this.mtl = mtl; + + /** + * The current byte we are filling with bits. + * @type {number} + * @public + */ + this.bytePtr = 0; + + /** + * Points at the bit within the current byte where the next bit will go. This number ranges + * from 0 to 7 and the direction of packing is indicated by the mtl property. + * @type {number} + * @public + */ + this.bitPtr = this.mtl ? 7 : 0; + } + + /** @returns {boolean} */ + getPackingDirection() { + return this.mtl; + } /** - * A write-only Bit buffer which uses a Uint8Array as a backing store. + * Sets the bit-packing direction. Default (false) is least-significant-bit (0) to + * most-significant (7). Changing the bit-packing direction when the bit pointer is in the + * middle of a byte will fill the rest of that byte with 0s using the current bit-packing + * direction and then set the bit pointer to the appropriate bit of the next byte. If there + * are no more bytes left in this buffer, it will throw an error. */ - class BitBuffer { - /** - * @param {number} numBytes The number of bytes to allocate. - * @param {boolean} mtl The bit-packing mode. True means pack bits from most-significant (7) to - * least-significant (0). Defaults false: least-significant (0) to most-significant (8). - */ - constructor(numBytes, mtl = false) { - if (typeof numBytes != typeof 1 || numBytes <= 0) { - throw "Error! ByteBuffer initialized with '" + numBytes + "'"; + setPackingDirection(mtl = false) { + if (this.mtl !== mtl) { + if (this.mtl && this.bitPtr !== 7) { + this.bytePtr++; + if (this.bytePtr >= this.data.byteLength) { + throw `No more bytes left when switching packing direction`; + } + this.bitPtr = 7; + } else if (!this.mtl && this.bitPtr !== 0) { + this.bytePtr++; + if (this.bytePtr >= this.data.byteLength) { + throw `No more bytes left when switching packing direction`; + } + this.bitPtr = 0; } - - /** - * @type {Uint8Array} - * @public - */ - this.data = new Uint8Array(numBytes); - - /** - * Whether we pack bits from most-significant-bit to least. Defaults false (least-to-most - * significant bit packing). - * @type {boolean} - * @private - */ - this.mtl = mtl; - - /** - * The current byte we are filling with bits. - * @type {number} - * @public - */ - this.bytePtr = 0; - - /** - * Points at the bit within the current byte where the next bit will go. This number ranges - * from 0 to 7 and the direction of packing is indicated by the mtl property. - * @type {number} - * @public - */ - this.bitPtr = this.mtl ? 7 : 0; } - /** @returns {boolean} */ - getPackingDirection() { - return this.mtl; + this.mtl = mtl; + } + + /** + * writeBits(3, 6) is the same as writeBits(0b000011, 6). + * Will throw an error (without writing) if this would over-flow the buffer. + * @param {number} val The bits to pack into the buffer. Negative values are not allowed. + * @param {number} numBits Must be positive, non-zero and less or equal to than 53, since + * JavaScript can only support 53-bit integers. + */ + writeBits(val, numBits) { + if (val < 0 || typeof val !== typeof 1) { + throw `Trying to write an invalid value into the BitBuffer: ${val}`; + } + if (numBits < 0 || numBits > 53) { + throw `Trying to write ${numBits} bits into the BitBuffer`; } - /** - * Sets the bit-packing direction. Default (false) is least-significant-bit (0) to - * most-significant (7). Changing the bit-packing direction when the bit pointer is in the - * middle of a byte will fill the rest of that byte with 0s using the current bit-packing - * direction and then set the bit pointer to the appropriate bit of the next byte. If there - * are no more bytes left in this buffer, it will throw an error. - */ - setPackingDirection(mtl = false) { - if (this.mtl !== mtl) { - if (this.mtl && this.bitPtr !== 7) { - this.bytePtr++; - if (this.bytePtr >= this.data.byteLength) { - throw `No more bytes left when switching packing direction`; + const totalBitsInBuffer = this.data.byteLength * 8; + const writtenBits = this.bytePtr * 8 + this.bitPtr; + const bitsLeftInBuffer = totalBitsInBuffer - writtenBits; + if (numBits > bitsLeftInBuffer) { + throw `Trying to write ${numBits} into the BitBuffer that only has ${bitsLeftInBuffer}`; + } + + // Least-to-most-significant bit packing method (LTM). + if (!this.mtl) { + let numBitsLeftToWrite = numBits; + while (numBitsLeftToWrite > 0) { + /** The number of bits available to fill in this byte. */ + const bitCapacityInThisByte = 8 - this.bitPtr; + /** The number of bits of val we will write into this byte. */ + const numBitsToWriteIntoThisByte = Math.min(numBitsLeftToWrite, bitCapacityInThisByte); + /** The number of bits that fit in subsequent bytes. */ + const numExcessBits = numBitsLeftToWrite - numBitsToWriteIntoThisByte; + if (numExcessBits < 0) { + throw `Error in LTM bit packing, # of excess bits is negative`; + } + /** The actual bits that need to be written into this byte. Starts at LSB. */ + let actualBitsToWrite = (val & BITMASK[numBitsToWriteIntoThisByte]); + // Only adjust and write bits if any are set to 1. + if (actualBitsToWrite > 0) { + actualBitsToWrite <<= this.bitPtr; + // Now write into the buffer. + this.data[this.bytePtr] |= actualBitsToWrite; + } + // Update the bit/byte pointers and remaining bits to write. + this.bitPtr += numBitsToWriteIntoThisByte; + if (this.bitPtr > 7) { + if (this.bitPtr !== 8) { + throw `Error in LTM bit packing. Tried to write more bits than it should have.`; } - this.bitPtr = 7; - } else if (!this.mtl && this.bitPtr !== 0) { this.bytePtr++; - if (this.bytePtr >= this.data.byteLength) { - throw `No more bytes left when switching packing direction`; - } this.bitPtr = 0; } + // Remove bits that have been written from LSB end. + val >>= numBitsToWriteIntoThisByte; + numBitsLeftToWrite -= numBitsToWriteIntoThisByte; } - - this.mtl = mtl; } - - /** - * writeBits(3, 6) is the same as writeBits(0b000011, 6). - * Will throw an error (without writing) if this would over-flow the buffer. - * @param {number} val The bits to pack into the buffer. Negative values are not allowed. - * @param {number} numBits Must be positive, non-zero and less or equal to than 53, since - * JavaScript can only support 53-bit integers. - */ - writeBits(val, numBits) { - if (val < 0 || typeof val !== typeof 1) { - throw `Trying to write an invalid value into the BitBuffer: ${val}`; - } - if (numBits < 0 || numBits > 53) { - throw `Trying to write ${numBits} bits into the BitBuffer`; - } - - const totalBitsInBuffer = this.data.byteLength * 8; - const writtenBits = this.bytePtr * 8 + this.bitPtr; - const bitsLeftInBuffer = totalBitsInBuffer - writtenBits; - if (numBits > bitsLeftInBuffer) { - throw `Trying to write ${numBits} into the BitBuffer that only has ${bitsLeftInBuffer}`; - } - - // Least-to-most-significant bit packing method (LTM). - if (!this.mtl) { - let numBitsLeftToWrite = numBits; - while (numBitsLeftToWrite > 0) { - /** The number of bits available to fill in this byte. */ - const bitCapacityInThisByte = 8 - this.bitPtr; - /** The number of bits of val we will write into this byte. */ - const numBitsToWriteIntoThisByte = Math.min(numBitsLeftToWrite, bitCapacityInThisByte); - /** The number of bits that fit in subsequent bytes. */ - const numExcessBits = numBitsLeftToWrite - numBitsToWriteIntoThisByte; - if (numExcessBits < 0) { - throw `Error in LTM bit packing, # of excess bits is negative`; - } - /** The actual bits that need to be written into this byte. Starts at LSB. */ - let actualBitsToWrite = (val & BITMASK[numBitsToWriteIntoThisByte]); - // Only adjust and write bits if any are set to 1. - if (actualBitsToWrite > 0) { - actualBitsToWrite <<= this.bitPtr; - // Now write into the buffer. - this.data[this.bytePtr] |= actualBitsToWrite; - } - // Update the bit/byte pointers and remaining bits to write. - this.bitPtr += numBitsToWriteIntoThisByte; - if (this.bitPtr > 7) { - if (this.bitPtr !== 8) { - throw `Error in LTM bit packing. Tried to write more bits than it should have.`; - } - this.bytePtr++; - this.bitPtr = 0; - } - // Remove bits that have been written from LSB end. - val >>= numBitsToWriteIntoThisByte; - numBitsLeftToWrite -= numBitsToWriteIntoThisByte; + // Most-to-least-significant bit packing method (MTL). + else { + let numBitsLeftToWrite = numBits; + while (numBitsLeftToWrite > 0) { + /** The number of bits available to fill in this byte. */ + const bitCapacityInThisByte = this.bitPtr + 1; + /** The number of bits of val we will write into this byte. */ + const numBitsToWriteIntoThisByte = Math.min(numBitsLeftToWrite, bitCapacityInThisByte); + /** The number of bits that fit in subsequent bytes. */ + const numExcessBits = numBitsLeftToWrite - numBitsToWriteIntoThisByte; + if (numExcessBits < 0) { + throw `Error in MTL bit packing, # of excess bits is negative`; } - } - // Most-to-least-significant bit packing method (MTL). - else { - let numBitsLeftToWrite = numBits; - while (numBitsLeftToWrite > 0) { - /** The number of bits available to fill in this byte. */ - const bitCapacityInThisByte = this.bitPtr + 1; - /** The number of bits of val we will write into this byte. */ - const numBitsToWriteIntoThisByte = Math.min(numBitsLeftToWrite, bitCapacityInThisByte); - /** The number of bits that fit in subsequent bytes. */ - const numExcessBits = numBitsLeftToWrite - numBitsToWriteIntoThisByte; - if (numExcessBits < 0) { - throw `Error in MTL bit packing, # of excess bits is negative`; + /** The actual bits that need to be written into this byte. Starts at MSB. */ + let actualBitsToWrite = ((val >> numExcessBits) & BITMASK[numBitsToWriteIntoThisByte]); + // Only adjust and write bits if any are set to 1. + if (actualBitsToWrite > 0) { + // If the number of bits left to write do not fill up this byte, we need to shift these + // bits to the left so they are written into the proper place in the buffer. + if (numBitsLeftToWrite < bitCapacityInThisByte) { + actualBitsToWrite <<= (bitCapacityInThisByte - numBitsLeftToWrite); } - /** The actual bits that need to be written into this byte. Starts at MSB. */ - let actualBitsToWrite = ((val >> numExcessBits) & BITMASK[numBitsToWriteIntoThisByte]); - // Only adjust and write bits if any are set to 1. - if (actualBitsToWrite > 0) { - // If the number of bits left to write do not fill up this byte, we need to shift these - // bits to the left so they are written into the proper place in the buffer. - if (numBitsLeftToWrite < bitCapacityInThisByte) { - actualBitsToWrite <<= (bitCapacityInThisByte - numBitsLeftToWrite); - } - // Now write into the buffer. - this.data[this.bytePtr] |= actualBitsToWrite; - } - // Update the bit/byte pointers and remaining bits to write - this.bitPtr -= numBitsToWriteIntoThisByte; - if (this.bitPtr < 0) { - if (this.bitPtr !== -1) { - throw `Error in MTL bit packing. Tried to write more bits than it should have.`; - } - this.bytePtr++; - this.bitPtr = 7; - } - // Remove bits that have been written from MSB end. - val -= (actualBitsToWrite << numExcessBits); - numBitsLeftToWrite -= numBitsToWriteIntoThisByte; + // Now write into the buffer. + this.data[this.bytePtr] |= actualBitsToWrite; } + // Update the bit/byte pointers and remaining bits to write + this.bitPtr -= numBitsToWriteIntoThisByte; + if (this.bitPtr < 0) { + if (this.bitPtr !== -1) { + throw `Error in MTL bit packing. Tried to write more bits than it should have.`; + } + this.bytePtr++; + this.bitPtr = 7; + } + // Remove bits that have been written from MSB end. + val -= (actualBitsToWrite << numExcessBits); + numBitsLeftToWrite -= numBitsToWriteIntoThisByte; } } } - - return BitBuffer; -})(); +} diff --git a/io/bitstream-worker.js b/io/bitstream-worker.js deleted file mode 100644 index f59a658..0000000 --- a/io/bitstream-worker.js +++ /dev/null @@ -1,316 +0,0 @@ -// THIS IS A GENERATED FILE! DO NOT EDIT, INSTEAD EDIT THE FILE IN bitjs/build/io. -var bitjs = bitjs || {}; -bitjs.io = bitjs.io || {}; -bitjs.io.BitStream = -/* - * bitstream-def.js - * - * Provides readers for bitstreams. - * - * Licensed under the MIT License - * - * Copyright(c) 2011 Google Inc. - * Copyright(c) 2011 antimatter15 - */ - -(function () { - // mask for getting N number of bits (0-8) - const BITMASK = [0, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF]; - - /** - * This object allows you to peek and consume bits and bytes out of a stream. - * Note that this stream is optimized, and thus, will *NOT* throw an error if - * the end of the stream is reached. Only use this in scenarios where you - * already have all the bits you need. - * - * Bit reading always proceeds from the first byte in the buffer, to the - * second byte, and so on. The MTL flag controls which bit is considered - * first *inside* the byte. - * - * An Example for how Most-To-Least vs Least-to-Most mode works: - * - * If you have an ArrayBuffer with the following two Uint8s: - * 185 (0b10111001) and 66 (0b01000010) - * and you perform a series of readBits: 2 bits, then 3, then 5, then 6. - * - * A BitStream in "mtl" mode will yield the following: - * - readBits(2) => 2 ('10') - * - readBits(3) => 7 ('111') - * - readBits(5) => 5 ('00101') - * - readBits(6) => 2 ('000010') - * - * A BitStream in "ltm" mode will yield the following: - * - readBits(2) => 1 ('01') - * - readBits(3) => 6 ('110') - * - readBits(5) => 21 ('10101') - * - readBits(6) => 16 ('010000') - */ - class BitStream { - /** - * @param {ArrayBuffer} ab An ArrayBuffer object or a Uint8Array. - * @param {boolean} mtl Whether the stream reads bits from the byte starting with the - * most-significant-bit (bit 7) to least-significant (bit 0). False means the direction is - * from least-significant-bit (bit 0) to most-significant (bit 7). - * @param {Number} opt_offset The offset into the ArrayBuffer - * @param {Number} opt_length The length of this BitStream - */ - constructor(ab, mtl, opt_offset, opt_length) { - if (!(ab instanceof ArrayBuffer)) { - throw 'Error! BitArray constructed with an invalid ArrayBuffer object'; - } - - const offset = opt_offset || 0; - const length = opt_length || ab.byteLength; - - /** - * The bytes in the stream. - * @type {Uint8Array} - * @private - */ - this.bytes = new Uint8Array(ab, offset, length); - - /** - * The byte in the stream that we are currently on. - * @type {Number} - * @private - */ - this.bytePtr = 0; - - /** - * The bit in the current byte that we will read next (can have values 0 through 7). - * @type {Number} - * @private - */ - this.bitPtr = 0; // tracks which bit we are on (can have values 0 through 7) - - /** - * An ever-increasing number. - * @type {Number} - * @private - */ - this.bitsRead_ = 0; - - this.peekBits = mtl ? this.peekBits_mtl : this.peekBits_ltm; - } - - /** - * Returns how many bites have been read in the stream since the beginning of time. - * @returns {number} - */ - getNumBitsRead() { - return this.bitsRead_; - } - - /** - * Returns how many bits are currently in the stream left to be read. - * @returns {number} - */ - getNumBitsLeft() { - const bitsLeftInByte = 8 - this.bitPtr; - return (this.bytes.byteLength - this.bytePtr - 1) * 8 + bitsLeftInByte; - } - - /** - * byte0 byte1 byte2 byte3 - * 7......0 | 7......0 | 7......0 | 7......0 - * - * The bit pointer starts at least-significant bit (0) of byte0 and moves left until it reaches - * bit7 of byte0, then jumps to bit0 of byte1, etc. - * @param {number} n The number of bits to peek, must be a positive integer. - * @param {boolean=} movePointers Whether to move the pointer, defaults false. - * @returns {number} The peeked bits, as an unsigned number. - */ - peekBits_ltm(n, opt_movePointers) { - const NUM = parseInt(n, 10); - let num = NUM; - if (n !== num || num <= 0) { - return 0; - } - - const movePointers = opt_movePointers || false; - let bytes = this.bytes; - let bytePtr = this.bytePtr; - let bitPtr = this.bitPtr; - let result = 0; - let bitsIn = 0; - - // keep going until we have no more bits left to peek at - while (num > 0) { - // We overflowed the stream, so just return what we got. - if (bytePtr >= bytes.length) { - break; - } - - const numBitsLeftInThisByte = (8 - bitPtr); - if (num >= numBitsLeftInThisByte) { - const mask = (BITMASK[numBitsLeftInThisByte] << bitPtr); - result |= (((bytes[bytePtr] & mask) >> bitPtr) << bitsIn); - - bytePtr++; - bitPtr = 0; - bitsIn += numBitsLeftInThisByte; - num -= numBitsLeftInThisByte; - } else { - const mask = (BITMASK[num] << bitPtr); - result |= (((bytes[bytePtr] & mask) >> bitPtr) << bitsIn); - - bitPtr += num; - break; - } - } - - if (movePointers) { - this.bitPtr = bitPtr; - this.bytePtr = bytePtr; - this.bitsRead_ += NUM; - } - - return result; - } - - /** - * byte0 byte1 byte2 byte3 - * 7......0 | 7......0 | 7......0 | 7......0 - * - * The bit pointer starts at bit7 of byte0 and moves right until it reaches - * bit0 of byte0, then goes to bit7 of byte1, etc. - * @param {number} n The number of bits to peek. Must be a positive integer. - * @param {boolean=} movePointers Whether to move the pointer, defaults false. - * @returns {number} The peeked bits, as an unsigned number. - */ - peekBits_mtl(n, opt_movePointers) { - const NUM = parseInt(n, 10); - let num = NUM; - if (n !== num || num <= 0) { - return 0; - } - - const movePointers = opt_movePointers || false; - let bytes = this.bytes; - let bytePtr = this.bytePtr; - let bitPtr = this.bitPtr; - let result = 0; - - // keep going until we have no more bits left to peek at - while (num > 0) { - // We overflowed the stream, so just return the bits we got. - if (bytePtr >= bytes.length) { - break; - } - - const numBitsLeftInThisByte = (8 - bitPtr); - if (num >= numBitsLeftInThisByte) { - result <<= numBitsLeftInThisByte; - result |= (BITMASK[numBitsLeftInThisByte] & bytes[bytePtr]); - bytePtr++; - bitPtr = 0; - num -= numBitsLeftInThisByte; - } else { - result <<= num; - const numBits = 8 - num - bitPtr; - result |= ((bytes[bytePtr] & (BITMASK[num] << numBits)) >> numBits); - - bitPtr += num; - break; - } - } - - if (movePointers) { - this.bitPtr = bitPtr; - this.bytePtr = bytePtr; - this.bitsRead_ += NUM; - } - - return result; - } - - /** - * Peek at 16 bits from current position in the buffer. - * Bit at (bytePtr,bitPtr) has the highest position in returning data. - * Taken from getbits.hpp in unrar. - * TODO: Move this out of BitStream and into unrar. - * @returns {number} - */ - getBits() { - return (((((this.bytes[this.bytePtr] & 0xff) << 16) + - ((this.bytes[this.bytePtr + 1] & 0xff) << 8) + - ((this.bytes[this.bytePtr + 2] & 0xff))) >>> (8 - this.bitPtr)) & 0xffff); - } - - /** - * Reads n bits out of the stream, consuming them (moving the bit pointer). - * @param {number} n The number of bits to read. Must be a positive integer. - * @returns {number} The read bits, as an unsigned number. - */ - readBits(n) { - return this.peekBits(n, true); - } - - /** - * This returns n bytes as a sub-array, advancing the pointer if movePointers - * is true. Only use this for uncompressed blocks as this throws away remaining - * bits in the current byte. - * @param {number} n The number of bytes to peek. Must be a positive integer. - * @param {boolean=} movePointers Whether to move the pointer, defaults false. - * @returns {Uint8Array} The subarray. - */ - peekBytes(n, opt_movePointers) { - const num = parseInt(n, 10); - if (n !== num || num < 0) { - throw 'Error! Called peekBytes() with a non-positive integer: ' + n; - } else if (num === 0) { - return new Uint8Array(); - } - - // Flush bits until we are byte-aligned. - // from http://tools.ietf.org/html/rfc1951#page-11 - // "Any bits of input up to the next byte boundary are ignored." - while (this.bitPtr != 0) { - this.readBits(1); - } - - const numBytesLeft = this.getNumBitsLeft() / 8; - if (num > numBytesLeft) { - throw 'Error! Overflowed the bit stream! n=' + num + ', bytePtr=' + this.bytePtr + - ', bytes.length=' + this.bytes.length + ', bitPtr=' + this.bitPtr; - } - - const movePointers = opt_movePointers || false; - const result = new Uint8Array(num); - let bytes = this.bytes; - let ptr = this.bytePtr; - let bytesLeftToCopy = num; - while (bytesLeftToCopy > 0) { - const bytesLeftInStream = bytes.length - ptr; - const sourceLength = Math.min(bytesLeftToCopy, bytesLeftInStream); - - result.set(bytes.subarray(ptr, ptr + sourceLength), num - bytesLeftToCopy); - - ptr += sourceLength; - // Overflowed the stream, just return what we got. - if (ptr >= bytes.length) { - break; - } - - bytesLeftToCopy -= sourceLength; - } - - if (movePointers) { - this.bytePtr += num; - this.bitsRead_ += (num * 8); - } - - return result; - } - - /** - * @param {number} n The number of bytes to read. - * @returns {Uint8Array} The subarray. - */ - readBytes(n) { - return this.peekBytes(n, true); - } - } - - return BitStream; -})(); diff --git a/io/bitstream.js b/io/bitstream.js index b2eabd6..f925c1c 100644 --- a/io/bitstream.js +++ b/io/bitstream.js @@ -1,314 +1,308 @@ -// THIS IS A GENERATED FILE! DO NOT EDIT, INSTEAD EDIT THE FILE IN bitjs/build/io. -export const BitStream = /* - * bitstream-def.js + * bitstream.js * * Provides readers for bitstreams. * * Licensed under the MIT License * - * Copyright(c) 2011 Google Inc. + * Copyright(c) 2023 Google Inc. * Copyright(c) 2011 antimatter15 */ -(function () { - // mask for getting N number of bits (0-8) - const BITMASK = [0, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF]; +// mask for getting N number of bits (0-8) +const BITMASK = [0, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF]; +/** + * This object allows you to peek and consume bits and bytes out of a stream. + * Note that this stream is optimized, and thus, will *NOT* throw an error if + * the end of the stream is reached. Only use this in scenarios where you + * already have all the bits you need. + * + * Bit reading always proceeds from the first byte in the buffer, to the + * second byte, and so on. The MTL flag controls which bit is considered + * first *inside* the byte. + * + * An Example for how Most-To-Least vs Least-to-Most mode works: + * + * If you have an ArrayBuffer with the following two Uint8s: + * 185 (0b10111001) and 66 (0b01000010) + * and you perform a series of readBits: 2 bits, then 3, then 5, then 6. + * + * A BitStream in "mtl" mode will yield the following: + * - readBits(2) => 2 ('10') + * - readBits(3) => 7 ('111') + * - readBits(5) => 5 ('00101') + * - readBits(6) => 2 ('000010') + * + * A BitStream in "ltm" mode will yield the following: + * - readBits(2) => 1 ('01') + * - readBits(3) => 6 ('110') + * - readBits(5) => 21 ('10101') + * - readBits(6) => 16 ('010000') + */ +export class BitStream { /** - * This object allows you to peek and consume bits and bytes out of a stream. - * Note that this stream is optimized, and thus, will *NOT* throw an error if - * the end of the stream is reached. Only use this in scenarios where you - * already have all the bits you need. - * - * Bit reading always proceeds from the first byte in the buffer, to the - * second byte, and so on. The MTL flag controls which bit is considered - * first *inside* the byte. - * - * An Example for how Most-To-Least vs Least-to-Most mode works: - * - * If you have an ArrayBuffer with the following two Uint8s: - * 185 (0b10111001) and 66 (0b01000010) - * and you perform a series of readBits: 2 bits, then 3, then 5, then 6. - * - * A BitStream in "mtl" mode will yield the following: - * - readBits(2) => 2 ('10') - * - readBits(3) => 7 ('111') - * - readBits(5) => 5 ('00101') - * - readBits(6) => 2 ('000010') - * - * A BitStream in "ltm" mode will yield the following: - * - readBits(2) => 1 ('01') - * - readBits(3) => 6 ('110') - * - readBits(5) => 21 ('10101') - * - readBits(6) => 16 ('010000') + * @param {ArrayBuffer} ab An ArrayBuffer object or a Uint8Array. + * @param {boolean} mtl Whether the stream reads bits from the byte starting with the + * most-significant-bit (bit 7) to least-significant (bit 0). False means the direction is + * from least-significant-bit (bit 0) to most-significant (bit 7). + * @param {Number} opt_offset The offset into the ArrayBuffer + * @param {Number} opt_length The length of this BitStream */ - class BitStream { - /** - * @param {ArrayBuffer} ab An ArrayBuffer object or a Uint8Array. - * @param {boolean} mtl Whether the stream reads bits from the byte starting with the - * most-significant-bit (bit 7) to least-significant (bit 0). False means the direction is - * from least-significant-bit (bit 0) to most-significant (bit 7). - * @param {Number} opt_offset The offset into the ArrayBuffer - * @param {Number} opt_length The length of this BitStream - */ - constructor(ab, mtl, opt_offset, opt_length) { - if (!(ab instanceof ArrayBuffer)) { - throw 'Error! BitArray constructed with an invalid ArrayBuffer object'; - } - - const offset = opt_offset || 0; - const length = opt_length || ab.byteLength; - - /** - * The bytes in the stream. - * @type {Uint8Array} - * @private - */ - this.bytes = new Uint8Array(ab, offset, length); - - /** - * The byte in the stream that we are currently on. - * @type {Number} - * @private - */ - this.bytePtr = 0; - - /** - * The bit in the current byte that we will read next (can have values 0 through 7). - * @type {Number} - * @private - */ - this.bitPtr = 0; // tracks which bit we are on (can have values 0 through 7) - - /** - * An ever-increasing number. - * @type {Number} - * @private - */ - this.bitsRead_ = 0; - - this.peekBits = mtl ? this.peekBits_mtl : this.peekBits_ltm; + constructor(ab, mtl, opt_offset, opt_length) { + if (!(ab instanceof ArrayBuffer)) { + throw 'Error! BitArray constructed with an invalid ArrayBuffer object'; } - /** - * Returns how many bites have been read in the stream since the beginning of time. - * @returns {number} - */ - getNumBitsRead() { - return this.bitsRead_; - } + const offset = opt_offset || 0; + const length = opt_length || ab.byteLength; /** - * Returns how many bits are currently in the stream left to be read. - * @returns {number} + * The bytes in the stream. + * @type {Uint8Array} + * @private */ - getNumBitsLeft() { - const bitsLeftInByte = 8 - this.bitPtr; - return (this.bytes.byteLength - this.bytePtr - 1) * 8 + bitsLeftInByte; - } + this.bytes = new Uint8Array(ab, offset, length); /** - * byte0 byte1 byte2 byte3 - * 7......0 | 7......0 | 7......0 | 7......0 - * - * The bit pointer starts at least-significant bit (0) of byte0 and moves left until it reaches - * bit7 of byte0, then jumps to bit0 of byte1, etc. - * @param {number} n The number of bits to peek, must be a positive integer. - * @param {boolean=} movePointers Whether to move the pointer, defaults false. - * @returns {number} The peeked bits, as an unsigned number. + * The byte in the stream that we are currently on. + * @type {Number} + * @private */ - peekBits_ltm(n, opt_movePointers) { - const NUM = parseInt(n, 10); - let num = NUM; - if (n !== num || num <= 0) { - return 0; - } - - const movePointers = opt_movePointers || false; - let bytes = this.bytes; - let bytePtr = this.bytePtr; - let bitPtr = this.bitPtr; - let result = 0; - let bitsIn = 0; - - // keep going until we have no more bits left to peek at - while (num > 0) { - // We overflowed the stream, so just return what we got. - if (bytePtr >= bytes.length) { - break; - } - - const numBitsLeftInThisByte = (8 - bitPtr); - if (num >= numBitsLeftInThisByte) { - const mask = (BITMASK[numBitsLeftInThisByte] << bitPtr); - result |= (((bytes[bytePtr] & mask) >> bitPtr) << bitsIn); - - bytePtr++; - bitPtr = 0; - bitsIn += numBitsLeftInThisByte; - num -= numBitsLeftInThisByte; - } else { - const mask = (BITMASK[num] << bitPtr); - result |= (((bytes[bytePtr] & mask) >> bitPtr) << bitsIn); - - bitPtr += num; - break; - } - } - - if (movePointers) { - this.bitPtr = bitPtr; - this.bytePtr = bytePtr; - this.bitsRead_ += NUM; - } - - return result; - } + this.bytePtr = 0; /** - * byte0 byte1 byte2 byte3 - * 7......0 | 7......0 | 7......0 | 7......0 - * - * The bit pointer starts at bit7 of byte0 and moves right until it reaches - * bit0 of byte0, then goes to bit7 of byte1, etc. - * @param {number} n The number of bits to peek. Must be a positive integer. - * @param {boolean=} movePointers Whether to move the pointer, defaults false. - * @returns {number} The peeked bits, as an unsigned number. + * The bit in the current byte that we will read next (can have values 0 through 7). + * @type {Number} + * @private */ - peekBits_mtl(n, opt_movePointers) { - const NUM = parseInt(n, 10); - let num = NUM; - if (n !== num || num <= 0) { - return 0; - } - - const movePointers = opt_movePointers || false; - let bytes = this.bytes; - let bytePtr = this.bytePtr; - let bitPtr = this.bitPtr; - let result = 0; - - // keep going until we have no more bits left to peek at - while (num > 0) { - // We overflowed the stream, so just return the bits we got. - if (bytePtr >= bytes.length) { - break; - } - - const numBitsLeftInThisByte = (8 - bitPtr); - if (num >= numBitsLeftInThisByte) { - result <<= numBitsLeftInThisByte; - result |= (BITMASK[numBitsLeftInThisByte] & bytes[bytePtr]); - bytePtr++; - bitPtr = 0; - num -= numBitsLeftInThisByte; - } else { - result <<= num; - const numBits = 8 - num - bitPtr; - result |= ((bytes[bytePtr] & (BITMASK[num] << numBits)) >> numBits); - - bitPtr += num; - break; - } - } - - if (movePointers) { - this.bitPtr = bitPtr; - this.bytePtr = bytePtr; - this.bitsRead_ += NUM; - } - - return result; - } + this.bitPtr = 0; // tracks which bit we are on (can have values 0 through 7) /** - * Peek at 16 bits from current position in the buffer. - * Bit at (bytePtr,bitPtr) has the highest position in returning data. - * Taken from getbits.hpp in unrar. - * TODO: Move this out of BitStream and into unrar. - * @returns {number} + * An ever-increasing number. + * @type {Number} + * @private */ - getBits() { - return (((((this.bytes[this.bytePtr] & 0xff) << 16) + - ((this.bytes[this.bytePtr + 1] & 0xff) << 8) + - ((this.bytes[this.bytePtr + 2] & 0xff))) >>> (8 - this.bitPtr)) & 0xffff); - } + this.bitsRead_ = 0; - /** - * Reads n bits out of the stream, consuming them (moving the bit pointer). - * @param {number} n The number of bits to read. Must be a positive integer. - * @returns {number} The read bits, as an unsigned number. - */ - readBits(n) { - return this.peekBits(n, true); - } - - /** - * This returns n bytes as a sub-array, advancing the pointer if movePointers - * is true. Only use this for uncompressed blocks as this throws away remaining - * bits in the current byte. - * @param {number} n The number of bytes to peek. Must be a positive integer. - * @param {boolean=} movePointers Whether to move the pointer, defaults false. - * @returns {Uint8Array} The subarray. - */ - peekBytes(n, opt_movePointers) { - const num = parseInt(n, 10); - if (n !== num || num < 0) { - throw 'Error! Called peekBytes() with a non-positive integer: ' + n; - } else if (num === 0) { - return new Uint8Array(); - } - - // Flush bits until we are byte-aligned. - // from http://tools.ietf.org/html/rfc1951#page-11 - // "Any bits of input up to the next byte boundary are ignored." - while (this.bitPtr != 0) { - this.readBits(1); - } - - const numBytesLeft = this.getNumBitsLeft() / 8; - if (num > numBytesLeft) { - throw 'Error! Overflowed the bit stream! n=' + num + ', bytePtr=' + this.bytePtr + - ', bytes.length=' + this.bytes.length + ', bitPtr=' + this.bitPtr; - } - - const movePointers = opt_movePointers || false; - const result = new Uint8Array(num); - let bytes = this.bytes; - let ptr = this.bytePtr; - let bytesLeftToCopy = num; - while (bytesLeftToCopy > 0) { - const bytesLeftInStream = bytes.length - ptr; - const sourceLength = Math.min(bytesLeftToCopy, bytesLeftInStream); - - result.set(bytes.subarray(ptr, ptr + sourceLength), num - bytesLeftToCopy); - - ptr += sourceLength; - // Overflowed the stream, just return what we got. - if (ptr >= bytes.length) { - break; - } - - bytesLeftToCopy -= sourceLength; - } - - if (movePointers) { - this.bytePtr += num; - this.bitsRead_ += (num * 8); - } - - return result; - } - - /** - * @param {number} n The number of bytes to read. - * @returns {Uint8Array} The subarray. - */ - readBytes(n) { - return this.peekBytes(n, true); - } + this.peekBits = mtl ? this.peekBits_mtl : this.peekBits_ltm; } - return BitStream; -})(); + /** + * Returns how many bites have been read in the stream since the beginning of time. + * @returns {number} + */ + getNumBitsRead() { + return this.bitsRead_; + } + + /** + * Returns how many bits are currently in the stream left to be read. + * @returns {number} + */ + getNumBitsLeft() { + const bitsLeftInByte = 8 - this.bitPtr; + return (this.bytes.byteLength - this.bytePtr - 1) * 8 + bitsLeftInByte; + } + + /** + * byte0 byte1 byte2 byte3 + * 7......0 | 7......0 | 7......0 | 7......0 + * + * The bit pointer starts at least-significant bit (0) of byte0 and moves left until it reaches + * bit7 of byte0, then jumps to bit0 of byte1, etc. + * @param {number} n The number of bits to peek, must be a positive integer. + * @param {boolean=} movePointers Whether to move the pointer, defaults false. + * @returns {number} The peeked bits, as an unsigned number. + */ + peekBits_ltm(n, opt_movePointers) { + const NUM = parseInt(n, 10); + let num = NUM; + if (n !== num || num <= 0) { + return 0; + } + + const movePointers = opt_movePointers || false; + let bytes = this.bytes; + let bytePtr = this.bytePtr; + let bitPtr = this.bitPtr; + let result = 0; + let bitsIn = 0; + + // keep going until we have no more bits left to peek at + while (num > 0) { + // We overflowed the stream, so just return what we got. + if (bytePtr >= bytes.length) { + break; + } + + const numBitsLeftInThisByte = (8 - bitPtr); + if (num >= numBitsLeftInThisByte) { + const mask = (BITMASK[numBitsLeftInThisByte] << bitPtr); + result |= (((bytes[bytePtr] & mask) >> bitPtr) << bitsIn); + + bytePtr++; + bitPtr = 0; + bitsIn += numBitsLeftInThisByte; + num -= numBitsLeftInThisByte; + } else { + const mask = (BITMASK[num] << bitPtr); + result |= (((bytes[bytePtr] & mask) >> bitPtr) << bitsIn); + + bitPtr += num; + break; + } + } + + if (movePointers) { + this.bitPtr = bitPtr; + this.bytePtr = bytePtr; + this.bitsRead_ += NUM; + } + + return result; + } + + /** + * byte0 byte1 byte2 byte3 + * 7......0 | 7......0 | 7......0 | 7......0 + * + * The bit pointer starts at bit7 of byte0 and moves right until it reaches + * bit0 of byte0, then goes to bit7 of byte1, etc. + * @param {number} n The number of bits to peek. Must be a positive integer. + * @param {boolean=} movePointers Whether to move the pointer, defaults false. + * @returns {number} The peeked bits, as an unsigned number. + */ + peekBits_mtl(n, opt_movePointers) { + const NUM = parseInt(n, 10); + let num = NUM; + if (n !== num || num <= 0) { + return 0; + } + + const movePointers = opt_movePointers || false; + let bytes = this.bytes; + let bytePtr = this.bytePtr; + let bitPtr = this.bitPtr; + let result = 0; + + // keep going until we have no more bits left to peek at + while (num > 0) { + // We overflowed the stream, so just return the bits we got. + if (bytePtr >= bytes.length) { + break; + } + + const numBitsLeftInThisByte = (8 - bitPtr); + if (num >= numBitsLeftInThisByte) { + result <<= numBitsLeftInThisByte; + result |= (BITMASK[numBitsLeftInThisByte] & bytes[bytePtr]); + bytePtr++; + bitPtr = 0; + num -= numBitsLeftInThisByte; + } else { + result <<= num; + const numBits = 8 - num - bitPtr; + result |= ((bytes[bytePtr] & (BITMASK[num] << numBits)) >> numBits); + + bitPtr += num; + break; + } + } + + if (movePointers) { + this.bitPtr = bitPtr; + this.bytePtr = bytePtr; + this.bitsRead_ += NUM; + } + + return result; + } + + /** + * Peek at 16 bits from current position in the buffer. + * Bit at (bytePtr,bitPtr) has the highest position in returning data. + * Taken from getbits.hpp in unrar. + * TODO: Move this out of BitStream and into unrar. + * @returns {number} + */ + getBits() { + return (((((this.bytes[this.bytePtr] & 0xff) << 16) + + ((this.bytes[this.bytePtr + 1] & 0xff) << 8) + + ((this.bytes[this.bytePtr + 2] & 0xff))) >>> (8 - this.bitPtr)) & 0xffff); + } + + /** + * Reads n bits out of the stream, consuming them (moving the bit pointer). + * @param {number} n The number of bits to read. Must be a positive integer. + * @returns {number} The read bits, as an unsigned number. + */ + readBits(n) { + return this.peekBits(n, true); + } + + /** + * This returns n bytes as a sub-array, advancing the pointer if movePointers + * is true. Only use this for uncompressed blocks as this throws away remaining + * bits in the current byte. + * @param {number} n The number of bytes to peek. Must be a positive integer. + * @param {boolean=} movePointers Whether to move the pointer, defaults false. + * @returns {Uint8Array} The subarray. + */ + peekBytes(n, opt_movePointers) { + const num = parseInt(n, 10); + if (n !== num || num < 0) { + throw 'Error! Called peekBytes() with a non-positive integer: ' + n; + } else if (num === 0) { + return new Uint8Array(); + } + + // Flush bits until we are byte-aligned. + // from http://tools.ietf.org/html/rfc1951#page-11 + // "Any bits of input up to the next byte boundary are ignored." + while (this.bitPtr != 0) { + this.readBits(1); + } + + const numBytesLeft = this.getNumBitsLeft() / 8; + if (num > numBytesLeft) { + throw 'Error! Overflowed the bit stream! n=' + num + ', bytePtr=' + this.bytePtr + + ', bytes.length=' + this.bytes.length + ', bitPtr=' + this.bitPtr; + } + + const movePointers = opt_movePointers || false; + const result = new Uint8Array(num); + let bytes = this.bytes; + let ptr = this.bytePtr; + let bytesLeftToCopy = num; + while (bytesLeftToCopy > 0) { + const bytesLeftInStream = bytes.length - ptr; + const sourceLength = Math.min(bytesLeftToCopy, bytesLeftInStream); + + result.set(bytes.subarray(ptr, ptr + sourceLength), num - bytesLeftToCopy); + + ptr += sourceLength; + // Overflowed the stream, just return what we got. + if (ptr >= bytes.length) { + break; + } + + bytesLeftToCopy -= sourceLength; + } + + if (movePointers) { + this.bytePtr += num; + this.bitsRead_ += (num * 8); + } + + return result; + } + + /** + * @param {number} n The number of bytes to read. + * @returns {Uint8Array} The subarray. + */ + readBytes(n) { + return this.peekBytes(n, true); + } +} diff --git a/io/bytebuffer-worker.js b/io/bytebuffer-worker.js deleted file mode 100644 index 0f6212b..0000000 --- a/io/bytebuffer-worker.js +++ /dev/null @@ -1,131 +0,0 @@ -// THIS IS A GENERATED FILE! DO NOT EDIT, INSTEAD EDIT THE FILE IN bitjs/build/io. -var bitjs = bitjs || {}; -bitjs.io = bitjs.io || {}; -bitjs.io.ByteBuffer = -/* - * bytebuffer-def.js - * - * Provides a writer for bytes. - * - * Licensed under the MIT License - * - * Copyright(c) 2011 Google Inc. - * Copyright(c) 2011 antimatter15 - */ - -(function () { - /** - * A write-only Byte buffer which uses a Uint8 Typed Array as a backing store. - */ - class ByteBuffer { - /** - * @param {number} numBytes The number of bytes to allocate. - */ - constructor(numBytes) { - if (typeof numBytes != typeof 1 || numBytes <= 0) { - throw "Error! ByteBuffer initialized with '" + numBytes + "'"; - } - - /** - * @type {Uint8Array} - * @public - */ - this.data = new Uint8Array(numBytes); - - /** - * @type {number} - * @public - */ - this.ptr = 0; - } - - - /** - * @param {number} b The byte to insert. - */ - insertByte(b) { - // TODO: throw if byte is invalid? - this.data[this.ptr++] = b; - } - - /** - * @param {Array.|Uint8Array|Int8Array} bytes The bytes to insert. - */ - insertBytes(bytes) { - // TODO: throw if bytes is invalid? - this.data.set(bytes, this.ptr); - this.ptr += bytes.length; - } - - /** - * Writes an unsigned number into the next n bytes. If the number is too large - * to fit into n bytes or is negative, an error is thrown. - * @param {number} num The unsigned number to write. - * @param {number} numBytes The number of bytes to write the number into. - */ - writeNumber(num, numBytes) { - if (numBytes < 1 || !numBytes) { - throw 'Trying to write into too few bytes: ' + numBytes; - } - if (num < 0) { - throw 'Trying to write a negative number (' + num + - ') as an unsigned number to an ArrayBuffer'; - } - if (num > (Math.pow(2, numBytes * 8) - 1)) { - throw 'Trying to write ' + num + ' into only ' + numBytes + ' bytes'; - } - - // Roll 8-bits at a time into an array of bytes. - const bytes = []; - while (numBytes-- > 0) { - const eightBits = num & 255; - bytes.push(eightBits); - num >>= 8; - } - - this.insertBytes(bytes); - } - - /** - * Writes a signed number into the next n bytes. If the number is too large - * to fit into n bytes, an error is thrown. - * @param {number} num The signed number to write. - * @param {number} numBytes The number of bytes to write the number into. - */ - writeSignedNumber(num, numBytes) { - if (numBytes < 1) { - throw 'Trying to write into too few bytes: ' + numBytes; - } - - const HALF = Math.pow(2, (numBytes * 8) - 1); - if (num >= HALF || num < -HALF) { - throw 'Trying to write ' + num + ' into only ' + numBytes + ' bytes'; - } - - // Roll 8-bits at a time into an array of bytes. - const bytes = []; - while (numBytes-- > 0) { - const eightBits = num & 255; - bytes.push(eightBits); - num >>= 8; - } - - this.insertBytes(bytes); - } - - /** - * @param {string} str The ASCII string to write. - */ - writeASCIIString(str) { - for (let i = 0; i < str.length; ++i) { - const curByte = str.charCodeAt(i); - if (curByte < 0 || curByte > 255) { - throw 'Trying to write a non-ASCII string!'; - } - this.insertByte(curByte); - } - } - } - - return ByteBuffer; -})(); diff --git a/io/bytebuffer.js b/io/bytebuffer.js index 68a7d35..1d62844 100644 --- a/io/bytebuffer.js +++ b/io/bytebuffer.js @@ -1,129 +1,123 @@ -// THIS IS A GENERATED FILE! DO NOT EDIT, INSTEAD EDIT THE FILE IN bitjs/build/io. -export const ByteBuffer = /* - * bytebuffer-def.js + * bytebuffer.js * * Provides a writer for bytes. * * Licensed under the MIT License * - * Copyright(c) 2011 Google Inc. + * Copyright(c) 2023 Google Inc. * Copyright(c) 2011 antimatter15 */ -(function () { +/** + * A write-only Byte buffer which uses a Uint8 Typed Array as a backing store. + */ +export class ByteBuffer { /** - * A write-only Byte buffer which uses a Uint8 Typed Array as a backing store. + * @param {number} numBytes The number of bytes to allocate. */ - class ByteBuffer { - /** - * @param {number} numBytes The number of bytes to allocate. - */ - constructor(numBytes) { - if (typeof numBytes != typeof 1 || numBytes <= 0) { - throw "Error! ByteBuffer initialized with '" + numBytes + "'"; - } - - /** - * @type {Uint8Array} - * @public - */ - this.data = new Uint8Array(numBytes); - - /** - * @type {number} - * @public - */ - this.ptr = 0; - } - - - /** - * @param {number} b The byte to insert. - */ - insertByte(b) { - // TODO: throw if byte is invalid? - this.data[this.ptr++] = b; + constructor(numBytes) { + if (typeof numBytes != typeof 1 || numBytes <= 0) { + throw "Error! ByteBuffer initialized with '" + numBytes + "'"; } /** - * @param {Array.|Uint8Array|Int8Array} bytes The bytes to insert. + * @type {Uint8Array} + * @public */ - insertBytes(bytes) { - // TODO: throw if bytes is invalid? - this.data.set(bytes, this.ptr); - this.ptr += bytes.length; - } + this.data = new Uint8Array(numBytes); /** - * Writes an unsigned number into the next n bytes. If the number is too large - * to fit into n bytes or is negative, an error is thrown. - * @param {number} num The unsigned number to write. - * @param {number} numBytes The number of bytes to write the number into. + * @type {number} + * @public */ - writeNumber(num, numBytes) { - if (numBytes < 1 || !numBytes) { - throw 'Trying to write into too few bytes: ' + numBytes; - } - if (num < 0) { - throw 'Trying to write a negative number (' + num + - ') as an unsigned number to an ArrayBuffer'; - } - if (num > (Math.pow(2, numBytes * 8) - 1)) { - throw 'Trying to write ' + num + ' into only ' + numBytes + ' bytes'; - } - - // Roll 8-bits at a time into an array of bytes. - const bytes = []; - while (numBytes-- > 0) { - const eightBits = num & 255; - bytes.push(eightBits); - num >>= 8; - } - - this.insertBytes(bytes); - } - - /** - * Writes a signed number into the next n bytes. If the number is too large - * to fit into n bytes, an error is thrown. - * @param {number} num The signed number to write. - * @param {number} numBytes The number of bytes to write the number into. - */ - writeSignedNumber(num, numBytes) { - if (numBytes < 1) { - throw 'Trying to write into too few bytes: ' + numBytes; - } - - const HALF = Math.pow(2, (numBytes * 8) - 1); - if (num >= HALF || num < -HALF) { - throw 'Trying to write ' + num + ' into only ' + numBytes + ' bytes'; - } - - // Roll 8-bits at a time into an array of bytes. - const bytes = []; - while (numBytes-- > 0) { - const eightBits = num & 255; - bytes.push(eightBits); - num >>= 8; - } - - this.insertBytes(bytes); - } - - /** - * @param {string} str The ASCII string to write. - */ - writeASCIIString(str) { - for (let i = 0; i < str.length; ++i) { - const curByte = str.charCodeAt(i); - if (curByte < 0 || curByte > 255) { - throw 'Trying to write a non-ASCII string!'; - } - this.insertByte(curByte); - } - } + this.ptr = 0; } - return ByteBuffer; -})(); + + /** + * @param {number} b The byte to insert. + */ + insertByte(b) { + // TODO: throw if byte is invalid? + this.data[this.ptr++] = b; + } + + /** + * @param {Array.|Uint8Array|Int8Array} bytes The bytes to insert. + */ + insertBytes(bytes) { + // TODO: throw if bytes is invalid? + this.data.set(bytes, this.ptr); + this.ptr += bytes.length; + } + + /** + * Writes an unsigned number into the next n bytes. If the number is too large + * to fit into n bytes or is negative, an error is thrown. + * @param {number} num The unsigned number to write. + * @param {number} numBytes The number of bytes to write the number into. + */ + writeNumber(num, numBytes) { + if (numBytes < 1 || !numBytes) { + throw 'Trying to write into too few bytes: ' + numBytes; + } + if (num < 0) { + throw 'Trying to write a negative number (' + num + + ') as an unsigned number to an ArrayBuffer'; + } + if (num > (Math.pow(2, numBytes * 8) - 1)) { + throw 'Trying to write ' + num + ' into only ' + numBytes + ' bytes'; + } + + // Roll 8-bits at a time into an array of bytes. + const bytes = []; + while (numBytes-- > 0) { + const eightBits = num & 255; + bytes.push(eightBits); + num >>= 8; + } + + this.insertBytes(bytes); + } + + /** + * Writes a signed number into the next n bytes. If the number is too large + * to fit into n bytes, an error is thrown. + * @param {number} num The signed number to write. + * @param {number} numBytes The number of bytes to write the number into. + */ + writeSignedNumber(num, numBytes) { + if (numBytes < 1) { + throw 'Trying to write into too few bytes: ' + numBytes; + } + + const HALF = Math.pow(2, (numBytes * 8) - 1); + if (num >= HALF || num < -HALF) { + throw 'Trying to write ' + num + ' into only ' + numBytes + ' bytes'; + } + + // Roll 8-bits at a time into an array of bytes. + const bytes = []; + while (numBytes-- > 0) { + const eightBits = num & 255; + bytes.push(eightBits); + num >>= 8; + } + + this.insertBytes(bytes); + } + + /** + * @param {string} str The ASCII string to write. + */ + writeASCIIString(str) { + for (let i = 0; i < str.length; ++i) { + const curByte = str.charCodeAt(i); + if (curByte < 0 || curByte > 255) { + throw 'Trying to write a non-ASCII string!'; + } + this.insertByte(curByte); + } + } +} diff --git a/io/bytestream-worker.js b/io/bytestream-worker.js deleted file mode 100644 index 57a0aa6..0000000 --- a/io/bytestream-worker.js +++ /dev/null @@ -1,312 +0,0 @@ -// THIS IS A GENERATED FILE! DO NOT EDIT, INSTEAD EDIT THE FILE IN bitjs/build/io. -var bitjs = bitjs || {}; -bitjs.io = bitjs.io || {}; -bitjs.io.ByteStream = -/* - * bytestream-def.js - * - * Provides readers for byte streams. - * - * Licensed under the MIT License - * - * Copyright(c) 2011 Google Inc. - * Copyright(c) 2011 antimatter15 - */ - -(function () { - /** - * This object allows you to peek and consume bytes as numbers and strings out - * of a stream. More bytes can be pushed into the back of the stream via the - * push() method. - */ - class ByteStream { - /** - * @param {ArrayBuffer} ab The ArrayBuffer object. - * @param {number=} opt_offset The offset into the ArrayBuffer - * @param {number=} opt_length The length of this ByteStream - */ - constructor(ab, opt_offset, opt_length) { - if (!(ab instanceof ArrayBuffer)) { - throw 'Error! BitArray constructed with an invalid ArrayBuffer object'; - } - - const offset = opt_offset || 0; - const length = opt_length || ab.byteLength; - - /** - * The current page of bytes in the stream. - * @type {Uint8Array} - * @private - */ - this.bytes = new Uint8Array(ab, offset, length); - - /** - * The next pages of bytes in the stream. - * @type {Array} - * @private - */ - this.pages_ = []; - - /** - * The byte in the current page that we will read next. - * @type {Number} - * @private - */ - this.ptr = 0; - - /** - * An ever-increasing number. - * @type {Number} - * @private - */ - this.bytesRead_ = 0; - } - - /** - * Returns how many bytes have been read in the stream since the beginning of time. - */ - getNumBytesRead() { - return this.bytesRead_; - } - - /** - * Returns how many bytes are currently in the stream left to be read. - */ - getNumBytesLeft() { - const bytesInCurrentPage = (this.bytes.byteLength - this.ptr); - return this.pages_.reduce((acc, arr) => acc + arr.length, bytesInCurrentPage); - } - - /** - * Move the pointer ahead n bytes. If the pointer is at the end of the current array - * of bytes and we have another page of bytes, point at the new page. This is a private - * method, no validation is done. - * @param {number} n Number of bytes to increment. - * @private - */ - movePointer_(n) { - this.ptr += n; - this.bytesRead_ += n; - while (this.ptr >= this.bytes.length && this.pages_.length > 0) { - this.ptr -= this.bytes.length; - this.bytes = this.pages_.shift(); - } - } - - /** - * Peeks at the next n bytes as an unsigned number but does not advance the - * pointer. - * @param {number} n The number of bytes to peek at. Must be a positive integer. - * @returns {number} The n bytes interpreted as an unsigned number. - */ - peekNumber(n) { - const num = parseInt(n, 10); - if (n !== num || num < 0) { - throw 'Error! Called peekNumber() with a non-positive integer'; - } else if (num === 0) { - return 0; - } - - if (n > 4) { - throw 'Error! Called peekNumber(' + n + - ') but this method can only reliably read numbers up to 4 bytes long'; - } - - if (this.getNumBytesLeft() < num) { - throw 'Error! Overflowed the byte stream while peekNumber()! n=' + num + - ', ptr=' + this.ptr + ', bytes.length=' + this.getNumBytesLeft(); - } - - let result = 0; - let curPage = this.bytes; - let pageIndex = 0; - let ptr = this.ptr; - for (let i = 0; i < num; ++i) { - result |= (curPage[ptr++] << (i * 8)); - - if (ptr >= curPage.length) { - curPage = this.pages_[pageIndex++]; - ptr = 0; - } - } - - return result; - } - - - /** - * Returns the next n bytes as an unsigned number (or -1 on error) - * and advances the stream pointer n bytes. - * @param {number} n The number of bytes to read. Must be a positive integer. - * @returns {number} The n bytes interpreted as an unsigned number. - */ - readNumber(n) { - const num = this.peekNumber(n); - this.movePointer_(n); - return num; - } - - - /** - * Returns the next n bytes as a signed number but does not advance the - * pointer. - * @param {number} n The number of bytes to read. Must be a positive integer. - * @returns {number} The bytes interpreted as a signed number. - */ - peekSignedNumber(n) { - let num = this.peekNumber(n); - const HALF = Math.pow(2, (n * 8) - 1); - const FULL = HALF * 2; - - if (num >= HALF) num -= FULL; - - return num; - } - - - /** - * Returns the next n bytes as a signed number and advances the stream pointer. - * @param {number} n The number of bytes to read. Must be a positive integer. - * @returns {number} The bytes interpreted as a signed number. - */ - readSignedNumber(n) { - const num = this.peekSignedNumber(n); - this.movePointer_(n); - return num; - } - - - /** - * This returns n bytes as a sub-array, advancing the pointer if movePointers - * is true. - * @param {number} n The number of bytes to read. Must be a positive integer. - * @param {boolean} movePointers Whether to move the pointers. - * @returns {Uint8Array} The subarray. - */ - peekBytes(n, movePointers) { - const num = parseInt(n, 10); - if (n !== num || num < 0) { - throw 'Error! Called peekBytes() with a non-positive integer'; - } else if (num === 0) { - return new Uint8Array(); - } - - const totalBytesLeft = this.getNumBytesLeft(); - if (num > totalBytesLeft) { - throw 'Error! Overflowed the byte stream during peekBytes! n=' + num + - ', ptr=' + this.ptr + ', bytes.length=' + this.getNumBytesLeft(); - } - - const result = new Uint8Array(num); - let curPage = this.bytes; - let ptr = this.ptr; - let bytesLeftToCopy = num; - let pageIndex = 0; - while (bytesLeftToCopy > 0) { - const bytesLeftInPage = curPage.length - ptr; - const sourceLength = Math.min(bytesLeftToCopy, bytesLeftInPage); - - result.set(curPage.subarray(ptr, ptr + sourceLength), num - bytesLeftToCopy); - - ptr += sourceLength; - if (ptr >= curPage.length) { - curPage = this.pages_[pageIndex++]; - ptr = 0; - } - - bytesLeftToCopy -= sourceLength; - } - - if (movePointers) { - this.movePointer_(num); - } - - return result; - } - - /** - * Reads the next n bytes as a sub-array. - * @param {number} n The number of bytes to read. Must be a positive integer. - * @returns {Uint8Array} The subarray. - */ - readBytes(n) { - return this.peekBytes(n, true); - } - - /** - * Peeks at the next n bytes as an ASCII string but does not advance the pointer. - * @param {number} n The number of bytes to peek at. Must be a positive integer. - * @returns {string} The next n bytes as a string. - */ - peekString(n) { - const num = parseInt(n, 10); - if (n !== num || num < 0) { - throw 'Error! Called peekString() with a non-positive integer'; - } else if (num === 0) { - return ''; - } - - const totalBytesLeft = this.getNumBytesLeft(); - if (num > totalBytesLeft) { - throw 'Error! Overflowed the byte stream while peekString()! n=' + num + - ', ptr=' + this.ptr + ', bytes.length=' + this.getNumBytesLeft(); - } - - let result = new Array(num); - let curPage = this.bytes; - let pageIndex = 0; - let ptr = this.ptr; - for (let i = 0; i < num; ++i) { - result[i] = String.fromCharCode(curPage[ptr++]); - if (ptr >= curPage.length) { - curPage = this.pages_[pageIndex++]; - ptr = 0; - } - } - - return result.join(''); - } - - /** - * Returns the next n bytes as an ASCII string and advances the stream pointer - * n bytes. - * @param {number} n The number of bytes to read. Must be a positive integer. - * @returns {string} The next n bytes as a string. - */ - readString(n) { - const strToReturn = this.peekString(n); - this.movePointer_(n); - return strToReturn; - } - - /** - * Feeds more bytes into the back of the stream. - * @param {ArrayBuffer} ab - */ - push(ab) { - if (!(ab instanceof ArrayBuffer)) { - throw 'Error! ByteStream.push() called with an invalid ArrayBuffer object'; - } - - this.pages_.push(new Uint8Array(ab)); - // If the pointer is at the end of the current page of bytes, this will advance - // to the next page. - this.movePointer_(0); - } - - /** - * Creates a new ByteStream from this ByteStream that can be read / peeked. - * @returns {ByteStream} A clone of this ByteStream. - */ - tee() { - const clone = new ByteStream(this.bytes.buffer); - clone.bytes = this.bytes; - clone.ptr = this.ptr; - clone.pages_ = this.pages_.slice(); - clone.bytesRead_ = this.bytesRead_; - return clone; - } - } - - return ByteStream; -})(); diff --git a/io/bytestream.js b/io/bytestream.js index 9337f46..8cc275d 100644 --- a/io/bytestream.js +++ b/io/bytestream.js @@ -1,310 +1,304 @@ -// THIS IS A GENERATED FILE! DO NOT EDIT, INSTEAD EDIT THE FILE IN bitjs/build/io. -export const ByteStream = /* - * bytestream-def.js + * bytestream.js * * Provides readers for byte streams. * * Licensed under the MIT License * - * Copyright(c) 2011 Google Inc. + * Copyright(c) 2023 Google Inc. * Copyright(c) 2011 antimatter15 */ -(function () { +/** + * This object allows you to peek and consume bytes as numbers and strings out + * of a stream. More bytes can be pushed into the back of the stream via the + * push() method. + */ +export class ByteStream { /** - * This object allows you to peek and consume bytes as numbers and strings out - * of a stream. More bytes can be pushed into the back of the stream via the - * push() method. + * @param {ArrayBuffer} ab The ArrayBuffer object. + * @param {number=} opt_offset The offset into the ArrayBuffer + * @param {number=} opt_length The length of this ByteStream */ - class ByteStream { - /** - * @param {ArrayBuffer} ab The ArrayBuffer object. - * @param {number=} opt_offset The offset into the ArrayBuffer - * @param {number=} opt_length The length of this ByteStream - */ - constructor(ab, opt_offset, opt_length) { - if (!(ab instanceof ArrayBuffer)) { - throw 'Error! BitArray constructed with an invalid ArrayBuffer object'; - } - - const offset = opt_offset || 0; - const length = opt_length || ab.byteLength; - - /** - * The current page of bytes in the stream. - * @type {Uint8Array} - * @private - */ - this.bytes = new Uint8Array(ab, offset, length); - - /** - * The next pages of bytes in the stream. - * @type {Array} - * @private - */ - this.pages_ = []; - - /** - * The byte in the current page that we will read next. - * @type {Number} - * @private - */ - this.ptr = 0; - - /** - * An ever-increasing number. - * @type {Number} - * @private - */ - this.bytesRead_ = 0; + constructor(ab, opt_offset, opt_length) { + if (!(ab instanceof ArrayBuffer)) { + throw 'Error! BitArray constructed with an invalid ArrayBuffer object'; } - /** - * Returns how many bytes have been read in the stream since the beginning of time. - */ - getNumBytesRead() { - return this.bytesRead_; - } + const offset = opt_offset || 0; + const length = opt_length || ab.byteLength; /** - * Returns how many bytes are currently in the stream left to be read. - */ - getNumBytesLeft() { - const bytesInCurrentPage = (this.bytes.byteLength - this.ptr); - return this.pages_.reduce((acc, arr) => acc + arr.length, bytesInCurrentPage); - } - - /** - * Move the pointer ahead n bytes. If the pointer is at the end of the current array - * of bytes and we have another page of bytes, point at the new page. This is a private - * method, no validation is done. - * @param {number} n Number of bytes to increment. + * The current page of bytes in the stream. + * @type {Uint8Array} * @private */ - movePointer_(n) { - this.ptr += n; - this.bytesRead_ += n; - while (this.ptr >= this.bytes.length && this.pages_.length > 0) { - this.ptr -= this.bytes.length; - this.bytes = this.pages_.shift(); - } - } + this.bytes = new Uint8Array(ab, offset, length); /** - * Peeks at the next n bytes as an unsigned number but does not advance the - * pointer. - * @param {number} n The number of bytes to peek at. Must be a positive integer. - * @returns {number} The n bytes interpreted as an unsigned number. + * The next pages of bytes in the stream. + * @type {Array} + * @private */ - peekNumber(n) { - const num = parseInt(n, 10); - if (n !== num || num < 0) { - throw 'Error! Called peekNumber() with a non-positive integer'; - } else if (num === 0) { - return 0; - } - - if (n > 4) { - throw 'Error! Called peekNumber(' + n + - ') but this method can only reliably read numbers up to 4 bytes long'; - } - - if (this.getNumBytesLeft() < num) { - throw 'Error! Overflowed the byte stream while peekNumber()! n=' + num + - ', ptr=' + this.ptr + ', bytes.length=' + this.getNumBytesLeft(); - } - - let result = 0; - let curPage = this.bytes; - let pageIndex = 0; - let ptr = this.ptr; - for (let i = 0; i < num; ++i) { - result |= (curPage[ptr++] << (i * 8)); - - if (ptr >= curPage.length) { - curPage = this.pages_[pageIndex++]; - ptr = 0; - } - } - - return result; - } - + this.pages_ = []; /** - * Returns the next n bytes as an unsigned number (or -1 on error) - * and advances the stream pointer n bytes. - * @param {number} n The number of bytes to read. Must be a positive integer. - * @returns {number} The n bytes interpreted as an unsigned number. + * The byte in the current page that we will read next. + * @type {Number} + * @private */ - readNumber(n) { - const num = this.peekNumber(n); - this.movePointer_(n); - return num; - } - + this.ptr = 0; /** - * Returns the next n bytes as a signed number but does not advance the - * pointer. - * @param {number} n The number of bytes to read. Must be a positive integer. - * @returns {number} The bytes interpreted as a signed number. + * An ever-increasing number. + * @type {Number} + * @private */ - peekSignedNumber(n) { - let num = this.peekNumber(n); - const HALF = Math.pow(2, (n * 8) - 1); - const FULL = HALF * 2; + this.bytesRead_ = 0; + } - if (num >= HALF) num -= FULL; + /** + * Returns how many bytes have been read in the stream since the beginning of time. + */ + getNumBytesRead() { + return this.bytesRead_; + } - return num; - } + /** + * Returns how many bytes are currently in the stream left to be read. + */ + getNumBytesLeft() { + const bytesInCurrentPage = (this.bytes.byteLength - this.ptr); + return this.pages_.reduce((acc, arr) => acc + arr.length, bytesInCurrentPage); + } - - /** - * Returns the next n bytes as a signed number and advances the stream pointer. - * @param {number} n The number of bytes to read. Must be a positive integer. - * @returns {number} The bytes interpreted as a signed number. - */ - readSignedNumber(n) { - const num = this.peekSignedNumber(n); - this.movePointer_(n); - return num; - } - - - /** - * This returns n bytes as a sub-array, advancing the pointer if movePointers - * is true. - * @param {number} n The number of bytes to read. Must be a positive integer. - * @param {boolean} movePointers Whether to move the pointers. - * @returns {Uint8Array} The subarray. - */ - peekBytes(n, movePointers) { - const num = parseInt(n, 10); - if (n !== num || num < 0) { - throw 'Error! Called peekBytes() with a non-positive integer'; - } else if (num === 0) { - return new Uint8Array(); - } - - const totalBytesLeft = this.getNumBytesLeft(); - if (num > totalBytesLeft) { - throw 'Error! Overflowed the byte stream during peekBytes! n=' + num + - ', ptr=' + this.ptr + ', bytes.length=' + this.getNumBytesLeft(); - } - - const result = new Uint8Array(num); - let curPage = this.bytes; - let ptr = this.ptr; - let bytesLeftToCopy = num; - let pageIndex = 0; - while (bytesLeftToCopy > 0) { - const bytesLeftInPage = curPage.length - ptr; - const sourceLength = Math.min(bytesLeftToCopy, bytesLeftInPage); - - result.set(curPage.subarray(ptr, ptr + sourceLength), num - bytesLeftToCopy); - - ptr += sourceLength; - if (ptr >= curPage.length) { - curPage = this.pages_[pageIndex++]; - ptr = 0; - } - - bytesLeftToCopy -= sourceLength; - } - - if (movePointers) { - this.movePointer_(num); - } - - return result; - } - - /** - * Reads the next n bytes as a sub-array. - * @param {number} n The number of bytes to read. Must be a positive integer. - * @returns {Uint8Array} The subarray. - */ - readBytes(n) { - return this.peekBytes(n, true); - } - - /** - * Peeks at the next n bytes as an ASCII string but does not advance the pointer. - * @param {number} n The number of bytes to peek at. Must be a positive integer. - * @returns {string} The next n bytes as a string. - */ - peekString(n) { - const num = parseInt(n, 10); - if (n !== num || num < 0) { - throw 'Error! Called peekString() with a non-positive integer'; - } else if (num === 0) { - return ''; - } - - const totalBytesLeft = this.getNumBytesLeft(); - if (num > totalBytesLeft) { - throw 'Error! Overflowed the byte stream while peekString()! n=' + num + - ', ptr=' + this.ptr + ', bytes.length=' + this.getNumBytesLeft(); - } - - let result = new Array(num); - let curPage = this.bytes; - let pageIndex = 0; - let ptr = this.ptr; - for (let i = 0; i < num; ++i) { - result[i] = String.fromCharCode(curPage[ptr++]); - if (ptr >= curPage.length) { - curPage = this.pages_[pageIndex++]; - ptr = 0; - } - } - - return result.join(''); - } - - /** - * Returns the next n bytes as an ASCII string and advances the stream pointer - * n bytes. - * @param {number} n The number of bytes to read. Must be a positive integer. - * @returns {string} The next n bytes as a string. - */ - readString(n) { - const strToReturn = this.peekString(n); - this.movePointer_(n); - return strToReturn; - } - - /** - * Feeds more bytes into the back of the stream. - * @param {ArrayBuffer} ab - */ - push(ab) { - if (!(ab instanceof ArrayBuffer)) { - throw 'Error! ByteStream.push() called with an invalid ArrayBuffer object'; - } - - this.pages_.push(new Uint8Array(ab)); - // If the pointer is at the end of the current page of bytes, this will advance - // to the next page. - this.movePointer_(0); - } - - /** - * Creates a new ByteStream from this ByteStream that can be read / peeked. - * @returns {ByteStream} A clone of this ByteStream. - */ - tee() { - const clone = new ByteStream(this.bytes.buffer); - clone.bytes = this.bytes; - clone.ptr = this.ptr; - clone.pages_ = this.pages_.slice(); - clone.bytesRead_ = this.bytesRead_; - return clone; + /** + * Move the pointer ahead n bytes. If the pointer is at the end of the current array + * of bytes and we have another page of bytes, point at the new page. This is a private + * method, no validation is done. + * @param {number} n Number of bytes to increment. + * @private + */ + movePointer_(n) { + this.ptr += n; + this.bytesRead_ += n; + while (this.ptr >= this.bytes.length && this.pages_.length > 0) { + this.ptr -= this.bytes.length; + this.bytes = this.pages_.shift(); } } - return ByteStream; -})(); + /** + * Peeks at the next n bytes as an unsigned number but does not advance the + * pointer. + * @param {number} n The number of bytes to peek at. Must be a positive integer. + * @returns {number} The n bytes interpreted as an unsigned number. + */ + peekNumber(n) { + const num = parseInt(n, 10); + if (n !== num || num < 0) { + throw 'Error! Called peekNumber() with a non-positive integer'; + } else if (num === 0) { + return 0; + } + + if (n > 4) { + throw 'Error! Called peekNumber(' + n + + ') but this method can only reliably read numbers up to 4 bytes long'; + } + + if (this.getNumBytesLeft() < num) { + throw 'Error! Overflowed the byte stream while peekNumber()! n=' + num + + ', ptr=' + this.ptr + ', bytes.length=' + this.getNumBytesLeft(); + } + + let result = 0; + let curPage = this.bytes; + let pageIndex = 0; + let ptr = this.ptr; + for (let i = 0; i < num; ++i) { + result |= (curPage[ptr++] << (i * 8)); + + if (ptr >= curPage.length) { + curPage = this.pages_[pageIndex++]; + ptr = 0; + } + } + + return result; + } + + + /** + * Returns the next n bytes as an unsigned number (or -1 on error) + * and advances the stream pointer n bytes. + * @param {number} n The number of bytes to read. Must be a positive integer. + * @returns {number} The n bytes interpreted as an unsigned number. + */ + readNumber(n) { + const num = this.peekNumber(n); + this.movePointer_(n); + return num; + } + + + /** + * Returns the next n bytes as a signed number but does not advance the + * pointer. + * @param {number} n The number of bytes to read. Must be a positive integer. + * @returns {number} The bytes interpreted as a signed number. + */ + peekSignedNumber(n) { + let num = this.peekNumber(n); + const HALF = Math.pow(2, (n * 8) - 1); + const FULL = HALF * 2; + + if (num >= HALF) num -= FULL; + + return num; + } + + + /** + * Returns the next n bytes as a signed number and advances the stream pointer. + * @param {number} n The number of bytes to read. Must be a positive integer. + * @returns {number} The bytes interpreted as a signed number. + */ + readSignedNumber(n) { + const num = this.peekSignedNumber(n); + this.movePointer_(n); + return num; + } + + + /** + * This returns n bytes as a sub-array, advancing the pointer if movePointers + * is true. + * @param {number} n The number of bytes to read. Must be a positive integer. + * @param {boolean} movePointers Whether to move the pointers. + * @returns {Uint8Array} The subarray. + */ + peekBytes(n, movePointers) { + const num = parseInt(n, 10); + if (n !== num || num < 0) { + throw 'Error! Called peekBytes() with a non-positive integer'; + } else if (num === 0) { + return new Uint8Array(); + } + + const totalBytesLeft = this.getNumBytesLeft(); + if (num > totalBytesLeft) { + throw 'Error! Overflowed the byte stream during peekBytes! n=' + num + + ', ptr=' + this.ptr + ', bytes.length=' + this.getNumBytesLeft(); + } + + const result = new Uint8Array(num); + let curPage = this.bytes; + let ptr = this.ptr; + let bytesLeftToCopy = num; + let pageIndex = 0; + while (bytesLeftToCopy > 0) { + const bytesLeftInPage = curPage.length - ptr; + const sourceLength = Math.min(bytesLeftToCopy, bytesLeftInPage); + + result.set(curPage.subarray(ptr, ptr + sourceLength), num - bytesLeftToCopy); + + ptr += sourceLength; + if (ptr >= curPage.length) { + curPage = this.pages_[pageIndex++]; + ptr = 0; + } + + bytesLeftToCopy -= sourceLength; + } + + if (movePointers) { + this.movePointer_(num); + } + + return result; + } + + /** + * Reads the next n bytes as a sub-array. + * @param {number} n The number of bytes to read. Must be a positive integer. + * @returns {Uint8Array} The subarray. + */ + readBytes(n) { + return this.peekBytes(n, true); + } + + /** + * Peeks at the next n bytes as an ASCII string but does not advance the pointer. + * @param {number} n The number of bytes to peek at. Must be a positive integer. + * @returns {string} The next n bytes as a string. + */ + peekString(n) { + const num = parseInt(n, 10); + if (n !== num || num < 0) { + throw 'Error! Called peekString() with a non-positive integer'; + } else if (num === 0) { + return ''; + } + + const totalBytesLeft = this.getNumBytesLeft(); + if (num > totalBytesLeft) { + throw 'Error! Overflowed the byte stream while peekString()! n=' + num + + ', ptr=' + this.ptr + ', bytes.length=' + this.getNumBytesLeft(); + } + + let result = new Array(num); + let curPage = this.bytes; + let pageIndex = 0; + let ptr = this.ptr; + for (let i = 0; i < num; ++i) { + result[i] = String.fromCharCode(curPage[ptr++]); + if (ptr >= curPage.length) { + curPage = this.pages_[pageIndex++]; + ptr = 0; + } + } + + return result.join(''); + } + + /** + * Returns the next n bytes as an ASCII string and advances the stream pointer + * n bytes. + * @param {number} n The number of bytes to read. Must be a positive integer. + * @returns {string} The next n bytes as a string. + */ + readString(n) { + const strToReturn = this.peekString(n); + this.movePointer_(n); + return strToReturn; + } + + /** + * Feeds more bytes into the back of the stream. + * @param {ArrayBuffer} ab + */ + push(ab) { + if (!(ab instanceof ArrayBuffer)) { + throw 'Error! ByteStream.push() called with an invalid ArrayBuffer object'; + } + + this.pages_.push(new Uint8Array(ab)); + // If the pointer is at the end of the current page of bytes, this will advance + // to the next page. + this.movePointer_(0); + } + + /** + * Creates a new ByteStream from this ByteStream that can be read / peeked. + * @returns {ByteStream} A clone of this ByteStream. + */ + tee() { + const clone = new ByteStream(this.bytes.buffer); + clone.bytes = this.bytes; + clone.ptr = this.ptr; + clone.pages_ = this.pages_.slice(); + clone.bytesRead_ = this.bytesRead_; + return clone; + } +} diff --git a/package.json b/package.json index e0fd33e..25ae119 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@codedread/bitjs", - "version": "1.0.11", + "version": "1.1.0", "description": "Binary Tools for JavaScript", "homepage": "https://github.com/codedread/bitjs", "author": "Jeff Schiller",