ludic/packages/ludic.base/ecs_map.ludic

97 lines
2.6 KiB
Text

# ludic.base/ecs_map.ludic - IntMap: a key (0 or more) to a value, open addressing with linear
# probing; what turns a stable id kept in a save or a message into a handle without a scan
export property IntMap {
keys: words = null # -1 empty, -2 a key that was deleted
vals: words = null
mask: int = 63
n: int = 0
used: int = 0 # live and deleted slots: what makes probing long
}
export function imap_new() -> IntMap {
let m = new IntMap
imap_alloc(m, 64)
return m
}
function imap_alloc(m: IntMap, cap: int) -> void {
m.keys = words(cap)
m.vals = words(cap)
for i in 0 .. cap { m.keys[i] = -1 }
m.mask = cap - 1
m.n = 0
m.used = 0
}
function imap_slot(m: IntMap, k: int) -> int { return ((k * -1640531535) >> 7) & m.mask }
# the value under k, or `none` when there is none
export function imap_get(m: IntMap, k: int, none: int) -> int {
var s = imap_slot(m, k)
while m.keys[s] != -1 {
if m.keys[s] == k { return m.vals[s] }
s = (s + 1) & m.mask
}
return none
}
export function imap_put(m: IntMap, k: int, v: int) -> void {
if (m.used + 1) * 4 > (m.mask + 1) * 3 { imap_grow(m) }
var s = imap_slot(m, k)
var tomb = -1
while m.keys[s] != -1 {
if m.keys[s] == k {
m.vals[s] = v
return
}
if m.keys[s] == -2 and tomb < 0 { tomb = s }
s = (s + 1) & m.mask
}
if tomb >= 0 { s = tomb } else { m.used += 1 }
m.keys[s] = k
m.vals[s] = v
m.n += 1
}
export function imap_del(m: IntMap, k: int) -> bool {
var s = imap_slot(m, k)
while m.keys[s] != -1 {
if m.keys[s] == k {
m.keys[s] = -2
m.n -= 1
return true
}
s = (s + 1) & m.mask
}
return false
}
export function imap_len(m: IntMap) -> int { return m.n }
# every key gone, in place: the arrays are kept at the size they grew to
export function imap_clear(m: IntMap) -> void {
for i in 0 .. len(m.keys) { m.keys[i] = -1 }
m.n = 0
m.used = 0
}
# room for `n` keys before the map grows, made now
@alloc_ok("at a world's start: the keys it will need")
export function imap_reserve(m: IntMap, n: int) -> void {
if (m.mask + 1) * 3 >= n * 4 { return }
var cap = m.mask + 1
while cap * 3 < n * 4 { cap = cap * 2 }
let ok = m.keys
let ov = m.vals
imap_alloc(m, cap)
for i in 0 .. len(ok) { if ok[i] >= 0 { imap_put(m, ok[i], ov[i]) } }
}
@alloc_ok("past imap_reserve only: doubles when half full; the old arrays are not given back")
function imap_grow(m: IntMap) -> void {
let ok = m.keys
let ov = m.vals
var cap = m.mask + 1
if m.n * 2 > cap { cap = cap * 2 }
imap_alloc(m, cap)
for i in 0 .. len(ok) { if ok[i] >= 0 { imap_put(m, ok[i], ov[i]) } }
}