mirror of
https://github.com/codedread/bitjs
synced 2025-10-03 17:49:16 +02:00
io: Add skip() method to BitStream and beef up unit tests
This commit is contained in:
parent
45fdedd663
commit
c07aa83e12
3 changed files with 160 additions and 58 deletions
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* bitstream.js
|
* bitstream.js
|
||||||
*
|
*
|
||||||
* Provides readers for bitstreams.
|
* A pull stream for binary bits.
|
||||||
*
|
*
|
||||||
* Licensed under the MIT License
|
* Licensed under the MIT License
|
||||||
*
|
*
|
||||||
|
@ -118,6 +118,8 @@ export class BitStream {
|
||||||
peekBits_ltm(n, opt_movePointers) {
|
peekBits_ltm(n, opt_movePointers) {
|
||||||
const NUM = parseInt(n, 10);
|
const NUM = parseInt(n, 10);
|
||||||
let num = NUM;
|
let num = NUM;
|
||||||
|
|
||||||
|
// TODO: Handle this consistently between ByteStream and BitStream. ByteStream throws an error.
|
||||||
if (n !== num || num <= 0) {
|
if (n !== num || num <= 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -176,6 +178,8 @@ export class BitStream {
|
||||||
peekBits_mtl(n, opt_movePointers) {
|
peekBits_mtl(n, opt_movePointers) {
|
||||||
const NUM = parseInt(n, 10);
|
const NUM = parseInt(n, 10);
|
||||||
let num = NUM;
|
let num = NUM;
|
||||||
|
|
||||||
|
// TODO: Handle this consistently between ByteStream and BitStream. ByteStream throws an error.
|
||||||
if (n !== num || num <= 0) {
|
if (n !== num || num <= 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -305,4 +309,30 @@ export class BitStream {
|
||||||
readBytes(n) {
|
readBytes(n) {
|
||||||
return this.peekBytes(n, true);
|
return this.peekBytes(n, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Skips n bits in the stream. Will throw an error if n is < 0 or greater than the number of
|
||||||
|
* bits left in the stream.
|
||||||
|
* @param {number} n The number of bits to skip. Must be a positive integer.
|
||||||
|
* @returns {BitStream} Returns this BitStream for chaining.
|
||||||
|
*/
|
||||||
|
skip(n) {
|
||||||
|
const num = parseInt(n, 10);
|
||||||
|
if (n !== num || num < 0) throw `Error! Called skip(${n})`;
|
||||||
|
else if (num === 0) return this;
|
||||||
|
|
||||||
|
const totalBitsLeft = this.getNumBitsLeft();
|
||||||
|
if (n > totalBitsLeft) {
|
||||||
|
throw `Error! Overflowed the bit stream for skip(${n}), ptrs=${this.bytePtr}/${this.bitPtr}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.bitsRead_ += num;
|
||||||
|
this.bitPtr += num;
|
||||||
|
if (this.bitPtr >= 8) {
|
||||||
|
this.bytePtr += Math.floor(this.bitPtr / 8);
|
||||||
|
this.bitPtr %= 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* bytestream.js
|
* bytestream.js
|
||||||
*
|
*
|
||||||
* Provides readers for byte streams.
|
* A pull stream for bytes.
|
||||||
*
|
*
|
||||||
* Licensed under the MIT License
|
* Licensed under the MIT License
|
||||||
*
|
*
|
||||||
|
@ -88,7 +88,7 @@ export class ByteStream {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns how many bytes have been read in the stream since the beginning of time.
|
* Returns how many bytes have been consumed (read or skipped) since the beginning of time.
|
||||||
* @returns {number}
|
* @returns {number}
|
||||||
*/
|
*/
|
||||||
getNumBytesRead() {
|
getNumBytesRead() {
|
||||||
|
|
|
@ -23,76 +23,148 @@ describe('bitjs.io.BitStream', () => {
|
||||||
expect(() => new BitStream()).throws();
|
expect(() => new BitStream()).throws();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('BitPeekAndRead_MTL', () => {
|
describe('Most-to-Least', () => {
|
||||||
const stream = new BitStream(array.buffer, true /* mtl */);
|
it('peek() and read()', () => {
|
||||||
|
const stream = new BitStream(array.buffer, true /** mtl */);
|
||||||
|
|
||||||
expect(stream.peekBits(0)).equals(0);
|
expect(stream.peekBits(0)).equals(0);
|
||||||
expect(stream.peekBits(-1)).equals(0);
|
expect(stream.peekBits(-1)).equals(0);
|
||||||
expect(stream.bytePtr).equals(0);
|
expect(stream.bytePtr).equals(0);
|
||||||
expect(stream.bitPtr).equals(0);
|
expect(stream.bitPtr).equals(0);
|
||||||
|
|
||||||
// 0110 = 2 + 4 = 6
|
// 0110
|
||||||
expect(stream.readBits(4)).equals(6);
|
expect(stream.readBits(4)).equals(0b0110);
|
||||||
expect(stream.getNumBitsRead()).equals(4);
|
expect(stream.getNumBitsRead()).equals(4);
|
||||||
|
|
||||||
// 0101 011 = 1 + 2 + 8 + 32 = 43
|
// 0101 011
|
||||||
expect(stream.readBits(7)).equals(43);
|
expect(stream.readBits(7)).equals(0b0101011);
|
||||||
// 00101 01100101 01 = 1 + 4 + 16 + 128 + 256 + 1024 + 4096 = 5525
|
// 00101 01100101 01
|
||||||
expect(stream.readBits(15)).equals(5525);
|
expect(stream.readBits(15)).equals(0b001010110010101);
|
||||||
// 10010 = 2 + 16 = 18
|
// 10010
|
||||||
expect(stream.readBits(5)).equals(18);
|
expect(stream.readBits(5)).equals(0b10010);
|
||||||
|
|
||||||
// Ensure the last bit is read, even if we flow past the end of the stream.
|
// Ensure the last bit is read, even if we flow past the end of the stream.
|
||||||
expect(stream.readBits(2)).equals(1);
|
expect(stream.readBits(2)).equals(1);
|
||||||
|
|
||||||
expect(stream.getNumBitsRead()).equals(33);
|
expect(stream.getNumBitsRead()).equals(33);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('skip() works correctly', () => {
|
||||||
|
const stream = new BitStream(array.buffer, true /** mtl */);
|
||||||
|
|
||||||
|
expect(stream.skip(0)).equals(stream);
|
||||||
|
expect(stream.getNumBitsRead()).equals(0);
|
||||||
|
expect(stream.skip(3)).equals(stream);
|
||||||
|
expect(stream.getNumBitsRead()).equals(3);
|
||||||
|
expect(stream.readBits(3)).equals(0b001);
|
||||||
|
expect(stream.getNumBitsRead()).equals(6);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('skip() works over byte boundary', () => {
|
||||||
|
const stream = new BitStream(array.buffer, true /** mtl */);
|
||||||
|
expect(stream.readBits(5)).equals(0b01100);
|
||||||
|
stream.skip(5);
|
||||||
|
expect(stream.getNumBitsRead()).equals(10);
|
||||||
|
expect(stream.readBits(5)).equals(0b10010);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('skip() throws errors if overflowed', () => {
|
||||||
|
const stream = new BitStream(array.buffer, true /** mtl */);
|
||||||
|
expect(() => stream.skip(-1)).throws();
|
||||||
|
stream.readBits(30);
|
||||||
|
expect(() => stream.skip(3)).throws();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('BitPeekAndRead_LTM', () => {
|
describe('Least-to-Most', () => {
|
||||||
/** @type {BitStream} */
|
it('peek() and read()', () => {
|
||||||
const stream = new BitStream(array.buffer, false /* mtl */);
|
/** @type {BitStream} */
|
||||||
|
const stream = new BitStream(array.buffer, false /** mtl */);
|
||||||
|
|
||||||
expect(stream.peekBits(0)).equals(0);
|
expect(stream.peekBits(0)).equals(0);
|
||||||
expect(stream.peekBits(-1)).equals(0);
|
expect(stream.peekBits(-1)).equals(0);
|
||||||
expect(stream.bytePtr).equals(0);
|
expect(stream.bytePtr).equals(0);
|
||||||
expect(stream.bitPtr).equals(0);
|
expect(stream.bitPtr).equals(0);
|
||||||
|
|
||||||
// 0101 = 2 + 4 = 6
|
// 0101
|
||||||
expect(stream.peekBits(4)).equals(5);
|
expect(stream.peekBits(4)).equals(0b0101);
|
||||||
expect(stream.readBits(4)).equals(5);
|
expect(stream.readBits(4)).equals(0b0101);
|
||||||
// 101 0110 = 2 + 4 + 16 + 64 = 86
|
// 101 0110
|
||||||
expect(stream.readBits(7)).equals(86);
|
expect(stream.readBits(7)).equals(0b1010110);
|
||||||
// 01 01100101 01100 = 4 + 8 + 32 + 128 + 1024 + 2048 + 8192 = 11436
|
// 01 01100101 01100
|
||||||
expect(stream.readBits(15)).equals(11436);
|
expect(stream.readBits(15)).equals(0b010110010101100);
|
||||||
// 11001 = 1 + 8 + 16 = 25
|
// 11001
|
||||||
expect(stream.readBits(5)).equals(25);
|
expect(stream.readBits(5)).equals(0b11001);
|
||||||
|
|
||||||
// Only 1 bit left in the buffer, make sure it reads in, even if we over-read.
|
// Only 1 bit left in the buffer, make sure it reads in, even if we over-read.
|
||||||
expect(stream.readBits(2)).equals(0);
|
expect(stream.readBits(2)).equals(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('skip() works correctly', () => {
|
||||||
|
const stream = new BitStream(array.buffer, false /** mtl */);
|
||||||
|
|
||||||
|
expect(stream.skip(0)).equals(stream);
|
||||||
|
expect(stream.getNumBitsRead()).equals(0);
|
||||||
|
expect(stream.skip(3)).equals(stream);
|
||||||
|
expect(stream.getNumBitsRead()).equals(3);
|
||||||
|
expect(stream.readBits(3)).equals(0b100);
|
||||||
|
expect(stream.getNumBitsRead()).equals(6);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('skip() works over byte boundary', () => {
|
||||||
|
const stream = new BitStream(array.buffer, false /** mtl */);
|
||||||
|
expect(stream.readBits(5)).equals(0b00101);
|
||||||
|
stream.skip(5);
|
||||||
|
expect(stream.getNumBitsRead()).equals(10);
|
||||||
|
expect(stream.readBits(5)).equals(0b11001);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('skip() throws errors if overflowed', () => {
|
||||||
|
const stream = new BitStream(array.buffer, false /** mtl */);
|
||||||
|
expect(() => stream.skip(-1)).throws();
|
||||||
|
stream.readBits(30);
|
||||||
|
expect(() => stream.skip(3)).throws();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('BitStreamReadBytes', () => {
|
describe('bytes', () => {
|
||||||
array[1] = Number('0b01010110');
|
it('peekBytes() and readBytes()', () => {
|
||||||
const stream = new BitStream(array.buffer);
|
array[1] = Number('0b01010110');
|
||||||
|
const stream = new BitStream(array.buffer);
|
||||||
|
|
||||||
let twoBytes = stream.peekBytes(2);
|
let twoBytes = stream.peekBytes(2);
|
||||||
expect(twoBytes instanceof Uint8Array).true;
|
expect(twoBytes instanceof Uint8Array).true;
|
||||||
expect(twoBytes.byteLength).equals(2);
|
expect(twoBytes.byteLength).equals(2);
|
||||||
expect(twoBytes[0]).equals(Number('0b01100101'));
|
expect(twoBytes[0]).equals(Number('0b01100101'));
|
||||||
expect(twoBytes[1]).equals(Number('0b01010110'));
|
expect(twoBytes[1]).equals(Number('0b01010110'));
|
||||||
|
|
||||||
twoBytes = stream.readBytes(2);
|
twoBytes = stream.readBytes(2);
|
||||||
expect(twoBytes instanceof Uint8Array).true;
|
expect(twoBytes instanceof Uint8Array).true;
|
||||||
expect(twoBytes.byteLength).equals(2);
|
expect(twoBytes.byteLength).equals(2);
|
||||||
expect(twoBytes[0]).equals(Number('0b01100101'));
|
expect(twoBytes[0]).equals(Number('0b01100101'));
|
||||||
expect(twoBytes[1]).equals(Number('0b01010110'));
|
expect(twoBytes[1]).equals(Number('0b01010110'));
|
||||||
|
|
||||||
expect(() => stream.readBytes(3)).throws();
|
expect(() => stream.readBytes(3)).throws();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('throws an error with weird values of peekBytes()', () => {
|
it('peekBytes(0) returns an empty array', () => {
|
||||||
/** @type {BitStream} */
|
const stream = new BitStream(array.buffer);
|
||||||
const stream = new BitStream(array.buffer);
|
const arr = stream.peekBytes(0);
|
||||||
expect(() => stream.peekBytes(-1)).throws();
|
expect(arr.byteLength).equals(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('peekBytes() should ignore bits until byte-aligned', () => {
|
||||||
|
array[1] = Number('0b01010110');
|
||||||
|
const stream = new BitStream(array.buffer);
|
||||||
|
stream.skip(3);
|
||||||
|
const bytes = stream.readBytes(2);
|
||||||
|
expect(bytes[0]).equals(0b01010110);
|
||||||
|
expect(bytes[1]).equals(0b01100101);
|
||||||
|
})
|
||||||
|
|
||||||
|
it('throws an error with weird values of peekBytes()', () => {
|
||||||
|
const stream = new BitStream(array.buffer);
|
||||||
|
expect(() => stream.peekBytes(-1)).throws();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue