The JavaScript BitView
class provides a similar interface to DataView
with support for bit-level reads and writes, allowing for manipulation of single bits within a specified byte offset. It offers methods to set a single bit, read a specified number of bits, and private properties for internal use, with dependencies on ArrayBuffer
or Buffer
objects and the DataView
class.
npm run import -- "bit buffer"
(function (root) {
/**********************************************************
*
* BitView
*
* BitView provides a similar interface to the standard
* DataView, but with support for bit-level reads / writes.
*
**********************************************************/
var BitView = function (source, byteOffset, byteLength) {
var isBuffer = source instanceof ArrayBuffer ||
(typeof Buffer !== 'undefined' && source instanceof Buffer);
if (!isBuffer) {
throw new Error('Must specify a valid ArrayBuffer or Buffer.');
}
byteOffset = byteOffset || 0;
byteLength = byteLength || source.byteLength /* ArrayBuffer */ || source.length /* Buffer */;
this._view = new Uint8Array(source, byteOffset, byteLength);
this.bigEndian = false;
};
// Used to massage fp values so we can operate on them
// at the bit level.
BitView._scratch = new DataView(new ArrayBuffer(8));
Object.defineProperty(BitView.prototype, 'buffer', {
get: function () { return typeof Buffer !== 'undefined' ? Buffer.from(this._view.buffer) : this._view.buffer; },
enumerable: true,
configurable: false
});
Object.defineProperty(BitView.prototype, 'byteLength', {
get: function () { return this._view.length; },
enumerable: true,
configurable: false
});
BitView.prototype._setBit = function (offset, on) {
if (on) {
this._view[offset >> 3] |= 1 << (offset & 7);
} else {
this._view[offset >> 3] &= ~(1 << (offset & 7));
}
};
BitView.prototype.getBits = function (offset, bits, signed) {
var available = (this._view.length * 8 - offset);
if (bits > available) {
throw new Error('Cannot get ' + bits + ' bit(s) from offset ' + offset + ', ' + available + ' available');
}
var value = 0;
for (var i = 0; i < bits;) {
var remaining = bits - i;
var bitOffset = offset & 7;
var currentByte = this._view[offset >> 3];
// the max number of bits we can read from the current byte
var read = Math.min(remaining, 8 - bitOffset);
var mask, readBits;
if (this.bigEndian) {
// create a mask with the correct bit width
mask = ~(0xFF << read);
// shift the bits we want to the start of the byte and mask of the rest
readBits = (currentByte >> (8 - read - bitOffset)) & mask;
value <<= read;
value |= readBits;
} else {
// create a mask with the correct bit width
mask = ~(0xFF << read);
// shift the bits we want to the start of the byte and mask off the rest
readBits = (currentByte >> bitOffset) & mask;
value |= readBits << i;
}
offset += read;
i += read;
}
if (signed) {
// If we're not working with a full 32 bits, check the
// imaginary MSB for this bit count and convert to a
// valid 32-bit signed value if set.
if (bits !== 32 && value & (1 << (bits - 1))) {
value |= -1 ^ ((1 << bits) - 1);
}
return value;
}
return value >>> 0;
};
BitView.prototype.setBits = function (offset, value, bits) {
var available = (this._view.length * 8 - offset);
if (bits > available) {
throw new Error('Cannot set ' + bits + ' bit(s) from offset ' + offset + ', ' + available + ' available');
}
for (var i = 0; i < bits;) {
var remaining = bits - i;
var bitOffset = offset & 7;
var byteOffset = offset >> 3;
var wrote = Math.min(remaining, 8 - bitOffset);
var mask, writeBits, destMask;
if (this.bigEndian) {
// create a mask with the correct bit width
mask = ~(~0 << wrote);
// shift the bits we want to the start of the byte and mask of the rest
writeBits = (value >> (bits - i - wrote)) & mask;
var destShift = 8 - bitOffset - wrote;
// destination mask to zero all the bits we're changing first
destMask = ~(mask << destShift);
this._view[byteOffset] =
(this._view[byteOffset] & destMask)
| (writeBits << destShift);
} else {
// create a mask with the correct bit width
mask = ~(0xFF << wrote);
// shift the bits we want to the start of the byte and mask of the rest
writeBits = value & mask;
value >>= wrote;
// destination mask to zero all the bits we're changing first
destMask = ~(mask << bitOffset);
this._view[byteOffset] =
(this._view[byteOffset] & destMask)
| (writeBits << bitOffset);
}
offset += wrote;
i += wrote;
}
};
BitView.prototype.getBoolean = function (offset) {
return this.getBits(offset, 1, false) !== 0;
};
BitView.prototype.getInt8 = function (offset) {
return this.getBits(offset, 8, true);
};
BitView.prototype.getUint8 = function (offset) {
return this.getBits(offset, 8, false);
};
BitView.prototype.getInt16 = function (offset) {
return this.getBits(offset, 16, true);
};
BitView.prototype.getUint16 = function (offset) {
return this.getBits(offset, 16, false);
};
BitView.prototype.getInt32 = function (offset) {
return this.getBits(offset, 32, true);
};
BitView.prototype.getUint32 = function (offset) {
return this.getBits(offset, 32, false);
};
BitView.prototype.getFloat32 = function (offset) {
BitView._scratch.setUint32(0, this.getUint32(offset));
return BitView._scratch.getFloat32(0);
};
BitView.prototype.getFloat64 = function (offset) {
BitView._scratch.setUint32(0, this.getUint32(offset));
// DataView offset is in bytes.
BitView._scratch.setUint32(4, this.getUint32(offset+32));
return BitView._scratch.getFloat64(0);
};
BitView.prototype.setBoolean = function (offset, value) {
this.setBits(offset, value ? 1 : 0, 1);
};
BitView.prototype.setInt8 =
BitView.prototype.setUint8 = function (offset, value) {
this.setBits(offset, value, 8);
};
BitView.prototype.setInt16 =
BitView.prototype.setUint16 = function (offset, value) {
this.setBits(offset, value, 16);
};
BitView.prototype.setInt32 =
BitView.prototype.setUint32 = function (offset, value) {
this.setBits(offset, value, 32);
};
BitView.prototype.setFloat32 = function (offset, value) {
BitView._scratch.setFloat32(0, value);
this.setBits(offset, BitView._scratch.getUint32(0), 32);
};
BitView.prototype.setFloat64 = function (offset, value) {
BitView._scratch.setFloat64(0, value);
this.setBits(offset, BitView._scratch.getUint32(0), 32);
this.setBits(offset+32, BitView._scratch.getUint32(4), 32);
};
BitView.prototype.getArrayBuffer = function (offset, byteLength) {
var buffer = new Uint8Array(byteLength);
for (var i = 0; i < byteLength; i++) {
buffer[i] = this.getUint8(offset + (i * 8));
}
return buffer;
};
/**********************************************************
*
* BitStream
*
* Small wrapper for a BitView to maintain your position,
* as well as to handle reading / writing of string data
* to the underlying buffer.
*
**********************************************************/
var reader = function (name, size) {
return function () {
if (this._index + size > this._length) {
throw new Error('Trying to read past the end of the stream');
}
var val = this._view[name](this._index);
this._index += size;
return val;
};
};
var writer = function (name, size) {
return function (value) {
this._view[name](this._index, value);
this._index += size;
};
};
function readASCIIString(stream, bytes) {
return readString(stream, bytes, false);
}
function readUTF8String(stream, bytes) {
return readString(stream, bytes, true);
}
function readString(stream, bytes, utf8) {
if (bytes === 0) {
return '';
}
var i = 0;
var chars = [];
var append = true;
var fixedLength = !!bytes;
if (!bytes) {
bytes = Math.floor((stream._length - stream._index) / 8);
}
// Read while we still have space available, or until we've
// hit the fixed byte length passed in.
while (i < bytes) {
var c = stream.readUint8();
// Stop appending chars once we hit 0x00
if (c === 0x00) {
append = false;
// If we don't have a fixed length to read, break out now.
if (!fixedLength) {
break;
}
}
if (append) {
chars.push(c);
}
i++;
}
var string = String.fromCharCode.apply(null, chars);
if (utf8) {
try {
return decodeURIComponent(escape(string)); // https://stackoverflow.com/a/17192845
} catch (e) {
return string;
}
} else {
return string;
}
}
function writeASCIIString(stream, string, bytes) {
var length = bytes || string.length + 1; // + 1 for NULL
for (var i = 0; i < length; i++) {
stream.writeUint8(i < string.length ? string.charCodeAt(i) : 0x00);
}
}
function writeUTF8String(stream, string, bytes) {
var byteArray = stringToByteArray(string);
var length = bytes || byteArray.length + 1; // + 1 for NULL
for (var i = 0; i < length; i++) {
stream.writeUint8(i < byteArray.length ? byteArray[i] : 0x00);
}
}
function stringToByteArray(str) { // https://gist.github.com/volodymyr-mykhailyk/2923227
var b = [], i, unicode;
for (i = 0; i < str.length; i++) {
unicode = str.charCodeAt(i);
// 0x00000000 - 0x0000007f -> 0xxxxxxx
if (unicode <= 0x7f) {
b.push(unicode);
// 0x00000080 - 0x000007ff -> 110xxxxx 10xxxxxx
} else if (unicode <= 0x7ff) {
b.push((unicode >> 6) | 0xc0);
b.push((unicode & 0x3F) | 0x80);
// 0x00000800 - 0x0000ffff -> 1110xxxx 10xxxxxx 10xxxxxx
} else if (unicode <= 0xffff) {
b.push((unicode >> 12) | 0xe0);
b.push(((unicode >> 6) & 0x3f) | 0x80);
b.push((unicode & 0x3f) | 0x80);
// 0x00010000 - 0x001fffff -> 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
} else {
b.push((unicode >> 18) | 0xf0);
b.push(((unicode >> 12) & 0x3f) | 0x80);
b.push(((unicode >> 6) & 0x3f) | 0x80);
b.push((unicode & 0x3f) | 0x80);
}
}
return b;
}
var BitStream = function (source, byteOffset, byteLength) {
var isBuffer = source instanceof ArrayBuffer ||
(typeof Buffer !== 'undefined' && source instanceof Buffer);
if (!(source instanceof BitView) && !isBuffer) {
throw new Error('Must specify a valid BitView, ArrayBuffer or Buffer');
}
if (isBuffer) {
this._view = new BitView(source, byteOffset, byteLength);
} else {
this._view = source;
}
this._index = 0;
this._startIndex = 0;
this._length = this._view.byteLength * 8;
};
Object.defineProperty(BitStream.prototype, 'index', {
get: function () { return this._index - this._startIndex; },
set: function (val) { this._index = val + this._startIndex; },
enumerable: true,
configurable: true
});
Object.defineProperty(BitStream.prototype, 'length', {
get: function () { return this._length - this._startIndex; },
set: function (val) { this._length = val + this._startIndex; },
enumerable : true,
configurable: true
});
Object.defineProperty(BitStream.prototype, 'bitsLeft', {
get: function () { return this._length - this._index; },
enumerable : true,
configurable: true
});
Object.defineProperty(BitStream.prototype, 'byteIndex', {
// Ceil the returned value, over compensating for the amount of
// bits written to the stream.
get: function () { return Math.ceil(this._index / 8); },
set: function (val) { this._index = val * 8; },
enumerable: true,
configurable: true
});
Object.defineProperty(BitStream.prototype, 'buffer', {
get: function () { return this._view.buffer; },
enumerable: true,
configurable: false
});
Object.defineProperty(BitStream.prototype, 'view', {
get: function () { return this._view; },
enumerable: true,
configurable: false
});
Object.defineProperty(BitStream.prototype, 'bigEndian', {
get: function () { return this._view.bigEndian; },
set: function (val) { this._view.bigEndian = val; },
enumerable: true,
configurable: false
});
BitStream.prototype.readBits = function (bits, signed) {
var val = this._view.getBits(this._index, bits, signed);
this._index += bits;
return val;
};
BitStream.prototype.writeBits = function (value, bits) {
this._view.setBits(this._index, value, bits);
this._index += bits;
};
BitStream.prototype.readBoolean = reader('getBoolean', 1);
BitStream.prototype.readInt8 = reader('getInt8', 8);
BitStream.prototype.readUint8 = reader('getUint8', 8);
BitStream.prototype.readInt16 = reader('getInt16', 16);
BitStream.prototype.readUint16 = reader('getUint16', 16);
BitStream.prototype.readInt32 = reader('getInt32', 32);
BitStream.prototype.readUint32 = reader('getUint32', 32);
BitStream.prototype.readFloat32 = reader('getFloat32', 32);
BitStream.prototype.readFloat64 = reader('getFloat64', 64);
BitStream.prototype.writeBoolean = writer('setBoolean', 1);
BitStream.prototype.writeInt8 = writer('setInt8', 8);
BitStream.prototype.writeUint8 = writer('setUint8', 8);
BitStream.prototype.writeInt16 = writer('setInt16', 16);
BitStream.prototype.writeUint16 = writer('setUint16', 16);
BitStream.prototype.writeInt32 = writer('setInt32', 32);
BitStream.prototype.writeUint32 = writer('setUint32', 32);
BitStream.prototype.writeFloat32 = writer('setFloat32', 32);
BitStream.prototype.writeFloat64 = writer('setFloat64', 64);
BitStream.prototype.readASCIIString = function (bytes) {
return readASCIIString(this, bytes);
};
BitStream.prototype.readUTF8String = function (bytes) {
return readUTF8String(this, bytes);
};
BitStream.prototype.writeASCIIString = function (string, bytes) {
writeASCIIString(this, string, bytes);
};
BitStream.prototype.writeUTF8String = function (string, bytes) {
writeUTF8String(this, string, bytes);
};
BitStream.prototype.readBitStream = function(bitLength) {
var slice = new BitStream(this._view);
slice._startIndex = this._index;
slice._index = this._index;
slice.length = bitLength;
this._index += bitLength;
return slice;
};
BitStream.prototype.writeBitStream = function(stream, length) {
if (!length) {
length = stream.bitsLeft;
}
var bitsToWrite;
while (length > 0) {
bitsToWrite = Math.min(length, 32);
this.writeBits(stream.readBits(bitsToWrite), bitsToWrite);
length -= bitsToWrite;
}
};
BitStream.prototype.readArrayBuffer = function(byteLength) {
var buffer = this._view.getArrayBuffer(this._index, byteLength);
this._index += (byteLength * 8);
return buffer;
};
BitStream.prototype.writeArrayBuffer = function(buffer, byteLength) {
this.writeBitStream(new BitStream(buffer), byteLength * 8);
};
// AMD / RequireJS
if (typeof define !== 'undefined' && define.amd) {
define(function () {
return {
BitView: BitView,
BitStream: BitStream
};
});
}
// Node.js
else if (typeof module !== 'undefined' && module.exports) {
module.exports = {
BitView: BitView,
BitStream: BitStream
};
}
}(this));
(function(root) {
/**********************************************************************
*
* BitView
*
* BitView provides a similar interface to the standard
* DataView, but with support for bit-level reads / writes.
*
**********************************************************************/
class BitView {
constructor(source, byteOffset, byteLength) {
if (!(source instanceof ArrayBuffer || (typeof Buffer!== 'undefined' && source instanceof Buffer))) {
throw new Error('Must specify a valid ArrayBuffer or Buffer.');
}
byteOffset = byteOffset || 0;
byteLength = byteLength || source.byteLength || source.length;
this._view = new Uint8Array(source, byteOffset, byteLength);
this.bigEndian = false;
this.buffer = source instanceof ArrayBuffer? source : Buffer.from(source);
this.byteLength = byteLength;
this._scratch = new DataView(new ArrayBuffer(8));
}
_setBit(offset, on) {
if (on) {
this._view[offset >> 3] |= 1 << (offset & 7);
} else {
this._view[offset >> 3] &= ~(1 << (offset & 7));
}
}
getBits(offset, bits, signed) {
const available = (this._view.length * 8 - offset);
if (bits > available) {
throw new Error(`Cannot get ${bits} bit(s) from offset ${offset}, ${available} available`);
}
let value = 0;
for (let i = 0; i < bits;) {
const remaining = bits - i;
const bitOffset = offset & 7;
const currentByte = this._view[offset >> 3];
const read = Math.min(remaining, 8 - bitOffset);
const mask = this.bigEndian? ~(0xFF << read) : ~0xFF;
const readBits = (this.bigEndian? (currentByte >> (8 - read - bitOffset)) : (currentByte >> bitOffset)) & mask;
value <<= read;
value |= readBits;
offset += read;
i += read;
}
if (signed) {
if (bits!== 32 && value & (1 << (bits - 1))) {
value |= -1 ^ ((1 << bits) - 1);
}
return value;
}
return value >>> 0;
}
setBits(offset, value, bits) {
const available = (this._view.length * 8 - offset);
if (bits > available) {
throw new Error(`Cannot set ${bits} bit(s) from offset ${offset}, ${available} available`);
}
for (let i = 0; i < bits;) {
const remaining = bits - i;
const bitOffset = offset & 7;
const byteOffset = offset >> 3;
const wrote = Math.min(remaining, 8 - bitOffset);
const mask = this.bigEndian? ~(~0 << wrote) : ~(0xFF << wrote);
const writeBits = (this.bigEndian? (value >> (bits - i - wrote)) & mask : value & mask);
const destMask = this.bigEndian? ~(mask << (8 - bitOffset - wrote)) : ~(mask << bitOffset);
this._view[byteOffset] = (this._view[byteOffset] & destMask) | (writeBits << (8 - bitOffset - wrote));
offset += wrote;
i += wrote;
}
}
getBoolean(offset) {
return this.getBits(offset, 1, false)!== 0;
}
getInt8(offset) {
return this.getBits(offset, 8, true);
}
getUint8(offset) {
return this.getBits(offset, 8, false);
}
getInt16(offset) {
return this.getBits(offset, 16, true);
}
getUint16(offset) {
return this.getBits(offset, 16, false);
}
getInt32(offset) {
return this.getBits(offset, 32, true);
}
getUint32(offset) {
return this.getBits(offset, 32, false);
}
getFloat32(offset) {
this._scratch.setUint32(0, this.getUint32(offset));
return this._scratch.getFloat32(0);
}
getFloat64(offset) {
this._scratch.setUint32(0, this.getUint32(offset));
this._scratch.setUint32(4, this.getUint32(offset + 32));
return this._scratch.getFloat64(0);
}
setBoolean(offset, value) {
this.setBits(offset, value? 1 : 0, 1);
}
setInt8(offset, value) {
this.setBits(offset, value, 8);
}
setUint8(offset, value) {
this.setBits(offset, value, 8);
}
setInt16(offset, value) {
this.setBits(offset, value, 16);
}
setUint16(offset, value) {
this.setBits(offset, value, 16);
}
setInt32(offset, value) {
this.setBits(offset, value, 32);
}
setUint32(offset, value) {
this.setBits(offset, value, 32);
}
setFloat32(offset, value) {
this._scratch.setFloat32(0, value);
this.setBits(offset, this._scratch.getUint32(0), 32);
}
setFloat64(offset, value) {
this._scratch.setFloat64(0, value);
this.setBits(offset, this._scratch.getUint32(0), 32);
this.setBits(offset + 32, this._scratch.getUint32(4), 32);
}
getArrayBuffer(offset, byteLength) {
const buffer = new Uint8Array(byteLength);
for (let i = 0; i < byteLength; i++) {
buffer[i] = this.getUint8(offset + (i * 8));
}
return buffer;
}
}
class BitStream {
constructor(source, byteOffset, byteLength) {
if (!(source instanceof BitView || (source instanceof ArrayBuffer || (typeof Buffer!== 'undefined' && source instanceof Buffer)))) {
throw new Error('Must specify a valid BitView, ArrayBuffer or Buffer');
}
if (source instanceof BitView) {
this._view = source;
} else {
this._view = new BitView(source, byteOffset, byteLength);
}
this._index = 0;
this._startIndex = 0;
this._length = this._view.byteLength * 8;
}
get index() {
return this._index - this._startIndex;
}
set index(value) {
this._index = value + this._startIndex;
}
get length() {
return this._length - this._startIndex;
}
set length(value) {
this._length = value + this._startIndex;
}
get bitsLeft() {
return this._length - this._index;
}
get byteIndex() {
return Math.ceil(this._index / 8);
}
set byteIndex(value) {
this._index = value * 8;
}
get buffer() {
return this._view.buffer;
}
get view() {
return this._view;
}
get bigEndian() {
return this._view.bigEndian;
}
set bigEndian(value) {
this._view.bigEndian = value;
}
readBits(bits, signed) {
const val = this._view.getBits(this._index, bits, signed);
this._index += bits;
return val;
}
writeBits(value, bits) {
this._view.setBits(this._index, value, bits);
this._index += bits;
}
readBoolean() {
return this._view.getBoolean(this._index++);
}
readInt8() {
return this._view.getInt8(this._index++);
}
readUint8() {
return this._view.getUint8(this._index++);
}
readInt16() {
return this._view.getInt16(this._index++);
}
readUint16() {
return this._view.getUint16(this._index++);
}
readInt32() {
return this._view.getInt32(this._index++);
}
readUint32() {
return this._view.getUint32(this._index++);
}
readFloat32() {
return this._view.getFloat32(this._index++);
}
readFloat64() {
return this._view.getFloat64(this._index++);
}
writeBoolean(value) {
this._view.setBoolean(this._index++, value);
}
writeInt8(value) {
this._view.setInt8(this._index++, value);
}
writeUint8(value) {
this._view.setUint8(this._index++, value);
}
writeInt16(value) {
this._view.setInt16(this._index++, value);
}
writeUint16(value) {
this._view.setUint16(this._index++, value);
}
writeInt32(value) {
this._view.setInt32(this._index++, value);
}
writeUint32(value) {
this._view.setUint32(this._index++, value);
}
writeFloat32(value) {
this._view.setFloat32(this._index++, value);
}
writeFloat64(value) {
this._view.setFloat64(this._index++, value);
}
readASCIIString(bytes) {
return readASCIIString(this, bytes);
}
readUTF8String(bytes) {
return readUTF8String(this, bytes);
}
writeASCIIString(string, bytes) {
writeASCIIString(this, string, bytes);
}
writeUTF8String(string, bytes) {
writeUTF8String(this, string, bytes);
}
readBitStream(bitLength) {
const slice = new BitStream(this._view);
slice._startIndex = this._index;
slice._index = this._index;
slice.length = bitLength;
this._index += bitLength;
return slice;
}
writeBitStream(stream, length) {
if (!length) {
length = stream.bitsLeft;
}
while (length > 0) {
const bitsToWrite = Math.min(length, 32);
this.writeBits(stream.readBits(bitsToWrite), bitsToWrite);
length -= bitsToWrite;
}
}
readArrayBuffer(byteLength) {
const buffer = this._view.getArrayBuffer(this._index, byteLength);
this._index += (byteLength * 8);
return buffer;
}
writeArrayBuffer(buffer, byteLength) {
this.writeBitStream(new BitStream(buffer), byteLength * 8);
}
}
// AMD / RequireJS
if (typeof define!== 'undefined' && define.amd) {
define(function() {
return {
BitView: BitView,
BitStream: BitStream
};
});
}
// Node.js
else if (typeof module!== 'undefined' && module.exports) {
module.exports = {
BitView: BitView,
BitStream: BitStream
};
}
}(this));
function readASCIIString(stream, bytes) {
return readString(stream, bytes, false);
}
function readUTF8String(stream, bytes) {
return readString(stream, bytes, true);
}
function readString(stream, bytes, utf8) {
if (bytes === 0) {
return '';
}
let i = 0;
let chars = [];
let append = true;
if (!bytes) {
bytes = Math.floor((stream._length - stream._index) / 8);
}
while (i < bytes) {
const c = stream.readUint8();
if (c === 0x00) {
append = false;
if (!stream.bitsLeft) {
break;
}
}
if (append) {
chars.push(c);
}
i++;
}
const string = String.fromCharCode.apply(null, chars);
if (utf8) {
try {
return decodeURIComponent(escape(string));
} catch (e) {
return string;
}
} else {
return string;
}
}
function writeASCIIString(stream, string, bytes) {
const length = bytes || string.length + 1; // + 1 for NULL
for (let i = 0; i < length; i++) {
stream.writeUint8(i < string.length? string.charCodeAt(i) : 0x00);
}
}
function writeUTF8String(stream, string, bytes) {
const byteArray = stringToByteArray(string);
const length = bytes || byteArray.length + 1; // + 1 for NULL
for (let i = 0; i < length; i++) {
stream.writeUint8(i < byteArray.length? byteArray[i] : 0x00);
}
}
function stringToByteArray(str) {
const b = [];
for (let i = 0; i < str.length; i++) {
const unicode = str.charCodeAt(i);
if (unicode <= 0x7f) {
b.push(unicode);
} else if (unicode <= 0x7ff) {
b.push((unicode >> 6) | 0xc0);
b.push((unicode & 0x3F) | 0x80);
} else if (unicode <= 0xffff) {
b.push((unicode >> 12) | 0xe0);
b.push(((unicode >> 6) & 0x3f) | 0x80);
b.push((unicode & 0x3f) | 0x80);
} else {
b.push((unicode >> 18) | 0xf0);
b.push(((unicode >> 12) & 0x3f) | 0x80);
b.push(((unicode >> 6) & 0x3f) | 0x80);
b.push((unicode & 0x3f) | 0x80);
}
}
return b;
}
Code Breakdown
This code defines a JavaScript class named BitView
which provides a similar interface to DataView
but with support for bit-level reads and writes.
The BitView
constructor takes three arguments:
source
: The source data, which must be an ArrayBuffer
or a Buffer
object.byteOffset
: The byte offset into the source data to start from (optional, defaults to 0).byteLength
: The number of bytes to read from the source data (optional, defaults to the length of the source data).The constructor checks if the source
argument is an ArrayBuffer
or a Buffer
, and if not, throws an error. It then creates a new Uint8Array
view of the source data, starting at the specified byteOffset
and with the specified byteLength
.
Two properties are defined on the BitView
prototype:
buffer
: Returns the underlying buffer object.byteLength
: Returns the length of the view in bytes.Three methods are defined on the BitView
prototype:
_setBit
: Sets a single bit at the specified offset
.getBits
: Reads a specified number of bits from the view, starting at the specified offset
. It takes two additional arguments: bits
(the number of bits to read) and signed
(whether the value should be interpreted as signed or unsigned)._scratch
: A private property used for internal purposes.ArrayBuffer
or a Buffer
object.Buffer
object, which is a Node.js module._scratch
that is used for internal purposes.