Skip to main content

linkrs_storage/edge/
csr_variant.rs

1//! CSR Variant
2//!
3//! Enum wrapper for different CSR implementations (mutable).
4//! Provides runtime polymorphism without dynamic dispatch (dyn).
5//!
6//! # CSR Type Selection (two-level)
7//!
8//! Selection is two-level: the record form (`RecordForm`: `Pure` /
9//! `Bundled` / `Columnar`) fixes bytes per edge first; under the
10//! `Columnar` form the `EdgeStrategy` enum then picks the row strategy:
11//! - `Multiple`: Standard `MutableCsr` for general multi-edge scenarios
12//! - `Single`: `SingleMutableCsr` for one-edge-per-vertex (O(1) access)
13//! - `None`: No edges stored
14//!
15//! `Pure` / `Bundled` variants are constructed directly from the record
16//! form by the group container; `Frozen` only arises from explicit freeze.
17//!
18//! # Variants
19//!
20//! - `CsrVariant::Multiple`: Mutable CSR with dynamic capacity growth
21//! - `CsrVariant::Single`: Mutable single-edge CSR
22//! - `CsrVariant::Pure`: Pure topology CSR without timestamps or ranks
23//! - `CsrVariant::Bundled`: Pure topology plus one inline scalar value column
24//! - `CsrVariant::Frozen`: Packed immutable CSR, writes rejected until unfrozen
25//! - `CsrVariant::None`: Placeholder for relationships with no edges
26//!
27//! Frozen groups may additionally carry a derived memory-mapped serving
28//! cache. Residency lives with the group container, never in this enum: the
29//! heap form stays authoritative and the mapping only accelerates reads.
30//!
31//! Layout by responsibility (`csr_variant/` subdirectory):
32//! - `core` holds construction, clearing, stats and capability queries.
33//! - `persistence` handles dump, load and scratch dumps.
34//! - `trait_impl` adapts the type to CSR traits.
35//! - `read` implements iterators, visits and range scans.
36//! - `maintenance` handles compaction and sorting.
37//! - `values` implements bundled inline-value helpers.
38//! - `iter` holds iterator enums and their impls.
39
40use super::bundled_csr::BundledCsr;
41use super::pure_csr::PureTopologyCsr;
42use super::{ImmutableCsr, MutableCsr, SingleMutableCsr};
43
44/// Macro for dispatching method calls to the underlying CSR variant.
45///
46/// Expands to a match statement with proper None handling. Mutability lives
47/// with the receiver, so this single macro serves both mutable and immutable
48/// call sites; a second macro would only duplicate the match below.
49///
50/// # Usage
51///
52/// - Method with no arguments and return value with default:
53///   `dispatch!(self, method() -> default_value)`
54/// - Method with arguments and return value with default:
55///   `dispatch!(self, method(arg1, arg2) -> default_value)`
56///
57/// # Examples
58///
59/// ```ignore
60/// let result = dispatch!(self, insert_edge(vid, dst, id, ts) -> false);
61/// let result = dispatch!(self, edges_of(vid, ts) -> Vec::new());
62/// ```
63macro_rules! dispatch {
64    // Method with arguments and return value with default for None
65    ($self:expr, $method:ident($($arg:expr),+ $(,)?) -> $default:expr) => {
66        match $self {
67            CsrVariant::Multiple(csr) => csr.$method($($arg),+),
68            CsrVariant::Single(csr) => csr.$method($($arg),+),
69            CsrVariant::Pure(csr) => csr.$method($($arg),+),
70            CsrVariant::Bundled(csr) => csr.$method($($arg),+),
71            CsrVariant::Frozen(csr) => csr.$method($($arg),+),
72            CsrVariant::None { .. } => $default,
73        }
74    };
75
76    // Method with no arguments and return value with default for None
77    ($self:expr, $method:ident() -> $default:expr) => {
78        match $self {
79            CsrVariant::Multiple(csr) => csr.$method(),
80            CsrVariant::Single(csr) => csr.$method(),
81            CsrVariant::Pure(csr) => csr.$method(),
82            CsrVariant::Bundled(csr) => csr.$method(),
83            CsrVariant::Frozen(csr) => csr.$method(),
84            CsrVariant::None { .. } => $default,
85        }
86    };
87}
88
89pub(crate) mod core;
90pub(crate) mod iter;
91pub(crate) mod maintenance;
92pub(crate) mod persistence;
93pub(crate) mod read;
94pub(crate) mod trait_impl;
95pub(crate) mod values;
96
97pub use iter::{CsrIterator, CsrRowIter};
98
99/// Polymorphic CSR wrapper supporting multiple implementation strategies.
100///
101/// Combines mutable CSR implementations into a single enum for runtime
102/// selection without generic monomorphization.
103///
104/// The enum dispatch via `CsrVariant` keeps one implementation per trait
105/// method; callers use the trait interface so behavior stays in one place.
106///
107/// Cross-layer traversal contract: row positions (`EdgePosition` chunk/slot
108/// pairs, primary-block offsets, overflow indices) are variant-local and
109/// never cross a layer boundary. Every handoff between layers (read to
110/// write, scan to point lookup, live table to migration) re-resolves the
111/// target through the edge-id key first; a position obtained from one
112/// variant is never interpreted by another.
113///
114/// # Row-view and ordering contract
115///
116/// All variants promise one shared row-view semantic: a row walk yields the
117/// physically stored entries of one vertex, each exactly once, as assembled
118/// `Nbr` records. Gap sentinels are excluded by every walk; tombstones are
119/// included so reclaim and audit paths observe them. Visibility is decided
120/// by the version authority above, never by these walks. Three access shapes
121/// serve it with unified naming: the borrowed [`Self::visit_physical`] walk
122/// (no allocation, preferred for inline forms and hot scans), the zero-alloc
123/// [`Self::fill_physical_into`] caller-buffer fill (preferred for batch
124/// scans), and the allocating `physical_edges_of` accessor (test and offline
125/// use only). No new traversal dialect may be added per variant; new needs
126/// go through these three.
127///
128/// Ordering is promised per form, never globally: mutable, single, pure and
129/// bundled rows are insertion-ordered and promise no order; frozen rows are
130/// packed sorted by `(endpoint, rank, edge_id)` and promise that order plus
131/// key-interval bisection. Freeze, compaction, compression and snapshot
132/// rebuilds may change the order; the query layer must never depend on an
133/// unpromised order. Plan selection uses `should_use_bisection`, combining
134/// the per-form promise with the memory-only sorted observation that is
135/// rebuilt on load and never cached across restarts.
136#[derive(Debug, Clone)]
137pub enum CsrVariant {
138    /// Multi-edge mutable CSR: each vertex can have multiple outgoing edges
139    Multiple(Box<MutableCsr>),
140    /// Single-edge mutable CSR: each vertex has at most one outgoing edge
141    Single(SingleMutableCsr),
142    /// Pure topology CSR: 12 bytes/edge, no rank, no timestamps
143    Pure(Box<PureTopologyCsr>),
144    /// Bundled CSR: 20 bytes/edge, inline single scalar value column
145    Bundled(Box<BundledCsr>),
146    /// Frozen packed CSR: read-only until explicitly unfrozen
147    Frozen(Box<ImmutableCsr>),
148    /// No-edge placeholder: vertices exist but have no outgoing edges
149    None { vertex_capacity: usize },
150}
151
152#[cfg(test)]
153mod tests;