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