Skip to content

Commit

Permalink
remove deep sink allocations
Browse files Browse the repository at this point in the history
  • Loading branch information
vanruch committed Mar 28, 2024
1 parent 6254944 commit 5cc5234
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 42 deletions.
17 changes: 6 additions & 11 deletions evm/evm-codec/src/codecs/array.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,12 @@ export class ArrayCodec<const T> implements Codec<readonly T[]> {
constructor(public readonly item: Codec<T>) {}

encode(sink: Sink, val: T[]) {
sink.offset();
sink.u32(val.length);
const tempSink = new Sink(val.length);
sink.dynamicOffset(val.length);
for (let i = 0; i < val.length; i++) {
this.item.encode(tempSink, val[i]);
this.item.encode(sink, val[i]);
}
sink.increaseSize(WORD_SIZE);
sink.append(tempSink);
sink.jumpBack();
sink.endDynamic();
}

decode(src: Src): T[] {
Expand Down Expand Up @@ -58,13 +55,11 @@ export class FixedArrayCodec<const T> implements Codec<readonly T[]> {
}

private encodeDynamic(sink: Sink, val: T[]) {
sink.offset();
const tempSink = new Sink(this.size);
sink.offset(this.size);
for (let i = 0; i < val.length; i++) {
this.item.encode(tempSink, val[i]);
this.item.encode(sink, val[i]);
}
sink.append(tempSink);
sink.jumpBack();
sink.endDynamic();
}

decode(src: Src): T[] {
Expand Down
4 changes: 2 additions & 2 deletions evm/evm-codec/src/codecs/primitives.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ export const string = <const>{
encode(sink: Sink, val: string) {
sink.offset();
sink.string(val);
sink.jumpBack();
sink.endDynamic();
},
decode(src: Src): string {
return src.string();
Expand All @@ -152,7 +152,7 @@ export const bytes = <const>{
encode(sink: Sink, val: Uint8Array) {
sink.offset();
sink.bytes(val);
sink.jumpBack();
sink.endDynamic();
},
decode(src: Src): Uint8Array {
return src.bytes();
Expand Down
9 changes: 3 additions & 6 deletions evm/evm-codec/src/codecs/struct.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,12 @@ export class StructCodec<const T extends Struct>
}

private encodeDynamic(sink: Sink, val: StructTypes<T>): void {
sink.offset();
const tempSink = new Sink(this.childrenSlotsCount);
sink.offset(this.childrenSlotsCount);
for (let i in this.components) {
let prop = this.components[i];
prop.encode(tempSink, val[i]);
prop.encode(sink, val[i]);
}

sink.append(tempSink);
sink.jumpBack();
sink.endDynamic();
}

public decode(src: Src): StructTypes<T> {
Expand Down
74 changes: 51 additions & 23 deletions evm/evm-codec/src/sink.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import assert from "node:assert";
import { WORD_SIZE } from "./codec";

export class Sink {
private pos = 0;
private previousPos = 0;
private buf: Buffer;
private view: DataView;
public size = 0;
private stack: { start: number; prev: number; size: number }[] = [];
constructor(fields: number, capacity: number = 1280) {
this.size = fields * WORD_SIZE;
this.stack.push({
start: 0,
prev: 0,
size: fields * WORD_SIZE,
});
this.buf = Buffer.alloc(capacity);
this.view = new DataView(
this.buf.buffer,
Expand All @@ -17,7 +21,11 @@ export class Sink {
}

result(): Buffer {
return this.buf.subarray(0, this.size);
assert(
this.stack.length === 1,
"Cannot get result during dynamic encoding"
);
return this.buf.subarray(0, this.size());
}

toString() {
Expand All @@ -30,6 +38,10 @@ export class Sink {
}
}

size() {
return this.stack.at(-1)!.size;
}

private _allocate(cap: number): void {
cap = Math.max(cap, this.buf.length * 2);
let buf = Buffer.alloc(cap);
Expand Down Expand Up @@ -128,7 +140,7 @@ export class Sink {
this.reserve(reservedSize);
this.buf.set(val, this.pos);
this.pos += reservedSize;
this.size += reservedSize + WORD_SIZE;
this.increaseSize(reservedSize + WORD_SIZE);
}

staticBytes(len: number, val: Uint8Array) {
Expand Down Expand Up @@ -156,36 +168,52 @@ export class Sink {
this.reserve(reservedSize);
this.buf.write(val, this.pos);
this.pos += reservedSize;
this.size += reservedSize + WORD_SIZE;
this.increaseSize(reservedSize + WORD_SIZE);
}

bool(val: boolean) {
this.u8(val ? 1 : 0);
}

offset() {
const ptr = this.size;
offset(slotsCount = 0) {
const ptr = this.size();
this.u32(ptr);
this.previousPos = this.pos;
this.reserve(ptr);
this.pos = ptr;
const _start = this.start();
this.startDynamic(_start + ptr, slotsCount);
this.pos = _start + ptr;
}

increaseSize(size: number) {
this.size += size;
dynamicOffset(slotsCount: number) {
const ptr = this.size();
this.u32(ptr);
const _start = this.start();
this.startDynamic(_start + ptr + WORD_SIZE, slotsCount);
this.pos = _start + ptr;
this.u32(slotsCount);
}

jumpBack() {
if (this.previousPos === 0) {
throw new Error("no jump destination found");
}
this.pos = this.previousPos;
private start() {
return this.stack.at(-1)!.start;
}

public increaseSize(amount: number) {
this.stack.at(-1)!.size += amount;
}

private startDynamic(start: number, slotsCount: number) {
const size = slotsCount * WORD_SIZE;
this.reserve(start + size);
this.stack.push({
start,
prev: this.pos,
size,
});
}

append(sink: Sink) {
this.reserve(sink.size);
this.buf.set(sink.result(), this.pos);
this.size += sink.size;
this.pos += sink.pos;
endDynamic() {
assert(this.stack.length > 1, "No dynamic encoding started");
const { prev, size } = this.stack.pop()!;
this.increaseSize(size);
this.pos = prev;
}
}
12 changes: 12 additions & 0 deletions evm/evm-codec/test/array.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,18 @@ describe("dynamic size array", () => {
expect(arr.decode(new Src(sink.result()))).toStrictEqual([1, 2, -3, -4, 5]);
});

it("array of arrays", () => {
const arr = array(array(int8));
const sink = new Sink(1);
const data = [
[1, 2, -3, -4, 5],
[1, 2, -3, -4, 5],
];
arr.encode(sink, data);
compareTypes(sink, [{ type: "int8[][]" }], [data]);
expect(arr.decode(new Src(sink.result()))).toStrictEqual(data);
});

it("dynamic types encoding", () => {
const arr = array(string);
const sink = new Sink(1);
Expand Down
5 changes: 5 additions & 0 deletions evm/evm-codec/test/sink.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,15 @@ describe("sink", () => {
const sink = new Sink(1);
sink.offset();
sink.string("hello");
sink.endDynamic();
compareTypes(sink, [{ type: "string" }], ["hello"]);
});

it("32 byte string", () => {
const sink = new Sink(1);
sink.offset();
sink.string("this string length is 32 bytes!!");
sink.endDynamic();
compareTypes(
sink,
[{ type: "string" }],
Expand All @@ -85,6 +87,7 @@ describe("sink", () => {
const sink = new Sink(1);
sink.offset();
sink.string("this string length is 33 bytes!!!");
sink.endDynamic();
compareTypes(
sink,
[{ type: "string" }],
Expand All @@ -96,6 +99,7 @@ describe("sink", () => {
const sink = new Sink(1);
sink.offset();
sink.string("привет 👍");
sink.endDynamic();
compareTypes(sink, [{ type: "string" }], ["привет 👍"]);
});
});
Expand All @@ -106,6 +110,7 @@ describe("sink", () => {
const buffer = Buffer.alloc(150);
buffer.fill("xd");
sink.bytes(buffer);
sink.endDynamic();
compareTypes(sink, [{ type: "bytes" }], [`0x${buffer.toString("hex")}`]);
});
});

0 comments on commit 5cc5234

Please sign in to comment.