1
0
Fork 0
mirror of https://github.com/codedread/bitjs synced 2025-10-03 17:49:16 +02:00

Make ByteStream and ByteBuffer into modules with Worker veresions via build for Issue #16

This commit is contained in:
codedread 2020-04-02 00:07:14 -07:00
parent a457564ff0
commit ab5f6fc6d2
16 changed files with 1308 additions and 410 deletions

View file

@ -11,9 +11,10 @@ A set of tools to handle binary data in JS (using Typed Arrays).
This namespace includes stream objects for reading and writing binary data at the bit and byte level: BitStream, ByteStream.
```javascript
var bstream = new bitjs.io.BitStream(someArrayBuffer, true, offset, length);
var crc = bstream.readBits(12); // read in 12 bits as CRC, advancing the pointer
var flagbits = bstream.peekBits(6); // look ahead at next 6 bits, but do not advance the pointer
import {BitStream} from './bitjs/io/bitstream.js';
const bstream = new BitStream(someArrayBuffer, true, offset, length);
const crc = bstream.readBits(12); // read in 12 bits as CRC, advancing the pointer
const flagbits = bstream.peekBits(6); // look ahead at next 6 bits, but do not advance the pointer
```
### bitjs.archive
@ -21,7 +22,7 @@ var flagbits = bstream.peekBits(6); // look ahead at next 6 bits, but do not adv
This namespace includes objects for unarchiving binary data in popular archive formats (zip, rar, tar) providing unzip, unrar and untar capabilities via JavaScript in the browser. The unarchiving actually happens inside a Web Worker.
```javascript
var unzipper = new bitjs.archive.Unzipper(zipFileArrayBuffer);
const unzipper = new bitjs.archive.Unzipper(zipFileArrayBuffer);
unzipper.addEventListener('progress', updateProgress);
unzipper.addEventListener('extract', receiveOneFile);
unzipper.addEventListener('finish', displayZipContents);
@ -44,7 +45,7 @@ function displayZipContents() {
The unarchivers also support streaming, if you are receiving the zipped file from a slow place (a Cloud API, for instance). For example:
```javascript
var unzipper = new bitjs.archive.Unzipper(anArrayBufferWithStartingBytes);
const unzipper = new bitjs.archive.Unzipper(anArrayBufferWithStartingBytes);
unzipper.addEventListener('progress', updateProgress);
unzipper.addEventListener('extract', receiveOneFile);
unzipper.addEventListener('finish', displayZipContents);
@ -78,7 +79,7 @@ convertWebPtoPNG(webpBuffer).then(pngBuf => {
This namespace includes code for dealing with files. It includes a sniffer which detects the type of file, given an ArrayBuffer.
```javascript
let mimeType = bitjs.file.findMimeType(someArrayBuffer);
const mimeType = bitjs.file.findMimeType(someArrayBuffer);
```
## Tests

View file

@ -13,7 +13,7 @@
// This file expects to be invoked as a Worker (see onmessage below).
importScripts('../io/bitstream-worker.js');
importScripts('../io/bytestream.js');
importScripts('../io/bytestream-worker.js');
importScripts('../io/bytebuffer.js');
importScripts('archive.js');
importScripts('rarvm.js');

View file

@ -11,7 +11,7 @@
*/
// This file expects to be invoked as a Worker (see onmessage below).
importScripts('../io/bytestream.js');
importScripts('../io/bytestream-worker.js');
importScripts('archive.js');
const UnarchiveState = {

View file

@ -15,7 +15,7 @@
// This file expects to be invoked as a Worker (see onmessage below).
importScripts('../io/bitstream-worker.js');
importScripts('../io/bytebuffer.js');
importScripts('../io/bytestream.js');
importScripts('../io/bytestream-worker.js');
importScripts('archive.js');
const UnarchiveState = {

View file

@ -1,12 +1,31 @@
OUT_PATH=/out/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
BYTEBUFFER_MODULE=${OUT_PATH}/bytebuffer.js
BYTEBUFFER_WORKER=${OUT_PATH}/bytebuffer-worker.js
BITSTREAM_DEF=bitstream-def.js
BYTESTREAM_DEF=bytestream-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}
all: ${BITSTREAM_MODULE} ${BITSTREAM_WORKER} \
${BYTESTREAM_MODULE} ${BYTESTREAM_WORKER} \
${BYTEBUFFER_MODULE} ${BYTEBUFFER_WORKER}
clean:
rm -rf ${BITSTREAM_MODULE}
rm -rf ${BITSTREAM_WORKER}
rm -rf ${BYTESTREAM_MODULE}
rm -rf ${BYTESTREAM_WORKER}
rm -rf ${BYTEBUFFER_MODULE}
rm -rf ${BYTEBUFFER_WORKER}
${BITSTREAM_MODULE}: Makefile ${BITSTREAM_DEF}
echo ${DISCLAIMER} > ${BITSTREAM_MODULE}
@ -20,6 +39,26 @@ ${BITSTREAM_WORKER}: Makefile ${BITSTREAM_DEF}
echo "bitjs.io.BitStream =" >> ${BITSTREAM_WORKER}
cat ${BITSTREAM_DEF} >> ${BITSTREAM_WORKER}
clean:
rm -rf ${BITSTREAM_MODULE}
rm -rf ${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}
${BYTEBUFFER_MODULE}: Makefile ${BYTEBUFFER_DEF}
echo ${DISCLAIMER} > ${BYTEBUFFER_MODULE}
echo "export const ByteBuffer =" >> ${BYTEBUFFER_MODULE}
cat ${BYTEBUFFER_DEF} >> ${BYTEBUFFER_MODULE}
${BYTEBUFFER_WORKER}: Makefile ${BYTEBUFFER_DEF}
echo ${DISCLAIMER} > ${BYTEBUFFER_WORKER}
echo "var bitjs = bitjs || {};" >> ${BYTEBUFFER_WORKER}
echo "bitjs.io = bitjs.io || {};" >> ${BYTEBUFFER_WORKER}
echo "bitjs.io.ByteBuffer =" >> ${BYTEBUFFER_WORKER}
cat ${BYTEBUFFER_DEF} >> ${BYTEBUFFER_WORKER}

View file

@ -283,5 +283,4 @@
}
return BitStream;
})();

117
build/io/bytebuffer-def.js Normal file
View file

@ -0,0 +1,117 @@
/*
* 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 + "'";
}
this.data = new Uint8Array(numBytes);
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;
})();

308
build/io/bytestream-def.js Normal file
View file

@ -0,0 +1,308 @@
/*
* 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.
* @return {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.
* @return {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.
* @return {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.
* @return {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.
* @return {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.
* @return {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.
* @return {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.
* @return {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.
* @return {ByteStream} A clone of this ByteStream.
*/
tee() {
const clone = new ByteStream(this.bytes.buffer);
clone.bytes = this.bytes;
clone.ptr = this.ptr;
clone.pages_ = this.pages_.slice();
clone.bytesRead_ = this.bytesRead_;
return clone;
}
}
return ByteStream;
})();

View file

@ -287,5 +287,4 @@ bitjs.io.BitStream =
}
return BitStream;
})();

View file

@ -285,5 +285,4 @@ export const BitStream =
}
return BitStream;
})();

121
io/bytebuffer-worker.js Normal file
View file

@ -0,0 +1,121 @@
// 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 + "'";
}
this.data = new Uint8Array(numBytes);
this.ptr = 0;
}
/**
* @param {number} b The byte to insert.
*/
insertByte(b) {
// TODO: throw if byte is invalid?
this.data[this.ptr++] = b;
}
/**
* @param {Array.<number>|Uint8Array|Int8Array} bytes The bytes to insert.
*/
insertBytes(bytes) {
// TODO: throw if bytes is invalid?
this.data.set(bytes, this.ptr);
this.ptr += bytes.length;
}
/**
* Writes an unsigned number into the next n bytes. If the number is too large
* to fit into n bytes or is negative, an error is thrown.
* @param {number} num The unsigned number to write.
* @param {number} numBytes The number of bytes to write the number into.
*/
writeNumber(num, numBytes) {
if (numBytes < 1 || !numBytes) {
throw 'Trying to write into too few bytes: ' + numBytes;
}
if (num < 0) {
throw 'Trying to write a negative number (' + num +
') as an unsigned number to an ArrayBuffer';
}
if (num > (Math.pow(2, numBytes * 8) - 1)) {
throw 'Trying to write ' + num + ' into only ' + numBytes + ' bytes';
}
// Roll 8-bits at a time into an array of bytes.
const bytes = [];
while (numBytes-- > 0) {
const eightBits = num & 255;
bytes.push(eightBits);
num >>= 8;
}
this.insertBytes(bytes);
}
/**
* Writes a signed number into the next n bytes. If the number is too large
* to fit into n bytes, an error is thrown.
* @param {number} num The signed number to write.
* @param {number} numBytes The number of bytes to write the number into.
*/
writeSignedNumber(num, numBytes) {
if (numBytes < 1) {
throw 'Trying to write into too few bytes: ' + numBytes;
}
const HALF = Math.pow(2, (numBytes * 8) - 1);
if (num >= HALF || num < -HALF) {
throw 'Trying to write ' + num + ' into only ' + numBytes + ' bytes';
}
// Roll 8-bits at a time into an array of bytes.
const bytes = [];
while (numBytes-- > 0) {
const eightBits = num & 255;
bytes.push(eightBits);
num >>= 8;
}
this.insertBytes(bytes);
}
/**
* @param {string} str The ASCII string to write.
*/
writeASCIIString(str) {
for (let i = 0; i < str.length; ++i) {
const curByte = str.charCodeAt(i);
if (curByte < 0 || curByte > 255) {
throw 'Trying to write a non-ASCII string!';
}
this.insertByte(curByte);
}
};
}
return ByteBuffer;
})();

View file

@ -1,5 +1,7 @@
// THIS IS A GENERATED FILE! DO NOT EDIT, INSTEAD EDIT THE FILE IN bitjs/build/io.
export const ByteBuffer =
/*
* bytestream.js
* bytebuffer-def.js
*
* Provides a writer for bytes.
*
@ -9,14 +11,11 @@
* Copyright(c) 2011 antimatter15
*/
var bitjs = bitjs || {};
bitjs.io = bitjs.io || {};
(function () {
/**
* A write-only Byte buffer which uses a Uint8 Typed Array as a backing store.
*/
bitjs.io.ByteBuffer = class {
class ByteBuffer {
/**
* @param {number} numBytes The number of bytes to allocate.
*/
@ -115,3 +114,6 @@ bitjs.io.ByteBuffer = class {
}
};
}
return ByteBuffer;
})();

312
io/bytestream-worker.js Normal file
View file

@ -0,0 +1,312 @@
// 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.
* @return {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.
* @return {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.
* @return {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.
* @return {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.
* @return {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.
* @return {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.
* @return {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.
* @return {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.
* @return {ByteStream} A clone of this ByteStream.
*/
tee() {
const clone = new ByteStream(this.bytes.buffer);
clone.bytes = this.bytes;
clone.ptr = this.ptr;
clone.pages_ = this.pages_.slice();
clone.bytesRead_ = this.bytesRead_;
return clone;
}
}
return ByteStream;
})();

View file

@ -1,5 +1,7 @@
// THIS IS A GENERATED FILE! DO NOT EDIT, INSTEAD EDIT THE FILE IN bitjs/build/io.
export const ByteStream =
/*
* bytestream.js
* bytestream-def.js
*
* Provides readers for byte streams.
*
@ -9,20 +11,17 @@
* Copyright(c) 2011 antimatter15
*/
var bitjs = bitjs || {};
bitjs.io = bitjs.io || {};
(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.
*/
bitjs.io.ByteStream = class {
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 BitStream
* @param {number=} opt_length The length of this ByteStream
*/
constructor(ab, opt_offset, opt_length) {
if (!(ab instanceof ArrayBuffer)) {
@ -295,10 +294,10 @@ bitjs.io.ByteStream = class {
/**
* Creates a new ByteStream from this ByteStream that can be read / peeked.
* @return {bitjs.io.ByteStream} A clone of this ByteStream.
* @return {ByteStream} A clone of this ByteStream.
*/
tee() {
const clone = new bitjs.io.ByteStream(this.bytes.buffer);
const clone = new ByteStream(this.bytes.buffer);
clone.bytes = this.bytes;
clone.ptr = this.ptr;
clone.pages_ = this.pages_.slice();
@ -306,3 +305,6 @@ bitjs.io.ByteStream = class {
return clone;
}
}
return ByteStream;
})();

View file

@ -3,9 +3,8 @@
<head>
<title>Unit tests for bitjs.io.ByteBufferBitStream</title>
<script src="muther.js"></script>
<script src="../io/bitstream.js"></script>
<script src="../io/bytebuffer.js"></script>
<script src="../io/bytestream.js"></script>
<script src="../io/bytebuffer-worker.js"></script>
<script src="../io/bytestream-worker.js"></script>
</head>
<body>
<script>

View file

@ -3,7 +3,7 @@
<head>
<title>Unit tests for bitjs.io.ByteStreamBitStream</title>
<script src="muther.js"></script>
<script src="../io/bytestream.js"></script>
<script src="../io/bytestream-worker.js"></script>
</head>
<body>
<script>