Skip to content

Commit

Permalink
feat: Sized compound types (#38)
Browse files Browse the repository at this point in the history
Co-authored-by: Dean Srebnik <[email protected]>
  • Loading branch information
MierenManz and load1n9 authored Jul 12, 2024
1 parent 0c5367a commit f7952bd
Show file tree
Hide file tree
Showing 10 changed files with 430 additions and 8 deletions.
42 changes: 42 additions & 0 deletions benchmarks/arrays.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { ArrayType, SizedArrayType, u32 } from "../mod.ts";

for (let i = 2; i < 10; i++) {
const codec = new ArrayType(u32, 1 << i);
const sizedCodec = new SizedArrayType(u32, 1 << i);
const data = new Array(1 << i).fill(0).map((_, i) => i);
const buff = new Uint32Array(1 << i).fill(0).map((_, i) => i);
const dt = new DataView(buff.buffer);

Deno.bench({
name: "Array (Read)",
baseline: true,
group: `read-${i}`,
fn: () => {
codec.readPacked(dt);
},
});

Deno.bench({
name: "Sized Array (Read)",
group: `read-${i}`,
fn: () => {
sizedCodec.readPacked(dt);
},
});
Deno.bench({
name: "Array (Write)",
baseline: true,
group: `write-${i}`,
fn: () => {
codec.writePacked(data, dt);
},
});

Deno.bench({
name: "Sized Array (Write)",
group: `write-${i}`,
fn: () => {
sizedCodec.writePacked(data, dt);
},
});
}
148 changes: 148 additions & 0 deletions benchmarks/big_struct.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import {
type InnerType,
SizedStruct,
Strings,
Struct,
u32,
u8,
} from "../mod.ts";
const innerDescriptor = {
name: new Strings.FixedLengthString(11),
hp: u8,
damage: u8,
shield: u32,
};

const baseDescriptor = {
handIndex: u8,
fieldIndex: u8,
};

const descriptor = {
...baseDescriptor,
card: new Struct(innerDescriptor),
};

const sizedDescriptor = {
...baseDescriptor,
card: new SizedStruct(innerDescriptor),
};

const codec = new Struct(descriptor);
const sizedCodec = new SizedStruct(sizedDescriptor);

const data: InnerType<typeof codec> = {
handIndex: 255,
fieldIndex: 255,
card: {
name: "InvalidCard",
hp: 255,
damage: 255,
shield: 255,
},
};

const object: InnerType<typeof codec> = {
handIndex: 0,
fieldIndex: 0,
card: {
name: "00000000000",
hp: 0,
damage: 0,
shield: 0,
},
};

const ARRAY_BUFFER = new ArrayBuffer(20);
const DATA_VIEW = new DataView(ARRAY_BUFFER);
const BIN_VIEW = new Uint8Array(ARRAY_BUFFER, 2, 11);
const DECODER = new TextDecoder();
const ENCODER = new TextEncoder();

Deno.bench("no-op", () => {});

Deno.bench({
baseline: true,
name: "object (Read)",
group: "read",
fn: () => {
object.handIndex;
object.fieldIndex;
object.card.name;
object.card.hp;
object.card.damage;
object.card.shield;
},
});

Deno.bench({
name: "Struct (Read)",
group: "read",
fn: () => {
codec.readPacked(DATA_VIEW);
},
});

Deno.bench({
name: "SizedStruct (Read)",
group: "read",
fn: () => {
sizedCodec.readPacked(DATA_VIEW);
},
});

Deno.bench({
name: "Primitives (Read)",
group: "read",
fn: () => {
DATA_VIEW.getUint8(0);
DATA_VIEW.getUint8(1);
DECODER.decode(BIN_VIEW);
DATA_VIEW.getUint8(13);
DATA_VIEW.getUint8(14);
DATA_VIEW.getUint32(16);
},
});

Deno.bench({
baseline: true,
name: "object (Write)",
group: "write",
fn: () => {
object.handIndex = 255;
object.fieldIndex = 255;
object.card.name = "InvalidCards";
object.card.hp = 255;
object.card.damage = 255;
object.card.shield = 255;
},
});

Deno.bench({
name: "Struct (Write)",
group: "write",
fn: () => {
codec.writePacked(data, DATA_VIEW);
},
});

Deno.bench({
name: "SizedStruct (Write)",
group: "write",
fn: () => {
sizedCodec.writePacked(data, DATA_VIEW);
},
});

Deno.bench({
name: "Primitives (Write)",
group: "write",
fn: () => {
DATA_VIEW.setUint8(0, 255);
DATA_VIEW.setUint8(1, 255);
ENCODER.encodeInto("InvalidCards", BIN_VIEW);
DATA_VIEW.setUint8(13, 255);
DATA_VIEW.setUint8(14, 255);
DATA_VIEW.setUint32(16, 255);
},
});
52 changes: 45 additions & 7 deletions benchmarks/popular_encodings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,32 @@ import {
decode as msgpackRead,
encode as msgpackWrite,
} from "jsr:@std/[email protected]";
import { SizedStruct } from "../src/compound/sized_struct.ts";

const descriptor = {
const innerDescriptor = {
name: new Strings.FixedLengthString(11),
hp: u8,
damage: u8,
shield: u32,
};

const baseDescriptor = {
handIndex: u8,
fieldIndex: u8,
card: new Struct({
name: new Strings.FixedLengthString(11),
hp: u8,
damage: u8,
shield: u32,
}),
};

const descriptor = {
...baseDescriptor,
card: new Struct(innerDescriptor),
};

const sizedDescriptor = {
...baseDescriptor,
card: new SizedStruct(innerDescriptor),
};

const codec = new Struct(descriptor);
const sizedCodec = new SizedStruct(sizedDescriptor);

const data: InnerType<typeof codec> = {
handIndex: 255,
Expand Down Expand Up @@ -89,6 +102,31 @@ Deno.bench({
},
});

Deno.bench({
name: "SizedStruct (Write)",
group: "write",
fn: () => {
sizedCodec.writePacked(data, DATA_VIEW);
},
});

Deno.bench({
name: "SizedStruct (Read)",
group: "read",
fn: () => {
sizedCodec.readPacked(DATA_VIEW);
},
});

Deno.bench({
name: "SizedStruct (Roundtrip)",
group: "roundtrip",
fn: () => {
sizedCodec.writePacked(data, DATA_VIEW);
sizedCodec.readPacked(DATA_VIEW);
},
});

Deno.bench({
name: "MsgPack (Write)",
group: "write",
Expand Down
25 changes: 25 additions & 0 deletions benchmarks/struct.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Struct, u32 } from "../mod.ts";
import { SizedStruct } from "../src/compound/sized_struct.ts";

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

Expand All @@ -8,6 +9,11 @@ const struct = new Struct({
b: u32,
});

const sizedStruct = new SizedStruct({
a: u32,
b: u32,
});

Deno.bench("no-op", () => {});

Deno.bench({
Expand All @@ -28,6 +34,14 @@ Deno.bench({
},
});

Deno.bench({
name: "struct Sized",
group: "read",
fn: () => {
sizedStruct.read(data);
},
});

Deno.bench({
name: "DataView",
group: "read",
Expand Down Expand Up @@ -58,6 +72,17 @@ Deno.bench({
},
});

Deno.bench({
name: "struct Sized",
group: "write",
fn: () => {
sizedStruct.write({
a: 0xffff,
b: 0xffff,
}, data);
},
});

Deno.bench({
name: "DataView",
group: "write",
Expand Down
1 change: 1 addition & 0 deletions src/array/mod.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from "./array_buffer.ts";
export * from "./array.ts";
export * from "./typed_array.ts";
export * from "./sized_array.ts";
31 changes: 31 additions & 0 deletions src/array/sized_array.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { type Options, SizedType } from "../types/mod.ts";
import { ArrayType } from "./array.ts";

export class SizedArrayType<T> extends SizedType<T[]> implements ArrayType<T> {
#inner: ArrayType<T>;

constructor(readonly type: SizedType<T>, readonly length: number) {
super(length * type.byteSize, type.byteAlignment);
this.#inner = new ArrayType(type, length);
}

readPacked(dt: DataView, options: Options = { byteOffset: 0 }): T[] {
return this.#inner.readPacked(dt, options);
}

read(dt: DataView, options: Options = { byteOffset: 0 }): T[] {
return this.#inner.read(dt, options);
}

writePacked(
value: T[],
dt: DataView,
options: Options = { byteOffset: 0 },
): void {
this.#inner.writePacked(value, dt, options);
}

write(value: T[], dt: DataView, options: Options = { byteOffset: 0 }): void {
this.#inner.write(value, dt, options);
}
}
1 change: 1 addition & 0 deletions src/compound/mod.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from "./struct.ts";
export * from "./tagged_union.ts";
export * from "./tuple.ts";
export * from "./sized_struct.ts";
Loading

0 comments on commit f7952bd

Please sign in to comment.