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

Add a BitBuffer to the io package and a few tests

This commit is contained in:
Jeff Schiller 2021-12-20 20:35:59 -08:00
parent 596a859bdc
commit 24b1603968
12 changed files with 747 additions and 25 deletions

View file

@ -6,17 +6,22 @@ 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:
@ -24,6 +29,8 @@ clean:
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}
@ -51,6 +58,18 @@ ${BYTESTREAM_WORKER}: Makefile ${BYTESTREAM_DEF}
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}

200
build/io/bitbuffer-def.js Normal file
View file

@ -0,0 +1,200 @@
/*
* bytebuffer-def.js
*
* Provides a writer for bits.
*
* Licensed under the MIT License
*
* Copyright(c) 2021 Google Inc.
*/
(function () {
const BITMASK = [
0,
0b00000001,
0b00000011,
0b00000111,
0b00001111,
0b00011111,
0b00111111,
0b01111111,
0b11111111,
]
/**
* A write-only Bit buffer which uses a Uint8Array as a backing store.
*/
class BitBuffer {
/**
* @param {number} numBytes The number of bytes to allocate.
* @param {boolean} mtl The bit-packing mode. True means pack bits from most-significant (7) to
* least-significant (0). Defaults false: least-significant (0) to most-significant (8).
*/
constructor(numBytes, mtl = false) {
if (typeof numBytes != typeof 1 || numBytes <= 0) {
throw "Error! ByteBuffer initialized with '" + numBytes + "'";
}
/**
* @type {Uint8Array}
* @public
*/
this.data = new Uint8Array(numBytes);
/**
* Whether we pack bits from most-significant-bit to least. Defaults false (least-to-most
* significant bit packing).
* @type {boolean}
* @private
*/
this.mtl = mtl;
/**
* The current byte we are filling with bits.
* @type {number}
* @public
*/
this.bytePtr = 0;
/**
* Points at the bit within the current byte where the next bit will go. This number ranges
* from 0 to 7 and the direction of packing is indicated by the mtl property.
* @type {number}
* @public
*/
this.bitPtr = this.mtl ? 7 : 0;
}
/** @returns {boolean} */
getPackingDirection() {
return this.mtl;
}
/**
* Sets the bit-packing direction. Default (false) is least-significant-bit (0) to
* most-significant (7). Changing the bit-packing direction when the bit pointer is in the
* middle of a byte will fill the rest of that byte with 0s using the current bit-packing
* direction and then set the bit pointer to the appropriate bit of the next byte. If there
* are no more bytes left in this buffer, it will throw an error.
*/
setPackingDirection(mtl = false) {
if (this.mtl !== mtl) {
if (this.mtl && this.bitPtr !== 7) {
this.bytePtr++;
if (this.bytePtr >= this.data.byteLength) {
throw `No more bytes left when switching packing direction`;
}
this.bitPtr = 7;
} else if (!this.mtl && this.bitPtr !== 0) {
this.bytePtr++;
if (this.bytePtr >= this.data.byteLength) {
throw `No more bytes left when switching packing direction`;
}
this.bitPtr = 0;
}
}
this.mtl = mtl;
}
/**
* writeBits(3, 6) is the same as writeBits(0b000011, 6).
* Will throw an error (without writing) if this would over-flow the buffer.
* @param {number} val The bits to pack into the buffer. Negative values are not allowed.
* @param {number} numBits Must be positive, non-zero and less or equal to than 53, since
* JavaScript can only support 53-bit integers.
*/
writeBits(val, numBits) {
if (val < 0 || typeof val !== typeof 1) {
throw `Trying to write an invalid value into the BitBuffer: ${val}`;
}
if (numBits < 0 || numBits > 53) {
throw `Trying to write ${numBits} bits into the BitBuffer`;
}
const totalBitsInBuffer = this.data.byteLength * 8;
const writtenBits = this.bytePtr * 8 + this.bitPtr;
const bitsLeftInBuffer = totalBitsInBuffer - writtenBits;
if (numBits > bitsLeftInBuffer) {
throw `Trying to write ${numBits} into the BitBuffer that only has ${bitsLeftInBuffer}`;
}
// Least-to-most-significant bit packing method (LTM).
if (!this.mtl) {
let numBitsLeftToWrite = numBits;
while (numBitsLeftToWrite > 0) {
/** The number of bits available to fill in this byte. */
const bitCapacityInThisByte = 8 - this.bitPtr;
/** The number of bits of val we will write into this byte. */
const numBitsToWriteIntoThisByte = Math.min(numBitsLeftToWrite, bitCapacityInThisByte);
/** The number of bits that fit in subsequent bytes. */
const numExcessBits = numBitsLeftToWrite - numBitsToWriteIntoThisByte;
if (numExcessBits < 0) {
throw `Error in LTM bit packing, # of excess bits is negative`;
}
/** The actual bits that need to be written into this byte. Starts at LSB. */
let actualBitsToWrite = (val & BITMASK[numBitsToWriteIntoThisByte]);
// Only adjust and write bits if any are set to 1.
if (actualBitsToWrite > 0) {
actualBitsToWrite <<= this.bitPtr;
// Now write into the buffer.
this.data[this.bytePtr] |= actualBitsToWrite;
}
// Update the bit/byte pointers and remaining bits to write.
this.bitPtr += numBitsToWriteIntoThisByte;
if (this.bitPtr > 7) {
if (this.bitPtr !== 8) {
throw `Error in LTM bit packing. Tried to write more bits than it should have.`;
}
this.bytePtr++;
this.bitPtr = 0;
}
// Remove bits that have been written from LSB end.
val >>= numBitsToWriteIntoThisByte;
numBitsLeftToWrite -= numBitsToWriteIntoThisByte;
}
}
// Most-to-least-significant bit packing method (MTL).
else {
let numBitsLeftToWrite = numBits;
while (numBitsLeftToWrite > 0) {
/** The number of bits available to fill in this byte. */
const bitCapacityInThisByte = this.bitPtr + 1;
/** The number of bits of val we will write into this byte. */
const numBitsToWriteIntoThisByte = Math.min(numBitsLeftToWrite, bitCapacityInThisByte);
/** The number of bits that fit in subsequent bytes. */
const numExcessBits = numBitsLeftToWrite - numBitsToWriteIntoThisByte;
if (numExcessBits < 0) {
throw `Error in MTL bit packing, # of excess bits is negative`;
}
/** The actual bits that need to be written into this byte. Starts at MSB. */
let actualBitsToWrite = ((val >> numExcessBits) & BITMASK[numBitsToWriteIntoThisByte]);
// Only adjust and write bits if any are set to 1.
if (actualBitsToWrite > 0) {
// If the number of bits left to write do not fill up this byte, we need to shift these
// bits to the left so they are written into the proper place in the buffer.
if (numBitsLeftToWrite < bitCapacityInThisByte) {
actualBitsToWrite <<= (bitCapacityInThisByte - numBitsLeftToWrite);
}
// Now write into the buffer.
this.data[this.bytePtr] |= actualBitsToWrite;
}
// Update the bit/byte pointers and remaining bits to write
this.bitPtr -= numBitsToWriteIntoThisByte;
if (this.bitPtr < 0) {
if (this.bitPtr !== -1) {
throw `Error in MTL bit packing. Tried to write more bits than it should have.`;
}
this.bytePtr++;
this.bitPtr = 7;
}
// Remove bits that have been written from MSB end.
val -= (actualBitsToWrite << numExcessBits);
numBitsLeftToWrite -= numBitsToWriteIntoThisByte;
}
}
}
}
return BitBuffer;
})();

View file

@ -22,12 +22,13 @@
class BitStream {
/**
* @param {ArrayBuffer} ab An ArrayBuffer object or a Uint8Array.
* @param {boolean} rtl Whether the stream reads bits from the byte starting
* from bit 7 to 0 (true) or bit 0 to 7 (false).
* @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 direciton 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, rtl, opt_offset, opt_length) {
constructor(ab, mtl, opt_offset, opt_length) {
if (!(ab instanceof ArrayBuffer)) {
throw 'Error! BitArray constructed with an invalid ArrayBuffer object';
}
@ -63,7 +64,7 @@
*/
this.bitsRead_ = 0;
this.peekBits = rtl ? this.peekBits_rtl : this.peekBits_ltr;
this.peekBits = mtl ? this.peekBits_mtl : this.peekBits_ltm;
}
/**
@ -85,13 +86,13 @@
* byte0 byte1 byte2 byte3
* 7......0 | 7......0 | 7......0 | 7......0
*
* The bit pointer starts at bit0 of byte0 and moves left until it reaches
* 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.
* @return {number} The peeked bits, as an unsigned number.
*/
peekBits_ltr(n, opt_movePointers) {
peekBits_ltm(n, opt_movePointers) {
const NUM = parseInt(n, 10);
let num = NUM;
if (n !== num || num <= 0) {
@ -149,7 +150,7 @@
* @param {boolean=} movePointers Whether to move the pointer, defaults false.
* @return {number} The peeked bits, as an unsigned number.
*/
peekBits_rtl(n, opt_movePointers) {
peekBits_mtl(n, opt_movePointers) {
const NUM = parseInt(n, 10);
let num = NUM;
if (n !== num || num <= 0) {

View file

@ -21,11 +21,13 @@
if (typeof numBytes != typeof 1 || numBytes <= 0) {
throw "Error! ByteBuffer initialized with '" + numBytes + "'";
}
/**
* @type {Uint8Array}
* @public
*/
this.data = new Uint8Array(numBytes);
/**
* @type {number}
* @public
@ -118,7 +120,7 @@
}
this.insertByte(curByte);
}
};
}
}
return ByteBuffer;

204
io/bitbuffer-worker.js Normal file
View file

@ -0,0 +1,204 @@
// 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;
})();

202
io/bitbuffer.js Normal file
View file

@ -0,0 +1,202 @@
// THIS IS A GENERATED FILE! DO NOT EDIT, INSTEAD EDIT THE FILE IN bitjs/build/io.
export const BitBuffer =
/*
* bytebuffer-def.js
*
* Provides a writer for bits.
*
* Licensed under the MIT License
*
* Copyright(c) 2021 Google Inc.
*/
(function () {
const BITMASK = [
0,
0b00000001,
0b00000011,
0b00000111,
0b00001111,
0b00011111,
0b00111111,
0b01111111,
0b11111111,
]
/**
* A write-only Bit buffer which uses a Uint8Array as a backing store.
*/
class BitBuffer {
/**
* @param {number} numBytes The number of bytes to allocate.
* @param {boolean} mtl The bit-packing mode. True means pack bits from most-significant (7) to
* least-significant (0). Defaults false: least-significant (0) to most-significant (8).
*/
constructor(numBytes, mtl = false) {
if (typeof numBytes != typeof 1 || numBytes <= 0) {
throw "Error! ByteBuffer initialized with '" + numBytes + "'";
}
/**
* @type {Uint8Array}
* @public
*/
this.data = new Uint8Array(numBytes);
/**
* Whether we pack bits from most-significant-bit to least. Defaults false (least-to-most
* significant bit packing).
* @type {boolean}
* @private
*/
this.mtl = mtl;
/**
* The current byte we are filling with bits.
* @type {number}
* @public
*/
this.bytePtr = 0;
/**
* Points at the bit within the current byte where the next bit will go. This number ranges
* from 0 to 7 and the direction of packing is indicated by the mtl property.
* @type {number}
* @public
*/
this.bitPtr = this.mtl ? 7 : 0;
}
/** @returns {boolean} */
getPackingDirection() {
return this.mtl;
}
/**
* Sets the bit-packing direction. Default (false) is least-significant-bit (0) to
* most-significant (7). Changing the bit-packing direction when the bit pointer is in the
* middle of a byte will fill the rest of that byte with 0s using the current bit-packing
* direction and then set the bit pointer to the appropriate bit of the next byte. If there
* are no more bytes left in this buffer, it will throw an error.
*/
setPackingDirection(mtl = false) {
if (this.mtl !== mtl) {
if (this.mtl && this.bitPtr !== 7) {
this.bytePtr++;
if (this.bytePtr >= this.data.byteLength) {
throw `No more bytes left when switching packing direction`;
}
this.bitPtr = 7;
} else if (!this.mtl && this.bitPtr !== 0) {
this.bytePtr++;
if (this.bytePtr >= this.data.byteLength) {
throw `No more bytes left when switching packing direction`;
}
this.bitPtr = 0;
}
}
this.mtl = mtl;
}
/**
* writeBits(3, 6) is the same as writeBits(0b000011, 6).
* Will throw an error (without writing) if this would over-flow the buffer.
* @param {number} val The bits to pack into the buffer. Negative values are not allowed.
* @param {number} numBits Must be positive, non-zero and less or equal to than 53, since
* JavaScript can only support 53-bit integers.
*/
writeBits(val, numBits) {
if (val < 0 || typeof val !== typeof 1) {
throw `Trying to write an invalid value into the BitBuffer: ${val}`;
}
if (numBits < 0 || numBits > 53) {
throw `Trying to write ${numBits} bits into the BitBuffer`;
}
const totalBitsInBuffer = this.data.byteLength * 8;
const writtenBits = this.bytePtr * 8 + this.bitPtr;
const bitsLeftInBuffer = totalBitsInBuffer - writtenBits;
if (numBits > bitsLeftInBuffer) {
throw `Trying to write ${numBits} into the BitBuffer that only has ${bitsLeftInBuffer}`;
}
// Least-to-most-significant bit packing method (LTM).
if (!this.mtl) {
let numBitsLeftToWrite = numBits;
while (numBitsLeftToWrite > 0) {
/** The number of bits available to fill in this byte. */
const bitCapacityInThisByte = 8 - this.bitPtr;
/** The number of bits of val we will write into this byte. */
const numBitsToWriteIntoThisByte = Math.min(numBitsLeftToWrite, bitCapacityInThisByte);
/** The number of bits that fit in subsequent bytes. */
const numExcessBits = numBitsLeftToWrite - numBitsToWriteIntoThisByte;
if (numExcessBits < 0) {
throw `Error in LTM bit packing, # of excess bits is negative`;
}
/** The actual bits that need to be written into this byte. Starts at LSB. */
let actualBitsToWrite = (val & BITMASK[numBitsToWriteIntoThisByte]);
// Only adjust and write bits if any are set to 1.
if (actualBitsToWrite > 0) {
actualBitsToWrite <<= this.bitPtr;
// Now write into the buffer.
this.data[this.bytePtr] |= actualBitsToWrite;
}
// Update the bit/byte pointers and remaining bits to write.
this.bitPtr += numBitsToWriteIntoThisByte;
if (this.bitPtr > 7) {
if (this.bitPtr !== 8) {
throw `Error in LTM bit packing. Tried to write more bits than it should have.`;
}
this.bytePtr++;
this.bitPtr = 0;
}
// Remove bits that have been written from LSB end.
val >>= numBitsToWriteIntoThisByte;
numBitsLeftToWrite -= numBitsToWriteIntoThisByte;
}
}
// Most-to-least-significant bit packing method (MTL).
else {
let numBitsLeftToWrite = numBits;
while (numBitsLeftToWrite > 0) {
/** The number of bits available to fill in this byte. */
const bitCapacityInThisByte = this.bitPtr + 1;
/** The number of bits of val we will write into this byte. */
const numBitsToWriteIntoThisByte = Math.min(numBitsLeftToWrite, bitCapacityInThisByte);
/** The number of bits that fit in subsequent bytes. */
const numExcessBits = numBitsLeftToWrite - numBitsToWriteIntoThisByte;
if (numExcessBits < 0) {
throw `Error in MTL bit packing, # of excess bits is negative`;
}
/** The actual bits that need to be written into this byte. Starts at MSB. */
let actualBitsToWrite = ((val >> numExcessBits) & BITMASK[numBitsToWriteIntoThisByte]);
// Only adjust and write bits if any are set to 1.
if (actualBitsToWrite > 0) {
// If the number of bits left to write do not fill up this byte, we need to shift these
// bits to the left so they are written into the proper place in the buffer.
if (numBitsLeftToWrite < bitCapacityInThisByte) {
actualBitsToWrite <<= (bitCapacityInThisByte - numBitsLeftToWrite);
}
// Now write into the buffer.
this.data[this.bytePtr] |= actualBitsToWrite;
}
// Update the bit/byte pointers and remaining bits to write
this.bitPtr -= numBitsToWriteIntoThisByte;
if (this.bitPtr < 0) {
if (this.bitPtr !== -1) {
throw `Error in MTL bit packing. Tried to write more bits than it should have.`;
}
this.bytePtr++;
this.bitPtr = 7;
}
// Remove bits that have been written from MSB end.
val -= (actualBitsToWrite << numExcessBits);
numBitsLeftToWrite -= numBitsToWriteIntoThisByte;
}
}
}
}
return BitBuffer;
})();

View file

@ -26,12 +26,13 @@ bitjs.io.BitStream =
class BitStream {
/**
* @param {ArrayBuffer} ab An ArrayBuffer object or a Uint8Array.
* @param {boolean} rtl Whether the stream reads bits from the byte starting
* from bit 7 to 0 (true) or bit 0 to 7 (false).
* @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 direciton 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, rtl, opt_offset, opt_length) {
constructor(ab, mtl, opt_offset, opt_length) {
if (!(ab instanceof ArrayBuffer)) {
throw 'Error! BitArray constructed with an invalid ArrayBuffer object';
}
@ -67,7 +68,7 @@ bitjs.io.BitStream =
*/
this.bitsRead_ = 0;
this.peekBits = rtl ? this.peekBits_rtl : this.peekBits_ltr;
this.peekBits = mtl ? this.peekBits_mtl : this.peekBits_ltm;
}
/**
@ -89,13 +90,13 @@ bitjs.io.BitStream =
* byte0 byte1 byte2 byte3
* 7......0 | 7......0 | 7......0 | 7......0
*
* The bit pointer starts at bit0 of byte0 and moves left until it reaches
* 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.
* @return {number} The peeked bits, as an unsigned number.
*/
peekBits_ltr(n, opt_movePointers) {
peekBits_ltm(n, opt_movePointers) {
const NUM = parseInt(n, 10);
let num = NUM;
if (n !== num || num <= 0) {
@ -153,7 +154,7 @@ bitjs.io.BitStream =
* @param {boolean=} movePointers Whether to move the pointer, defaults false.
* @return {number} The peeked bits, as an unsigned number.
*/
peekBits_rtl(n, opt_movePointers) {
peekBits_mtl(n, opt_movePointers) {
const NUM = parseInt(n, 10);
let num = NUM;
if (n !== num || num <= 0) {

View file

@ -24,12 +24,13 @@ export const BitStream =
class BitStream {
/**
* @param {ArrayBuffer} ab An ArrayBuffer object or a Uint8Array.
* @param {boolean} rtl Whether the stream reads bits from the byte starting
* from bit 7 to 0 (true) or bit 0 to 7 (false).
* @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 direciton 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, rtl, opt_offset, opt_length) {
constructor(ab, mtl, opt_offset, opt_length) {
if (!(ab instanceof ArrayBuffer)) {
throw 'Error! BitArray constructed with an invalid ArrayBuffer object';
}
@ -65,7 +66,7 @@ export const BitStream =
*/
this.bitsRead_ = 0;
this.peekBits = rtl ? this.peekBits_rtl : this.peekBits_ltr;
this.peekBits = mtl ? this.peekBits_mtl : this.peekBits_ltm;
}
/**
@ -87,13 +88,13 @@ export const BitStream =
* byte0 byte1 byte2 byte3
* 7......0 | 7......0 | 7......0 | 7......0
*
* The bit pointer starts at bit0 of byte0 and moves left until it reaches
* 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.
* @return {number} The peeked bits, as an unsigned number.
*/
peekBits_ltr(n, opt_movePointers) {
peekBits_ltm(n, opt_movePointers) {
const NUM = parseInt(n, 10);
let num = NUM;
if (n !== num || num <= 0) {
@ -151,7 +152,7 @@ export const BitStream =
* @param {boolean=} movePointers Whether to move the pointer, defaults false.
* @return {number} The peeked bits, as an unsigned number.
*/
peekBits_rtl(n, opt_movePointers) {
peekBits_mtl(n, opt_movePointers) {
const NUM = parseInt(n, 10);
let num = NUM;
if (n !== num || num <= 0) {

View file

@ -25,11 +25,13 @@ bitjs.io.ByteBuffer =
if (typeof numBytes != typeof 1 || numBytes <= 0) {
throw "Error! ByteBuffer initialized with '" + numBytes + "'";
}
/**
* @type {Uint8Array}
* @public
*/
this.data = new Uint8Array(numBytes);
/**
* @type {number}
* @public
@ -122,7 +124,7 @@ bitjs.io.ByteBuffer =
}
this.insertByte(curByte);
}
};
}
}
return ByteBuffer;

View file

@ -23,11 +23,13 @@ export const ByteBuffer =
if (typeof numBytes != typeof 1 || numBytes <= 0) {
throw "Error! ByteBuffer initialized with '" + numBytes + "'";
}
/**
* @type {Uint8Array}
* @public
*/
this.data = new Uint8Array(numBytes);
/**
* @type {number}
* @public
@ -120,7 +122,7 @@ export const ByteBuffer =
}
this.insertByte(curByte);
}
};
}
}
return ByteBuffer;

View file

@ -0,0 +1,88 @@
/*
* io-bitbuffer.spec.js
*
* Licensed under the MIT License
*
* Copyright(c) 2021 Google Inc.
*/
import { BitBuffer } from '../io/bitbuffer.js';
import 'mocha';
import { expect } from 'chai';
describe('bitjs.io.BitBuffer', () => {
let buffer;
describe('least-to-most-significant bit-packing', () => {
beforeEach(() => {
buffer = new BitBuffer(4);
});
it('bit/byte pointers initialized properly', () => {
expect(buffer.bytePtr).equals(0);
expect(buffer.bitPtr).equals(0);
})
it('write multiple bits', () => {
buffer.writeBits(0b01011, 5); // Should result in: 0b00001011.
expect(buffer.bytePtr).equals(0);
expect(buffer.bitPtr).equals(0 + 5);
expect(Array.from(buffer.data)).to.have.ordered.members([1 + 2 + 8, 0, 0, 0]);
});
it('write multiple bits across byte boundary', () => {
buffer.writeBits(0b01011, 5);
buffer.writeBits(0b11101, 5); // Should result in: 0b10101011 and 0b00000011.
expect(buffer.bytePtr).equals(Math.floor((5 + 5) / 8));
expect(buffer.bitPtr).equals((5 + 5) % 8);
expect(Array.from(buffer.data)).to.have.ordered.members(
[1 + 2 + 8 + 32 + 128, 1 + 2, 0, 0]);
});
it('write multiple bytes to buffer', () => {
buffer.writeBits(0, 1);
buffer.writeBits(0x1ffff, 17); // Should result in 0b111111110, 0b11111111, 0b00000011.
expect(buffer.bytePtr).equals(2);
expect(buffer.bitPtr).equals(2);
expect(Array.from(buffer.data)).to.have.ordered.members(
[0xfe, 0xff, 0x03, 0x00]);
});
});
describe('most-to-least-significant bit-packing', () => {
beforeEach(() => {
buffer = new BitBuffer(4, true);
});
it('bit/byte pointers initialized properly', () => {
expect(buffer.bytePtr).equals(0);
expect(buffer.bitPtr).equals(7);
})
it('write multiple bits', () => {
buffer.writeBits(0b01011, 5); // Should result in: 0b01011000
expect(buffer.bytePtr).equals(0);
expect(buffer.bitPtr).equals(7 - 5);
expect(Array.from(buffer.data)).to.have.ordered.members(
[64+16+8, 0, 0, 0]);
});
it('write multiple bits across byte boundary', () => {
buffer.writeBits(0b01011, 5);
buffer.writeBits(0b11101, 5); // Should result in: 0b01011111 and 0b01000000.
expect(buffer.bytePtr).equals(Math.floor((5 + 5) / 8));
expect(buffer.bitPtr).equals(7 - ((5 + 5) % 8));
expect(Array.from(buffer.data)).to.have.ordered.members(
[64+16+8+4+2+1, 64, 0, 0]);
});
it('write multiple bytes to buffer', () => {
buffer.writeBits(0, 1);
buffer.writeBits(0x1ffff, 17); // Should result in 0b011111111, 0b11111111, 0b11000000.
expect(buffer.bytePtr).equals(2);
expect(buffer.bitPtr).equals(5);
expect(Array.from(buffer.data)).to.have.ordered.members(
[0x7f, 0xff, 0xc0, 0x00]);
});
});
});

View file

@ -12,7 +12,7 @@ import 'mocha';
import { expect } from 'chai';
// TODO: Only test ByteBuffer here.
describe('bitjs.io.ByteBufferBitStream', () => {
describe('bitjs.io.ByteBuffer', () => {
let buffer;
beforeEach(() => {