Skip to content

Commit

Permalink
add structs to javascript, migrate devicetree to them
Browse files Browse the repository at this point in the history
  • Loading branch information
tombl committed Dec 1, 2024
1 parent 9820b16 commit ad47e85
Show file tree
Hide file tree
Showing 2 changed files with 276 additions and 116 deletions.
174 changes: 174 additions & 0 deletions tools/wasm/src/bytes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
interface Type<T> {
get(dv: DataView, offset: number): T;
set(dv: DataView, offset: number, value: T): void;
size: number;
}
export type Unwrap<T> = T extends Type<infer U> ? U : never;

export function Struct<T extends object>(
layout: { [K in keyof T]: Type<T[K]> },
) {
const TheStruct = class {
_dv: DataView;
constructor(dv: DataView) {
this._dv = dv;
}
} as { new (dv: DataView): T };

let size = 0;
for (
const [key, type] of Object.entries(
layout as Record<PropertyKey, Type<unknown>>,
)
) {
const offset = size;
Object.defineProperty(TheStruct.prototype, key, {
get() {
return type.get(this._dv, offset);
},
set(value) {
type.set(this._dv, offset, value);
},
});
size += type.size;
}

const type: Type<T> = {
get(dv, offset) {
if (offset !== 0) dv = new DataView(dv.buffer, dv.byteOffset + offset);
return new TheStruct(dv);
},
set(dv, offset, value) {
if (offset !== 0) dv = new DataView(dv.buffer, dv.byteOffset + offset);
Object.assign(new TheStruct(dv), value);
},
size,
};

return Object.assign(TheStruct, type);
}

export const U8: Type<number> = {
get(dv, offset) {
return dv.getUint8(offset);
},
set(dv, offset, value) {
dv.setUint8(offset, value);
},
size: 1,
};
export const U16LE: Type<number> = {
get(dv, offset) {
return dv.getUint16(offset, true);
},
set(dv, offset, value) {
dv.setUint16(offset, value, true);
},
size: 2,
};
export const U32LE: Type<number> = {
get(dv, offset) {
return dv.getUint32(offset, true);
},
set(dv, offset, value) {
dv.setUint32(offset, value, true);
},
size: 4,
};
export const U64LE: Type<bigint> = {
get(dv, offset) {
return dv.getBigUint64(offset, true);
},
set(dv, offset, value) {
dv.setBigUint64(offset, value, true);
},
size: 8,
};
export const U16BE: Type<number> = {
get(dv, offset) {
return dv.getUint16(offset, false);
},
set(dv, offset, value) {
dv.setUint16(offset, value, false);
},
size: 2,
};
export const U32BE: Type<number> = {
get(dv, offset) {
return dv.getUint32(offset, false);
},
set(dv, offset, value) {
dv.setUint32(offset, value, false);
},
size: 4,
};
export const U64BE: Type<bigint> = {
get(dv, offset) {
return dv.getBigUint64(offset, false);
},
set(dv, offset, value) {
dv.setBigUint64(offset, value, false);
},
size: 8,
};

export interface Allocated<T> {
value: T;
}

export class Bytes {
#array: Uint8Array;
length = 0;

get capacity() {
return this.#array.length;
}
get array() {
return this.#array.slice(0, this.length);
}

constructor(capacity = 32) {
this.#array = new Uint8Array(capacity);
}

#ensure_capacity(capacity: number) {
if (this.#array.length < capacity) {
let length = this.#array.length;
while (length < capacity) length *= 2;
const next = new Uint8Array(length);
next.set(this.#array);
this.#array = next;
this.#dv = undefined;
}
}

bump(length: number) {
const offset = this.length;
this.#ensure_capacity(this.length + length);
this.length += length;
return offset;
}

append(bytes: Uint8Array) {
const offset = this.bump(bytes.length);
this.#array.set(bytes, offset);
}

#dv?: DataView;
get dv() {
return this.#dv ??= new DataView(this.#array.buffer);
}

alloc<T>(type: Type<T>): Allocated<T> {
const offset = this.bump(type.size);
const self = this;
return {
get value() {
return type.get(self.dv, offset);
},
set value(value: T) {
type.set(self.dv, offset, value);
},
};
}
}
Loading

0 comments on commit ad47e85

Please sign in to comment.