Permalink
Browse files

rename add* to write* and add unsigned number LE/BE support

  • Loading branch information...
1 parent 0a0fae9 commit 3e86fa83bf7bba651d11276f707f110ffcb05814 @kkaefer committed Jun 1, 2012
Showing with 189 additions and 49 deletions.
  1. +12 −8 README.md
  2. +83 −22 bitstream.js
  3. +94 −19 test/packing.test.js
View
@@ -9,22 +9,26 @@ var bs = new Bitstream;
bs.pipe(target);
// Adds 1111111 (seven) bits (the first seven on 0xFF);
-bs.addBits(new Buffer([ 0xFF ]), 7);
+bs.writeBits(new Buffer([ 0xFF ]), 7);
// Adds 10010 (five) bits
-bs.addBits(new Buffer([ 0xD2 ]), 4);
+bs.writeUnsigned(0x12, 5);
-// Adds 11111111 (8) bits (directly after the eleven previous bits)
-bs.addByte(255);
+// Adds 11111111 (8) bits (directly after the twelve previous bits)
+bs.writeByte(0xFF);
-// Aligns the stream to an 4 byte boundary (inserting zeros).
-bs.align(4);
+// Adds 1001000110100 in little endian byte order to the bitstream
+bs.writeUnsignedLE(0x1234, 13);
-// Flush cached input (except for the last partial byte, if any)
+// Aligns the stream to an 2 byte boundary (inserting zeros).
+bs.align(2);
+
+// // Flush cached input (except for the last partial byte, if any)
bs.flush();
// Finish stream (flushes implicitly)
bs.end();
-// Final output of the stream: 01111111 11111001 00001111 00000000
+// Final output of the stream: <Buffer 7f f9 4f 23 01 00>
+// In binary notation: 01111111 11111001 01001111 00100011 00000001 00000000
```
View
@@ -10,60 +10,77 @@ function Bitstream() {
Bitstream.BUFFER_SIZE = 1024;
-// byte is a Number.
-Bitstream.prototype.addByte = function(bits) {
+
+/**
+ * Writes a byte to the bitstream
+ *
+ * @param bits {Number} Byte to write.
+ */
+Bitstream.prototype.writeByte = function(bits) {
+
if (this._intra === 0) {
// Aligned at byte boundary.
- this._buffer[this._pos++] = bits;
+ this._buffer[this._pos] = bits;
+ this._pos++;
+ if (this._pos == this._buffer.length) this.flush();
} else {
// Copy first portion to current byte.
this._buffer[this._pos] |= bits << this._intra;
+ this._pos++;
+ if (this._pos == this._buffer.length) this.flush();
// Copy second portion to next byte.
- if (++this._pos == this._buffer.length) this.flush();
this._buffer[this._pos] = (bits & 0xFF) >> (8 - this._intra);
}
this._total += 8;
};
-Bitstream.prototype._addBits = function(bits, length) {
+
+/**
+ * Writes an unsigned integer up to 8 bits to the bitstream
+ *
+ * @param number {Number} Number to write.
+ * @param length {Number} Amount of bits of the number to write.
+ */
+Bitstream.prototype.writeUnsigned = function(bits, length) {
+ if (length > 8) throw new Error('You need to specify an endianness when writing more than 8 bits');
+
// Make sure we're not accidentally setting bits that shouldn't be set.
bits &= (1 << length) - 1;
+ var current = 8 - this._intra;
if (this._intra === 0) {
// Aligned at byte boundary.
this._buffer[this._pos] = bits;
} else {
// Number of bits we can fit into the current byte.
// node's Buffer implementation clamps this to 0xFF.
this._buffer[this._pos] |= bits << this._intra;
-
- var current = 8 - this._intra;
- if (current < length) {
- // We also have to add bits to the second byte.
- if (++this._pos == this._buffer.length) this.flush();
- this._buffer[this._pos] = bits >> current;
- }
}
this._total += length;
this._intra += length;
-
if (this._intra >= 8) {
this._intra -= 8;
this._pos++;
+ if (this._pos == this._buffer.length) this.flush();
+
+ if (current < length) {
+ // We also have to write bits to the second byte.
+ this._buffer[this._pos] = bits >> current;
+ }
}
};
/**
- * Adds bits to the bitstream
+ * Writes bits to the bitstream
*
- * @param bits {Buffer} Contains the bits to add, aligned at position 0.
+ * @param bits {Buffer} Contains the bits to write, aligned at position 0.
* Bits are | 76543210 | FEDCBA98 | etc.
* @param length {Number} Amount of valid bits in the buffer.
*/
-Bitstream.prototype.addBits = function(bits, length) {
+Bitstream.prototype.writeBits = function(bits, length) {
if (!this._buffer) throw new Error('Stream is closed');
var remainder = length % 8;
@@ -80,6 +97,7 @@ Bitstream.prototype.addBits = function(bits, length) {
if (max > 0) {
bits.copy(this._buffer, this._pos, 0, max);
this._pos += max;
+ if (this._pos == this._buffer.length) this.flush();
}
} else {
// The new bits wouldn't fit into the current buffer anyway, so flush
@@ -91,16 +109,59 @@ Bitstream.prototype.addBits = function(bits, length) {
} else {
// Do unaligned copy.
for (var pos = 0; pos < max; pos++) {
- this.addByte(bits[pos]);
+ this.writeByte(bits[pos]);
}
}
- // Add last byte.
+ // Write last byte.
if (remainder) {
- this._addBits(bits[max], remainder);
+ this.writeUnsigned(bits[max], remainder);
}
};
+/**
+ * Writes an unsigned big endian integer with a specified length to the bitstream
+ *
+ * @param number {Number} Number to write.
+ * @param length {Number} Amount of bits of the number to write.
+ */
+Bitstream.prototype.writeUnsignedBE = function(number, length) {
+ if (!this._buffer) throw new Error('Stream is closed');
+
+ var remainder = length % 8;
+ var max = length - remainder;
+
+ if (remainder) {
+ this.writeUnsigned(number >>> max, remainder);
+ }
+
+ for (var pos = max - 8; pos >= 0; pos -= 8) {
+ this.writeByte(number >>> pos);
+ }
+};
+
+/**
+ * Writes an unsigned little endian integer with a specified length to the bitstream
+ *
+ * @param number {Number} Number to write.
+ * @param length {Number} Amount of bits of the number to write.
+ */
+Bitstream.prototype.writeUnsignedLE = function(number, length) {
+ if (!this._buffer) throw new Error('Stream is closed');
+
+ var remainder = length % 8;
+ var max = length - remainder;
+
+ for (var pos = 0; pos < max; pos += 8) {
+ this.writeByte(number >>> pos);
+ }
+
+ if (remainder) {
+ this.writeUnsigned(number >>> max, remainder);
+ }
+};
+
+
Bitstream.prototype.end = function() {
this.align();
this.flush();
@@ -109,7 +170,7 @@ Bitstream.prototype.end = function() {
delete this._pos;
};
-// Aligns to stream to the next byte boundary by adding zeros.
+// Aligns to stream to the next byte boundary by writing zeros.
Bitstream.prototype.align = function(boundary) {
if (typeof boundary == 'undefined' || boundary < 0 || !boundary) {
boundary = 1;
@@ -121,7 +182,7 @@ Bitstream.prototype.align = function(boundary) {
var valid = this._total % (boundary * 8);
if (valid > 0) {
- this.addBits(Bitstream._nulls, boundary * 8 - valid);
+ this.writeBits(Bitstream._nulls, boundary * 8 - valid);
}
};
@@ -140,7 +201,7 @@ Bitstream.prototype.flush = function() {
// Stream API
// Emit 'data', 'end', 'close', 'error'
Bitstream.prototype.readable = true;
-Bitstream.prototype.writable = false; // We don't support being piped into; just adding bits with .add()
+Bitstream.prototype.writable = false; // We don't support being piped into; just writing bits with .writeBits()
Bitstream._nulls = new Buffer(32);
Bitstream._nulls.fill(0);
View
@@ -26,11 +26,11 @@ Assembler.prototype.end = function() {};
it('should assemble the stream correctly', function() {
var bs = new Bitstream;
var blob = bs.pipe(new Assembler);
- bs.addBits(new Buffer([ 0xFF ]), 3);
- bs.addBits(new Buffer([ 0x00 ]), 4);
- bs.addBits(new Buffer([ 0xFF ]), 1);
- bs.addBits(new Buffer([ 0x00 ]), 2);
- bs.addBits(new Buffer([ 0xFF ]), 3);
+ bs.writeBits(new Buffer([ 0xFF ]), 3);
+ bs.writeBits(new Buffer([ 0x00 ]), 4);
+ bs.writeBits(new Buffer([ 0xFF ]), 1);
+ bs.writeBits(new Buffer([ 0x00 ]), 2);
+ bs.writeBits(new Buffer([ 0xFF ]), 3);
bs.end();
var reference = new Buffer([ 0x87, 0x1C ]);
assert.deepEqual(blob.buffer, reference);
@@ -39,30 +39,69 @@ Assembler.prototype.end = function() {};
it('should handle bitstreams longer than 1 byte correctly', function() {
var bs = new Bitstream;
var blob = bs.pipe(new Assembler);
- bs.addBits(new Buffer([ 0xFE, 0xED ]), 16);
- bs.addBits(new Buffer([ 0xFF ]), 3);
- bs.addBits(new Buffer([ 0x00, 0x00 ]), 12);
- bs.addBits(new Buffer([ 0xFF ]), 1);
+ bs.writeBits(new Buffer([ 0xFE, 0xED ]), 16);
+ bs.writeBits(new Buffer([ 0xFF ]), 3);
+ bs.writeBits(new Buffer([ 0x00, 0x00 ]), 12);
+ bs.writeBits(new Buffer([ 0xFF ]), 1);
bs.end();
var reference = new Buffer([ 0xFE, 0xED, 0x07, 0x80 ]);
assert.deepEqual(blob.buffer, reference);
});
+ it('should write unsigned big endian integers of arbitary bit length', function() {
+ var bs = new Bitstream;
+ var blob = bs.pipe(new Assembler);
+ bs.writeUnsignedBE(0xFEED, 16);
+ bs.writeUnsignedBE(0xFF, 3);
+ bs.writeUnsignedBE(0x0000, 12);
+ bs.writeUnsignedBE(0x1, 1);
+ bs.writeUnsignedBE(0x0, 2);
+ bs.writeUnsignedBE(0x3, 2);
+ bs.writeUnsignedBE(0xA5, 8);
+ bs.writeUnsignedBE(0x0, 4);
+ bs.writeUnsignedBE(0x1, 1);
+ bs.writeUnsignedBE(0x335A, 15);
+ bs.end();
+
+ var reference = new Buffer([ 0xFE, 0xED, 0x07, 0x80, 0x5C, 0x0A, 0x67, 0x5A ]);
+ assert.deepEqual(blob.buffer, reference);
+ });
+
+ it('should write unsigned little endian integers of arbitary bit length', function() {
+ var bs = new Bitstream;
+ var blob = bs.pipe(new Assembler);
+ bs.writeUnsignedLE(0xFEED, 16);
+ bs.writeUnsignedLE(0xFF, 3);
+ bs.writeUnsignedLE(0x0000, 12);
+ bs.writeUnsignedLE(0x1, 1);
+
+ bs.writeUnsignedLE(0x0, 2);
+ bs.writeUnsignedLE(0x3, 2);
+ bs.writeUnsignedLE(0xA5, 8);
+ bs.writeUnsignedLE(0x0, 4);
+ bs.writeUnsignedLE(0x1, 1);
+ bs.writeUnsignedLE(0x335A, 15);
+ bs.end();
+
+ var reference = new Buffer([ 0xED, 0xFE, 0x07, 0x80, 0x5C, 0x0A, 0xB5, 0x66 ]);
+ assert.deepEqual(blob.buffer, reference);
+ });
+
it('should align bits correctly', function() {
var bs = new Bitstream;
var blob = bs.pipe(new Assembler);
- bs.addBits(new Buffer([ 0xFF ]), 3);
+ bs.writeBits(new Buffer([ 0xFF ]), 3);
bs.align();
- bs.addBits(new Buffer([ 0xFF, 0xFF ]), 10);
+ bs.writeBits(new Buffer([ 0xFF, 0xFF ]), 10);
bs.align(2);
- bs.addBits(new Buffer([ 0xFF ]), 5);
+ bs.writeBits(new Buffer([ 0xFF ]), 5);
bs.align();
- bs.addBits(new Buffer([ 0xFF, 0xFF, 0xFF, 0xFF ]), 28);
+ bs.writeBits(new Buffer([ 0xFF, 0xFF, 0xFF, 0xFF ]), 28);
bs.align(8);
- bs.addBits(new Buffer([ 0xFF ]), 6);
+ bs.writeBits(new Buffer([ 0xFF ]), 6);
bs.align();
- bs.addBits(new Buffer([ 0xFF, 0xFF, 0xFF, 0xFF ]), 26);
+ bs.writeBits(new Buffer([ 0xFF, 0xFF, 0xFF, 0xFF ]), 26);
bs.align(4);
bs.end();
@@ -80,18 +119,54 @@ Assembler.prototype.end = function() {};
});
+describe('readme example', function() {
+ it('should produce the correct output', function() {
+ var target = new Assembler;
+
+ var bs = new Bitstream;
+ bs.pipe(target);
+
+ // Adds 1111111 (seven) bits (the first seven on 0xFF);
+ bs.writeBits(new Buffer([ 0xFF ]), 7);
+
+ // Adds 10010 (five) bits
+ bs.writeUnsigned(0x12, 5);
+
+ // Adds 11111111 (8) bits (directly after the twelve previous bits)
+ bs.writeByte(0xFF);
+
+ // Adds 1001000110100 in little endian byte order to the bitstream
+ bs.writeUnsignedLE(0x1234, 13);
+
+ // Aligns the stream to an 2 byte boundary (inserting zeros).
+ bs.align(2);
+
+ // // Flush cached input (except for the last partial byte, if any)
+ bs.flush();
+
+ // Finish stream (flushes implicitly)
+ bs.end();
+
+ // Final output of the stream: <Buffer 7f f9 4f 23 01 00>
+ // In binary notation: 01111111 11111001 01001111 00100011 00000001 00000000
+
+ assert.deepEqual(target.buffer, new Buffer([ 0x7F, 0xF9, 0x4F, 0x23, 0x01, 0x00 ]));
+ });
+});
+
+
describe('error handling', function() {
it('should throw when fewer data than indicated is passed', function() {
var bs = new Bitstream;
assert.throws(function() {
- bs.addBits(new Buffer([ 0xFF ]), 12);
+ bs.writeBits(new Buffer([ 0xFF ]), 12);
}, '12 bits expected, but 8 passed');
});
it('should throw when no data is passed', function() {
var bs = new Bitstream;
assert.throws(function() {
- bs.addBits(new Buffer([]), 3);
+ bs.writeBits(new Buffer([]), 3);
}, '3 bits expected, but 0 passed');
});
@@ -102,11 +177,11 @@ describe('error handling', function() {
}, 'Maximum boundary align size is 32');
});
- it('should disallow adding bits after stream is closed', function() {
+ it('should disallow writing bits after stream is closed', function() {
var bs = new Bitstream;
bs.end();
assert.throws(function() {
- bs.addBits(new Buffer([0xFF]), 8);
+ bs.writeBits(new Buffer([0xFF]), 8);
}, 'Stream is closed');
});
});

0 comments on commit 3e86fa8

Please sign in to comment.