Skip to content

Commit 8d24c8b

Browse files
authored
Merge pull request #18 from Malvineous/big-endian
Add read and write support for big-endian byte order
2 parents 3f57fae + b1a5cc2 commit 8d24c8b

File tree

3 files changed

+296
-14
lines changed

3 files changed

+296
-14
lines changed

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ BitBuffer provides two objects, `BitView` and `BitStream`. `BitView` is a wrappe
1212
bb.buffer // Underlying ArrayBuffer.
1313
```
1414

15+
```javascript
16+
bb.bigEndian = true; // Switch to big endian (default is little)
17+
```
18+
1519
### Methods
1620

1721
#### BitView(buffer, optional byteOffset, optional byteLength)
@@ -77,6 +81,10 @@ bb.index; // Get the current index in bits
7781
bb.index = 0// Set the current index in bits
7882
```
7983

84+
```javascript
85+
bb.bigEndian = true; // Switch to big endian (default is little)
86+
```
87+
8088
### Methods
8189

8290
#### BitStream(view)

bit-buffer.js

Lines changed: 57 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ var BitView = function (source, byteOffset, byteLength) {
2020
byteLength = byteLength || source.byteLength /* ArrayBuffer */ || source.length /* Buffer */;
2121

2222
this._view = new Uint8Array(source, byteOffset, byteLength);
23+
24+
this.bigEndian = false;
2325
};
2426

2527
// Used to massage fp values so we can operate on them
@@ -62,11 +64,23 @@ BitView.prototype.getBits = function (offset, bits, signed) {
6264
// the max number of bits we can read from the current byte
6365
var read = Math.min(remaining, 8 - bitOffset);
6466

65-
// create a mask with the correct bit width
66-
var mask = (1 << read) - 1;
67-
// shift the bits we want to the start of the byte and mask of the rest
68-
var readBits = (currentByte >> bitOffset) & mask;
69-
value |= readBits << i;
67+
var mask, readBits;
68+
if (this.bigEndian) {
69+
// create a mask with the correct bit width
70+
mask = ~(0xFF << read);
71+
// shift the bits we want to the start of the byte and mask of the rest
72+
readBits = (currentByte >> (8 - read - bitOffset)) & mask;
73+
74+
value <<= read;
75+
value |= readBits;
76+
} else {
77+
// create a mask with the correct bit width
78+
mask = ~(0xFF << read);
79+
// shift the bits we want to the start of the byte and mask off the rest
80+
readBits = (currentByte >> bitOffset) & mask;
81+
82+
value |= readBits << i;
83+
}
7084

7185
offset += read;
7286
i += read;
@@ -94,19 +108,41 @@ BitView.prototype.setBits = function (offset, value, bits) {
94108
}
95109

96110
for (var i = 0; i < bits;) {
97-
var wrote;
111+
var remaining = bits - i;
112+
var bitOffset = offset & 7;
113+
var byteOffset = offset >> 3;
114+
var wrote = Math.min(remaining, 8 - bitOffset);
115+
116+
var mask, writeBits, destMask;
117+
if (this.bigEndian) {
118+
// create a mask with the correct bit width
119+
mask = ~(~0 << wrote);
120+
// shift the bits we want to the start of the byte and mask of the rest
121+
writeBits = (value >> (bits - i - wrote)) & mask;
122+
123+
var destShift = 8 - bitOffset - wrote;
124+
// destination mask to zero all the bits we're changing first
125+
destMask = ~(mask << destShift);
126+
127+
this._view[byteOffset] =
128+
(this._view[byteOffset] & destMask)
129+
| (writeBits << destShift);
98130

99-
// Write an entire byte if we can.
100-
if ((bits - i) >= 8 && ((offset & 7) === 0)) {
101-
this._view[offset >> 3] = value & 0xFF;
102-
wrote = 8;
103131
} else {
104-
this._setBit(offset, value & 0x1);
105-
wrote = 1;
132+
// create a mask with the correct bit width
133+
mask = ~(0xFF << wrote);
134+
// shift the bits we want to the start of the byte and mask of the rest
135+
writeBits = value & mask;
136+
value >>= wrote;
137+
138+
// destination mask to zero all the bits we're changing first
139+
destMask = ~(mask << bitOffset);
140+
141+
this._view[byteOffset] =
142+
(this._view[byteOffset] & destMask)
143+
| (writeBits << bitOffset);
106144
}
107145

108-
value = (value >> wrote);
109-
110146
offset += wrote;
111147
i += wrote;
112148
}
@@ -361,6 +397,13 @@ Object.defineProperty(BitStream.prototype, 'view', {
361397
configurable: false
362398
});
363399

400+
Object.defineProperty(BitStream.prototype, 'bigEndian', {
401+
get: function () { return this._view.bigEndian; },
402+
set: function (val) { this._view.bigEndian = val; },
403+
enumerable: true,
404+
configurable: false
405+
});
406+
364407
BitStream.prototype.readBits = function (bits, signed) {
365408
var val = this._view.getBits(this._index, bits, signed);
366409
this._index += bits;

test.js

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,3 +371,234 @@ suite('BitBuffer', function () {
371371
assert.equal(0xFFFF, buffer.readUInt16LE(0));
372372
});
373373
});
374+
375+
suite('Reading big/little endian', function () {
376+
var array, u8, bv, bsw, bsr;
377+
378+
setup(function () {
379+
array = new ArrayBuffer(64);
380+
u8 = new Uint8Array(array);
381+
u8[0] = 0x01;
382+
u8[1] = 0x02;
383+
// Test initializing straight from the array.
384+
bsr = new BitStream(array);
385+
});
386+
387+
test('4b, little-endian', function () {
388+
assert.equal(bsr.index, 0, 'BitStream didn\'t init at offset 0');
389+
390+
var result = [];
391+
result.push(bsr.readBits(4));
392+
result.push(bsr.readBits(4));
393+
result.push(bsr.readBits(4));
394+
result.push(bsr.readBits(4));
395+
396+
// 0000 0001 0000 0010 [01 02]
397+
// [#2] [#1] [#4] [#3]
398+
assert.deepEqual(result, [1, 0, 2, 0]);
399+
});
400+
401+
test('8b, little-endian', function () {
402+
assert.equal(bsr.index, 0, 'BitStream didn\'t init at offset 0');
403+
404+
var result = [];
405+
result.push(bsr.readBits(8));
406+
result.push(bsr.readBits(8));
407+
408+
// 0000 0001 0000 0010 [01 02]
409+
// [ #1] [ #2]
410+
assert.deepEqual(result, [1, 2]);
411+
});
412+
413+
test('10b, little-endian', function () {
414+
assert.equal(bsr.index, 0, 'BitStream didn\'t init at offset 0');
415+
416+
var result = [];
417+
result.push(bsr.readBits(10));
418+
419+
// 0000 0001 0000 0010 [01 02]
420+
// ... #1] [ #2][#1...
421+
assert.deepEqual(result, [513]);
422+
});
423+
424+
test('16b, little-endian', function () {
425+
assert.equal(bsr.index, 0, 'BitStream didn\'t init at offset 0');
426+
427+
var result = [];
428+
result.push(bsr.readBits(16));
429+
430+
// 0000 0001 0000 0010 [01 02]
431+
// [ #1]
432+
assert.deepEqual(result, [0x201]);
433+
});
434+
435+
test('24b, little-endian', function () {
436+
u8[2] = 0x03;
437+
assert.equal(bsr.index, 0, 'BitStream didn\'t init at offset 0');
438+
439+
var result = [];
440+
result.push(bsr.readBits(24));
441+
442+
// 0000 0001 0000 0010 0000 0011 [01 02 03]
443+
// [ #1]
444+
assert.deepEqual(result, [0x30201]);
445+
});
446+
447+
test('4b, big-endian', function () {
448+
bsr.bigEndian = true;
449+
assert.equal(bsr.index, 0, 'BitStream didn\'t init at offset 0');
450+
451+
var result = [];
452+
result.push(bsr.readBits(4));
453+
result.push(bsr.readBits(4));
454+
result.push(bsr.readBits(4));
455+
result.push(bsr.readBits(4));
456+
457+
// 0000 0001 0000 0010 [01 02]
458+
// [#1] [#2] [#3] [#4]
459+
assert.deepEqual(result, [0, 1, 0, 2]);
460+
});
461+
462+
test('8b, big-endian', function () {
463+
bsr.bigEndian = true;
464+
assert.equal(bsr.index, 0, 'BitStream didn\'t init at offset 0');
465+
466+
var result = [];
467+
result.push(bsr.readBits(8));
468+
result.push(bsr.readBits(8));
469+
470+
// 0000 0001 0000 0010 [01 02]
471+
// [ #1] [ #2]
472+
assert.deepEqual(result, [1, 2]);
473+
});
474+
475+
test('10b, big-endian', function () {
476+
bsr.bigEndian = true;
477+
assert.equal(bsr.index, 0, 'BitStream didn\'t init at offset 0');
478+
479+
var result = [];
480+
result.push(bsr.readBits(10));
481+
result.push(bsr.readBits(6));
482+
483+
// 0000 0001 0000 0010 [01 02]
484+
// [ #1][ #2]
485+
assert.deepEqual(result, [4, 2]);
486+
});
487+
488+
test('16b, big-endian', function () {
489+
bsr.bigEndian = true;
490+
assert.equal(bsr.index, 0, 'BitStream didn\'t init at offset 0');
491+
492+
var result = [];
493+
result.push(bsr.readBits(16));
494+
495+
// 0000 0001 0000 0010 [01 02]
496+
// [ #1]
497+
assert.deepEqual(result, [0x102]);
498+
});
499+
500+
test('24b, big-endian', function () {
501+
u8[2] = 0x03;
502+
bsr.bigEndian = true;
503+
assert.equal(bsr.index, 0, 'BitStream didn\'t init at offset 0');
504+
505+
var result = [];
506+
result.push(bsr.readBits(24));
507+
508+
// 0000 0001 0000 0010 0000 0011 [01 02 03]
509+
// [ #1]
510+
assert.deepEqual(result, [0x10203]);
511+
});
512+
});
513+
514+
suite('Writing big/little endian', function () {
515+
var array, u8, bv, bsw, bsr;
516+
517+
setup(function () {
518+
array = new ArrayBuffer(2);
519+
u8 = new Uint8Array(array);
520+
bv = new BitView(array);
521+
bsw = new BitStream(bv);
522+
});
523+
524+
test('4b, little-endian', function () {
525+
// 0000 0001 0000 0010 [01 02]
526+
// [#2] [#1] [#4] [#3]
527+
bsw.writeBits(1, 4);
528+
bsw.writeBits(0, 4);
529+
bsw.writeBits(2, 4);
530+
bsw.writeBits(0, 4);
531+
532+
assert.deepEqual(u8, [0x01, 0x02]);
533+
});
534+
535+
test('8b, little-endian', function () {
536+
// 0000 0001 0000 0010 [01 02]
537+
// [ #1] [ #2]
538+
bsw.writeBits(1, 8);
539+
bsw.writeBits(2, 8);
540+
541+
assert.deepEqual(u8, [0x01, 0x02]);
542+
});
543+
544+
test('10b, little-endian', function () {
545+
// 0000 0001 0000 0010 [01 02]
546+
// ... #1] [ #2][#1...
547+
bsw.writeBits(513, 10);
548+
549+
assert.deepEqual(u8, [0x01, 0x02]);
550+
});
551+
552+
test('16b, little-endian', function () {
553+
// 0000 0001 0000 0010 [01 02]
554+
// [ #1]
555+
bsw.writeBits(0x201, 16);
556+
557+
assert.deepEqual(u8, [0x01, 0x02]);
558+
});
559+
560+
test('4b, big-endian', function () {
561+
bsw.bigEndian = true;
562+
563+
// 0000 0001 0000 0010 [01 02]
564+
// [#1] [#2] [#3] [#4]
565+
bsw.writeBits(0, 4);
566+
bsw.writeBits(1, 4);
567+
bsw.writeBits(0, 4);
568+
bsw.writeBits(2, 4);
569+
570+
assert.deepEqual(u8, [0x01, 0x02]);
571+
});
572+
573+
test('8b, big-endian', function () {
574+
bsw.bigEndian = true;
575+
576+
// 0000 0001 0000 0010 [01 02]
577+
// [ #1] [ #2]
578+
bsw.writeBits(1, 8);
579+
bsw.writeBits(2, 8);
580+
581+
assert.deepEqual(u8, [0x01, 0x02]);
582+
});
583+
584+
test('10b, big-endian', function () {
585+
bsw.bigEndian = true;
586+
587+
// 0000 0001 0000 0010 [01 02]
588+
// [ #1][ #2]
589+
bsw.writeBits(4, 10);
590+
bsw.writeBits(2, 6);
591+
592+
assert.deepEqual(u8, [0x01, 0x02]);
593+
});
594+
595+
test('16b, big-endian', function () {
596+
bsw.bigEndian = true;
597+
598+
// 0000 0001 0000 0010 [01 02]
599+
// [ #1]
600+
bsw.writeBits(0x102, 16);
601+
602+
assert.deepEqual(u8, [0x01, 0x02]);
603+
});
604+
});

0 commit comments

Comments
 (0)