# 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]) } } }