Skip to content

Commit

Permalink
feat: add I64LEB128 (#12)
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 committed Apr 26, 2023
1 parent dc59df0 commit 80802e1
Show file tree
Hide file tree
Showing 22 changed files with 481 additions and 110 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v2
uses: actions/checkout@v3

- name: Setup latest deno version
uses: denoland/setup-deno@main
Expand All @@ -22,7 +22,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v2
uses: actions/checkout@v3

- name: Setup latest deno version
uses: denoland/setup-deno@main
Expand Down
4 changes: 2 additions & 2 deletions benchmarks/string.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { FixedUTF8String } from "../types/string/mod.ts";
import { FixedLengthString } from "../mod.ts";

const stringThing = new FixedUTF8String(12);
const stringThing = new FixedLengthString(12);

const ab = new TextEncoder().encode("Hello World!").buffer;
const dt = new DataView(ab);
Expand Down
3 changes: 1 addition & 2 deletions benchmarks/struct.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { AlignedStruct } from "../types/struct/mod.ts";
import { u32 } from "../types/primitive/mod.ts";
import { AlignedStruct, u32 } from "../mod.ts";

const data = new DataView(new ArrayBuffer(8));

Expand Down
3 changes: 1 addition & 2 deletions benchmarks/tuple.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Tuple } from "../types/tuple/mod.ts";
import { u32 } from "../types/primitive/u32.ts";
import { Tuple, u32 } from "../mod.ts";

const benchTuple = new Tuple([u32, u32]);
const u32arr = new Uint32Array([2, 4]);
Expand Down
10 changes: 10 additions & 0 deletions deno.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"imports": {
"std/": "https://deno.land/std@0.184.0/",
"wabt": "npm:wabt@1.0.32"
},
"tasks": {
"build": "deno task build_i64leb128",
"build_i64leb128": "deno run --allow-read --allow-write ./scripts/build_i64leb128.ts"
}
}
20 changes: 20 additions & 0 deletions deno.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions mod.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from "./types/mod.ts";
export * from "./utils.ts";
43 changes: 43 additions & 0 deletions scripts/build_i64leb128.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import wabt from "wabt";

const { parseWat } = await wabt();

const source = "./types/leb128/_i64leb128.wat";
const destination = "./types/leb128/_i64leb128.ts";

const wat = await Deno.readTextFile(source);
const module = parseWat(source, wat);
const wasm = module.toBinary({}).buffer;
const encoded = btoa(String.fromCharCode(...wasm));

const content = `\
// Copyright 2023 the Blocktopus authors. All rights reserved. MIT license.
// Copyright 2023 the denosaurs team. All rights reserved. MIT license.
const bytes = Uint8Array.from(
atob(
"${encoded}"
),
(c) => c.charCodeAt(0)
);
const { instance } = await WebAssembly.instantiate(bytes);
const exports = instance.exports as {
memory: WebAssembly.Memory;
read: (pointer: number) => [bigint, number];
write: (value: bigint) => number;
};
const memory = new Uint8Array(exports.memory.buffer);
export function read(buffer: Uint8Array): [bigint, number] {
memory.set(buffer, 0);
return exports.read(0);
}
export function write(value: bigint): Uint8Array {
return memory.subarray(0, exports.write(value));
}
`;

await Deno.writeTextFile(destination, content);
File renamed without changes.
4 changes: 2 additions & 2 deletions types/array/mod.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export * from "./array.ts";
export * from "./arrayBuffer.ts";
export * from "./typedArray.ts";
export * from "./array_buffer.ts";
export * from "./typed_array.ts";
File renamed without changes.
27 changes: 27 additions & 0 deletions types/leb128/_i64leb128.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright 2023 the Blocktopus authors. All rights reserved. MIT license.
// Copyright 2023 the denosaurs team. All rights reserved. MIT license.

const bytes = Uint8Array.from(
atob(
"AGFzbQEAAAABDAJgAX8Cfn9gAX4BfwMDAgABBQMBAAEHGQMGbWVtb3J5AgAEcmVhZAAABXdyaXRlAAEKhAECQwEDfgJAA0AgADEAACIDQv8AgyAChiABhCEBIAJCB3wiAiADQoABg1ANASAAQQFqIQBCxgBUDQALAAsgASACQgeApws+AQF/AkADQCAAQoB/g1ANASABIABC/wCDQoABhDwAACAAQgeIIQAgAUEBaiEBDAALCyABIAA8AABBASABags=",
),
(c) => c.charCodeAt(0),
);
const { instance } = await WebAssembly.instantiate(bytes);

const exports = instance.exports as {
memory: WebAssembly.Memory;
read: (pointer: number) => [bigint, number];
write: (value: bigint) => number;
};

const memory = new Uint8Array(exports.memory.buffer);

export function read(buffer: Uint8Array): [bigint, number] {
memory.set(buffer, 0);
return exports.read(0);
}

export function write(value: bigint): Uint8Array {
return memory.subarray(0, exports.write(value));
}
109 changes: 109 additions & 0 deletions types/leb128/_i64leb128.wat
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
;; Copyright 2023 the Blocktopus authors. All rights reserved. MIT license.
;; Copyright 2023 the denosaurs team. All rights reserved. MIT license.
;; This file is here as a reference as to what the embedded wasm binary is.
(module
(memory (export "memory") 1)
(func $read
(export "read")
(param $ptr i32)
(result i64 i32)
(local $v i64)
(local $length i64)
(local $temp i64)

(block $B0
(loop $L0
;; CurrentByte
local.get $ptr
i64.load8_u
local.tee $temp
i64.const 127
i64.and

;; << 7 * length
local.get $length
i64.shl
;; value |= i64.shl
local.get $v
i64.or
local.set $v

;; length++;
local.get $length
i64.const 7
i64.add
local.tee $length

;; CurrentByte
local.get $temp
i64.const 128
i64.and
i64.eqz
br_if $B0

;; Move to next iteration
local.get $ptr
i32.const 1
i32.add
local.set $ptr

;; Branch if not over 70
i64.const 70
i64.lt_u
br_if $L0
)
unreachable
)

local.get $v
local.get $length
i64.const 7
i64.div_u
i32.wrap_i64
)

(func $write
(export "write")
(param $value i64)
(result i32)
(local $length i32)

(block $B0
(loop $L0
local.get $value
i64.const -128
i64.and
i64.eqz
br_if $B0

local.get $length

local.get $value
i64.const 127
i64.and
i64.const 128
i64.or
i64.store8

local.get $value
i64.const 7
i64.shr_u
local.set $value

local.get $length
i32.const 1
i32.add
local.set $length

br $L0
)
)
local.get $length
local.get $value
i64.store8

i32.const 1
local.get $length
i32.add
)
)
2 changes: 1 addition & 1 deletion types/varint/leb128.ts → types/leb128/i32leb128.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Type } from "../types.ts";
import type { Type } from "../types.ts";

const SEGMENT_BITS = 0x7F;
const CONTINUE_BIT = 0x80;
Expand Down
80 changes: 80 additions & 0 deletions types/leb128/i32leb128_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { assertEquals, assertThrows } from "std/testing/asserts.ts";
import { i32leb128 } from "./mod.ts";

Deno.test("i32leb128", async ({ step }) => {
await step("read", async ({ step }) => {
await step("positive", () => {
let data = Uint8Array.of(127);
let result = i32leb128.read(new DataView(data.buffer));
assertEquals(result, 127);

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

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

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

await step("negative", () => {
let data = Uint8Array.of(255, 255, 255, 255, 15);
let result = i32leb128.read(new DataView(data.buffer));
assertEquals(result, -1);

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

await step("bad", () => {
const data = Uint8Array.of(255, 255, 255, 255, 255, 15);
assertThrows(() => i32leb128.read(new DataView(data.buffer)));
});

await step("i32 max", () => {
const data = Uint8Array.of(255, 255, 255, 255, 7);
assertEquals(i32leb128.read(new DataView(data.buffer)), 2147483647);
});
});

await step("write", async ({ step }) => {
await step("positive", () => {
let data = new Uint8Array(1);
i32leb128.write(127, new DataView(data.buffer));
assertEquals(data, Uint8Array.of(127));

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

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

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

await step("negative", () => {
let data = new Uint8Array(5);
i32leb128.write(-1, new DataView(data.buffer));
assertEquals(data, Uint8Array.of(255, 255, 255, 255, 15));

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

await step("i32 max", () => {
const data = new Uint8Array(5);
i32leb128.write(2147483647, new DataView(data.buffer));
assertEquals(data, Uint8Array.of(255, 255, 255, 255, 7));
});
});
});
27 changes: 27 additions & 0 deletions types/leb128/i64leb128.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import type { Type } from "../types.ts";
import { read, write } from "./_i64leb128.ts";

export class I64LEB128 implements Type<bigint> {
read(dataView: DataView, byteOffset = 0): bigint {
try {
const [value, _bytesRead] = read(
new Uint8Array(dataView.buffer, dataView.byteOffset + byteOffset),
);
return value;
} catch {
throw new RangeError("I64LEB128 is too large");
}
}

write(value: bigint, dataView: DataView, byteOffset = 0): void {
const view = write(value);
const writeView = new Uint8Array(
dataView.buffer,
dataView.byteOffset + byteOffset,
dataView.byteLength,
);
writeView.set(view, 0);
}
}

export const i64leb128 = new I64LEB128();
Loading

0 comments on commit 80802e1

Please sign in to comment.