UNPKG

@thi.ng/soa

Version:

SOA & AOS memory mapped structured views with optional & extensible serialization

164 lines (163 loc) 4.64 kB
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 };