Created
March 24, 2025 13:08
-
-
Save bytejon/6e9abca901948dc6c9421a0a8744b592 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
--!strict | |
-- jonbyte, 2025 | |
-- types | |
type Int = number | |
type i53 = Int | |
type Type = Int | |
type Index = Int | |
type Map<K, V> = { [K]: V } | |
type Array<V> = Map<Int, V> | |
-- data | |
local types = {} :: Array<{ | |
read indexMap: Map<Index, Int>, -- sparse set | |
read entities: Array<i53>, -- dense set | |
next: Int, -- count of alive entities [0, max + 1] | |
max: Index -- highest allocated index | |
}> | |
-- functions | |
local function assertType( | |
type: Int | |
) | |
if not types[type] then | |
error('invalid type') | |
end | |
return types[type] | |
end | |
local function getIndex( | |
entity: i53 | |
) | |
return (entity // 2^5) % 2^32 | |
end | |
local function decode( | |
entity: i53 | |
) | |
-- LO [type(5b)][index(32b)][version(16b)] HI | |
local type = entity % 2^5 | |
local version = entity // 2^37 | |
return type, getIndex(entity), version | |
end | |
local function encode( | |
type: Type, | |
index: Index, | |
version: Int | |
) | |
return (version * 2^37) + (index * 2^5) + type | |
end | |
local function createType() | |
table.insert(types, { | |
indexMap = {}, | |
entities = {}, | |
next = 0, | |
max = -1 | |
}) | |
return #types | |
end | |
local function createEntity( | |
type: Type | |
) | |
local scope = assertType(type) | |
local indexMap = scope.indexMap | |
local entities = scope.entities | |
local entity: i53 | |
if scope.next <= scope.max then | |
scope.next += 1 | |
entity = entities[scope.next] | |
local index = getIndex(entity) | |
indexMap[index] = scope.next | |
else | |
scope.max += 1 | |
scope.next += 1 | |
local index = scope.max | |
entity = encode(type, index, 0) | |
indexMap[index] = scope.next | |
table.insert(entities, entity) | |
end | |
return entity | |
end | |
local function exists( | |
entity: i53 | |
) | |
local type, index, version = decode(entity) | |
local scope = assertType(type) | |
local entities = scope.entities | |
local pos = scope.indexMap[index] | |
if not pos then | |
--[[ | |
1. the entity was never created | |
2. the entity was destroyed | |
3. the index is invalid | |
]] | |
return false | |
end | |
if version ~= entities[pos] // 2^37 then | |
-- the entity index version is stale | |
return false | |
end | |
return true | |
end | |
local function destroy( | |
entity: i53 | |
) | |
if not exists(entity) then | |
return | |
end | |
local type, index, version = decode(entity) | |
local scope = types[type] | |
local entities = scope.entities | |
local pos = scope.indexMap[index] :: number | |
local next = scope.next | |
if pos ~= next then | |
scope.indexMap[getIndex(entities[next])] = pos | |
entities[pos], entities[next] = entities[next], entities[pos] | |
end | |
entities[scope.next] += 2^37 -- increment version | |
scope.indexMap[index] = nil | |
scope.next -= 1 | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment