97 lines
2.6 KiB
Text
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]) } }
|
|
}
|