Skip to content

Commit

Permalink
feat: add I32LEB128 (#11)
Browse files Browse the repository at this point in the history
Co-authored-by: Elias Sjögreen <eliassjogreen1@gmail.com>
  • Loading branch information
MierenManz and eliassjogreen authored Apr 21, 2023
1 parent 90cc9a8 commit dc59df0
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 0 deletions.
40 changes: 40 additions & 0 deletions types/varint/leb128.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { Type } from "../types.ts";

const SEGMENT_BITS = 0x7F;
const CONTINUE_BIT = 0x80;

export class I32LEB128 implements Type<number> {
read(dataView: DataView, byteOffset = 0): number {
let value = 0, position = 0;
while (true) {
const currentByte = dataView.getInt8(byteOffset);
value |= (currentByte & SEGMENT_BITS) << position;

if ((currentByte & CONTINUE_BIT) === 0) break;

position += 7;
byteOffset++;

if (position >= 32) {
throw new TypeError("I32LEB128 cannot exceed 32 bits in length");
}
}

return value;
}

write(value: number, dataView: DataView, byteOffset = 0): void {
while (true) {
if ((value & ~SEGMENT_BITS) === 0) {
dataView.setInt8(byteOffset, value);
return;
}

dataView.setInt8(byteOffset, value & SEGMENT_BITS | CONTINUE_BIT);
byteOffset++;
value >>>= 7;
}
}
}

export const i32leb128 = new I32LEB128();
94 changes: 94 additions & 0 deletions types/varint/leb128_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { I32LEB128 } from "./mod.ts";
import {
assertEquals,
assertThrows,
} from "https://deno.land/std@0.183.0/testing/asserts.ts";

Deno.test({
name: "Read Positive varint",
fn: () => {
let data = Uint8Array.of(127);
let result = new I32LEB128().read(new DataView(data.buffer));
assertEquals(result, 127);

data = Uint8Array.of(128, 1);
result = new I32LEB128().read(new DataView(data.buffer));
assertEquals(result, 128);

data = Uint8Array.of(221, 199, 1);
result = new I32LEB128().read(new DataView(data.buffer));
assertEquals(result, 25565);

data = Uint8Array.of(255, 255, 255, 255, 7);
result = new I32LEB128().read(new DataView(data.buffer));
assertEquals(result, 2147483647);
},
});

Deno.test({
name: "Read Negative varint",
fn: () => {
let data = Uint8Array.of(255, 255, 255, 255, 15);
let result = new I32LEB128().read(new DataView(data.buffer));
assertEquals(result, -1);

data = Uint8Array.of(128, 128, 128, 128, 8);
result = new I32LEB128().read(new DataView(data.buffer));
assertEquals(result, -2147483648);
},
});

Deno.test({
name: "Read Bad varint",
fn: () => {
const data = Uint8Array.of(255, 255, 255, 255, 255, 15);
assertThrows(() => new I32LEB128().read(new DataView(data.buffer)));
},
});

Deno.test({
name: "Write Positive varint",
fn: () => {
let data = new Uint8Array(1);
new I32LEB128().write(127, new DataView(data.buffer));
assertEquals(data, Uint8Array.of(127));

data = new Uint8Array(2);
new I32LEB128().write(128, new DataView(data.buffer));
assertEquals(data, Uint8Array.of(128, 1));

data = new Uint8Array(3);
new I32LEB128().write(25565, new DataView(data.buffer));
assertEquals(data, Uint8Array.of(221, 199, 1));

data = new Uint8Array(5);
new I32LEB128().write(2147483647, new DataView(data.buffer));
assertEquals(data, Uint8Array.of(255, 255, 255, 255, 7));
},
});

Deno.test({
name: "Write Negative varint",
fn: () => {
let data = new Uint8Array(5);
new I32LEB128().write(-1, new DataView(data.buffer));
assertEquals(data, Uint8Array.of(255, 255, 255, 255, 15));

data = new Uint8Array(5);
new I32LEB128().write(-2147483648, new DataView(data.buffer));
assertEquals(data, Uint8Array.of(128, 128, 128, 128, 8));
},
});

Deno.test({
name: "Write & read i32 MAX",
fn: () => {
const value = 2_147_483_647;
const decoder = new I32LEB128();
const bytes = new Uint8Array(5);
const dt = new DataView(bytes.buffer);
decoder.write(value, dt, 0);
const decodedValue = decoder.read(dt);
assertEquals(decodedValue, value);
},
});
1 change: 1 addition & 0 deletions types/varint/mod.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./leb128.ts";

0 comments on commit dc59df0

Please sign in to comment.