@thi.ng/bitstream
Version:
ES6 iterator based read/write bit streams with support for variable word widths
117 lines (116 loc) • 2.72 kB
JavaScript
import { illegalArgs } from "@thi.ng/errors/illegal-arguments";
import { illegalState } from "@thi.ng/errors/illegal-state";
const U32 = 4294967296;
class BitInputStream {
buffer;
start;
limit;
pos;
bitPos;
bit;
constructor(buffer, offset = 0, limit = buffer.length << 3) {
this.buffer = buffer;
this.start = offset;
this.limit = limit;
this.seek(offset);
}
*[Symbol.iterator]() {
let j = this.start;
let i = j >>> 3;
let b = 7 - (j & 7);
while (j < this.limit) {
yield this.buffer[i] >>> b & 1;
if (--b < 0) {
i++;
b = 7;
}
j++;
}
}
get length() {
return this.limit;
}
get position() {
return this.bitPos;
}
seek(pos) {
if (pos < this.start || pos >= this.limit) {
illegalArgs(`seek pos out of bounds: ${pos}`);
}
this.pos = pos >>> 3;
this.bit = 8 - (pos & 7);
this.bitPos = pos;
return this;
}
read(wordSize = 1, safe = true) {
if (wordSize > 32) {
return this.read(wordSize - 32, safe) * U32 + this.read(32, safe);
} else if (wordSize > 8) {
let out = 0;
let n = wordSize & -8;
let msb = wordSize - n;
if (msb > 0) {
out = this._read(msb, safe);
}
while (n > 0) {
out = (out << 8 | this._read(8, safe)) >>> 0;
n -= 8;
}
return out;
} else {
return this._read(wordSize, safe);
}
}
readFields(fields, safe = true) {
return fields.map((word) => this.read(word, safe));
}
readWords(n, wordSize = 8, safe = true) {
let out = [];
while (n-- > 0) {
out.push(this.read(wordSize, safe));
}
return out;
}
readStruct(fields, safe = true) {
return fields.reduce((acc, [id, word]) => {
return acc[id] = this.read(word, safe), acc;
}, {});
}
readBit(safe = true) {
safe && this.checkLimit(1);
this.bit--;
this.bitPos++;
let out = this.buffer[this.pos] >>> this.bit & 1;
if (this.bit === 0) {
this.pos++;
this.bit = 8;
}
return out;
}
_read(wordSize, safe = true) {
safe && this.checkLimit(wordSize);
let l = this.bit - wordSize, out;
if (l >= 0) {
this.bit = l;
out = this.buffer[this.pos] >>> l & (1 << wordSize) - 1;
if (l === 0) {
this.pos++;
this.bit = 8;
}
} else {
out = (this.buffer[this.pos++] & (1 << this.bit) - 1) << -l;
this.bit = 8 + l;
out = out | this.buffer[this.pos] >>> this.bit;
}
this.bitPos += wordSize;
return out;
}
checkLimit(requested) {
if (this.bitPos + requested > this.limit) {
illegalState(`can't read past EOF`);
}
}
}
export {
BitInputStream
};