UNPKG

3.54 kBJavaScriptView Raw
1// Copyright 2012 The Obvious Corporation.
2
3/*
4 * bits: Bitwise buffer utilities. The utilities here treat a buffer
5 * as a little-endian bigint, so the lowest-order bit is bit #0 of
6 * `buffer[0]`, and the highest-order bit is bit #7 of
7 * `buffer[buffer.length - 1]`.
8 */
9
10/*
11 * Modules used
12 */
13"use strict";
14/*
15 * Exported bindings
16 */
17
18/**
19 * Extracts the given number of bits from the buffer at the indicated
20 * index, returning a simple number as the result. If bits are requested
21 * that aren't covered by the buffer, the `defaultBit` is used as their
22 * value.
23 *
24 * The `bitLength` must be no more than 32. The `defaultBit` if not
25 * specified is taken to be `0`.
26 */
27
28export function extract(buffer, bitIndex, bitLength, defaultBit) {
29 if (bitLength < 0 || bitLength > 32) {
30 throw new Error("Bad value for bitLength.");
31 }
32
33 if (defaultBit === undefined) {
34 defaultBit = 0;
35 } else if (defaultBit !== 0 && defaultBit !== 1) {
36 throw new Error("Bad value for defaultBit.");
37 }
38
39 var defaultByte = defaultBit * 0xff;
40 var result = 0; // All starts are inclusive. The {endByte, endBit} pair is exclusive, but
41 // if endBit !== 0, then endByte is inclusive.
42
43 var lastBit = bitIndex + bitLength;
44 var startByte = Math.floor(bitIndex / 8);
45 var startBit = bitIndex % 8;
46 var endByte = Math.floor(lastBit / 8);
47 var endBit = lastBit % 8;
48
49 if (endBit !== 0) {
50 // `(1 << endBit) - 1` is the mask of all bits up to but not including
51 // the endBit.
52 result = get(endByte) & (1 << endBit) - 1;
53 }
54
55 while (endByte > startByte) {
56 endByte--;
57 result = result << 8 | get(endByte);
58 }
59
60 result >>>= startBit;
61 return result;
62
63 function get(index) {
64 var result = buffer[index];
65 return result === undefined ? defaultByte : result;
66 }
67}
68/**
69 * Injects the given bits into the given buffer at the given index. Any
70 * bits in the value beyond the length to set are ignored.
71 */
72
73export function inject(buffer, bitIndex, bitLength, value) {
74 if (bitLength < 0 || bitLength > 32) {
75 throw new Error("Bad value for bitLength.");
76 }
77
78 var lastByte = Math.floor((bitIndex + bitLength - 1) / 8);
79
80 if (bitIndex < 0 || lastByte >= buffer.length) {
81 throw new Error("Index out of range.");
82 } // Just keeping it simple, until / unless profiling shows that this
83 // is a problem.
84
85
86 var atByte = Math.floor(bitIndex / 8);
87 var atBit = bitIndex % 8;
88
89 while (bitLength > 0) {
90 if (value & 1) {
91 buffer[atByte] |= 1 << atBit;
92 } else {
93 buffer[atByte] &= ~(1 << atBit);
94 }
95
96 value >>= 1;
97 bitLength--;
98 atBit = (atBit + 1) % 8;
99
100 if (atBit === 0) {
101 atByte++;
102 }
103 }
104}
105/**
106 * Gets the sign bit of the given buffer.
107 */
108
109export function getSign(buffer) {
110 return buffer[buffer.length - 1] >>> 7;
111}
112/**
113 * Gets the zero-based bit number of the highest-order bit with the
114 * given value in the given buffer.
115 *
116 * If the buffer consists entirely of the other bit value, then this returns
117 * `-1`.
118 */
119
120export function highOrder(bit, buffer) {
121 var length = buffer.length;
122 var fullyWrongByte = (bit ^ 1) * 0xff; // the other-bit extended to a full byte
123
124 while (length > 0 && buffer[length - 1] === fullyWrongByte) {
125 length--;
126 }
127
128 if (length === 0) {
129 // Degenerate case. The buffer consists entirely of ~bit.
130 return -1;
131 }
132
133 var byteToCheck = buffer[length - 1];
134 var result = length * 8 - 1;
135
136 for (var i = 7; i > 0; i--) {
137 if ((byteToCheck >> i & 1) === bit) {
138 break;
139 }
140
141 result--;
142 }
143
144 return result;
145}
\No newline at end of file