Last active
May 31, 2024 16:52
-
-
Save ArcaneEngineer/0adf94cded8ed7443f1c1e166c97697a to your computer and use it in GitHub Desktop.
Array-of-struct emulation using TypedArray and some utility functions -- fast, contiguously allocated arrays unlike regular JS arrays of objects.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const systemWordSize = 8; //bytes | |
const strideLength = 1; //bytes | |
//...these functions can be optimised to take advantage of 4 or 8 byte reads / writes, depending on system word size. | |
//Utility functions | |
function padAndCalcMemberOffsets(type) | |
{ | |
let structSize = 0; | |
for (let memberName in type) | |
{ | |
let member = type[memberName]; | |
console.log(memberName, member); | |
member.offset = structSize; | |
structSize += member.size; | |
} | |
let remainder = structSize % systemWordSize; | |
if (remainder != 0) | |
{ | |
let padSize = systemWordSize - remainder; | |
type._padding = {size: padSize, offset: structSize}; | |
structSize += padSize; | |
} | |
return structSize; | |
} | |
function read(array, index, type, memberName) | |
{ | |
let bytesLength = type[memberName].size; | |
let ibase = index * type._size; | |
let i = ibase + type[memberName].offset; | |
let iStart = i; | |
let value = 0; | |
//read out sequentially into a single multibyte value. | |
for (let b = 0; b < bytesLength; b += strideLength) | |
{ | |
let bitsToShiftBy = b * 8; //bits per byte | |
value |= array[i] << bitsToShiftBy; | |
i += strideLength; //1 byte at a time | |
} | |
console.log("read ", type._name, "at ["+ibase+"],", memberName, "at ["+iStart+"] (length="+bytesLength+"):", value); | |
return value; | |
} | |
//write a multibyte field. | |
function write(array, index, type, memberName, value) | |
{ | |
let bytesLength = type[memberName].size; | |
let ibase = index * type._size; | |
let i = ibase + type[memberName].offset; | |
let iStart = i; | |
let valueNew = 0; | |
//read out sequentially into a single multibyte value. | |
for (let b = 0; b < bytesLength; b += strideLength) | |
{ | |
let bitsToShiftBy = b * 8; //bits per byte | |
let valueSegment = (value >> bitsToShiftBy) & 255; | |
array[i] = valueSegment; | |
i += strideLength; //1 byte at a time | |
} | |
console.log("write", type._name, "at ["+ibase+"],", memberName, "at ["+iStart+"] (length="+bytesLength+"):", value); | |
} | |
function prepareStructTypes() | |
{ | |
for (let structTypeName in structTypes) | |
{ | |
let structType = structTypes[structTypeName]; | |
structType._size = padAndCalcMemberOffsets(structType); | |
structType._name = structTypeName; //optional, only used for console.log | |
console.log(structTypeName, structType); | |
} | |
} | |
//Set up structs | |
let structTypes = { | |
Boxer: | |
{ | |
id: {size: 2, offset: 0}, | |
hits: {size: 3, offset: 0}, | |
time: {size: 4, offset: 0}, | |
//total size of this struct would be 1+4+2=7 bytes, which does not align with | |
//typical 4 or 8 byte word boundaries, which make access (very) inefficient. So pad it. | |
}, | |
Javelineer: | |
{ | |
a: {size: 1, offset: 0}, | |
b: {size: 3, offset: 0}, | |
}, | |
} | |
prepareStructTypes(structTypes); //pad them, figure out member offsets, and get struct total size. | |
//Test | |
const Boxer = structTypes.Boxer; | |
const arrayOfBoxer = new Uint8Array(8 * Boxer._size); | |
console.log("Interleaved Boxer array length:", arrayOfBoxer.length, "size per element", Boxer._size); | |
let i = 3; | |
let id = 888; | |
let time = 23983291; | |
//write | |
write(arrayOfBoxer, i, Boxer, "id", id); // i * Boxer._size + Boxer.id.offset, | |
write(arrayOfBoxer, i, Boxer, "time", time); // i * Boxer._size + Boxer.id.offset, | |
console.log(arrayOfBoxer); | |
//read | |
read(arrayOfBoxer, i, Boxer, "id"); | |
read(arrayOfBoxer, i, Boxer, "time"); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Outputs:
And: