cabana/src/models/bitarray.js

221 lines
5.6 KiB
JavaScript

// https://github.com/bitwiseshiftleft/blob/master/core/bitArray.js
const bitArray = {
/**
* Array slices in units of bits.
* @param {bitArray} a The array to slice.
* @param {Number} bstart The offset to the start of the slice, in bits.
* @param {Number} bend The offset to the end of the slice, in bits. If this is undefined,
* slice until the end of the array.
* @return {bitArray} The requested slice.
*/
bitSlice: function(a, bstart, bend) {
a = bitArray._shiftRight(a.slice(bstart / 32), 32 - (bstart & 31)).slice(1);
return bend === undefined ? a : bitArray.clamp(a, bend - bstart);
},
/**
* Extract a number packed into a bit array.
* @param {bitArray} a The array to slice.
* @param {Number} bstart The offset to the start of the slice, in bits.
* @param {Number} blength The length of the number to extract.
* @return {Number} The requested slice.
*/
extract: function(a, bstart, blength) {
// FIXME: this Math.floor is not necessary at all, but for some reason
// seems to suppress a bug in the Chromium JIT.
var x,
sh = Math.floor((-bstart - blength) & 31);
if (((bstart + blength - 1) ^ bstart) & -32) {
// it crosses a boundary
x =
(a[(bstart / 32) | 0] << (32 - sh)) ^ (a[(bstart / 32 + 1) | 0] >>> sh);
} else {
// within a single word
x = a[(bstart / 32) | 0] >>> sh;
}
return x & ((1 << blength) - 1);
},
/**
* Concatenate two bit arrays.
* @param {bitArray} a1 The first array.
* @param {bitArray} a2 The second array.
* @return {bitArray} The concatenation of a1 and a2.
*/
concat: function(a1, a2) {
if (a1.length === 0 || a2.length === 0) {
return a1.concat(a2);
}
var last = a1[a1.length - 1],
shift = bitArray.getPartial(last);
if (shift === 32) {
return a1.concat(a2);
} else {
return bitArray._shiftRight(
a2,
shift,
last | 0,
a1.slice(0, a1.length - 1)
);
}
},
/**
* Find the length of an array of bits.
* @param {bitArray} a The array.
* @return {Number} The length of a, in bits.
*/
bitLength: function(a) {
var l = a.length,
x;
if (l === 0) {
return 0;
}
x = a[l - 1];
return (l - 1) * 32 + bitArray.getPartial(x);
},
/**
* Truncate an array.
* @param {bitArray} a The array.
* @param {Number} len The length to truncate to, in bits.
* @return {bitArray} A new array, truncated to len bits.
*/
clamp: function(a, len) {
if (a.length * 32 < len) {
return a;
}
a = a.slice(0, Math.ceil(len / 32));
var l = a.length;
len &= 31;
if (l > 0 && len) {
a[l - 1] = bitArray.partial(len, a[l - 1] & (0x80000000 >> (len - 1)), 1);
}
return a;
},
/**
* Make a partial word for a bit array.
* @param {Number} len The number of bits in the word.
* @param {Number} x The bits.
* @param {Number} [_end=0] Pass 1 if x has already been shifted to the high side.
* @return {Number} The partial word.
*/
partial: function(len, x, _end) {
if (len === 32) {
return x;
}
return (_end ? x | 0 : x << (32 - len)) + len * 0x10000000000;
},
/**
* Get the number of bits used by a partial word.
* @param {Number} x The partial word.
* @return {Number} The number of bits used by the partial word.
*/
getPartial: function(x) {
return Math.round(x / 0x10000000000) || 32;
},
/**
* Compare two arrays for equality in a predictable amount of time.
* @param {bitArray} a The first array.
* @param {bitArray} b The second array.
* @return {boolean} true if a == b; false otherwise.
*/
equal: function(a, b) {
if (bitArray.bitLength(a) !== bitArray.bitLength(b)) {
return false;
}
var x = 0,
i;
for (i = 0; i < a.length; i++) {
x |= a[i] ^ b[i];
}
return x === 0;
},
/** Shift an array right.
* @param {bitArray} a The array to shift.
* @param {Number} shift The number of bits to shift.
* @param {Number} [carry=0] A byte to carry in
* @param {bitArray} [out=[]] An array to prepend to the output.
* @private
*/
_shiftRight: function(a, shift, carry, out) {
var i,
last2 = 0,
shift2;
if (out === undefined) {
out = [];
}
for (; shift >= 32; shift -= 32) {
out.push(carry);
carry = 0;
}
if (shift === 0) {
return out.concat(a);
}
for (i = 0; i < a.length; i++) {
out.push(carry | (a[i] >>> shift));
carry = a[i] << (32 - shift);
}
last2 = a.length ? a[a.length - 1] : 0;
shift2 = bitArray.getPartial(last2);
out.push(
bitArray.partial(
(shift + shift2) & 31,
shift + shift2 > 32 ? carry : out.pop(),
1
)
);
return out;
},
/** xor a block of 4 words together.
* @private
*/
_xor4: function(x, y) {
return [x[0] ^ y[0], x[1] ^ y[1], x[2] ^ y[2], x[3] ^ y[3]];
},
/** byteswap a word array inplace.
* (does not handle partial words)
* @param {bitArray} a word array
* @return {bitArray} byteswapped array
*/
byteswapM: function(a) {
var i,
v,
m = 0xff00;
for (i = 0; i < a.length; ++i) {
v = a[i];
a[i] = (v >>> 24) | ((v >>> 8) & m) | ((v & m) << 8) | (v << 24);
}
return a;
}
};
export default {
fromBytes: function(bytes) {
var out = [],
i,
tmp = 0;
for (i = 0; i < bytes.length; i++) {
tmp = (tmp << 8) | bytes[i];
if ((i & 3) === 3) {
out.push(tmp);
tmp = 0;
}
}
if (i & 3) {
out.push(bitArray.partial(8 * (i & 3), tmp));
}
return out;
},
...bitArray
};