bitabs
nimony/src/lib/bitabs.nim
A BiTable is a table that can be seen as an optimized pair of (Table[Id, Val], Table[Val, Id]).
type BiTable = object vals: seq[T] keys: seq[Id]
proc initBiTable(): BiTable[Id,T]proc len(t: BiTable[Id,T]): int64proc hasId(t: BiTable[Id,T]; x: Id): boolproc isIndexed(t: BiTable[Id,T]): boolWhether the reverse (value -> id) index exists. It does not, and only does not, between an
addOrderedfill and the first thing that needs it.proc ensureIndexed(t: var BiTable[Id,T])Build the reverse index if
addOrderedleft it unbuilt. Idempotent, and free for a table that was interned into normally.getOrInclcalls this itself.getKeyIdcannot — it takes the table immutably and changing that would break every caller in the ecosystem for the sake of the rare one — so a caller that looks up BY VALUE in a table that might have been filled withaddOrderedcalls this first. Getting it wrong is an assertion ingetKeyId, not a wrong answer.proc addOrdered(t: var BiTable[Id,T]; v: sink T): IdAppend
vunder the next id WITHOUT hashing it.For a table deserialized in id order, every entry's id is its position, so the reverse index costs a hash and an insert per entry to reproduce something the file already states.
bif.loadfills four pools that way, and on a 68-module--ic:onbuild of the Nim compiler that was 451ms of a 637msload, nearly all of it the symbol pool.The table is still a BiTable:
getOrInclbuilds the index on demand (andensureIndexeddoes it explicitly forgetKeyId), so a pool that does get interned into behaves exactly as before — it just pays for the index at the point something needs it rather than always. Callers that only ever map id -> value never pay at all.proc getKeyId(t: BiTable[Id,T]; v: T): Idproc getOrIncl(t: var BiTable[Id,T]; v: T): Idproc getOrInclFromView(t: var BiTable[Id,T]; v: View): IdOptimized version that only materializes from the view
vif the value does not exist yet.proc [](t: BiTable[Id,T]; strId: Id): var Tproc hash(t: BiTable[Id,T]): uint64as the keys are hashes of the values, we simply use them instead
proc memSize(t: BiTable[Id,T]): int64type BiTableFloat = distinct BiTable[Id,uint64]proc getOrIncl(t: var BiTableFloat[Id]; v: float64): Idproc [](t: BiTableFloat[Id]; strId: Id): float64