Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
407 changes: 237 additions & 170 deletions README.md

Large diffs are not rendered by default.

25 changes: 16 additions & 9 deletions benchmarks/public/dist/compiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ function restrictedFieldName(name) {
case "e":
case "_viewingIndex":
case "ref":
case "isNull":
case "index":
return true;
default:
return false;
Expand Down Expand Up @@ -184,6 +184,11 @@ function reservedJsKeyword(word) {
return false;
}
}
export function invalidClassName(name) {
return (!validVariableName(name)
|| reservedJsKeyword(name)
|| name.length < 1);
}
export function validateCompileOptions(input) {
if (typeof input !== "object"
|| input === null
Expand All @@ -195,9 +200,7 @@ export function validateCompileOptions(input) {
throw TypeError("option 'pathToLib' missing");
}
if (typeof input.className !== "string"
|| !validVariableName(input.className)
|| reservedJsKeyword(input.className)
|| input.className.length < 1) {
|| invalidClassName(input.className)) {
throw SyntaxError(`inputted class name is not a valid javascript class name, got "${input.className}"`);
}
switch (input.exportSyntax) {
Expand All @@ -218,7 +221,7 @@ export function createVecDef(tokens, structDef, { lang, pathToLib, className, ex
const ts = lang === "ts";
const generic = `<${def}>`;
const libPath = `"${pathToLib}"`;
const importStatement = `import {Vec${ts ? ", StructDef, Struct, CursorConstructor" : ""}} from ${libPath}`;
const importStatement = `import {Vec${ts ? ", StructDef, Struct, CursorConstructor, VecCursor, DetachedVecCursor" : ""}} from ${libPath}`;
const CursorConstructor = "CursorConstructor" + generic;
const memory = ts ?
"(this.self as unknown as {_f32Memory: Float32Array})._f32Memory"
Expand All @@ -236,9 +239,11 @@ ${ts || runtimeCompile
* @extends {Vec${generic}}
*/`}
${exportSyntax === "named" ? "export " : ""}class ${className} extends Vec${ts ? generic : ""} {
${ts ? "protected " : ""}static Cursor = class Cursor {
_viewingIndex = 0${ts ? "\n\t\tself: Vec" + generic : ""}
constructor(self${ts ? ": Vec" + generic : ""}) { this.self = self }
static ${ts ? "readonly " : ""}def${ts ? ": StructDef" : ""} = ${def}
static ${ts ? "readonly " : ""}elementSize${ts ? ": number" : ""} = ${elementSize}
${ts ? "protected " : ""}static Cursor = class ${className}Cursor {
${ts ? `_viewingIndex: number\n\t\tself: Vec${generic}` : ""}
constructor(self${ts ? ": Vec" + generic : ""}, index${ts ? ": number" : ""}) { this.self = self;this._viewingIndex = index}
${float32Fields.map(({ field, offset }) => {
const fieldOffset = offset < 1 ? "" : (" + " + offset.toString());
const base = `${memory}[this._viewingIndex${fieldOffset}]`;
Expand Down Expand Up @@ -279,7 +284,9 @@ ${exportSyntax === "named" ? "export " : ""}class ${className} extends Vec${ts ?
}).join(";")}; }
get e()${ts ? ": Struct" + generic : ""} { return {${fieldNames.map((field) => {
return field + ": this." + field;
}).join(", ")}} }
}).join(", ")}} }
get ref()${ts ? `: VecCursor${generic}` : ""} { return new ${className}.Cursor(this.self, this._viewingIndex) }
index(index${ts ? ": number" : ""})${ts ? `: DetachedVecCursor${generic}` : ""} { this._viewingIndex = index * this.self.elementSize; return this }
}${ts ? " as " + CursorConstructor : ""}
get elementSize()${ts ? ": number" : ""} { return ${elementSize} }
get def()${ts ? ": StructDef" : ""} { return ${def} }
Expand Down
141 changes: 62 additions & 79 deletions benchmarks/public/dist/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,23 @@ export const VALID_DATA_TYPES_INTERNAL = [
];
export class Vec {
constructor(initialCapacity = 15, memory) {
try {
let vecCapacity = 0;
let vecLength = 0;
let buffer;
if (!memory) {
vecCapacity = Math.abs(initialCapacity);
buffer = this.createMemory(vecCapacity);
}
else {
vecLength = memory[memory.length - 1];
vecCapacity = memory[memory.length - 2];
buffer = memory.buffer;
}
this._f32Memory = new Float32Array(buffer);
this._i32Memory = new Int32Array(buffer);
this._length = vecLength;
this._capacity = vecCapacity;
this._cursor = new this.cursorDef(this);
let vecCapacity = 0;
let vecLength = 0;
let buffer;
if (!memory) {
vecCapacity = Math.abs(initialCapacity);
buffer = this.createMemory(vecCapacity);
}
catch (err) {
throw new Error(`[Vec::allocator] buffer memory failed to initialize. ${err}`);
else {
vecLength = memory[memory.length - 1];
vecCapacity = memory[memory.length - 2];
buffer = memory.buffer;
}
this._f32Memory = new Float32Array(buffer);
this._i32Memory = new Int32Array(buffer);
this._length = vecLength;
this._capacity = vecCapacity;
this._cursor = new this.cursorDef(this, 0);
}
static isVec(candidate) {
return candidate instanceof this;
Expand Down Expand Up @@ -346,30 +341,24 @@ export class Vec {
return this;
}
reserve(additional) {
try {
const elementSize = this.elementSize;
const length = this._length;
const capacity = this._capacity;
if (length + additional <= capacity) {
return;
}
const newCapacity = length + additional;
const elementsMemory = (MEMORY_LAYOUT.BYTES_PER_ELEMENT
* elementSize
* newCapacity);
const bufferSize = (8
+ elementsMemory);
const buffer = new BUFFER_TYPE(bufferSize);
const memory = new MEMORY_LAYOUT(buffer);
memory.set(this._f32Memory);
this.replaceMemory(memory);
this._capacity = newCapacity;
return this;
}
catch (err) {
console.error(`Vec ::allocator: runtime failed to allocate more memory for vec. Aborting operation`, err);
throw err;
const elementSize = this.elementSize;
const length = this._length;
const capacity = this._capacity;
if (length + additional <= capacity) {
return;
}
const newCapacity = length + additional;
const elementsMemory = (MEMORY_LAYOUT.BYTES_PER_ELEMENT
* elementSize
* newCapacity);
const bufferSize = (8
+ elementsMemory);
const buffer = new BUFFER_TYPE(bufferSize);
const memory = new MEMORY_LAYOUT(buffer);
memory.set(this._f32Memory);
this.replaceMemory(memory);
this._capacity = newCapacity;
return this;
}
reverse() {
const elementSize = this.elementSize;
Expand Down Expand Up @@ -481,25 +470,20 @@ export class Vec {
const capacity = this._capacity;
const minimumCapcity = length + structs.length;
if (minimumCapcity > capacity) {
try {
const targetCapacity = capacity * 2;
const newCapacity = minimumCapcity > targetCapacity
? minimumCapcity + 15
: targetCapacity;
const elementsMemory = (MEMORY_LAYOUT.BYTES_PER_ELEMENT
* elementSize
* newCapacity);
const bufferSize = (8
+ elementsMemory);
const buffer = new BUFFER_TYPE(bufferSize);
const memory = new MEMORY_LAYOUT(buffer);
memory.set(this._f32Memory);
this.replaceMemory(memory);
this._capacity = newCapacity;
}
catch (err) {
throw new Error(`[Vec::allocator] runtime failed to allocate more memory for vec. ${err}`);
}
const targetCapacity = capacity * 2;
const newCapacity = minimumCapcity > targetCapacity
? minimumCapcity + 15
: targetCapacity;
const elementsMemory = (MEMORY_LAYOUT.BYTES_PER_ELEMENT
* elementSize
* newCapacity);
const bufferSize = (8
+ elementsMemory);
const buffer = new BUFFER_TYPE(bufferSize);
const memory = new MEMORY_LAYOUT(buffer);
memory.set(this._f32Memory);
this.replaceMemory(memory);
this._capacity = newCapacity;
}
const previousIndex = this._cursor._viewingIndex;
for (let i = 0; i < structs.length; i += 1) {
Expand Down Expand Up @@ -621,30 +605,24 @@ export class Vec {
return newLength;
}
shrinkTo(minCapacity = 15) {
try {
const elementSize = this.elementSize;
const length = this._length;
const capacity = this._capacity;
const minCapacityNormalize = minCapacity < 0
? 0
: minCapacity;
const newCapacity = length + minCapacityNormalize;
if (newCapacity >= capacity) {
return this;
}
this._f32Memory = this.shrinkCapacity(newCapacity);
this._capacity = newCapacity;
const length = this._length;
const capacity = this._capacity;
const minCapacityNormalize = minCapacity < 0
? 0
: minCapacity;
const newCapacity = length + minCapacityNormalize;
if (newCapacity >= capacity) {
return this;
}
catch (err) {
throw new Error(`[Vec::allocator] runtime failed to deallocate memory for vec. ${err}`);
}
this._f32Memory = this.shrinkCapacity(newCapacity);
this._capacity = newCapacity;
return this;
}
sort(compareFn) {
if (this._length < 2) {
return this;
}
const helperCursor = new this.cursorDef(this);
const helperCursor = new this.cursorDef(this, 0);
this.reserve(1);
const elementSize = this.elementSize;
const temporaryIndex = this._length * elementSize;
Expand Down Expand Up @@ -701,6 +679,9 @@ export class Vec {
memoryStr += `${this.elementSize},${this._capacity},${this._length}]`;
return memoryStr;
}
detachedCursor(index) {
return new this.cursorDef(this, index);
}
createMemory(capacity) {
const elementsMemory = (MEMORY_LAYOUT.BYTES_PER_ELEMENT
* this.elementSize
Expand Down Expand Up @@ -738,3 +719,5 @@ export class Vec {
this._i32Memory = new Int32Array(memory.buffer);
}
}
Vec.def = {};
Vec.elementSize = 0;
14 changes: 9 additions & 5 deletions benchmarks/public/dist/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Vec as BaseVec } from "./core.js";
import { tokenizeStructDef, ERR_PREFIX, createVecDef, validateCompileOptions } from "./compiler.js";
import { tokenizeStructDef, ERR_PREFIX, createVecDef, validateCompileOptions, invalidClassName } from "./compiler.js";
export { Vec } from "./core.js";
export function validateStructDef(def) {
try {
Expand All @@ -10,21 +10,25 @@ export function validateStructDef(def) {
return false;
}
}
export function vec(structDef) {
export function vec(structDef, options = {}) {
if (typeof SharedArrayBuffer === "undefined") {
throw new Error(`${ERR_PREFIX} sharedArrayBuffers are not supported in this environment and are required for vecs`);
}
const tokens = tokenizeStructDef(structDef);
const { def, className } = createVecDef(tokens, structDef, {
const { className = "AnonymousVec" } = options;
if (invalidClassName(className)) {
throw SyntaxError(`inputted class name (className option) is not a valid javascript class name, got "${className}"`);
}
const { def, className: clsName } = createVecDef(tokens, structDef, {
lang: "js",
exportSyntax: "none",
pathToLib: "none",
className: "AnonymousVec",
className,
runtimeCompile: true
});
const genericVec = Function(`"use strict";return (Vec) => {
${def}
return ${className}
return ${clsName}
}`)()(BaseVec);
return genericVec;
}
Expand Down
10 changes: 3 additions & 7 deletions benchmarks/public/index.mjs
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
import {vec} from "../../dist/index.js"
import {Benchmark} from "./lib.mjs"

const Position = vec({
x: "f32",
y: "f32",
z: "f32"
})
const Position = vec({x: "f32", y: "f32", z: "f32"})

const elementCount = 10_000_000

const benchmark = new Benchmark()
benchmark
/*
.add("vec push", () => {
const container = new Position()
for (let i = 0; i < elementCount; i += 1) {
Expand All @@ -24,7 +19,7 @@ benchmark
container.push({x: 1, y: 1, z: 1})
}
})
*/
/*
.add("vec push with pre-alloc", () => {
const container = new Position()
container.reserve(elementCount)
Expand All @@ -38,4 +33,5 @@ benchmark
container.push({x: 1, y: 1, z: 1})
}
})
*/
.run()
Binary file modified buildInfo/index.js.br
Binary file not shown.
8 changes: 8 additions & 0 deletions codegenTests/codegen.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ describe("generated vec classes have all the standard methods and properties", (
expect(typeof NamedTs.fromMemory).toBe("function")
expect(typeof NamedTs.fromString).toBe("function")
expect(typeof NamedTs.isVec).toBe("function")
expect(NamedTs.def).toEqual(structdef)
expect(NamedTs.elementSize).toBe(3)
const v = new NamedTs(15).fill({
x: true, y: 10, z: "a"
})
Expand Down Expand Up @@ -69,6 +71,8 @@ describe("generated vec classes have all the standard methods and properties", (
expect(typeof NamedJs.fromMemory).toBe("function")
expect(typeof NamedJs.fromString).toBe("function")
expect(typeof NamedJs.isVec).toBe("function")
expect(NamedJs.def).toEqual(structdef)
expect(NamedJs.elementSize).toBe(3)
const v = new NamedJs(15).fill({
x: true, y: 10, z: "a"
})
Expand Down Expand Up @@ -125,6 +129,8 @@ describe("generated vec classes have all the standard methods and properties", (
expect(typeof defaultTs.DefaultTs.fromMemory).toBe("function")
expect(typeof defaultTs.DefaultTs.fromString).toBe("function")
expect(typeof defaultTs.DefaultTs.isVec).toBe("function")
expect(defaultTs.DefaultTs.def).toEqual(structdef)
expect(defaultTs.DefaultTs.elementSize).toBe(3)
const v = new defaultTs.DefaultTs(15).fill({
x: true, y: 10, z: "a"
})
Expand Down Expand Up @@ -181,6 +187,8 @@ describe("generated vec classes have all the standard methods and properties", (
expect(typeof defaultJs.DefaultJs.fromMemory).toBe("function")
expect(typeof defaultJs.DefaultJs.fromString).toBe("function")
expect(typeof defaultJs.DefaultJs.isVec).toBe("function")
expect(defaultJs.DefaultJs.def).toEqual(structdef)
expect(defaultJs.DefaultJs.elementSize).toBe(3)
const v = new defaultJs.DefaultJs(15).fill({
x: true, y: 10, z: "a"
})
Expand Down
12 changes: 8 additions & 4 deletions codegenTests/default-js.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,19 @@ import {Vec} from "../dist"
* @extends {Vec<{"x":"bool","y":"f32","z":"char"}>}
*/
class DefaultJs extends Vec {
static Cursor = class Cursor {
_viewingIndex = 0
constructor(self) { this.self = self }
static def = {"x":"bool","y":"f32","z":"char"}
static elementSize = 3
static Cursor = class DefaultJsCursor {

constructor(self, index) { this.self = self;this._viewingIndex = index}
get y() { return this.self._f32Memory[this._viewingIndex] }; set y(newValue) { this.self._f32Memory[this._viewingIndex] = newValue };

get z() { return String.fromCodePoint(this.self._i32Memory[this._viewingIndex + 1] || 32) }; set z(newValue) { this.self._i32Memory[this._viewingIndex + 1] = newValue.codePointAt(0) || 32 };
get x() { return Boolean(this.self._i32Memory[this._viewingIndex + 2] & 1) }; set x(newValue) { this.self._i32Memory[this._viewingIndex + 2] &= -2;this.self._i32Memory[this._viewingIndex + 2] |= Boolean(newValue)};
set e({y, z, x}) { this.y = y;this.z = z;this.x = x; }
get e() { return {y: this.y, z: this.z, x: this.x} }
get e() { return {y: this.y, z: this.z, x: this.x} }
get ref() { return new DefaultJs.Cursor(this.self, this._viewingIndex) }
index(index) { this._viewingIndex = index * this.self.elementSize; return this }
}
get elementSize() { return 3 }
get def() { return {"x":"bool","y":"f32","z":"char"} }
Expand Down
Loading