Skip to content

Instantly share code, notes, and snippets.

@saolsen
Last active August 6, 2019 17:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save saolsen/24e9526a65814bceb3e22f6cbe005bc7 to your computer and use it in GitHub Desktop.
Save saolsen/24e9526a65814bceb3e22f6cbe005bc7 to your computer and use it in GitHub Desktop.
trying out structs in javascript

I've been thinking about trying to build something like terra but with javascript as the host language and wasm as the low level thing you metaprogram for. Could be used to just "jit" expensive functions and call them from javascript or to build a fully wasm program but metaprogram it from javascript or whatever inbetween. One of the things I think I need to get that to be nice is to be able to have a struct implementation that can be easily shared between wasm and js.

console.log("Trying out some struct stuff");
// There are two different tools that browsers have.
// ArrayBuffer is the raw storage. Fixed length buffer.
// Typed Array
// - A view of an array buffer that is an array of typed numbers (ints, floats, bytes)
// DataView
// - A view of an array buffer that provides a getter/setter api to read and write
// arbitrary data to the buffer. It was meant to let you read external files and stuff
// but might be the raw tool I need to implement structs.
let buffer = new ArrayBuffer(16);
console.log(buffer);
if (buffer.byteLength === 16) {
console.log("Yes, it is 16 bytes.")
} else {
console.log("Oh ho, it's the wrong size!")
}
let int32View = new Int32Array(buffer);
for (let i = 0; i < int32View.length; i++) {
int32View[i] = i * 2;
}
console.log(int32View);
let int16View = new Int16Array(buffer);
for (let i = 0; i < int16View.length; i++) {
console.log('Entry ' + i + ': ' + int16View[i]);
}
// Now lets see if we can figure out how to use a data view for structs
// @Q: Does one dataview have to corrispond to a single memory spot? so I'd need a dataview
// per element if I had an array of elements?
let buffer2 = new ArrayBuffer(24);
let bytes = new Uint8Array(buffer2);
let view = new DataView(buffer2, 0);
view.setInt32(1, 512, true); // little endian
console.log(bytes);
console.log(view);
console.log(view.getInt32(1, true));
// So I think I get the low level details. To build structs I think would look something like this.
// 1) Do some struct size layout math.
// 2) Calculate all the offsets and stuff.
// 3) Provide helpers for everything.
// What's the right level of abstraction for this.
// A struct is going to be like a factory thing?
// It could create either a single instance or an array of them I think.
let spec = [
{name: "foo", type: "i32", count: 1},
{name: "bar", type: "f32", count: 1},
{name: "baz", type: "u16", count: 1},
{name: "zzz", type: "i32", count: 6} // i32[6]
];
console.log(spec);
let type_sizes = {
"i8": 1,
"u8": 1,
"i16": 2,
"u16": 2,
"i32": 4,
"u32": 4,
"f32": 4,
"f64": 8
};
// @TODO: Alignment
function field_offsets(spec) {
let field_offsets = {}
let field_types = {}
let offset = 0;
for (let i = 0; i < spec.length; i ++) {
let {name, type, count} = spec[i];
console.assert(count > 0, "count must be > 0");
console.assert(type in type_sizes, "unknown type");
field_offsets[name] = offset;
field_types[name] = type;
offset += type_sizes[type] * count;
}
return {types: field_types, offsets: field_offsets, size: offset};
}
let {types, offsets, size} = field_offsets(spec);
console.log(offsets);
console.log(types)
console.log("size: " + size);
let type_getters = {
"i8": "getInt8",
"u8": "getUint8",
"i16": "getInt16",
"u16": "getUint16",
"i32": "getInt32",
"u32": "getUint32",
"f32": "getFloat32",
"f64": "getFloat64"
}
let type_setters = {
"i8": "setInt8",
"u8": "setUint8",
"i16": "setInt16",
"u16": "setUint16",
"i32": "setInt32",
"u32": "setUint32",
"f32": "setFloat32",
"f64": "setFloat64"
}
// Then tbd how this is created but you end up with something sorta like this.
// Hopefully something a little better than an object per struct.
// @TODO: Is this the way you do javascript or something about a prototype?
// @TODO: Handle arrays.
function MyStruct() {
this._store = new ArrayBuffer(size);
this._debugView = new Uint8Array(this._store);
this._dataView = new DataView(this._store, 0);
for (let field in offsets) {
let offset = offsets[field];
let type = types[field];
console.assert(field in types, "types and offsets don't match up");
console.assert(type in type_getters);
console.assert(type in type_setters);
let getter = type_getters[type];
let setter = type_setters[type];
Object.defineProperty(this, field, {
get: function() { return this._dataView[getter](offset, true) },
set: function(i) { this._dataView[setter](offset, i, true) }
})
}
}
let s = new MyStruct();
s.foo = 12;
s.bar = 9;
s.baz = 15;
console.log(s)
console.log(s.foo);
console.log(s._debugView);
s.foo = 12;
console.log(s._debugView);
console.log(s.foo);
console.log(s.bar);
console.log(s.baz);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment