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, UnarchiveProgressEvent, UnarchiveStartEvent, Unarchiver,
UnrarrerInternal, UntarrerInternal, UnzipperInternal, UnrarrerInternal, UntarrerInternal, UnzipperInternal,
getUnarchiverInternal } from './decompress-internal.js'; getUnarchiverInternal } from './decompress-internal.js';
import { Unzipper, Unrarrer, Untarrer, getUnarchiver } from './decompress.js';
export { export {
UnarchiveAppendEvent, UnarchiveAppendEvent,
@ -26,65 +27,7 @@ export {
UnarchiveProgressEvent, UnarchiveProgressEvent,
UnarchiveStartEvent, UnarchiveStartEvent,
Unarchiver, Unarchiver,
Unzipper, Unrarrer, Untarrer, getUnarchiver
} }
/** console.error(`bitjs: Stop importing archive.js, this module will be removed. Import decompress.js instead.`);
* 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);
}

View file

@ -53,7 +53,7 @@ export {
* just node's worker_threads ? * 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 // Thin wrappers of compressors for clients who want to construct a specific
// unarchiver themselves rather than use getUnarchiver(). // unarchiver themselves rather than use getUnarchiver().

View file

@ -6,6 +6,8 @@
* Copyright(c) 2017 Google Inc. * Copyright(c) 2017 Google Inc.
*/ */
import { BitStream } from '../io/bitstream.js';
/** /**
* CRC Implementation. * CRC Implementation.
*/ */
@ -104,13 +106,13 @@ function CRC(startCRC, arr) {
/** /**
* RarVM Implementation. * RarVM Implementation.
*/ */
const VM_MEMSIZE = 0x40000; export const VM_MEMSIZE = 0x40000;
const VM_MEMMASK = (VM_MEMSIZE - 1); export const VM_MEMMASK = (VM_MEMSIZE - 1);
const VM_GLOBALMEMADDR = 0x3C000; export const VM_GLOBALMEMADDR = 0x3C000;
const VM_GLOBALMEMSIZE = 0x2000; export const VM_GLOBALMEMSIZE = 0x2000;
const VM_FIXEDGLOBALSIZE = 64; export const VM_FIXEDGLOBALSIZE = 64;
const MAXWINSIZE = 0x400000; export const MAXWINSIZE = 0x400000;
const MAXWINMASK = (MAXWINSIZE - 1); export const MAXWINMASK = (MAXWINSIZE - 1);
/** /**
*/ */
@ -448,7 +450,7 @@ const StdList = [
/** /**
* @constructor * @constructor
*/ */
class RarVM { export class RarVM {
constructor() { constructor() {
/** @private {Uint8Array} */ /** @private {Uint8Array} */
this.mem_ = null; this.mem_ = null;
@ -486,7 +488,7 @@ class RarVM {
/** /**
* @param {VM_PreparedOperand} op * @param {VM_PreparedOperand} op
* @param {boolean} byteMode * @param {boolean} byteMode
* @param {bitjs.io.BitStream} bstream A rtl bit stream. * @param {BitStream} bstream A rtl bit stream.
*/ */
decodeArg(op, byteMode, bstream) { decodeArg(op, byteMode, bstream) {
const data = bstream.peekBits(16); const data = bstream.peekBits(16);
@ -808,7 +810,7 @@ class RarVM {
//InitBitInput(); //InitBitInput();
//memcpy(InBuf,Code,Min(CodeSize,BitInput::MAX_SIZE)); //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. // Calculate the single byte XOR checksum to check validity of VM code.
let xorSum = 0; let xorSum = 0;
@ -970,7 +972,7 @@ class RarVM {
/** /**
* Static function that reads in the next set of bits for the VM * Static function that reads in the next set of bits for the VM
* (might return 4, 8, 16 or 32 bits). * (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. * @returns {number} The value of the bits read.
*/ */
static readData(bstream) { static readData(bstream) {

View file

@ -12,10 +12,10 @@
// present. // present.
// This file expects to be invoked as a Worker (see onmessage below). // This file expects to be invoked as a Worker (see onmessage below).
importScripts('../io/bitstream-worker.js'); import { BitStream } from '../io/bitstream.js';
importScripts('../io/bytestream-worker.js'); import { ByteStream } from '../io/bytestream.js';
importScripts('../io/bytebuffer-worker.js'); import { ByteBuffer } from '../io/bytebuffer.js';
importScripts('rarvm.js'); import { VM_GLOBALMEMADDR, VM_GLOBALMEMSIZE, VM_FIXEDGLOBALSIZE, MAXWINMASK } from './rarvm.js';
const UnarchiveState = { const UnarchiveState = {
NOT_STARTED: 0, NOT_STARTED: 0,
@ -86,7 +86,7 @@ const ENDARC_HEAD = 0x7b;
*/ */
class RarVolumeHeader { class RarVolumeHeader {
/** /**
* @param {bitjs.io.ByteStream} bstream * @param {ByteStream} bstream
*/ */
constructor(bstream) { constructor(bstream) {
let headBytesRead = 0; let headBytesRead = 0;
@ -316,19 +316,19 @@ const RD = { //rep decode
}; };
/** /**
* @type {Array<bitjs.io.ByteBuffer>} * @type {Array<ByteBuffer>}
*/ */
const rOldBuffers = []; const rOldBuffers = [];
/** /**
* The current buffer we are unpacking to. * The current buffer we are unpacking to.
* @type {bitjs.io.ByteBuffer} * @type {ByteBuffer}
*/ */
let rBuffer; let rBuffer;
/** /**
* The buffer of the final bytes after filtering (only used in Unpack29). * The buffer of the final bytes after filtering (only used in Unpack29).
* @type {bitjs.io.ByteBuffer} * @type {ByteBuffer}
*/ */
let wBuffer; let wBuffer;
@ -346,7 +346,7 @@ let wBuffer;
/** /**
* Read in Huffman tables for RAR * Read in Huffman tables for RAR
* @param {bitjs.io.BitStream} bstream * @param {BitStream} bstream
*/ */
function RarReadTables(bstream) { function RarReadTables(bstream) {
const BitLength = new Array(rBC); const BitLength = new Array(rBC);
@ -491,7 +491,7 @@ function RarMakeDecodeTables(BitLength, offset, dec, size) {
// TODO: implement // TODO: implement
/** /**
* @param {bitjs.io.BitStream} bstream * @param {BitStream} bstream
* @param {boolean} Solid * @param {boolean} Solid
*/ */
function Unpack15(bstream, Solid) { function Unpack15(bstream, Solid) {
@ -500,7 +500,7 @@ function Unpack15(bstream, Solid) {
/** /**
* Unpacks the bit stream into rBuffer using the Unpack20 algorithm. * Unpacks the bit stream into rBuffer using the Unpack20 algorithm.
* @param {bitjs.io.BitStream} bstream * @param {BitStream} bstream
* @param {boolean} Solid * @param {boolean} Solid
*/ */
function Unpack20(bstream, Solid) { function Unpack20(bstream, Solid) {
@ -694,7 +694,7 @@ function InitFilters() {
*/ */
function RarAddVMCode(firstByte, vmCode) { function RarAddVMCode(firstByte, vmCode) {
VM.init(); VM.init();
const bstream = new bitjs.io.BitStream(vmCode.buffer, true /* rtl */); const bstream = new BitStream(vmCode.buffer, true /* rtl */);
let filtPos; let filtPos;
if (firstByte & 0x80) { if (firstByte & 0x80) {
@ -864,7 +864,7 @@ function RarAddVMCode(firstByte, vmCode) {
/** /**
* @param {!bitjs.io.BitStream} bstream * @param {!BitStream} bstream
*/ */
function RarReadVMCode(bstream) { function RarReadVMCode(bstream) {
const firstByte = bstream.readBits(8); const firstByte = bstream.readBits(8);
@ -886,7 +886,7 @@ function RarReadVMCode(bstream) {
/** /**
* Unpacks the bit stream into rBuffer using the Unpack29 algorithm. * Unpacks the bit stream into rBuffer using the Unpack29 algorithm.
* @param {bitjs.io.BitStream} bstream * @param {BitStream} bstream
* @param {boolean} Solid * @param {boolean} Solid
*/ */
function Unpack29(bstream, Solid) { function Unpack29(bstream, Solid) {
@ -1258,9 +1258,9 @@ function unpack(v) {
// TODO: implement what happens when unpVer is < 15 // TODO: implement what happens when unpVer is < 15
const Ver = v.header.unpVer <= 15 ? 15 : v.header.unpVer; const Ver = v.header.unpVer <= 15 ? 15 : v.header.unpVer;
const Solid = v.header.flags.LHD_SOLID; 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) { if (logToConsole) {
info('Unpacking ' + v.filename + ' RAR v' + Ver); info('Unpacking ' + v.filename + ' RAR v' + Ver);
@ -1276,7 +1276,7 @@ function unpack(v) {
break; break;
case 29: // rar 3.x compression case 29: // rar 3.x compression
case 36: // alternative hash case 36: // alternative hash
wBuffer = new bitjs.io.ByteBuffer(rBuffer.data.length); wBuffer = new ByteBuffer(rBuffer.data.length);
Unpack29(bstream, Solid); Unpack29(bstream, Solid);
break; break;
} // switch(method) } // switch(method)
@ -1290,7 +1290,7 @@ function unpack(v) {
*/ */
class RarLocalFile { class RarLocalFile {
/** /**
* @param {bitjs.io.ByteStream} bstream * @param {ByteStream} bstream
*/ */
constructor(bstream) { constructor(bstream) {
this.header = new RarVolumeHeader(bstream); this.header = new RarVolumeHeader(bstream);
@ -1325,7 +1325,7 @@ class RarLocalFile {
// Create a new buffer and copy it over. // Create a new buffer and copy it over.
const len = this.header.packSize; const len = this.header.packSize;
const newBuffer = new bitjs.io.ByteBuffer(len); const newBuffer = new ByteBuffer(len);
newBuffer.insertBytes(this.fileData); newBuffer.insertBytes(this.fileData);
this.fileData = newBuffer.data; this.fileData = newBuffer.data;
} else { } else {
@ -1402,7 +1402,7 @@ onmessage = function (event) {
// This is the very first time we have been called. Initialize the bytestream. // This is the very first time we have been called. Initialize the bytestream.
if (!bytestream) { if (!bytestream) {
bytestream = new bitjs.io.ByteStream(bytes); bytestream = new ByteStream(bytes);
currentFilename = ''; currentFilename = '';
currentFileNumber = 0; currentFileNumber = 0;

View file

@ -11,7 +11,7 @@
*/ */
// This file expects to be invoked as a Worker (see onmessage below). // 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 = { const UnarchiveState = {
NOT_STARTED: 0, NOT_STARTED: 0,
@ -166,7 +166,7 @@ onmessage = function (event) {
// This is the very first time we have been called. Initialize the bytestream. // This is the very first time we have been called. Initialize the bytestream.
if (!bytestream) { if (!bytestream) {
bytestream = new bitjs.io.ByteStream(bytes); bytestream = new ByteStream(bytes);
} else { } else {
bytestream.push(bytes); bytestream.push(bytes);
} }

View file

@ -13,9 +13,9 @@
*/ */
// This file expects to be invoked as a Worker (see onmessage below). // This file expects to be invoked as a Worker (see onmessage below).
importScripts('../io/bitstream-worker.js'); import { BitStream } from '../io/bitstream.js';
importScripts('../io/bytebuffer-worker.js'); import { ByteBuffer } from '../io/bytebuffer.js';
importScripts('../io/bytestream-worker.js'); import { ByteStream } from '../io/bytestream.js';
const UnarchiveState = { const UnarchiveState = {
NOT_STARTED: 0, NOT_STARTED: 0,
@ -74,7 +74,7 @@ const BIT = [0x01, 0x02, 0x04, 0x08,
class ZipLocalFile { class ZipLocalFile {
/** /**
* @param {bitjs.io.ByteStream} bstream * @param {ByteStream} bstream
*/ */
constructor(bstream) { constructor(bstream) {
if (typeof bstream != typeof {} || !bstream.readNumber || typeof bstream.readNumber != typeof function () { }) { 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 // Copy all the read bytes into a buffer and examine the last 16 bytes to see if they are the
// data descriptor. // data descriptor.
let bufferedByteArr = savedBstream.peekBytes(numBytesSeeked); 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 maybeDescriptorSig = descriptorStream.readNumber(4);
const maybeCrc32 = descriptorStream.readNumber(4); const maybeCrc32 = descriptorStream.readNumber(4);
const maybeCompressedSize = 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 * Extract one bit at a time until we find a matching Huffman Code
* then return that symbol. * then return that symbol.
* @param {bitjs.io.BitStream} bstream * @param {BitStream} bstream
* @param {Map<number, SymbolLengthPair>} hcTable * @param {Map<number, SymbolLengthPair>} hcTable
* @returns {number} * @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>} hcLiteralTable
* @param {Map<number, SymbolLengthPair>} hcDistanceTable * @param {Map<number, SymbolLengthPair>} hcDistanceTable
* @param {bitjs.io.ByteBuffer} buffer * @param {ByteBuffer} buffer
* @returns * @returns
*/ */
function inflateBlockData(bstream, hcLiteralTable, hcDistanceTable, buffer) { function inflateBlockData(bstream, hcLiteralTable, hcDistanceTable, buffer) {
@ -491,13 +491,13 @@ function inflateBlockData(bstream, hcLiteralTable, hcDistanceTable, buffer) {
*/ */
function inflate(compressedData, numDecompressedBytes) { function inflate(compressedData, numDecompressedBytes) {
// Bit stream representing the compressed data. // Bit stream representing the compressed data.
/** @type {bitjs.io.BitStream} */ /** @type {BitStream} */
const bstream = new bitjs.io.BitStream(compressedData.buffer, const bstream = new BitStream(compressedData.buffer,
false /* mtl */, false /* mtl */,
compressedData.byteOffset, compressedData.byteOffset,
compressedData.byteLength); compressedData.byteLength);
/** @type {bitjs.io.ByteBuffer} */ /** @type {ByteBuffer} */
const buffer = new bitjs.io.ByteBuffer(numDecompressedBytes); const buffer = new ByteBuffer(numDecompressedBytes);
let blockSize = 0; let blockSize = 0;
// block format: http://tools.ietf.org/html/rfc1951#page-9 // 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. // This is the very first time we have been called. Initialize the bytestream.
if (!bytestream) { if (!bytestream) {
bytestream = new bitjs.io.ByteStream(bytes); bytestream = new ByteStream(bytes);
} else { } else {
bytestream.push(bytes); bytestream.push(bytes);
} }

View file

@ -12,9 +12,7 @@
*/ */
// This file expects to be invoked as a Worker (see onmessage below). // This file expects to be invoked as a Worker (see onmessage below).
importScripts('../io/bitstream-worker.js'); import { ByteBuffer } from '../io/bytebuffer.js';
importScripts('../io/bytebuffer-worker.js');
importScripts('../io/bytestream-worker.js');
/** /**
* The client sends messages to this Worker containing files to archive in order. The client * 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 * @param {FileInfo} file
* @returns {bitjs.io.ByteBuffer} * @returns {ByteBuffer}
*/ */
function zipOneFile(file) { function zipOneFile(file) {
// Zip Local File Header has 30 bytes and then the filename and extrafields. // Zip Local File Header has 30 bytes and then the filename and extrafields.
const fileHeaderSize = 30 + file.fileName.length; const fileHeaderSize = 30 + file.fileName.length;
/** @type {bitjs.io.ByteBuffer} */ /** @type {ByteBuffer} */
const buffer = new bitjs.io.ByteBuffer(fileHeaderSize + file.fileData.length); const buffer = new ByteBuffer(fileHeaderSize + file.fileData.length);
buffer.writeNumber(zLocalFileHeaderSignature, 4); // Magic number. buffer.writeNumber(zLocalFileHeaderSignature, 4); // Magic number.
buffer.writeNumber(0x0A, 2); // Version. buffer.writeNumber(0x0A, 2); // Version.
@ -190,13 +188,13 @@ function zipOneFile(file) {
} }
/** /**
* @returns {bitjs.io.ByteBuffer} * @returns {ByteBuffer}
*/ */
function writeCentralFileDirectory() { function writeCentralFileDirectory() {
// Each central directory file header is 46 bytes + the filename. // Each central directory file header is 46 bytes + the filename.
let cdsLength = filesCompressed.map(f => f.fileName.length + 46).reduce((a, c) => a + c); let cdsLength = filesCompressed.map(f => f.fileName.length + 46).reduce((a, c) => a + c);
// 22 extra bytes for the end-of-central-dir header. // 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) { for (const cdInfo of centralDirectoryInfos) {
buffer.writeNumber(zCentralFileHeaderSignature, 4); // Magic number. 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: clean:
$(MAKE) -C io clean
$(MAKE) -C image/webp-shim clean $(MAKE) -C image/webp-shim clean
bitjs-io:
$(MAKE) -C io
# Make webp-shim/ # Make webp-shim/
bitjs-image-webp-shim: bitjs-image-webp-shim:
$(MAKE) -C image/webp-shim $(MAKE) -C image/webp-shim

View file

@ -1,7 +1,8 @@
# Build # Build
This folder contains files needed to build various pieces of the library. You only need to worry 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 ## Prerequisites
* Install Docker * 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, UnarchiveEvent, UnarchiveEventType, UnarchiveInfoEvent, UnarchiveErrorEvent,
UnarchiveStartEvent, UnarchiveFinishEvent, UnarchiveProgressEvent, UnarchiveExtractEvent, UnarchiveStartEvent, UnarchiveFinishEvent, UnarchiveProgressEvent, UnarchiveExtractEvent,
Unarchiver, Unzipper, Unrarrer, Untarrer, getUnarchiver Unarchiver, Unzipper, Unrarrer, Untarrer, getUnarchiver
} from './archive/archive.js'; } from './archive/decompress.js';
export { getFullMIMEString, getShortMIMEString } from './codecs/codecs.js'; export { getFullMIMEString, getShortMIMEString } from './codecs/codecs.js';
export { findMimeType } from './file/sniffer.js'; export { findMimeType } from './file/sniffer.js';
export { convertWebPtoPNG, convertWebPtoJPG } from './image/webp-shim/webp-shim.js'; export { convertWebPtoPNG, convertWebPtoJPG } from './image/webp-shim/webp-shim.js';
export { BitBuffer } from './io/bitbuffer.js';
export { BitStream } from './io/bitstream.js'; export { BitStream } from './io/bitstream.js';
export { ByteBuffer } from './io/bytebuffer.js'; export { ByteBuffer } from './io/bytebuffer.js';
export { ByteStream } from './io/bytestream.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 * Licensed under the MIT License
* *
* Copyright(c) 2021 Google Inc. * Copyright(c) 2023 Google Inc.
* Copyright(c) 2011 antimatter15
*/ */
(function () { const BITMASK = [
const BITMASK = [ 0,
0, 0b00000001,
0b00000001, 0b00000011,
0b00000011, 0b00000111,
0b00000111, 0b00001111,
0b00001111, 0b00011111,
0b00011111, 0b00111111,
0b00111111, 0b01111111,
0b01111111, 0b11111111,
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 { setPackingDirection(mtl = false) {
/** if (this.mtl !== mtl) {
* @param {number} numBytes The number of bytes to allocate. if (this.mtl && this.bitPtr !== 7) {
* @param {boolean} mtl The bit-packing mode. True means pack bits from most-significant (7) to this.bytePtr++;
* least-significant (0). Defaults false: least-significant (0) to most-significant (8). if (this.bytePtr >= this.data.byteLength) {
*/ throw `No more bytes left when switching packing direction`;
constructor(numBytes, mtl = false) { }
if (typeof numBytes != typeof 1 || numBytes <= 0) { this.bitPtr = 7;
throw "Error! ByteBuffer initialized with '" + numBytes + "'"; } 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} */ this.mtl = mtl;
getPackingDirection() { }
return this.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;
* Sets the bit-packing direction. Default (false) is least-significant-bit (0) to const writtenBits = this.bytePtr * 8 + this.bitPtr;
* most-significant (7). Changing the bit-packing direction when the bit pointer is in the const bitsLeftInBuffer = totalBitsInBuffer - writtenBits;
* middle of a byte will fill the rest of that byte with 0s using the current bit-packing if (numBits > bitsLeftInBuffer) {
* direction and then set the bit pointer to the appropriate bit of the next byte. If there throw `Trying to write ${numBits} into the BitBuffer that only has ${bitsLeftInBuffer}`;
* are no more bytes left in this buffer, it will throw an error. }
*/
setPackingDirection(mtl = false) { // Least-to-most-significant bit packing method (LTM).
if (this.mtl !== mtl) { if (!this.mtl) {
if (this.mtl && this.bitPtr !== 7) { let numBitsLeftToWrite = numBits;
this.bytePtr++; while (numBitsLeftToWrite > 0) {
if (this.bytePtr >= this.data.byteLength) { /** The number of bits available to fill in this byte. */
throw `No more bytes left when switching packing direction`; 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++; this.bytePtr++;
if (this.bytePtr >= this.data.byteLength) {
throw `No more bytes left when switching packing direction`;
}
this.bitPtr = 0; this.bitPtr = 0;
} }
// Remove bits that have been written from LSB end.
val >>= numBitsToWriteIntoThisByte;
numBitsLeftToWrite -= numBitsToWriteIntoThisByte;
} }
this.mtl = mtl;
} }
// Most-to-least-significant bit packing method (MTL).
/** else {
* writeBits(3, 6) is the same as writeBits(0b000011, 6). let numBitsLeftToWrite = numBits;
* Will throw an error (without writing) if this would over-flow the buffer. while (numBitsLeftToWrite > 0) {
* @param {number} val The bits to pack into the buffer. Negative values are not allowed. /** The number of bits available to fill in this byte. */
* @param {number} numBits Must be positive, non-zero and less or equal to than 53, since const bitCapacityInThisByte = this.bitPtr + 1;
* JavaScript can only support 53-bit integers. /** The number of bits of val we will write into this byte. */
*/ const numBitsToWriteIntoThisByte = Math.min(numBitsLeftToWrite, bitCapacityInThisByte);
writeBits(val, numBits) { /** The number of bits that fit in subsequent bytes. */
if (val < 0 || typeof val !== typeof 1) { const numExcessBits = numBitsLeftToWrite - numBitsToWriteIntoThisByte;
throw `Trying to write an invalid value into the BitBuffer: ${val}`; if (numExcessBits < 0) {
} throw `Error in MTL bit packing, # of excess bits is negative`;
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;
} }
} /** The actual bits that need to be written into this byte. Starts at MSB. */
// Most-to-least-significant bit packing method (MTL). let actualBitsToWrite = ((val >> numExcessBits) & BITMASK[numBitsToWriteIntoThisByte]);
else { // Only adjust and write bits if any are set to 1.
let numBitsLeftToWrite = numBits; if (actualBitsToWrite > 0) {
while (numBitsLeftToWrite > 0) { // If the number of bits left to write do not fill up this byte, we need to shift these
/** The number of bits available to fill in this byte. */ // bits to the left so they are written into the proper place in the buffer.
const bitCapacityInThisByte = this.bitPtr + 1; if (numBitsLeftToWrite < bitCapacityInThisByte) {
/** The number of bits of val we will write into this byte. */ actualBitsToWrite <<= (bitCapacityInThisByte - numBitsLeftToWrite);
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. */ // Now write into the buffer.
let actualBitsToWrite = ((val >> numExcessBits) & BITMASK[numBitsToWriteIntoThisByte]); this.data[this.bytePtr] |= actualBitsToWrite;
// 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;
} }
// 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. * Provides readers for bitstreams.
* *
* Licensed under the MIT License * Licensed under the MIT License
* *
* Copyright(c) 2011 Google Inc. * Copyright(c) 2023 Google Inc.
* Copyright(c) 2011 antimatter15 * Copyright(c) 2011 antimatter15
*/ */
(function () { // mask for getting N number of bits (0-8)
// mask for getting N number of bits (0-8) const BITMASK = [0, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF];
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. * @param {ArrayBuffer} ab An ArrayBuffer object or a Uint8Array.
* Note that this stream is optimized, and thus, will *NOT* throw an error if * @param {boolean} mtl Whether the stream reads bits from the byte starting with the
* the end of the stream is reached. Only use this in scenarios where you * most-significant-bit (bit 7) to least-significant (bit 0). False means the direction is
* already have all the bits you need. * from least-significant-bit (bit 0) to most-significant (bit 7).
* * @param {Number} opt_offset The offset into the ArrayBuffer
* Bit reading always proceeds from the first byte in the buffer, to the * @param {Number} opt_length The length of this BitStream
* 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 { constructor(ab, mtl, opt_offset, opt_length) {
/** if (!(ab instanceof ArrayBuffer)) {
* @param {ArrayBuffer} ab An ArrayBuffer object or a Uint8Array. throw 'Error! BitArray constructed with an invalid ArrayBuffer object';
* @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;
} }
/** const offset = opt_offset || 0;
* Returns how many bites have been read in the stream since the beginning of time. const length = opt_length || ab.byteLength;
* @returns {number}
*/
getNumBitsRead() {
return this.bitsRead_;
}
/** /**
* Returns how many bits are currently in the stream left to be read. * The bytes in the stream.
* @returns {number} * @type {Uint8Array}
* @private
*/ */
getNumBitsLeft() { this.bytes = new Uint8Array(ab, offset, length);
const bitsLeftInByte = 8 - this.bitPtr;
return (this.bytes.byteLength - this.bytePtr - 1) * 8 + bitsLeftInByte;
}
/** /**
* byte0 byte1 byte2 byte3 * The byte in the stream that we are currently on.
* 7......0 | 7......0 | 7......0 | 7......0 * @type {Number}
* * @private
* 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) { this.bytePtr = 0;
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 * The bit in the current byte that we will read next (can have values 0 through 7).
* 7......0 | 7......0 | 7......0 | 7......0 * @type {Number}
* * @private
* 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) { this.bitPtr = 0; // tracks which bit we are on (can have values 0 through 7)
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. * An ever-increasing number.
* Bit at (bytePtr,bitPtr) has the highest position in returning data. * @type {Number}
* Taken from getbits.hpp in unrar. * @private
* TODO: Move this out of BitStream and into unrar.
* @returns {number}
*/ */
getBits() { this.bitsRead_ = 0;
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.peekBits = mtl ? this.peekBits_mtl : this.peekBits_ltm;
* 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; /**
})(); * 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. * Provides a writer for bytes.
* *
* Licensed under the MIT License * Licensed under the MIT License
* *
* Copyright(c) 2011 Google Inc. * Copyright(c) 2023 Google Inc.
* Copyright(c) 2011 antimatter15 * 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 { constructor(numBytes) {
/** if (typeof numBytes != typeof 1 || numBytes <= 0) {
* @param {number} numBytes The number of bytes to allocate. throw "Error! ByteBuffer initialized with '" + numBytes + "'";
*/
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. * @type {Uint8Array}
* @public
*/ */
insertBytes(bytes) { this.data = new Uint8Array(numBytes);
// 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 * @type {number}
* to fit into n bytes or is negative, an error is thrown. * @public
* @param {number} num The unsigned number to write.
* @param {number} numBytes The number of bytes to write the number into.
*/ */
writeNumber(num, numBytes) { this.ptr = 0;
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;
})(); /**
* @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. * Provides readers for byte streams.
* *
* Licensed under the MIT License * Licensed under the MIT License
* *
* Copyright(c) 2011 Google Inc. * Copyright(c) 2023 Google Inc.
* Copyright(c) 2011 antimatter15 * 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 * @param {ArrayBuffer} ab The ArrayBuffer object.
* of a stream. More bytes can be pushed into the back of the stream via the * @param {number=} opt_offset The offset into the ArrayBuffer
* push() method. * @param {number=} opt_length The length of this ByteStream
*/ */
class ByteStream { constructor(ab, opt_offset, opt_length) {
/** if (!(ab instanceof ArrayBuffer)) {
* @param {ArrayBuffer} ab The ArrayBuffer object. throw 'Error! BitArray constructed with an invalid 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;
} }
/** const offset = opt_offset || 0;
* Returns how many bytes have been read in the stream since the beginning of time. const length = opt_length || ab.byteLength;
*/
getNumBytesRead() {
return this.bytesRead_;
}
/** /**
* Returns how many bytes are currently in the stream left to be read. * The current page of bytes in the stream.
*/ * @type {Uint8Array}
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 * @private
*/ */
movePointer_(n) { this.bytes = new Uint8Array(ab, offset, length);
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 * The next pages of bytes in the stream.
* pointer. * @type {Array<Uint8Array>}
* @param {number} n The number of bytes to peek at. Must be a positive integer. * @private
* @returns {number} The n bytes interpreted as an unsigned number.
*/ */
peekNumber(n) { this.pages_ = [];
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) * The byte in the current page that we will read next.
* and advances the stream pointer n bytes. * @type {Number}
* @param {number} n The number of bytes to read. Must be a positive integer. * @private
* @returns {number} The n bytes interpreted as an unsigned number.
*/ */
readNumber(n) { this.ptr = 0;
const num = this.peekNumber(n);
this.movePointer_(n);
return num;
}
/** /**
* Returns the next n bytes as a signed number but does not advance the * An ever-increasing number.
* pointer. * @type {Number}
* @param {number} n The number of bytes to read. Must be a positive integer. * @private
* @returns {number} The bytes interpreted as a signed number.
*/ */
peekSignedNumber(n) { this.bytesRead_ = 0;
let num = this.peekNumber(n); }
const HALF = Math.pow(2, (n * 8) - 1);
const FULL = HALF * 2;
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);
}
/**
/** * Move the pointer ahead n bytes. If the pointer is at the end of the current array
* Returns the next n bytes as a signed number and advances the stream pointer. * of bytes and we have another page of bytes, point at the new page. This is a private
* @param {number} n The number of bytes to read. Must be a positive integer. * method, no validation is done.
* @returns {number} The bytes interpreted as a signed number. * @param {number} n Number of bytes to increment.
*/ * @private
readSignedNumber(n) { */
const num = this.peekSignedNumber(n); movePointer_(n) {
this.movePointer_(n); this.ptr += n;
return num; this.bytesRead_ += n;
} while (this.ptr >= this.bytes.length && this.pages_.length > 0) {
this.ptr -= this.bytes.length;
this.bytes = this.pages_.shift();
/**
* 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; /**
})(); * 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", "name": "@codedread/bitjs",
"version": "1.0.11", "version": "1.1.0",
"description": "Binary Tools for JavaScript", "description": "Binary Tools for JavaScript",
"homepage": "https://github.com/codedread/bitjs", "homepage": "https://github.com/codedread/bitjs",
"author": "Jeff Schiller", "author": "Jeff Schiller",