1
0
Fork 0
mirror of https://github.com/codedread/bitjs synced 2025-10-04 01:59:15 +02:00

Issue #28: Removed build step for bitjs.io now that all browsers (Firefox 114+) support ES Module Workers.

This commit is contained in:
Jeff Schiller 2023-05-28 10:39:35 -07:00
parent fce8d69612
commit 63e84dadb0
26 changed files with 916 additions and 2965 deletions

28
CHANGELOG.md Normal file
View file

@ -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.

View file

@ -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.`);

View file

@ -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().

View file

@ -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) {

View file

@ -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<bitjs.io.ByteBuffer>}
* @type {Array<ByteBuffer>}
*/
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;

View file

@ -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);
}

View file

@ -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<number, SymbolLengthPair>} hcTable
* @returns {number}
*/
@ -417,10 +417,10 @@ const DistLookupTable = [
];
/**
* @param {bitjs.io.BitStream} bstream
* @param {BitStream} bstream
* @param {Map<number, SymbolLengthPair>} hcLiteralTable
* @param {Map<number, SymbolLengthPair>} 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);
}

View file

@ -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.

View file

@ -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

View file

@ -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

View file

@ -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}

View file

@ -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;
})();

View file

@ -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;
})();

View file

@ -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.<number>|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;
})();

View file

@ -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<Uint8Array>}
* @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;
})();

View file

@ -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';

View file

@ -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.

View file

@ -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;
})();

View file

@ -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;
})();
}

View file

@ -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;
})();

View file

@ -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);
}
}

View file

@ -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.<number>|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;
})();

View file

@ -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.<number>|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.<number>|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);
}
}
}

View file

@ -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<Uint8Array>}
* @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;
})();

View file

@ -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<Uint8Array>}
* @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<Uint8Array>}
* @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;
}
}

View file

@ -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",