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