@thi.ng/soa
Version:
SOA & AOS memory mapped structured views with optional & extensible serialization
164 lines (163 loc) • 4.64 kB
JavaScript
import { SIZEOF, typedArray } from "@thi.ng/api/typedarray";
import { assert } from "@thi.ng/errors/assert";
import { ensureIndex } from "@thi.ng/errors/out-of-bounds";
import { prepareSpec } from "./utils.js";
class SOA {
length;
buffers;
specs;
constructor(num, specs) {
this.length = num;
this.buffers = {};
this.specs = {};
this.addSpecs(specs);
}
keys() {
return Object.keys(this.specs);
}
*values(from = 0, to = this.length) {
ensureIndex(from, 0, this.length);
ensureIndex(to, from, this.length + 1);
for (; from < to; from++) {
yield this.indexUnsafe(from);
}
}
attribValues(id, from = 0, to = this.length) {
this.ensureAttrib(id);
ensureIndex(from, 0, this.length);
ensureIndex(to, from, this.length + 1);
let { size, stride, type } = this.specs[id];
const buf = this.buffers[id].buffer;
stride *= SIZEOF[type];
from *= stride;
to *= stride;
const res = [];
for (; from < to; from += stride) {
res.push(typedArray(type, buf, from, size));
}
return res;
}
attribValue(id, i) {
this.ensureAttrib(id);
ensureIndex(i, 0, this.length);
return this.attribValueUnsafe(id, i);
}
attribValueUnsafe(id, i) {
const spec = this.specs[id];
i *= spec.stride;
return this.buffers[id].subarray(i, i + spec.size);
}
setAttribValue(id, i, val) {
this.ensureAttrib(id);
ensureIndex(i, 0, this.length);
const spec = this.specs[id];
assert(val.length <= spec.size, `${id} value too large`);
this.buffers[id].set(val, i * spec.stride);
}
setAttribValueUnsafe(id, i, val) {
this.buffers[id].set(val, i * this.specs[id].stride);
return this;
}
setAttribValues(id, vals, from = 0) {
this.ensureAttrib(id);
ensureIndex(from, 0, this.length);
const buf = this.buffers[id];
const stride = this.specs[id].stride;
const end = this.length * stride;
let i = from * stride;
for (let v of vals) {
buf.set(v, i);
i += stride;
if (i >= end) break;
}
return this;
}
index(i, ids) {
ensureIndex(i, 0, this.length);
return this.indexUnsafe(i, ids);
}
indexUnsafe(i, ids) {
const res = {};
if (ids) {
for (let i2 = ids.length; i2-- > 0; ) {
const id = ids[i2];
res[id] = this.attribValueUnsafe(id, i2);
}
} else {
for (let id in this.specs) {
res[id] = this.attribValueUnsafe(id, i);
}
}
return res;
}
setIndex(i, vals) {
ensureIndex(i, 0, this.length);
for (let id in vals) {
this.setAttribValue(id, i, vals[id]);
}
return this;
}
setIndexUnsafe(i, vals) {
for (let id in vals) {
this.setAttribValueUnsafe(id, i, vals[id]);
}
return this;
}
setValues(vals, from = 0) {
for (let id in vals) {
this.setAttribValues(id, vals[id], from);
}
return this;
}
copyTo(dest, ids, destFrom = 0, srcFrom = 0, srcTo = this.length) {
ensureIndex(srcFrom, 0, this.length);
ensureIndex(srcTo, srcFrom, this.length + 1);
const num = srcTo - srcFrom;
ensureIndex(destFrom, 0, dest.length);
ensureIndex(destFrom + num, destFrom, dest.length + 1);
ids = ids || Object.keys(this.specs);
for (let k = ids.length; k-- > 0; ) {
const id = ids[k];
for (let i = srcFrom, j = destFrom; i < srcTo; i++, j++) {
dest.setAttribValueUnsafe(id, j, this.attribValueUnsafe(id, i));
}
}
return dest;
}
addSpecs(specs) {
const num = this.length;
for (let id in specs) {
assert(!this.specs[id], `attrib ${id} already exists`);
const spec = prepareSpec(specs[id]);
this.validateSpec(id, spec);
const { stride, default: defVal } = spec;
const buffer = spec.buf ? typedArray(spec.type, spec.buf, spec.byteOffset || 0) : typedArray(spec.type, num * stride);
if (defVal) {
for (let i = 0; i < num; i++) {
buffer.set(defVal, i * stride);
}
}
this.specs[id] = spec;
this.buffers[id] = buffer;
}
}
validateSpec(id, spec) {
assert(spec.stride >= spec.size, `${id} illegal stride`);
assert(
!spec.buf || spec.buf.byteLength >= ((this.length - 1) * spec.stride + spec.size) * SIZEOF[spec.type],
`${id} buffer too small`
);
assert(
spec.default === void 0 || spec.default.length === spec.size,
`illegal default value for ${id}, expected size: ${spec.size}`
);
}
ensureAttrib(id) {
assert(!!this.specs[id], `invalid attrib ${id}`);
}
}
const soa = (num, specs) => new SOA(num, specs);
export {
SOA,
soa
};