Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add BitSequence, improve $.bitSequence #69

Merged
merged 4 commits into from
Jul 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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