83 lines
2.5 KiB
Text
83 lines
2.5 KiB
Text
# ludic.base/ecs_index.ludic - a cached query: for each value of an int column (a kind), the rows
|
|
# holding it, kept current by the table's setters, so "every fire" is a list and "how many" a length
|
|
export property IntIndex {
|
|
col: int = 0
|
|
gate: int = -1 # an int column: a row is filed only while it is non-zero (-1: always)
|
|
lists: []words = null # value -> its rows
|
|
pos: words = null # row -> where it stands in its value's list, -1 not filed
|
|
val: words = null # row -> the slot of the value it is filed under
|
|
empty: words = null # what a value with no rows answers
|
|
sparse: IntMap = null # a value past IX_DENSE (a chunk's packed cell) to its list's slot
|
|
}
|
|
export function tb_index<T>(tb: Table<T>, col: int, gate: int) -> IntIndex {
|
|
let ix = new IntIndex
|
|
ix.col = col
|
|
ix.gate = gate
|
|
ix.lists = new []words
|
|
ix.pos = words(0)
|
|
ix.val = words(0)
|
|
ix.empty = words(0)
|
|
ix.sparse = imap_new()
|
|
for r in 0 .. tb.n { ix_grow(ix) }
|
|
push(tb.idx, ix)
|
|
for r in 0 .. tb.n { tb_ix_refile(tb, ix, r) }
|
|
return ix
|
|
}
|
|
|
|
# the rows filed under v - the index's own list: read it, never push to it
|
|
export function ix_rows(ix: IntIndex, v: int) -> words {
|
|
let s = ix_slot(ix, v, false)
|
|
if s < 0 { return ix.empty }
|
|
return ix.lists[s]
|
|
}
|
|
export function ix_count(ix: IntIndex, v: int) -> int { return len(ix_rows(ix, v)) }
|
|
# the first row filed under v, or -1
|
|
export function ix_first(ix: IntIndex, v: int) -> int {
|
|
let rows = ix_rows(ix, v)
|
|
if len(rows) == 0 { return -1 }
|
|
return rows[0]
|
|
}
|
|
|
|
function ix_grow(ix: IntIndex) -> void {
|
|
kept_push(ix.pos, -1)
|
|
kept_push(ix.val, -1)
|
|
}
|
|
|
|
function ix_shrink(ix: IntIndex) -> void {
|
|
List.pop(ix.pos)
|
|
List.pop(ix.val)
|
|
}
|
|
|
|
function ix_file(ix: IntIndex, r: int, v: int) -> void {
|
|
let s = ix_slot(ix, v, true)
|
|
if ix.pos[r] >= 0 and ix.val[r] == s { return }
|
|
ix_unfile(ix, r)
|
|
if s < 0 { return }
|
|
ix.pos[r] = len(ix.lists[s])
|
|
ix.val[r] = s
|
|
kept_push(ix.lists[s], r)
|
|
}
|
|
|
|
# out of its list: the list's last row takes its place
|
|
function ix_unfile(ix: IntIndex, r: int) -> void {
|
|
let p = ix.pos[r]
|
|
if p < 0 { return }
|
|
let l = ix.lists[ix.val[r]]
|
|
let last = List.pop(l)
|
|
if last != r {
|
|
l[p] = last
|
|
ix.pos[last] = p
|
|
}
|
|
ix.pos[r] = -1
|
|
ix.val[r] = -1
|
|
}
|
|
|
|
# row `from` becomes row `to` (which is not filed)
|
|
function ix_moved(ix: IntIndex, from: int, to: int) -> void {
|
|
let p = ix.pos[from]
|
|
ix.pos[to] = p
|
|
ix.val[to] = ix.val[from]
|
|
if p >= 0 { ix.lists[ix.val[from]][p] = to }
|
|
ix.pos[from] = -1
|
|
ix.val[from] = -1
|
|
}
|