oxgraph_hyper_bcsr/word.rs
1//! Borrowed-section word abstraction for bipartite-CSR payloads.
2
3use core::marker::PhantomData;
4
5use oxgraph_layout_util::SnapshotWidth;
6pub use oxgraph_layout_util::{LayoutIndex, LayoutSnapshotWord, LayoutWord};
7
8use crate::snapshot::{
9 SNAPSHOT_KIND_BCSR_HEAD_OFFSETS_BASE, SNAPSHOT_KIND_BCSR_HEAD_PARTICIPANTS_BASE,
10 SNAPSHOT_KIND_BCSR_TAIL_OFFSETS_BASE, SNAPSHOT_KIND_BCSR_TAIL_PARTICIPANTS_BASE,
11 SNAPSHOT_KIND_BCSR_VERTEX_INCOMING_HYPEREDGES_BASE,
12 SNAPSHOT_KIND_BCSR_VERTEX_INCOMING_OFFSETS_BASE,
13 SNAPSHOT_KIND_BCSR_VERTEX_OUTGOING_HYPEREDGES_BASE,
14 SNAPSHOT_KIND_BCSR_VERTEX_OUTGOING_OFFSETS_BASE,
15};
16
17/// Section version written and expected for all BCSR layout payloads.
18pub const SNAPSHOT_BCSR_SECTION_VERSION: u32 = 1;
19
20/// Bundles the three index widths and three storage words of one BCSR view.
21///
22/// [`BcsrHypergraph`](crate::BcsrHypergraph) is generic over exactly one
23/// `BcsrWords` family instead of six coupled type parameters. The three
24/// `*Index` types are the logical dense widths; the three `*Word` types are
25/// the in-memory representations stored in the borrowed sections, each
26/// constrained to decode to its matching index. Two families exist:
27/// [`NativeWords`] for host-order build-path views (including `usize`) and
28/// [`LeWords`] for little-endian snapshot-backed views.
29///
30/// # Performance
31///
32/// `perf: unspecified`; this is a type-level trait with no methods.
33pub trait BcsrWords {
34 /// Logical vertex index width.
35 type VertexIndex: LayoutIndex;
36 /// Logical hyperedge (relation) index width.
37 type RelationIndex: LayoutIndex;
38 /// Logical incidence index width.
39 type IncidenceIndex: LayoutIndex;
40 /// Storage word of the four offset sections; decodes to
41 /// [`Self::IncidenceIndex`].
42 type OffsetWord: LayoutWord<Index = Self::IncidenceIndex>;
43 /// Storage word of the hyperedge-major participant sections; decodes to
44 /// [`Self::VertexIndex`].
45 type VertexWord: LayoutWord<Index = Self::VertexIndex>;
46 /// Storage word of the vertex-major hyperedge sections; decodes to
47 /// [`Self::RelationIndex`].
48 type RelationWord: LayoutWord<Index = Self::RelationIndex>;
49}
50
51/// Type-level brand carried by the word-family markers; the `fn() -> …`
52/// wrapper keeps auto traits and variance independent of the index widths.
53type WordFamilyBrand<VertexIndex, RelationIndex, IncidenceIndex> =
54 PhantomData<fn() -> (VertexIndex, RelationIndex, IncidenceIndex)>;
55
56/// Native host word family: sections store each index type directly.
57///
58/// Selects identity storage words (`OffsetWord = IncidenceIndex`, and so on),
59/// which is the build-path representation. `usize` is permitted because
60/// nothing is persisted. This is a type-level carrier — it is never
61/// constructed.
62///
63/// # Performance
64///
65/// `perf: unspecified`; this is a type-level marker.
66pub struct NativeWords<VertexIndex, RelationIndex, IncidenceIndex> {
67 /// Type-level brand selecting the three index widths.
68 _family: WordFamilyBrand<VertexIndex, RelationIndex, IncidenceIndex>,
69}
70
71impl<VertexIndex, RelationIndex, IncidenceIndex> BcsrWords
72 for NativeWords<VertexIndex, RelationIndex, IncidenceIndex>
73where
74 VertexIndex: LayoutIndex + LayoutWord<Index = VertexIndex>,
75 RelationIndex: LayoutIndex + LayoutWord<Index = RelationIndex>,
76 IncidenceIndex: LayoutIndex + LayoutWord<Index = IncidenceIndex>,
77{
78 type IncidenceIndex = IncidenceIndex;
79 type OffsetWord = IncidenceIndex;
80 type RelationIndex = RelationIndex;
81 type RelationWord = RelationIndex;
82 type VertexIndex = VertexIndex;
83 type VertexWord = VertexIndex;
84}
85
86/// Little-endian snapshot word family: sections store fixed-width
87/// little-endian words.
88///
89/// Selects each width's [`SnapshotWidth::LittleEndianWord`] as the storage
90/// word, which is the view-path representation over persisted snapshot bytes.
91/// `usize` is excluded because snapshot bytes are fixed-width. This is a
92/// type-level carrier — it is never constructed.
93///
94/// # Performance
95///
96/// `perf: unspecified`; this is a type-level marker.
97pub struct LeWords<VertexIndex, RelationIndex, IncidenceIndex> {
98 /// Type-level brand selecting the three index widths.
99 _family: WordFamilyBrand<VertexIndex, RelationIndex, IncidenceIndex>,
100}
101
102impl<VertexIndex, RelationIndex, IncidenceIndex> BcsrWords
103 for LeWords<VertexIndex, RelationIndex, IncidenceIndex>
104where
105 VertexIndex: SnapshotWidth,
106 RelationIndex: SnapshotWidth,
107 IncidenceIndex: SnapshotWidth,
108{
109 type IncidenceIndex = IncidenceIndex;
110 type OffsetWord = IncidenceIndex::LittleEndianWord;
111 type RelationIndex = RelationIndex;
112 type RelationWord = RelationIndex::LittleEndianWord;
113 type VertexIndex = VertexIndex;
114 type VertexWord = VertexIndex::LittleEndianWord;
115}
116
117/// Width-specific section-kind tags for persisted BCSR layout payloads.
118///
119/// This is the thin BCSR-specific layer over the shared
120/// [`SnapshotWidth`](oxgraph_layout_util::SnapshotWidth) contract: it derives
121/// the eight per-width section kinds from the crate's 4-aligned base constants
122/// and [`SnapshotWidth::WIDTH_CODE`], and adds the section version. The
123/// little-endian storage word and the native/LE conversions come from
124/// `SnapshotWidth`, so `Index::LittleEndianWord` and `Index::to_le_word` keep
125/// resolving through that trait.
126///
127/// `usize` deliberately does not implement this trait: snapshot bytes are
128/// fixed-width.
129///
130/// # Performance
131///
132/// Reading the kind/version constants is `O(1)`.
133pub trait BcsrSnapshotIndex: SnapshotWidth {
134 /// Head offsets section kind for this width.
135 const HEAD_OFFSETS_KIND: u32 = SNAPSHOT_KIND_BCSR_HEAD_OFFSETS_BASE | Self::WIDTH_CODE;
136 /// Head participants section kind for this width.
137 const HEAD_PARTICIPANTS_KIND: u32 =
138 SNAPSHOT_KIND_BCSR_HEAD_PARTICIPANTS_BASE | Self::WIDTH_CODE;
139 /// Tail offsets section kind for this width.
140 const TAIL_OFFSETS_KIND: u32 = SNAPSHOT_KIND_BCSR_TAIL_OFFSETS_BASE | Self::WIDTH_CODE;
141 /// Tail participants section kind for this width.
142 const TAIL_PARTICIPANTS_KIND: u32 =
143 SNAPSHOT_KIND_BCSR_TAIL_PARTICIPANTS_BASE | Self::WIDTH_CODE;
144 /// Vertex outgoing offsets section kind for this width.
145 const VERTEX_OUTGOING_OFFSETS_KIND: u32 =
146 SNAPSHOT_KIND_BCSR_VERTEX_OUTGOING_OFFSETS_BASE | Self::WIDTH_CODE;
147 /// Vertex outgoing hyperedges section kind for this width.
148 const VERTEX_OUTGOING_HYPEREDGES_KIND: u32 =
149 SNAPSHOT_KIND_BCSR_VERTEX_OUTGOING_HYPEREDGES_BASE | Self::WIDTH_CODE;
150 /// Vertex incoming offsets section kind for this width.
151 const VERTEX_INCOMING_OFFSETS_KIND: u32 =
152 SNAPSHOT_KIND_BCSR_VERTEX_INCOMING_OFFSETS_BASE | Self::WIDTH_CODE;
153 /// Vertex incoming hyperedges section kind for this width.
154 const VERTEX_INCOMING_HYPEREDGES_KIND: u32 =
155 SNAPSHOT_KIND_BCSR_VERTEX_INCOMING_HYPEREDGES_BASE | Self::WIDTH_CODE;
156 /// Section version written for this width's BCSR payloads.
157 const SECTION_VERSION: u32 = SNAPSHOT_BCSR_SECTION_VERSION;
158}
159
160impl BcsrSnapshotIndex for u16 {}
161impl BcsrSnapshotIndex for u32 {}
162impl BcsrSnapshotIndex for u64 {}