mirror of
https://github.com/codedread/bitjs
synced 2025-10-03 09:39:16 +02:00
Make ByteStream pushable (allow multiple pages of bytes)
This commit is contained in:
parent
f9f0bc10de
commit
26d06e8d3b
2 changed files with 168 additions and 33 deletions
122
io/bytestream.js
122
io/bytestream.js
|
@ -34,6 +34,7 @@ bitjs.io.ByteStream = class {
|
|||
const length = opt_length || ab.byteLength;
|
||||
this.bytes = new Uint8Array(ab, offset, length);
|
||||
this.ptr = 0;
|
||||
this.pages_ = [];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -41,7 +42,23 @@ bitjs.io.ByteStream = class {
|
|||
* @private
|
||||
*/
|
||||
getBytesLeft_() {
|
||||
return (this.bytes.byteLength - this.ptr);
|
||||
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;
|
||||
while (this.ptr >= this.bytes.length && this.pages_.length > 0) {
|
||||
this.ptr -= this.bytes.length;
|
||||
this.bytes = this.pages_.shift();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -52,26 +69,33 @@ bitjs.io.ByteStream = class {
|
|||
* @return {number} The n bytes interpreted as an unsigned number.
|
||||
*/
|
||||
peekNumber(n) {
|
||||
let num = parseInt(n, 10);
|
||||
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;
|
||||
}
|
||||
|
||||
let result = 0;
|
||||
// read from last byte to first byte and roll them in
|
||||
let curByte = this.ptr + num - 1;
|
||||
while (curByte >= this.ptr) {
|
||||
if (curByte >= this.bytes.byteLength) {
|
||||
throw 'Error! Overflowed the byte stream while peekNumber()! n=' + num +
|
||||
', ptr=' + this.ptr + ', bytes.length=' + this.bytes.length;
|
||||
}
|
||||
// TODO: Throw an error if n > 4.
|
||||
|
||||
result <<= 8;
|
||||
result |= this.bytes[curByte];
|
||||
--curByte;
|
||||
if (this.getBytesLeft_() < num) {
|
||||
throw 'Error! Overflowed the byte stream while peekNumber()! n=' + num +
|
||||
', ptr=' + this.ptr + ', bytes.length=' + this.bytes.length;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -84,7 +108,7 @@ bitjs.io.ByteStream = class {
|
|||
*/
|
||||
readNumber(n) {
|
||||
const num = this.peekNumber(n);
|
||||
this.ptr += n;
|
||||
this.movePointer_(n);
|
||||
return num;
|
||||
}
|
||||
|
||||
|
@ -113,7 +137,7 @@ bitjs.io.ByteStream = class {
|
|||
*/
|
||||
readSignedNumber(n) {
|
||||
const num = this.peekSignedNumber(n);
|
||||
this.ptr += n;
|
||||
this.movePointer_(n);
|
||||
return num;
|
||||
}
|
||||
|
||||
|
@ -126,23 +150,33 @@ bitjs.io.ByteStream = class {
|
|||
* @return {Uint8Array} The subarray.
|
||||
*/
|
||||
peekBytes(n, movePointers) {
|
||||
let num = parseInt(n, 10);
|
||||
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();
|
||||
}
|
||||
|
||||
if (num > this.getBytesLeft_()) {
|
||||
//if (this.ptr + num > this.bytes.byteLength) {
|
||||
const totalBytesLeft = this.getBytesLeft_();
|
||||
if (num > totalBytesLeft) {
|
||||
throw 'Error! Overflowed the byte stream! n=' + num + ', ptr=' + this.ptr +
|
||||
', bytes.length=' + this.bytes.length;
|
||||
}
|
||||
|
||||
const result = this.bytes.subarray(this.ptr, this.ptr + num);
|
||||
const result = new Uint8Array(num);
|
||||
let curPage = this.bytes;
|
||||
let pageIndex = 0;
|
||||
let ptr = this.ptr;
|
||||
for (let i = 0; i < num; ++i) {
|
||||
result[i] = curPage[ptr++];
|
||||
if (ptr >= curPage.length) {
|
||||
curPage = this.pages_[pageIndex++];
|
||||
ptr = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (movePointers) {
|
||||
this.ptr += num;
|
||||
this.movePointer_(num);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -163,25 +197,32 @@ bitjs.io.ByteStream = class {
|
|||
* @return {string} The next n bytes as a string.
|
||||
*/
|
||||
peekString(n) {
|
||||
let num = parseInt(n, 10);
|
||||
const num = parseInt(n, 10);
|
||||
if (n !== num || num < 0) {
|
||||
throw 'Error! Called peekString() with a non-positive integer';
|
||||
} else if (num === 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// TODO: return error if n would go past the end of the stream.
|
||||
|
||||
let result = "";
|
||||
for (let p = this.ptr, end = this.ptr + n; p < end; ++p) {
|
||||
if (p >= this.bytes.byteLength) {
|
||||
throw 'Error! Overflowed the byte stream while peekString()! n=' + num +
|
||||
', ptr=' + this.ptr + ', bytes.length=' + this.bytes.length;
|
||||
}
|
||||
|
||||
result += String.fromCharCode(this.bytes[p]);
|
||||
const totalBytesLeft = this.getBytesLeft_();
|
||||
if (num > totalBytesLeft) {
|
||||
throw 'Error! Overflowed the byte stream while peekString()! n=' + num +
|
||||
', ptr=' + this.ptr + ', bytes.length=' + this.bytes.length;
|
||||
}
|
||||
return result;
|
||||
|
||||
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('');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -192,7 +233,22 @@ bitjs.io.ByteStream = class {
|
|||
*/
|
||||
readString(n) {
|
||||
const strToReturn = this.peekString(n);
|
||||
this.ptr += 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! 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -75,6 +75,85 @@
|
|||
assertThrows(() => stream.readString(5),
|
||||
'Did not throw when trying to readString past end of stream');
|
||||
},
|
||||
|
||||
testPushThenReadNumber() {
|
||||
array = new Uint8Array(1);
|
||||
array[0] = (1234 & 0xff);
|
||||
const stream = new bitjs.io.ByteStream(array.buffer);
|
||||
|
||||
const anotherArray = new Uint8Array(1);
|
||||
anotherArray[0] = ((1234 >> 8) & 0xff);
|
||||
stream.push(anotherArray.buffer);
|
||||
|
||||
assertEquals(1234, stream.readNumber(2), 'Could not read number across pages');
|
||||
},
|
||||
|
||||
testReadBytesThenPushThenReadByte() {
|
||||
for (let i = 0; i < 4; ++i) array[i] = i;
|
||||
const stream = new bitjs.io.ByteStream(array.buffer);
|
||||
|
||||
const bytes = stream.readBytes(4);
|
||||
assertThrows(() => stream.readBytes(1),
|
||||
'Did not throw when trying to readBytes past end of stream');
|
||||
|
||||
const anotherArray = new Uint8Array(1);
|
||||
anotherArray[0] = 4;
|
||||
stream.push(anotherArray.buffer);
|
||||
|
||||
assertEquals(4, stream.readNumber(1), 'Could not read in byte after pushing');
|
||||
},
|
||||
|
||||
testPushThenReadBytesAcrossOnePage() {
|
||||
for (let i = 0; i < 4; ++i) array[i] = i;
|
||||
const stream = new bitjs.io.ByteStream(array.buffer);
|
||||
|
||||
const anotherArray = new Uint8Array(1);
|
||||
anotherArray[0] = 4;
|
||||
stream.push(anotherArray.buffer);
|
||||
|
||||
const bytes = stream.readBytes(5);
|
||||
assertEquals(5, bytes.length, 'Could not read bytes across pages');
|
||||
for (let i = 0; i < 5; ++i) {
|
||||
assertEquals(i, bytes[i], 'Byte ' + i + ' not right');
|
||||
}
|
||||
},
|
||||
|
||||
testPushThenReadBytesAcrossMultiplePages() {
|
||||
for (let i = 0; i < 4; ++i) array[i] = i;
|
||||
const stream = new bitjs.io.ByteStream(array.buffer);
|
||||
|
||||
const anotherArray = new Uint8Array(4);
|
||||
for (let i = 0; i < 4; ++i) anotherArray[i] = i + 4;
|
||||
|
||||
const yetAnotherArray = new Uint8Array(4);
|
||||
for (let i = 0; i < 4; ++i) yetAnotherArray[i] = i + 8;
|
||||
|
||||
stream.push(anotherArray.buffer);
|
||||
stream.push(yetAnotherArray.buffer);
|
||||
|
||||
const bytes = stream.readBytes(12);
|
||||
assertEquals(12, bytes.length, 'Could not read bytes across multiple pages');
|
||||
for (let i = 0; i < 12; ++i) {
|
||||
assertEquals(i, bytes[i], 'Byte ' + i + ' not right');
|
||||
}
|
||||
},
|
||||
|
||||
testPushThenReadStringAcrossMultiplePages() {
|
||||
for (let i = 0; i < 4; ++i) array[i] = 65 + i;
|
||||
const stream = new bitjs.io.ByteStream(array.buffer);
|
||||
|
||||
const anotherArray = new Uint8Array(4);
|
||||
for (let i = 0; i < 4; ++i) anotherArray[i] = 69 + i;
|
||||
|
||||
const yetAnotherArray = new Uint8Array(4);
|
||||
for (let i = 0; i < 4; ++i) yetAnotherArray[i] = 73 + i;
|
||||
|
||||
stream.push(anotherArray.buffer);
|
||||
stream.push(yetAnotherArray.buffer);
|
||||
|
||||
const str = stream.readString(12);
|
||||
assertEquals('ABCDEFGHIJKL', str, 'String was ' + str);
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue