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:
parent
fce8d69612
commit
63e84dadb0
26 changed files with 916 additions and 2965 deletions
28
CHANGELOG.md
Normal file
28
CHANGELOG.md
Normal 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.
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
|
@ -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().
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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}
|
|
|
@ -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;
|
|
||||||
})();
|
|
|
@ -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;
|
|
||||||
})();
|
|
|
@ -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;
|
|
||||||
})();
|
|
|
@ -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;
|
|
||||||
})();
|
|
3
index.js
3
index.js
|
@ -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';
|
||||||
|
|
|
@ -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.
|
|
|
@ -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;
|
|
||||||
})();
|
|
|
@ -1,17 +1,15 @@
|
||||||
// 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,
|
||||||
|
@ -21,12 +19,12 @@ export const BitBuffer =
|
||||||
0b00111111,
|
0b00111111,
|
||||||
0b01111111,
|
0b01111111,
|
||||||
0b11111111,
|
0b11111111,
|
||||||
]
|
]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A write-only Bit buffer which uses a Uint8Array as a backing store.
|
* A write-only Bit buffer which uses a Uint8Array as a backing store.
|
||||||
*/
|
*/
|
||||||
class BitBuffer {
|
export class BitBuffer {
|
||||||
/**
|
/**
|
||||||
* @param {number} numBytes The number of bytes to allocate.
|
* @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
|
* @param {boolean} mtl The bit-packing mode. True means pack bits from most-significant (7) to
|
||||||
|
@ -196,7 +194,4 @@ export const BitBuffer =
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return BitBuffer;
|
|
||||||
})();
|
|
||||||
|
|
|
@ -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;
|
|
||||||
})();
|
|
|
@ -1,21 +1,18 @@
|
||||||
// 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.
|
* 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
|
* 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
|
* the end of the stream is reached. Only use this in scenarios where you
|
||||||
|
@ -43,7 +40,7 @@ export const BitStream =
|
||||||
* - readBits(5) => 21 ('10101')
|
* - readBits(5) => 21 ('10101')
|
||||||
* - readBits(6) => 16 ('010000')
|
* - readBits(6) => 16 ('010000')
|
||||||
*/
|
*/
|
||||||
class BitStream {
|
export class BitStream {
|
||||||
/**
|
/**
|
||||||
* @param {ArrayBuffer} ab An ArrayBuffer object or a Uint8Array.
|
* @param {ArrayBuffer} ab An ArrayBuffer object or a Uint8Array.
|
||||||
* @param {boolean} mtl Whether the stream reads bits from the byte starting with the
|
* @param {boolean} mtl Whether the stream reads bits from the byte starting with the
|
||||||
|
@ -308,7 +305,4 @@ export const BitStream =
|
||||||
readBytes(n) {
|
readBytes(n) {
|
||||||
return this.peekBytes(n, true);
|
return this.peekBytes(n, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return BitStream;
|
|
||||||
})();
|
|
||||||
|
|
|
@ -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;
|
|
||||||
})();
|
|
|
@ -1,21 +1,18 @@
|
||||||
// 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.
|
* A write-only Byte buffer which uses a Uint8 Typed Array as a backing store.
|
||||||
*/
|
*/
|
||||||
class ByteBuffer {
|
export class ByteBuffer {
|
||||||
/**
|
/**
|
||||||
* @param {number} numBytes The number of bytes to allocate.
|
* @param {number} numBytes The number of bytes to allocate.
|
||||||
*/
|
*/
|
||||||
|
@ -123,7 +120,4 @@ export const ByteBuffer =
|
||||||
this.insertByte(curByte);
|
this.insertByte(curByte);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ByteBuffer;
|
|
||||||
})();
|
|
||||||
|
|
|
@ -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;
|
|
||||||
})();
|
|
|
@ -1,23 +1,20 @@
|
||||||
// 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
|
* 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
|
* of a stream. More bytes can be pushed into the back of the stream via the
|
||||||
* push() method.
|
* push() method.
|
||||||
*/
|
*/
|
||||||
class ByteStream {
|
export class ByteStream {
|
||||||
/**
|
/**
|
||||||
* @param {ArrayBuffer} ab The ArrayBuffer object.
|
* @param {ArrayBuffer} ab The ArrayBuffer object.
|
||||||
* @param {number=} opt_offset The offset into the ArrayBuffer
|
* @param {number=} opt_offset The offset into the ArrayBuffer
|
||||||
|
@ -304,7 +301,4 @@ export const ByteStream =
|
||||||
clone.bytesRead_ = this.bytesRead_;
|
clone.bytesRead_ = this.bytesRead_;
|
||||||
return clone;
|
return clone;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ByteStream;
|
|
||||||
})();
|
|
||||||
|
|
|
@ -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",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue