Skip to content

Commit

Permalink
feat: add BitSequence & improve $.bitSequence (#69)
Browse files Browse the repository at this point in the history
  • Loading branch information
tjjfvi authored Jul 22, 2022
1 parent 4fd4038 commit 1ca5c77
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 20 deletions.
8 changes: 7 additions & 1 deletion bitSequence/__snapshots__/test.ts.snap
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
export const snapshot = {};

snapshot[`bitSequence Uint8Array(0) [] 1`] = ``;
snapshot[`bitSequence BitSequence { data: Uint8Array(0) [], length: 0 } 1`] = `00`;

snapshot[`bitSequence BitSequence { data: Uint8Array(2) [ 106, 40 ], length: 13 } 1`] = `
34
6a
28
`;
79 changes: 70 additions & 9 deletions bitSequence/codec.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,78 @@
import { Codec, createCodec } from "../common.ts";
import { nCompact } from "../compact/codec.ts";

export const bitSequence: Codec<Uint8Array> = createCodec({
export class BitSequence {
readonly data;

[index: number]: boolean

constructor(readonly length = 0, data?: Uint8Array) {
const byteLength = Math.ceil(length / 8);
data ??= new Uint8Array(byteLength);
if (data.length !== byteLength) {
throw new Error("Incorrectly sized Uint8Array passed to BitSequence constructor");
}
this.data = data;
}

static from(array: (boolean | 0 | 1)[]) {
const sequence = new BitSequence(array.length);
for (let i = 0; i < array.length; i++) {
sequence._setBit(i, array[i]!);
}
return sequence;
}

get byteLength() {
return this.data.length;
}

_hasBit(index: number): boolean {
return 0 <= index && index < this.length && index === Math.floor(index);
}

_getBit(index: number): boolean | undefined {
if (!this._hasBit(index)) return undefined;
const i = Math.floor(index / 8);
const j = 7 - index % 8;
return !!(this.data[i]! & (1 << j));
}

_setBit(index: number, bit: boolean | 0 | 1): boolean {
if (!this._hasBit(index)) return false;
const i = Math.floor(index / 8);
const j = 7 - index % 8;
this.data[i] = this.data[i]! & ~(1 << j) | (+!!bit << j);
return true;
}
}

Object.setPrototypeOf(
BitSequence.prototype,
new Proxy(Object.prototype, {
get: (target, k, receiver) => {
const i = typeof k === "string" ? +k : NaN;
if (isNaN(i)) return Reflect.get(target, k, receiver);
return (receiver as BitSequence)._getBit(i);
},
set: (target, k, v, receiver) => {
const i = typeof k === "string" ? +k : NaN;
if (isNaN(i)) return Reflect.set(target, k, v, receiver);
return (receiver as BitSequence)._setBit(i, v);
},
}),
);

export const bitSequence: Codec<BitSequence> = createCodec({
_metadata: null,
_staticSize: 0,
_encode(_buffer, _value) {
// TODO
return undefined!;
_staticSize: nCompact._staticSize,
_encode(buffer, value) {
nCompact._encode(buffer, value.length);
buffer.insertArray(value.data);
},
_decode(buffer) {
const len = Math.ceil(nCompact._decode(buffer) / 8);
const beg = buffer.index;
const end = buffer.index += len;
return buffer.array.subarray(beg, end);
const length = nCompact._decode(buffer);
const byteLength = Math.ceil(length / 8);
return new BitSequence(length, buffer.array.subarray(buffer.index, buffer.index += byteLength));
},
});
6 changes: 4 additions & 2 deletions bitSequence/fixtures.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use bitvec::{bitvec, order::Msb0};

// TODO
crate::fixtures!(bitvec![u8, Msb0; 1; 8]);
crate::fixtures!(
bitvec![u8, Msb0; ],
bitvec![u8, Msb0; 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1]
);
18 changes: 16 additions & 2 deletions bitSequence/test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,21 @@
import { assertEquals } from "std/testing/asserts.ts";
import * as $ from "../mod.ts";
import { testCodec } from "../test-util.ts";

// TODO
Deno.test("BitSequence", () => {
const sequence = new $.BitSequence(100);
for (let i = 0; i < 100; i++) {
sequence[i] = Math.sqrt(i) === (Math.sqrt(i) | 0);
}
assertEquals(sequence[0], true);
assertEquals(sequence[12], false);
assertEquals(sequence[49], true);
assertEquals(sequence[-1], undefined);
assertEquals(sequence[1.5], undefined);
assertEquals(sequence[121], undefined);
});

testCodec("bitSequence", $.bitSequence, [
new Uint8Array([]),
$.BitSequence.from([]),
$.BitSequence.from([0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1]),
]);
3 changes: 0 additions & 3 deletions fixtures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,6 @@ pub(crate) fn test_fixtures(
path: &'static str,
values: Vec<(&'static str, Vec<u8>)>,
) {
if path == "./bitSequence/fixtures.rs" {
return;
}
let snapshot = fs::read_to_string(
Path::new(path)
.parent()
Expand Down
3 changes: 0 additions & 3 deletions test-util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@ export const files = { lipsum: lipsum!, words: words!, cargoLock: cargoLock! };

export function testCodec<T>(name: string, codec: Codec<T>, values: NoInfer<T>[] | Record<string, NoInfer<T>>): void;
export function testCodec<T>(name: string, codec: Codec<T>, values: T[] | Record<string, T>) {
if (name === "bitSequence") {
return;
}
for (const key in values) {
const value = values[key as never] as T;
const label = values instanceof Array
Expand Down

0 comments on commit 1ca5c77

Please sign in to comment.