Skip to main content

oxgraph_hyper_bcsr/internal/
snapshot_open.rs

1//! Snapshot-backed constructors for [`BcsrHypergraph`].
2//!
3//! This module wires width-specific bipartite-CSR section kinds to the
4//! borrowed slice inputs accepted by [`BcsrHypergraph::open`].
5
6use oxgraph_layout_util::SnapshotWidth;
7use oxgraph_snapshot::{SectionBindError, Snapshot};
8
9use crate::{
10    error::{BcsrSection, BcsrSnapshotError},
11    internal::{
12        validation::BcsrValidation,
13        view::{BcsrHypergraph, BcsrSections},
14    },
15    word::{BcsrSnapshotIndex, LeWords},
16};
17
18/// Snapshot section bundle for the requested BCSR index widths.
19type SnapshotSections<'view, VertexIndex, RelationIndex, IncidenceIndex> = BcsrSections<
20    'view,
21    <IncidenceIndex as SnapshotWidth>::LittleEndianWord,
22    <VertexIndex as SnapshotWidth>::LittleEndianWord,
23    <RelationIndex as SnapshotWidth>::LittleEndianWord,
24>;
25
26impl<'view, VertexIndex, RelationIndex, IncidenceIndex>
27    BcsrHypergraph<'view, LeWords<VertexIndex, RelationIndex, IncidenceIndex>>
28where
29    VertexIndex: BcsrSnapshotIndex,
30    RelationIndex: BcsrSnapshotIndex,
31    IncidenceIndex: BcsrSnapshotIndex,
32{
33    /// Opens a [`BcsrHypergraph`] backed by sections from `snapshot`.
34    ///
35    /// # Errors
36    ///
37    /// Returns [`BcsrSnapshotError`] when any required section is missing,
38    /// has the wrong width for the requested typed view, or fails
39    /// bipartite-CSR validation.
40    ///
41    /// # Performance
42    ///
43    /// `O(s + P_head + P_tail + P_outgoing + P_incoming)` for `s` snapshot
44    /// sections and the four payload counts.
45    pub fn from_snapshot(snapshot: &Snapshot<'view>) -> Result<Self, BcsrSnapshotError> {
46        Self::from_snapshot_with(snapshot, BcsrValidation::Layout)
47    }
48
49    /// Opens a [`BcsrHypergraph`] from `snapshot` at the requested validation level.
50    ///
51    /// # Errors
52    ///
53    /// Returns [`BcsrSnapshotError`] for missing or wrong-width sections, and
54    /// any [`crate::BcsrError`] surfaced through validation.
55    ///
56    /// # Performance
57    ///
58    /// As [`Self::from_snapshot`] at [`BcsrValidation::Layout`]; with
59    /// [`BcsrValidation::Strict`], adds `O((P_head + P_tail) * log d)` for
60    /// the cross-direction walk.
61    pub fn from_snapshot_with(
62        snapshot: &Snapshot<'view>,
63        level: BcsrValidation,
64    ) -> Result<Self, BcsrSnapshotError> {
65        let sections = load_sections::<VertexIndex, RelationIndex, IncidenceIndex>(snapshot)?;
66        Ok(Self::open_with(sections, level)?)
67    }
68}
69
70/// Loads the eight bipartite-CSR sections from `snapshot`.
71fn load_sections<'view, VertexIndex, RelationIndex, IncidenceIndex>(
72    snapshot: &Snapshot<'view>,
73) -> Result<SnapshotSections<'view, VertexIndex, RelationIndex, IncidenceIndex>, BcsrSnapshotError>
74where
75    VertexIndex: BcsrSnapshotIndex,
76    RelationIndex: BcsrSnapshotIndex,
77    IncidenceIndex: BcsrSnapshotIndex,
78{
79    let head_offsets = load_section::<IncidenceIndex>(
80        snapshot,
81        BcsrSection::HeadOffsets,
82        IncidenceIndex::HEAD_OFFSETS_KIND,
83        IncidenceIndex::SECTION_VERSION,
84    )?;
85    let head_participants = load_section::<VertexIndex>(
86        snapshot,
87        BcsrSection::HeadParticipants,
88        VertexIndex::HEAD_PARTICIPANTS_KIND,
89        VertexIndex::SECTION_VERSION,
90    )?;
91    let tail_offsets = load_section::<IncidenceIndex>(
92        snapshot,
93        BcsrSection::TailOffsets,
94        IncidenceIndex::TAIL_OFFSETS_KIND,
95        IncidenceIndex::SECTION_VERSION,
96    )?;
97    let tail_participants = load_section::<VertexIndex>(
98        snapshot,
99        BcsrSection::TailParticipants,
100        VertexIndex::TAIL_PARTICIPANTS_KIND,
101        VertexIndex::SECTION_VERSION,
102    )?;
103    let vertex_outgoing_offsets = load_section::<IncidenceIndex>(
104        snapshot,
105        BcsrSection::VertexOutgoingOffsets,
106        IncidenceIndex::VERTEX_OUTGOING_OFFSETS_KIND,
107        IncidenceIndex::SECTION_VERSION,
108    )?;
109    let vertex_outgoing_hyperedges = load_section::<RelationIndex>(
110        snapshot,
111        BcsrSection::VertexOutgoingHyperedges,
112        RelationIndex::VERTEX_OUTGOING_HYPEREDGES_KIND,
113        RelationIndex::SECTION_VERSION,
114    )?;
115    let vertex_incoming_offsets = load_section::<IncidenceIndex>(
116        snapshot,
117        BcsrSection::VertexIncomingOffsets,
118        IncidenceIndex::VERTEX_INCOMING_OFFSETS_KIND,
119        IncidenceIndex::SECTION_VERSION,
120    )?;
121    let vertex_incoming_hyperedges = load_section::<RelationIndex>(
122        snapshot,
123        BcsrSection::VertexIncomingHyperedges,
124        RelationIndex::VERTEX_INCOMING_HYPEREDGES_KIND,
125        RelationIndex::SECTION_VERSION,
126    )?;
127    Ok(BcsrSections {
128        head_offsets,
129        head_participants,
130        tail_offsets,
131        tail_participants,
132        vertex_outgoing_offsets,
133        vertex_outgoing_hyperedges,
134        vertex_incoming_offsets,
135        vertex_incoming_hyperedges,
136    })
137}
138
139/// Binds `kind` in `snapshot` as a little-endian word slice for width `W`.
140///
141/// Wraps [`Snapshot::typed_section`] and maps the shared
142/// [`SectionBindError`] into the BCSR-specific [`BcsrSnapshotError`] variants.
143fn load_section<'view, W>(
144    snapshot: &Snapshot<'view>,
145    section: BcsrSection,
146    kind: u32,
147    version: u32,
148) -> Result<&'view [W::LittleEndianWord], BcsrSnapshotError>
149where
150    W: SnapshotWidth,
151{
152    snapshot
153        .typed_section::<W>(kind, version)
154        .map_err(|error| map_section_bind(section, error))
155}
156
157/// Maps a [`SectionBindError`] into the BCSR-specific snapshot error.
158const fn map_section_bind(section: BcsrSection, error: SectionBindError) -> BcsrSnapshotError {
159    match error {
160        SectionBindError::Missing { kind } => BcsrSnapshotError::MissingSection { section, kind },
161        SectionBindError::VersionMismatch {
162            kind,
163            expected,
164            actual,
165        } => BcsrSnapshotError::VersionMismatch {
166            section,
167            kind,
168            expected,
169            actual,
170        },
171        SectionBindError::View { error, .. } => BcsrSnapshotError::SectionView { section, error },
172    }
173}