bit-buffer
Advanced tools
Comparing version
@@ -22,4 +22,3 @@ (function (root) { | ||
this._buffer = source; | ||
this._view = new Uint8Array(this._buffer, byteOffset, byteLength); | ||
this._view = new Uint8Array(source, byteOffset, byteLength); | ||
}; | ||
@@ -32,3 +31,3 @@ | ||
Object.defineProperty(BitView.prototype, 'buffer', { | ||
get: function () { return this._buffer; }, | ||
get: function () { return Buffer.from(this._view.buffer); }, | ||
enumerable: true, | ||
@@ -44,6 +43,2 @@ configurable: false | ||
BitView.prototype._getBit = function (offset) { | ||
return this._view[offset >> 3] >> (offset & 7) & 0x1; | ||
}; | ||
BitView.prototype._setBit = function (offset, on) { | ||
@@ -66,13 +61,15 @@ if (on) { | ||
for (var i = 0; i < bits;) { | ||
var read; | ||
var remaining = bits - i; | ||
var bitOffset = offset & 7; | ||
var currentByte = this._view[offset >> 3]; | ||
// Read an entire byte if we can. | ||
if ((bits - i) >= 8 && ((offset & 7) === 0)) { | ||
value |= (this._view[offset >> 3] << i); | ||
read = 8; | ||
} else { | ||
value |= (this._getBit(offset) << i); | ||
read = 1; | ||
} | ||
// the max number of bits we can read from the current byte | ||
var read = Math.min(remaining, 8 - bitOffset); | ||
// create a mask with the correct bit width | ||
var mask = (1 << read) - 1; | ||
// shift the bits we want to the start of the byte and mask of the rest | ||
var readBits = (currentByte >> bitOffset) & mask; | ||
value |= readBits << i; | ||
offset += read; | ||
@@ -122,2 +119,5 @@ i += read; | ||
BitView.prototype.getBoolean = function (offset) { | ||
return this.getBits(offset, 1, false) !== 0; | ||
}; | ||
BitView.prototype.getInt8 = function (offset) { | ||
@@ -152,2 +152,5 @@ return this.getBits(offset, 8, true); | ||
BitView.prototype.setBoolean = function (offset, value) { | ||
this.setBits(offset, value ? 1 : 0, 1); | ||
}; | ||
BitView.prototype.setInt8 = | ||
@@ -174,2 +177,9 @@ BitView.prototype.setUint8 = function (offset, value) { | ||
}; | ||
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; | ||
}; | ||
@@ -187,2 +197,5 @@ /********************************************************** | ||
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); | ||
@@ -202,9 +215,24 @@ 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 (!bytes || (bytes && i < bytes)) { | ||
while (i < bytes) { | ||
var c = stream.readUint8(); | ||
@@ -217,7 +245,6 @@ | ||
// If we don't have a fixed length to read, break out now. | ||
if (!bytes) { | ||
if (!fixedLength) { | ||
break; | ||
} | ||
} | ||
if (append) { | ||
@@ -230,6 +257,12 @@ chars.push(c); | ||
// Convert char code array back to string. | ||
return chars.map(function (x) { | ||
return String.fromCharCode(x); | ||
}).join(''); | ||
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; | ||
} | ||
} | ||
@@ -245,2 +278,39 @@ | ||
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) { | ||
@@ -261,4 +331,26 @@ var isBuffer = source instanceof ArrayBuffer || | ||
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', { | ||
@@ -296,2 +388,3 @@ // Ceil the returned value, over compensating for the amount of | ||
BitStream.prototype.readBoolean = reader('getBoolean', 1); | ||
BitStream.prototype.readInt8 = reader('getInt8', 8); | ||
@@ -306,2 +399,3 @@ BitStream.prototype.readUint8 = reader('getUint8', 8); | ||
BitStream.prototype.writeBoolean = writer('setBoolean', 1); | ||
BitStream.prototype.writeInt8 = writer('setInt8', 8); | ||
@@ -320,2 +414,6 @@ BitStream.prototype.writeUint8 = writer('setUint8', 8); | ||
BitStream.prototype.readUTF8String = function (bytes) { | ||
return readUTF8String(this, bytes); | ||
}; | ||
BitStream.prototype.writeASCIIString = function (string, bytes) { | ||
@@ -325,2 +423,19 @@ 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.readArrayBuffer = function(byteLength) { | ||
var buffer = this._view.getArrayBuffer(this._index, byteLength); | ||
this._index += (byteLength * 8); | ||
return buffer; | ||
}; | ||
// AMD / RequireJS | ||
@@ -343,2 +458,2 @@ if (typeof define !== 'undefined' && define.amd) { | ||
}(this)); | ||
}(this)); |
{ | ||
"name": "bit-buffer", | ||
"version": "0.0.3", | ||
"version": "0.1.0", | ||
"description": "Bit-level reads and writes for ArrayBuffers", | ||
"main": "bit-buffer.js", | ||
"types": "./bit-buffer.d.ts", | ||
"directories": { | ||
@@ -23,5 +24,13 @@ "test": "test" | ||
"author": "Anthony Pesch", | ||
"contributors": [ | ||
"Robin Appelman" | ||
], | ||
"license": "MIT", | ||
"gitHead": "cd4417237bed1f22dd5adfd8a6b961ea7234d9c9", | ||
"readmeFilename": "README.md" | ||
"readmeFilename": "README.md", | ||
"devDependencies": { | ||
"@types/node": "^7.0.5", | ||
"jshint": "^2.9.4", | ||
"mocha": "^3.2.0" | ||
} | ||
} |
# BitBuffer | ||
[](https://travis-ci.org/inolen/bit-buffer) | ||
BitBuffer provides two objects, `BitView` and `BitStream`. `BitView` is a wrapper for ArrayBuffers, similar to JavaScript's [DataView](https://developer.mozilla.org/en-US/docs/JavaScript/Typed_arrays/DataView), but with support for bit-level reads and writes. `BitStream` is a wrapper for a `BitView` used to help maintain your current buffer position, as well as to provide higher-level read / write operations such as for ASCII strings. | ||
## BitView | ||
@@ -58,3 +59,3 @@ | ||
```javascript | ||
bb.byteIndex // Get current index in bytes. | ||
bb.byteIndex; // Get current index in bytes. | ||
bb.byteIndex = 0; // Set current index in bytes. | ||
@@ -64,5 +65,18 @@ ``` | ||
```javascript | ||
bb.view // Underlying BitView | ||
bb.view; // Underlying BitView | ||
``` | ||
```javascript | ||
bb.length; // Get the length of the stream in bits | ||
``` | ||
```javascript | ||
bb.bitsLeft; // The number of bits left in the stream | ||
``` | ||
```javascript | ||
bb.index; // Get the current index in bits | ||
bb.index = 0// Set the current index in bits | ||
``` | ||
### Methods | ||
@@ -72,3 +86,3 @@ | ||
Default constructor, takes in a single argument of a BitView. | ||
Default constructor, takes in a single argument of a `BitView`, `ArrayBuffer` or node `Buffer`. | ||
@@ -87,13 +101,44 @@ #### BitSteam(buffer, optional byteOffset, optional byteLength) | ||
#### readASCIIString(optional bytes) | ||
#### readUInt8(), readUInt16(), readUInt32(), readInt8(), readInt16(), readInt32() | ||
Read a 8, 16 or 32 bits (unsigned) integer at the current index, updating the index. | ||
Reads bytes from the underlying view at the current index until either `bytes` count is reached or a 0x00 terminator is reached. | ||
#### writeUInt8(value), writeUInt16(value), writeUInt32(value), writeInt8(value), writeInt16(value), writeInt32(value) | ||
Write 8, 16 or 32 bits from `value` as (unsigned) integer at the current index, updating the index. | ||
#### writeASCIIString(string, optional bytes) | ||
#### readFloat32(), readFloat64() | ||
Read a 32 or 64 bit floating point number at the current index, updating the index. | ||
#### writeFloat32(value), writeFloat64() | ||
Set 32 or 64 bits from `value` as floating point value at the current index, updating the index. | ||
#### readBoolean() | ||
Read a single bit from the view at the current index, updating the index. | ||
#### writeBoolean(value) | ||
Write a single bit to the view at the current index, updating the index. | ||
#### readASCIIString(optional bytes), readUTF8String(optional bytes) | ||
Reads bytes from the underlying view at the current index until either `bytes` count is reached or a 0x00 terminator is reached. | ||
#### writeASCIIString(string, optional bytes), writeUTF8String(string, optional bytes) | ||
Writes a string followed by a NULL character to the underlying view starting at the current index. If the string is longer than `bytes` it will be truncated, and if it is shorter 0x00 will be written in its place. | ||
#### readBitStream(length) | ||
Create a new `BitStream` from the underlying view starting the the current index and a length of `length` bits. Updating the index of the existing `BitStream` | ||
#### readArrayBuffer(byteLength) | ||
Read `byteLength` bytes of data from the underlying view as `ArrayBuffer`, updating the index. | ||
## license | ||
MIT | ||
MIT |
149
test.js
var assert = require('assert'), | ||
BitView = require('../bit-buffer').BitView, | ||
BitStream = require('../bit-buffer').BitStream; | ||
BitView = require('./bit-buffer').BitView, | ||
BitStream = require('./bit-buffer').BitStream; | ||
@@ -160,2 +160,12 @@ suite('BitBuffer', function () { | ||
test('Read ASCII string, 0 length', function () { | ||
var str = 'foobar'; | ||
bsw.writeASCIIString(str); | ||
assert(bsw.byteIndex === str.length + 1); // +1 for 0x00 | ||
assert(bsr.readASCIIString(0) === ''); | ||
assert(bsr.byteIndex === 0); | ||
}); | ||
test('Read overflow', function () { | ||
@@ -184,2 +194,135 @@ var exception = false; | ||
}); | ||
}); | ||
test('Get boolean', function () { | ||
bv.setUint8(0, 1); | ||
assert(bv.getBoolean(0)); | ||
bv.setUint8(0, 0); | ||
assert(!bv.getBoolean(0)); | ||
}); | ||
test('Set boolean', function () { | ||
bv.setBoolean(0, true); | ||
assert(bv.getBoolean(0)); | ||
bv.setBoolean(0, false); | ||
assert(!bv.getBoolean(0)); | ||
}); | ||
test('Read boolean', function () { | ||
bv.setBits(0, 1, 1); | ||
bv.setBits(1, 0, 1); | ||
assert(bsr.readBoolean()); | ||
assert(!bsr.readBoolean()); | ||
}); | ||
test('Write boolean', function () { | ||
bsr.writeBoolean(true); | ||
assert(bv.getBits(0, 1, false) === 1); | ||
bsr.writeBoolean(false); | ||
assert(bv.getBits(1, 1, false) === 0); | ||
}); | ||
test('Read / write UTF8 string, only ASCII characters', function () { | ||
var str = 'foobar'; | ||
bsw.writeUTF8String(str); | ||
assert(bsw.byteIndex === str.length + 1); // +1 for 0x00 | ||
assert(bsr.readUTF8String() === str); | ||
assert(bsr.byteIndex === str.length + 1); | ||
}); | ||
test('Read / write UTF8 string, non ASCII characters', function () { | ||
var str = '日本語'; | ||
var bytes = [ | ||
0xE6, | ||
0x97, | ||
0xA5, | ||
0xE6, | ||
0x9C, | ||
0xAC, | ||
0xE8, | ||
0xAA, | ||
0x9E | ||
]; | ||
bsw.writeUTF8String(str); | ||
for (var i = 0; i < bytes.length; i++) { | ||
assert.equal(bytes[i], bv.getBits(i * 8, 8)); | ||
} | ||
assert.equal(bsw.byteIndex, bytes.length + 1); // +1 for 0x00 | ||
assert.equal(str, bsr.readUTF8String()); | ||
assert.equal(bsr.byteIndex, bytes.length + 1); | ||
}); | ||
test('readBitStream', function () { | ||
bsw.writeBits(0xF0, 8); //0b11110000 | ||
bsw.writeBits(0xF1, 8); //0b11110001 | ||
bsr.readBits(3); //offset | ||
var slice = bsr.readBitStream(8); | ||
assert.equal(slice.readBits(6), 0x3E); //0b111110 | ||
assert.equal(9, slice._index); | ||
assert.equal(6, slice.index); | ||
assert.equal(8, slice.length); | ||
assert.equal(2, slice.bitsLeft); | ||
assert.equal(bsr._index, 11); | ||
assert.equal((64 * 8) - 11, bsr.bitsLeft); | ||
}); | ||
test('readBitStream overflow', function () { | ||
bsw.writeBits(0xF0, 8); //0b11110000 | ||
bsw.writeBits(0xF1, 8); //0b11110001 | ||
bsr.readBits(3); //offset | ||
var slice = bsr.readBitStream(4); | ||
var exception = false; | ||
try { | ||
slice.readUint8(); | ||
} catch (e) { | ||
exception = true; | ||
} | ||
assert(exception); | ||
}); | ||
test('readArrayBuffer', function () { | ||
bsw.writeBits(0xF0, 8); //0b11110000 | ||
bsw.writeBits(0xF1, 8); //0b11110001 | ||
bsw.writeBits(0xF0, 8); //0b11110000 | ||
bsr.readBits(3); //offset | ||
var buffer = bsr.readArrayBuffer(2); | ||
assert.equal(0x3E, buffer[0]); //0b00111110 | ||
assert.equal(0x1E, buffer[1]); //0b00011110 | ||
assert.equal(3 + (2 * 8), bsr._index); | ||
}); | ||
test('Get buffer from view', function() { | ||
bv.setBits(0, 0xFFFFFFFF, 32); | ||
var buffer = bv.buffer; | ||
assert.equal(64, buffer.length); | ||
assert.equal(0xFFFF, buffer.readUInt16LE(0)); | ||
}); | ||
test('Get buffer from stream', function () { | ||
bsw.writeBits(0xFFFFFFFF, 32); | ||
var buffer = bsr.buffer; | ||
assert.equal(64, buffer.length); | ||
assert.equal(0xFFFF, buffer.readUInt16LE(0)); | ||
}); | ||
}); |
Sorry, the diff of this file is not supported yet
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
79108
375.07%14
180%663
60.92%141
48.42%3
Infinity%1
Infinity%