sliver_tree/_live_index_cache library

Internal: lazy live-index-in-parent cache backing TreeController.getIndexInParent.

Pure stamp algebra and storage. The controller owns the refresh loop, because a refresh needs both the raw sibling list (NodeStore) and the pending-deletion flags (AnimationCoordinator), neither of which this component knows anything about. That split is what keeps it standalone-testable.

Validity model

One monotonic generation; a per-PARENT stamp recording the generation at which that parent's child list was last refreshed (a scalar serves the root list, which has no nid); and a per-CHILD slot stamp recording the generation at which that nid's cached index was written. A cached answer is trusted only when BOTH stamps equal the current generation.

The slot stamp is what makes reads self-protecting: a refresh writes slots only for live members actually in the list, so a key whose parent link and sibling list disagree (a transient mid-mutation state) reads -1, and a recycled nid's previous-life slot is stale by construction because every adopt happens inside a mutator that bumps before the next read.

Invariants

  • generation starts at 1 and NEVER rewinds. Zero-filled fresh or regrown stamp slots are therefore stale by construction: no init fill pass exists, ever. A rewound generation meeting a surviving stamp is the only way to manufacture a false-valid slot.
  • reset drops every array and zeroes the roots stamp; it does not touch the generation (monotonic covers it).
  • Mutators bump AFTER their last raw-list write and before notifications (exit placement). Entry-only bumping is unsound: a user comparator can read getIndexInParent mid-mutation, and an entry-bumped refresh against a half-mutated list would be trusted after the method returns. With exit placement such a refresh is discarded by the exit bump, so mid-window reads are self-healing.
  • A NEW raw-sibling-list mutator must add its own exit bump AND join the mutation script in live_index_oracle_fuzz_test.dart. That fuzz guards both rules: it interleaves reads with mutations, so a missing or misplaced bump surfaces as a divergence from its independent oracle. A mutator absent from the script is simply unguarded.

Classes

LiveIndexCache
Stamp storage for the live-index cache: no tree knowledge and no refresh logic, just the generation algebra described above.

Constants

kRootListParentNid → const int
Sentinel parent nid for the root list, which has no nid of its own.