Skip to content

Commit c07aa83

Browse files
committed
io: Add skip() method to BitStream and beef up unit tests
1 parent 45fdedd commit c07aa83

File tree

3 files changed

+172
-70
lines changed

3 files changed

+172
-70
lines changed

io/bitstream.js

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
* bitstream.js
33
*
4-
* Provides readers for bitstreams.
4+
* A pull stream for binary bits.
55
*
66
* Licensed under the MIT License
77
*
@@ -118,6 +118,8 @@ export class BitStream {
118118
peekBits_ltm(n, opt_movePointers) {
119119
const NUM = parseInt(n, 10);
120120
let num = NUM;
121+
122+
// TODO: Handle this consistently between ByteStream and BitStream. ByteStream throws an error.
121123
if (n !== num || num <= 0) {
122124
return 0;
123125
}
@@ -176,6 +178,8 @@ export class BitStream {
176178
peekBits_mtl(n, opt_movePointers) {
177179
const NUM = parseInt(n, 10);
178180
let num = NUM;
181+
182+
// TODO: Handle this consistently between ByteStream and BitStream. ByteStream throws an error.
179183
if (n !== num || num <= 0) {
180184
return 0;
181185
}
@@ -305,4 +309,30 @@ export class BitStream {
305309
readBytes(n) {
306310
return this.peekBytes(n, true);
307311
}
312+
313+
/**
314+
* Skips n bits in the stream. Will throw an error if n is < 0 or greater than the number of
315+
* bits left in the stream.
316+
* @param {number} n The number of bits to skip. Must be a positive integer.
317+
* @returns {BitStream} Returns this BitStream for chaining.
318+
*/
319+
skip(n) {
320+
const num = parseInt(n, 10);
321+
if (n !== num || num < 0) throw `Error! Called skip(${n})`;
322+
else if (num === 0) return this;
323+
324+
const totalBitsLeft = this.getNumBitsLeft();
325+
if (n > totalBitsLeft) {
326+
throw `Error! Overflowed the bit stream for skip(${n}), ptrs=${this.bytePtr}/${this.bitPtr}`;
327+
}
328+
329+
this.bitsRead_ += num;
330+
this.bitPtr += num;
331+
if (this.bitPtr >= 8) {
332+
this.bytePtr += Math.floor(this.bitPtr / 8);
333+
this.bitPtr %= 8;
334+
}
335+
336+
return this;
337+
}
308338
}

io/bytestream.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
* bytestream.js
33
*
4-
* Provides readers for byte streams.
4+
* A pull stream for bytes.
55
*
66
* Licensed under the MIT License
77
*
@@ -88,7 +88,7 @@ export class ByteStream {
8888
}
8989

9090
/**
91-
* Returns how many bytes have been read in the stream since the beginning of time.
91+
* Returns how many bytes have been consumed (read or skipped) since the beginning of time.
9292
* @returns {number}
9393
*/
9494
getNumBytesRead() {

tests/io-bitstream.spec.js

Lines changed: 139 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -23,76 +23,148 @@ describe('bitjs.io.BitStream', () => {
2323
expect(() => new BitStream()).throws();
2424
});
2525

26-
it('BitPeekAndRead_MTL', () => {
27-
const stream = new BitStream(array.buffer, true /* mtl */);
28-
29-
expect(stream.peekBits(0)).equals(0);
30-
expect(stream.peekBits(-1)).equals(0);
31-
expect(stream.bytePtr).equals(0);
32-
expect(stream.bitPtr).equals(0);
33-
34-
// 0110 = 2 + 4 = 6
35-
expect(stream.readBits(4)).equals(6);
36-
expect(stream.getNumBitsRead()).equals(4);
37-
38-
// 0101 011 = 1 + 2 + 8 + 32 = 43
39-
expect(stream.readBits(7)).equals(43);
40-
// 00101 01100101 01 = 1 + 4 + 16 + 128 + 256 + 1024 + 4096 = 5525
41-
expect(stream.readBits(15)).equals(5525);
42-
// 10010 = 2 + 16 = 18
43-
expect(stream.readBits(5)).equals(18);
44-
45-
// Ensure the last bit is read, even if we flow past the end of the stream.
46-
expect(stream.readBits(2)).equals(1);
47-
48-
expect(stream.getNumBitsRead()).equals(33);
26+
describe('Most-to-Least', () => {
27+
it('peek() and read()', () => {
28+
const stream = new BitStream(array.buffer, true /** mtl */);
29+
30+
expect(stream.peekBits(0)).equals(0);
31+
expect(stream.peekBits(-1)).equals(0);
32+
expect(stream.bytePtr).equals(0);
33+
expect(stream.bitPtr).equals(0);
34+
35+
// 0110
36+
expect(stream.readBits(4)).equals(0b0110);
37+
expect(stream.getNumBitsRead()).equals(4);
38+
39+
// 0101 011
40+
expect(stream.readBits(7)).equals(0b0101011);
41+
// 00101 01100101 01
42+
expect(stream.readBits(15)).equals(0b001010110010101);
43+
// 10010
44+
expect(stream.readBits(5)).equals(0b10010);
45+
46+
// Ensure the last bit is read, even if we flow past the end of the stream.
47+
expect(stream.readBits(2)).equals(1);
48+
49+
expect(stream.getNumBitsRead()).equals(33);
50+
});
51+
52+
it('skip() works correctly', () => {
53+
const stream = new BitStream(array.buffer, true /** mtl */);
54+
55+
expect(stream.skip(0)).equals(stream);
56+
expect(stream.getNumBitsRead()).equals(0);
57+
expect(stream.skip(3)).equals(stream);
58+
expect(stream.getNumBitsRead()).equals(3);
59+
expect(stream.readBits(3)).equals(0b001);
60+
expect(stream.getNumBitsRead()).equals(6);
61+
});
62+
63+
it('skip() works over byte boundary', () => {
64+
const stream = new BitStream(array.buffer, true /** mtl */);
65+
expect(stream.readBits(5)).equals(0b01100);
66+
stream.skip(5);
67+
expect(stream.getNumBitsRead()).equals(10);
68+
expect(stream.readBits(5)).equals(0b10010);
69+
});
70+
71+
it('skip() throws errors if overflowed', () => {
72+
const stream = new BitStream(array.buffer, true /** mtl */);
73+
expect(() => stream.skip(-1)).throws();
74+
stream.readBits(30);
75+
expect(() => stream.skip(3)).throws();
76+
});
4977
});
5078

51-
it('BitPeekAndRead_LTM', () => {
52-
/** @type {BitStream} */
53-
const stream = new BitStream(array.buffer, false /* mtl */);
54-
55-
expect(stream.peekBits(0)).equals(0);
56-
expect(stream.peekBits(-1)).equals(0);
57-
expect(stream.bytePtr).equals(0);
58-
expect(stream.bitPtr).equals(0);
59-
60-
// 0101 = 2 + 4 = 6
61-
expect(stream.peekBits(4)).equals(5);
62-
expect(stream.readBits(4)).equals(5);
63-
// 101 0110 = 2 + 4 + 16 + 64 = 86
64-
expect(stream.readBits(7)).equals(86);
65-
// 01 01100101 01100 = 4 + 8 + 32 + 128 + 1024 + 2048 + 8192 = 11436
66-
expect(stream.readBits(15)).equals(11436);
67-
// 11001 = 1 + 8 + 16 = 25
68-
expect(stream.readBits(5)).equals(25);
69-
70-
// Only 1 bit left in the buffer, make sure it reads in, even if we over-read.
71-
expect(stream.readBits(2)).equals(0);
72-
});
73-
74-
it('BitStreamReadBytes', () => {
75-
array[1] = Number('0b01010110');
76-
const stream = new BitStream(array.buffer);
77-
78-
let twoBytes = stream.peekBytes(2);
79-
expect(twoBytes instanceof Uint8Array).true;
80-
expect(twoBytes.byteLength).equals(2);
81-
expect(twoBytes[0]).equals(Number('0b01100101'));
82-
expect(twoBytes[1]).equals(Number('0b01010110'));
83-
84-
twoBytes = stream.readBytes(2);
85-
expect(twoBytes instanceof Uint8Array).true;
86-
expect(twoBytes.byteLength).equals(2);
87-
expect(twoBytes[0]).equals(Number('0b01100101'));
88-
expect(twoBytes[1]).equals(Number('0b01010110'));
89-
90-
expect(() => stream.readBytes(3)).throws();
79+
describe('Least-to-Most', () => {
80+
it('peek() and read()', () => {
81+
/** @type {BitStream} */
82+
const stream = new BitStream(array.buffer, false /** mtl */);
83+
84+
expect(stream.peekBits(0)).equals(0);
85+
expect(stream.peekBits(-1)).equals(0);
86+
expect(stream.bytePtr).equals(0);
87+
expect(stream.bitPtr).equals(0);
88+
89+
// 0101
90+
expect(stream.peekBits(4)).equals(0b0101);
91+
expect(stream.readBits(4)).equals(0b0101);
92+
// 101 0110
93+
expect(stream.readBits(7)).equals(0b1010110);
94+
// 01 01100101 01100
95+
expect(stream.readBits(15)).equals(0b010110010101100);
96+
// 11001
97+
expect(stream.readBits(5)).equals(0b11001);
98+
99+
// Only 1 bit left in the buffer, make sure it reads in, even if we over-read.
100+
expect(stream.readBits(2)).equals(0);
101+
});
102+
103+
it('skip() works correctly', () => {
104+
const stream = new BitStream(array.buffer, false /** mtl */);
105+
106+
expect(stream.skip(0)).equals(stream);
107+
expect(stream.getNumBitsRead()).equals(0);
108+
expect(stream.skip(3)).equals(stream);
109+
expect(stream.getNumBitsRead()).equals(3);
110+
expect(stream.readBits(3)).equals(0b100);
111+
expect(stream.getNumBitsRead()).equals(6);
112+
});
113+
114+
it('skip() works over byte boundary', () => {
115+
const stream = new BitStream(array.buffer, false /** mtl */);
116+
expect(stream.readBits(5)).equals(0b00101);
117+
stream.skip(5);
118+
expect(stream.getNumBitsRead()).equals(10);
119+
expect(stream.readBits(5)).equals(0b11001);
120+
});
121+
122+
it('skip() throws errors if overflowed', () => {
123+
const stream = new BitStream(array.buffer, false /** mtl */);
124+
expect(() => stream.skip(-1)).throws();
125+
stream.readBits(30);
126+
expect(() => stream.skip(3)).throws();
127+
});
91128
});
92129

93-
it('throws an error with weird values of peekBytes()', () => {
94-
/** @type {BitStream} */
95-
const stream = new BitStream(array.buffer);
96-
expect(() => stream.peekBytes(-1)).throws();
130+
describe('bytes', () => {
131+
it('peekBytes() and readBytes()', () => {
132+
array[1] = Number('0b01010110');
133+
const stream = new BitStream(array.buffer);
134+
135+
let twoBytes = stream.peekBytes(2);
136+
expect(twoBytes instanceof Uint8Array).true;
137+
expect(twoBytes.byteLength).equals(2);
138+
expect(twoBytes[0]).equals(Number('0b01100101'));
139+
expect(twoBytes[1]).equals(Number('0b01010110'));
140+
141+
twoBytes = stream.readBytes(2);
142+
expect(twoBytes instanceof Uint8Array).true;
143+
expect(twoBytes.byteLength).equals(2);
144+
expect(twoBytes[0]).equals(Number('0b01100101'));
145+
expect(twoBytes[1]).equals(Number('0b01010110'));
146+
147+
expect(() => stream.readBytes(3)).throws();
148+
});
149+
150+
it('peekBytes(0) returns an empty array', () => {
151+
const stream = new BitStream(array.buffer);
152+
const arr = stream.peekBytes(0);
153+
expect(arr.byteLength).equals(0);
154+
});
155+
156+
it('peekBytes() should ignore bits until byte-aligned', () => {
157+
array[1] = Number('0b01010110');
158+
const stream = new BitStream(array.buffer);
159+
stream.skip(3);
160+
const bytes = stream.readBytes(2);
161+
expect(bytes[0]).equals(0b01010110);
162+
expect(bytes[1]).equals(0b01100101);
163+
})
164+
165+
it('throws an error with weird values of peekBytes()', () => {
166+
const stream = new BitStream(array.buffer);
167+
expect(() => stream.peekBytes(-1)).throws();
168+
});
97169
});
98170
});

0 commit comments

Comments
 (0)