Skip to main content

linkrs_storage/edge/
immutable_csr.rs

1//! Frozen immutable CSR topology.
2//!
3//! Packed read-only form of one group's adjacency: a single contiguous
4//! neighbor segment plus a per-row degree table. Empty rows hold no slots.
5//! There is no capacity array, no overflow chain, no live index and no lock:
6//! a frozen group pays only its entries plus two small per-row arrays.
7//!
8//! A frozen group keeps every neighbor byte of the mutable group it was
9//! packed from, including edge ids, delete timestamps and tombstones, except
10//! reserved-slot gap sentinels (`INVALID_EDGE_ID` fillers), which carry no
11//! edge and are dropped at pack time. Freezing changes the physical layout
12//! and the row order, so timestamp-filtered reads observe the same logical
13//! content before and after, not the same byte order. Visibility authority
14//! stays above this layer; row stamps are physical replicas as in the
15//! mutable form.
16//!
17//! Every packed row is sorted by `(endpoint, rank, edge_id)`.
18//! Endpoint and rank keep one point-query key contiguous, and edge ids make
19//! the order total. Point queries bisect the key range and return the first
20//! timestamp-visible version inside it. Scans stay linear over the sorted
21//! rows.
22//!
23//! A frozen group packed from a valued bundled group additionally carries
24//! the inline value column: `values`/`valid` run slot-parallel to the packed
25//! halves in the same row order, so freezing a valued bundled group preserves
26//! its properties without migrating to the columnar form. Groups packed from
27//! any other source hold no value columns and read every value as NULL.
28//!
29//! Row offsets are rebuilt in memory on open and on load, never persisted.
30//! Every mutating entry point rejects writes: a frozen group must be
31//! explicitly unfrozen back into a mutable variant before it accepts writes
32//! again. There is no implicit unfreeze on the write path.
33
34use super::{ColdStamps, HotNbr};
35use bitvec::order::Lsb0;
36use bitvec::vec::BitVec;
37
38pub(crate) mod iter;
39pub(crate) mod maintenance;
40pub(crate) mod pack;
41pub(crate) mod persistence;
42pub(crate) mod read;
43pub(crate) mod trait_impl;
44
45pub use iter::{FrozenRowIter, ImmutableCsrIterator};
46
47/// Packed immutable adjacency of one group.
48///
49/// `hot_entries`/`cold_entries` hold every row back to back in row order,
50/// each row sorted by `(endpoint, rank, edge_id)`;
51/// `degrees[row]` is the row length and `offsets[row]` its start inside the
52/// halves. Empty rows contribute no slots. `offsets` is memory-only state
53/// rebuilt by packing and by loading.
54///
55/// `values`/`valid` carry the bundled inline value column slot-parallel to
56/// the packed halves when the group was packed from a valued bundled group;
57/// otherwise both stay empty and every value reads as NULL.
58#[derive(Debug, Clone)]
59pub struct ImmutableCsr {
60    hot_entries: Vec<HotNbr>,
61    cold_entries: Vec<ColdStamps>,
62    degrees: Vec<u32>,
63    offsets: Vec<u32>,
64    edge_count: u64,
65    values: Vec<u64>,
66    valid: BitVec<u8, Lsb0>,
67}
68
69impl Default for ImmutableCsr {
70    fn default() -> Self {
71        Self::new()
72    }
73}
74
75#[cfg(test)]
76mod tests;