Skip to content

Instantly share code, notes, and snippets.

@alexsasharegan
Last active December 8, 2018 01:22
Show Gist options
  • Save alexsasharegan/549d942ce4e17c20eec241cdba8e5d6f to your computer and use it in GitHub Desktop.
Save alexsasharegan/549d942ce4e17c20eec241cdba8e5d6f to your computer and use it in GitHub Desktop.
Bitwise manipulations wrapped in a class with semantically meaningful methods.
export class Bits {
static get Nil() {
return 0;
}
static get All() {
// Flip the sign bit out of all bits engaged.
return ~0 ^ (1 << 31);
}
value: number;
constructor(n?: number = Bits.Nil) {
this.value = n;
}
/**
* Equals
*/
eq(n: number): boolean {
return this.value === n;
}
/**
* Not Equals
*/
ne(n: number): boolean {
return this.value !== n;
}
/**
* Greater Than
*/
gt(n: number): boolean {
return this.value > n;
}
/**
* Greater Than or Equal
*/
gte(n: number): boolean {
return this.value >= n;
}
/**
* Less Than
*/
lt(n: number): boolean {
return this.value < n;
}
/**
* Less Than or Equal
*/
lte(n: number): boolean {
return this.value <= n;
}
/**
* Test the value of `Bits` to see if they include all the bits in `mask`.
*/
includes(mask: number): boolean {
return (this.value & mask) === mask;
}
/**
* Test the value of `Bits` to see if they include any of the bits in `mask`.
*/
some(mask: number): boolean {
return (this.value & mask) > 0;
}
/**
* Test the value of `Bits` to see if they include all the bits in `mask`.
*/
every(mask: number): boolean {
return this.includes(mask);
}
/**
* Combine the value of `Bits` with all the given flags,
* and return a new instance.
*/
combine(...flags: number[]) {
return new this.constructor(
flags.reduce((n, flag) => n | flag, this.value),
);
}
/**
* Combine the value of `Bits` with all the given flags,
* and mutate the instance.
*/
combineMut(...flags: number[]) {
this.value = flags.reduce((n, flag) => n | flag, this.value);
return this;
}
/**
* Flip the bits in `mask` in the value of `Bits`,
* and return a new instance.
*/
flip(mask: number) {
return new this.constructor(this.value ^ mask);
}
/**
* Flip the bits in `mask` in the value of `Bits`,
* and mutate the instance.
*/
flipMut(mask: number) {
this.value = this.value ^ mask;
return this;
}
invert() {
return new this.constructor(~this.value);
}
invertMut() {
this.value = ~this.value;
return this;
}
/**
* Remove the bits in `mask` from the value of `Bits`,
* and return a new instance.
*/
remove(mask: number) {
return new this.constructor(this.value & ~mask);
}
/**
* Remove the bits in `mask` from the value of `Bits`,
* and mutate the instance.
*/
removeMut(mask: number) {
this.value = this.value & ~mask;
return this;
}
toString(): string {
return this.value.toString(2);
}
keys(): number[] {
return Array.from(bitKeys(this.value));
}
values(): number[] {
return Array.from(bitValues(this.value));
}
entries(): [number, number][] {
// $FlowIgnore
return Array.from(this);
}
// $FlowIgnore
[Symbol.iterator]() {
return bitEntries(this.value);
}
static fromString(s: string) {
return new this(atoi(s));
}
}
/**
* ASCII to Integer (Binary)
*/
export function atoi(a?: any): number {
if (!a || typeof a !== 'string') {
return 0;
}
let n = parseInt(a, 2);
if (Number.isNaN(n)) {
return 0;
}
return n;
}
/**
* Yields bit indexes for enabled bits
*/
export function* bitKeys(n: number): Iterator<number> {
for (let i = 0; i < 32; i++) {
if ((n & (1 << i)) > 0) {
yield i;
}
}
}
/**
* Yields base 2 values for enabled bits
*/
export function* bitValues(n: number): Iterator<number> {
for (let i = 0; i < 32; i++) {
if ((n & (1 << i)) > 0) {
yield 1 << i;
}
}
}
/**
* Yields [bit index, bit value] for enabled bits
*/
export function* bitEntries(n: number): Iterator<[number, number]> {
for (let i = 0; i < 32; i++) {
if ((n & (1 << i)) > 0) {
yield [i, 1 << i];
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment