Skip to content

Commit

Permalink
refactor(bitfield): update backing array types from u32 -> u8
Browse files Browse the repository at this point in the history
- update BitField & BitMatrix to use Uint8Array
  • Loading branch information
postspectacular committed Oct 22, 2022
1 parent e56eded commit aaa0ecb
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 38 deletions.
32 changes: 17 additions & 15 deletions packages/bitfield/src/bitfield.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,19 @@ import { assert } from "@thi.ng/errors/assert";
import { binOp, toString } from "./util.js";

/**
* 1D bit field, backed by a Uint32Array. Hence size is always rounded
* up to a multiple of 32.
* 1D bit field, backed by a Uint8Array. Hence size is always rounded
* up to a multiple of 8.
*/
export class BitField implements IClear, ICopy<BitField>, ILength {
data: Uint32Array;
/** Backing byte array */
data: Uint8Array;
/** Field size in bits (always a multiple of 8) */
n: number;

constructor(bits: number | string | ArrayLike<boolean | number>) {
const isNumber = typeof bits === "number";
this.n = align(isNumber ? <number>bits : (<any>bits).length, 32);
this.data = new Uint32Array(this.n >>> 5);
this.n = align(isNumber ? <number>bits : (<any>bits).length, 8);
this.data = new Uint8Array(this.n >>> 3);
!isNumber && this.setRange(0, <any>bits);
}

Expand All @@ -30,7 +32,7 @@ export class BitField implements IClear, ICopy<BitField>, ILength {
*[Symbol.iterator]() {
const { data, n } = this;
for (let i = 0; i < n; i++) {
yield data[i >>> 5] & (1 << (~i & 31)) ? 1 : 0;
yield data[i >>> 3] & (1 << (~i & 7)) ? 1 : 0;
}
}

Expand All @@ -40,7 +42,7 @@ export class BitField implements IClear, ICopy<BitField>, ILength {
*positions() {
const { data, n } = this;
for (let i = 0; i < n; i++) {
if (data[i >>> 5] & (1 << (~i & 31))) yield i;
if (data[i >>> 3] & (1 << (~i & 7))) yield i;
}
}

Expand All @@ -56,14 +58,14 @@ export class BitField implements IClear, ICopy<BitField>, ILength {

/**
* Resizes bitfield to new size given (rounded up to multiples of
* 32).
* 8).
*
* @param n - new size
*/
resize(n: number) {
n = align(n, 32);
n = align(n, 8);
if (n === this.n) return this;
const dest = new Uint32Array(n >>> 5);
const dest = new Uint8Array(n >>> 3);
dest.set(this.data.slice(0, dest.length));
this.data = dest;
this.n = n;
Expand All @@ -77,7 +79,7 @@ export class BitField implements IClear, ICopy<BitField>, ILength {
* @param n - bit number
*/
at(n: number) {
return this.data[n >>> 5] & (1 << (~n & 31));
return this.data[n >>> 3] & (1 << (~n & 7));
}

/**
Expand All @@ -88,8 +90,8 @@ export class BitField implements IClear, ICopy<BitField>, ILength {
* @param v - new bit value
*/
setAt(n: number, v: boolean | number = true) {
const id = n >>> 5;
const mask = 1 << (~n & 31);
const id = n >>> 3;
const mask = 1 << (~n & 7);
const r = this.data[id] & mask;
if (v) {
this.data[id] |= mask;
Expand Down Expand Up @@ -125,8 +127,8 @@ export class BitField implements IClear, ICopy<BitField>, ILength {
* @param n - bit number
*/
toggleAt(n: number) {
const id = n >>> 5;
const mask = 1 << (~n & 31);
const id = n >>> 3;
const mask = 1 << (~n & 7);
const r = this.data[id] & mask;
if (r) {
this.data[id] &= ~mask;
Expand Down
42 changes: 23 additions & 19 deletions packages/bitfield/src/bitmatrix.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,24 @@ import { BitField } from "./bitfield.js";
import { binOp, toString } from "./util.js";

/**
* MxN row-major 2D bit matrix, backed by a Uint32Array. Hence the width
* (number of columns) is always rounded up to a multiple of 32.
* MxN row-major 2D bit matrix, backed by a Uint8Array. Hence the width
* (number of columns) is always rounded up to a multiple of 8.
*/
export class BitMatrix implements IClear, ICopy<BitMatrix> {
data: Uint32Array;
/** Backing byte array */
data: Uint8Array;
/** Number of bytes per row */
stride: number;
/** Number of rows */
m: number;
/** Number of columns */
n: number;

constructor(rows: number, cols = rows) {
this.m = rows;
this.n = cols = align(cols, 32);
this.stride = cols >>> 5;
this.data = new Uint32Array(rows * this.stride);
this.n = cols = align(cols, 8);
this.stride = cols >>> 3;
this.data = new Uint8Array(rows * this.stride);
}

get length() {
Expand All @@ -40,19 +44,19 @@ export class BitMatrix implements IClear, ICopy<BitMatrix> {

/**
* Resizes matrix to new size given (width always rounded up to
* multiples of 32).
* multiples of 8).
*
* @param m - new number of rows
* @param n - new number of cols
*/
resize(m: number, n = m) {
n = align(n, 32);
n = align(n, 8);
if (m === this.m && n === this.n) return this;
const dstride = n >>> 5;
const dstride = n >>> 3;
const sstride = this.stride;
const w = Math.min(dstride, sstride);
const src = this.data;
const dest = new Uint32Array(m * dstride);
const dest = new Uint8Array(m * dstride);
for (
let i = Math.min(m, this.m) - 1, si = i * sstride, di = i * dstride;
i >= 0;
Expand All @@ -75,7 +79,7 @@ export class BitMatrix implements IClear, ICopy<BitMatrix> {
* @param n - column
*/
at(m: number, n: number) {
return this.data[(n >>> 5) + m * this.stride] & (1 << (~n & 31));
return this.data[(n >>> 3) + m * this.stride] & (1 << (~n & 7));
}

/**
Expand All @@ -87,8 +91,8 @@ export class BitMatrix implements IClear, ICopy<BitMatrix> {
* @param v - bit value
*/
setAt(m: number, n: number, v: boolean | number = true) {
const id = (n >>> 5) + m * this.stride;
const mask = 1 << (~n & 31);
const id = (n >>> 3) + m * this.stride;
const mask = 1 << (~n & 7);
const r = this.data[id] & mask;
if (v) {
this.data[id] |= mask;
Expand All @@ -106,8 +110,8 @@ export class BitMatrix implements IClear, ICopy<BitMatrix> {
* @param n - column
*/
toggleAt(m: number, n: number) {
const id = (n >>> 5) + m * this.stride;
const mask = 1 << (~n & 31);
const id = (n >>> 3) + m * this.stride;
const mask = 1 << (~n & 7);
const r = this.data[id] & mask;
if (r) {
this.data[id] &= ~mask;
Expand Down Expand Up @@ -149,9 +153,9 @@ export class BitMatrix implements IClear, ICopy<BitMatrix> {
popCountColumn(n: number) {
ensureIndex(n, 0, this.n);
const { data, stride, m } = this;
const mask = 1 << (~n & 31);
const mask = 1 << (~n & 7);
let res = 0;
for (let i = n >>> 5, j = 0; j < m; i += stride, j++) {
for (let i = n >>> 3, j = 0; j < m; i += stride, j++) {
data[i] & mask && res++;
}
return res;
Expand All @@ -176,8 +180,8 @@ export class BitMatrix implements IClear, ICopy<BitMatrix> {
ensureIndex(n, 0, this.n);
const { data, stride, m } = this;
const column = new BitField(m);
const mask = 1 << (~n & 31);
for (let i = n >>> 5, j = 0; j < m; i += stride, j++) {
const mask = 1 << (~n & 7);
for (let i = n >>> 3, j = 0; j < m; i += stride, j++) {
data[i] & mask && column.setAt(j);
}
return column;
Expand Down
8 changes: 4 additions & 4 deletions packages/bitfield/src/util.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// thing:no-export
import type { Fn2 } from "@thi.ng/api";
import { B32 } from "@thi.ng/strings/radix";
import { B8 } from "@thi.ng/strings/radix";

/**
* Converts 1D bitfield to binary string.
Expand All @@ -9,7 +9,7 @@ import { B32 } from "@thi.ng/strings/radix";
*
* @internal
*/
export const toString = (data: Uint32Array) => [...data].map(B32).join("");
export const toString = (data: Uint8Array) => [...data].map(B8).join("");

/**
* @param dest -
Expand All @@ -19,8 +19,8 @@ export const toString = (data: Uint32Array) => [...data].map(B32).join("");
* @internal
*/
export const binOp = (
dest: Uint32Array,
src: Uint32Array,
dest: Uint8Array,
src: Uint8Array,
op: Fn2<number, number, number>
) => {
for (let i = src.length; i-- > 0; ) dest[i] = op(src[i], dest[i]);
Expand Down

0 comments on commit aaa0ecb

Please sign in to comment.