arquero
Version:
Query processing and transformation of array-backed data tables.
166 lines (145 loc) • 3.39 kB
JavaScript
const ONE = 0x80000000;
const ALL = 0xFFFFFFFF;
/**
* Represent an indexable set of bits.
*/
export class BitSet {
/**
* Instantiate a new BitSet instance.
* @param {number} size The number of bits.
*/
constructor(size) {
this._size = size;
this._bits = new Uint32Array(Math.ceil(size / 32));
}
/**
* The number of bits.
* @return {number}
*/
get length() {
return this._size;
}
/**
* The number of bits set to one.
* https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetKernighan
* @return {number}
*/
count() {
const n = this._bits.length;
let count = 0;
for (let i = 0; i < n; ++i) {
for (let b = this._bits[i]; b; ++count) {
b &= b - 1;
}
}
return count;
}
/**
* Get the bit at a given index.
* @param {number} i The bit index.
*/
get(i) {
return this._bits[i >> 5] & (ONE >>> i);
}
/**
* Set the bit at a given index to one.
* @param {number} i The bit index.
*/
set(i) {
this._bits[i >> 5] |= (ONE >>> i);
}
/**
* Clear the bit at a given index to zero.
* @param {number} i The bit index.
*/
clear(i) {
this._bits[i >> 5] &= ~(ONE >>> i);
}
/**
* Scan the bits, invoking a callback function with the index of
* each non-zero bit.
* @param {(i: number) => void} fn A callback function.
*/
scan(fn) {
for (let i = this.next(0); i >= 0; i = this.next(i + 1)) {
fn(i);
}
}
/**
* Get the next non-zero bit starting from a given index.
* @param {number} i The bit index.
*/
next(i) {
const bits = this._bits;
const n = bits.length;
let index = i >> 5;
let curr = bits[index] & (ALL >>> i);
for (; index < n; curr = bits[++index]) {
if (curr !== 0) {
return (index << 5) + Math.clz32(curr);
}
}
return -1;
}
/**
* Return the index of the nth non-zero bit.
* @param {number} n The number of non-zero bits to advance.
* @return {number} The index of the nth non-zero bit.
*/
nth(n) {
let i = this.next(0);
while (n-- && i >= 0) i = this.next(i + 1);
return i;
}
/**
* Negate all bits in this bitset.
* Modifies this BitSet in place.
* @return {this}
*/
not() {
const bits = this._bits;
const n = bits.length;
// invert all bits
for (let i = 0; i < n; ++i) {
bits[i] = ~bits[i];
}
// unset extraneous trailing bits
const tail = this._size % 32;
if (tail) {
bits[n - 1] &= ONE >> (tail - 1);
}
return this;
}
/**
* Compute the logical AND of this BitSet and another.
* @param {BitSet} bitset The BitSet to combine with.
* @return {BitSet} This BitSet updated with the logical AND.
*/
and(bitset) {
if (bitset) {
const a = this._bits;
const b = bitset._bits;
const n = a.length;
for (let i = 0; i < n; ++i) {
a[i] &= b[i];
}
}
return this;
}
/**
* Compute the logical OR of this BitSet and another.
* @param {BitSet} bitset The BitSet to combine with.
* @return {BitSet} This BitSet updated with the logical OR.
*/
or(bitset) {
if (bitset) {
const a = this._bits;
const b = bitset._bits;
const n = a.length;
for (let i = 0; i < n; ++i) {
a[i] |= b[i];
}
}
return this;
}
}