Skip to main content

oxgraph_snapshot/
container_error.rs

1//! Concrete error types for snapshot reading, viewing, and writing.
2//!
3//! Each error type implements [`Display`](core::fmt::Display) and
4//! [`core::error::Error`] without depending on `alloc`, `std`, or external
5//! error frameworks.
6
7use core::fmt;
8
9/// Snapshot container validation error.
10///
11/// Returned by [`Snapshot::open`](crate::Snapshot::open) and
12/// [`Snapshot::open_with`](crate::Snapshot::open_with) for any header,
13/// section table, or layout-level invariant violation.
14///
15/// # Performance
16///
17/// `perf: unspecified`; errors are returned only from validation paths.
18#[derive(Clone, Debug, Eq, PartialEq)]
19pub enum SnapshotError {
20    /// Snapshot bytes were shorter than the fixed header.
21    TruncatedHeader {
22        /// Bytes required for the fixed header.
23        needed: usize,
24        /// Bytes actually provided.
25        actual: usize,
26    },
27    /// Header bytes were present but could not be interpreted.
28    MalformedHeader,
29    /// Magic bytes did not match [`FORMAT_MAGIC`](crate::FORMAT_MAGIC).
30    BadMagic {
31        /// Actual magic bytes from the snapshot.
32        actual: [u8; 8],
33    },
34    /// Format major version did not equal the supported value.
35    FormatMajorMismatch {
36        /// Major version recorded in the snapshot.
37        actual: u32,
38        /// Major version this library supports.
39        supported: u32,
40    },
41    /// Format minor version was newer than this library can read.
42    FormatMinorTooNew {
43        /// Minor version recorded in the snapshot.
44        actual: u32,
45        /// Highest minor version this library accepts.
46        max_supported: u32,
47    },
48    /// Header `header_size` field did not match the expected value.
49    HeaderSizeMismatch {
50        /// `header_size` value recorded in the snapshot.
51        actual: u32,
52        /// Header size this library expects.
53        expected: u32,
54    },
55    /// Header reserved bytes were not all zero.
56    NonZeroHeaderReserved,
57    /// `section_count` exceeded the v1 cap.
58    SectionCountTooLarge {
59        /// Section count recorded in the snapshot.
60        count: u32,
61        /// Maximum permitted section count.
62        max: u32,
63    },
64    /// Bytes after the header were too short for the declared section table.
65    TruncatedSectionTable {
66        /// Bytes required for the declared section table.
67        needed: usize,
68        /// Bytes available after the header.
69        actual: usize,
70    },
71    /// Section table bytes could not be interpreted.
72    MalformedSectionTable,
73    /// A section entry's `reserved_checksum` bytes were not all zero.
74    NonZeroEntryChecksum {
75        /// Section kind whose entry violated the invariant.
76        kind: u32,
77    },
78    /// A section entry's trailing reserved bytes were not all zero.
79    NonZeroEntryReserved {
80        /// Section kind whose entry violated the invariant.
81        kind: u32,
82    },
83    /// A section entry declared an unsupported flags bit.
84    UnsupportedFlags {
85        /// Section kind whose entry violated the invariant.
86        kind: u32,
87        /// Flags byte recorded in the entry.
88        flags: u8,
89    },
90    /// A section entry declared an `alignment_log2` larger than permitted.
91    AlignmentLog2TooLarge {
92        /// Section kind whose entry violated the invariant.
93        kind: u32,
94        /// Declared `alignment_log2` value.
95        alignment_log2: u8,
96    },
97    /// `offset + length` overflowed `u64` for one entry.
98    SectionRangeOverflow {
99        /// Section kind whose entry violated the invariant.
100        kind: u32,
101    },
102    /// A section's byte range fell outside the snapshot.
103    SectionOutOfBounds {
104        /// Section kind whose entry violated the invariant.
105        kind: u32,
106        /// Declared byte offset.
107        offset: u64,
108        /// Declared byte length.
109        length: u64,
110        /// Total snapshot byte length.
111        snapshot_len: u64,
112    },
113    /// Section table entries were not in monotonic non-decreasing offset order
114    /// or one entry overlapped its predecessor.
115    UnsortedSectionTable {
116        /// Index of the entry whose offset violated monotonicity.
117        index: usize,
118    },
119    /// Two section entries shared the same kind.
120    DuplicateKind {
121        /// Duplicated section kind.
122        kind: u32,
123    },
124    /// A `u64` value could not be represented as `usize` on this target.
125    UsizeOverflow {
126        /// Value that could not be represented as `usize`.
127        value: u64,
128    },
129}
130
131impl fmt::Display for SnapshotError {
132    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
133        match self {
134            Self::TruncatedHeader { needed, actual } => write!(
135                formatter,
136                "snapshot header is truncated: needed {needed} bytes, got {actual}"
137            ),
138            Self::MalformedHeader => formatter.write_str("snapshot header is malformed"),
139            Self::BadMagic { actual } => write!(formatter, "bad snapshot magic: {actual:?}"),
140            Self::FormatMajorMismatch { actual, supported } => write!(
141                formatter,
142                "unsupported snapshot format major: snapshot is {actual}, this reader supports {supported}"
143            ),
144            Self::FormatMinorTooNew {
145                actual,
146                max_supported,
147            } => write!(
148                formatter,
149                "snapshot format minor {actual} is newer than this reader's maximum {max_supported}"
150            ),
151            Self::HeaderSizeMismatch { actual, expected } => write!(
152                formatter,
153                "header_size mismatch: snapshot reports {actual}, this reader expects {expected}"
154            ),
155            Self::NonZeroHeaderReserved => {
156                formatter.write_str("snapshot header reserved bytes are not all zero")
157            }
158            Self::SectionCountTooLarge { count, max } => {
159                write!(formatter, "section count {count} exceeds maximum {max}")
160            }
161            Self::TruncatedSectionTable { needed, actual } => write!(
162                formatter,
163                "section table is truncated: needed {needed} bytes, got {actual}"
164            ),
165            Self::MalformedSectionTable => formatter.write_str("section table bytes are malformed"),
166            Self::NonZeroEntryChecksum { kind } => write!(
167                formatter,
168                "section {kind} entry reserved checksum bytes are not all zero"
169            ),
170            Self::NonZeroEntryReserved { kind } => write!(
171                formatter,
172                "section {kind} entry trailing reserved bytes are not all zero"
173            ),
174            Self::UnsupportedFlags { kind, flags } => write!(
175                formatter,
176                "section {kind} entry has unsupported flags byte {flags:#04x}"
177            ),
178            Self::AlignmentLog2TooLarge {
179                kind,
180                alignment_log2,
181            } => write!(
182                formatter,
183                "section {kind} alignment_log2 {alignment_log2} exceeds maximum"
184            ),
185            Self::SectionRangeOverflow { kind } => {
186                write!(formatter, "section {kind} offset + length overflows u64")
187            }
188            Self::SectionOutOfBounds {
189                kind,
190                offset,
191                length,
192                snapshot_len,
193            } => write!(
194                formatter,
195                "section {kind} is out of bounds: offset {offset}, length {length}, snapshot length {snapshot_len}"
196            ),
197            Self::UnsortedSectionTable { index } => write!(
198                formatter,
199                "section table entry at index {index} is unsorted or overlaps its predecessor"
200            ),
201            Self::DuplicateKind { kind } => write!(formatter, "duplicate section kind {kind}"),
202            Self::UsizeOverflow { value } => {
203                write!(formatter, "u64 value {value} does not fit usize")
204            }
205        }
206    }
207}
208
209impl core::error::Error for SnapshotError {}
210
211/// Error returned when borrowing a section payload as a typed slice fails.
212///
213/// # Performance
214///
215/// `perf: unspecified`; errors are returned only from typed-view paths.
216#[derive(Clone, Copy, Debug, Eq, PartialEq)]
217pub enum SectionViewError {
218    /// The requested element type is zero-sized, so it cannot tile a payload.
219    ZeroSizedType,
220    /// Payload byte length is not an exact multiple of `size_of::<T>()`.
221    LengthNotMultipleOfSize {
222        /// Section payload length in bytes.
223        length: usize,
224        /// `core::mem::size_of::<T>()` for the requested element type.
225        elem_size: usize,
226    },
227    /// Payload base address does not satisfy `align_of::<T>()`.
228    AlignmentMismatch {
229        /// Address of the payload's first byte.
230        ptr_addr: usize,
231        /// `core::mem::align_of::<T>()` for the requested element type.
232        required: usize,
233    },
234}
235
236impl fmt::Display for SectionViewError {
237    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
238        match self {
239            Self::ZeroSizedType => {
240                formatter.write_str("cannot borrow a section payload as a zero-sized type")
241            }
242            Self::LengthNotMultipleOfSize { length, elem_size } => write!(
243                formatter,
244                "section length {length} is not a multiple of element size {elem_size}"
245            ),
246            Self::AlignmentMismatch { ptr_addr, required } => write!(
247                formatter,
248                "section payload at address {ptr_addr:#x} is not aligned to {required}"
249            ),
250        }
251    }
252}
253
254impl core::error::Error for SectionViewError {}
255
256/// Error returned when binding a width-typed section by kind and version.
257///
258/// Returned by [`Snapshot::typed_section`](crate::Snapshot::typed_section): a
259/// single error covering the lookup, version check, and typed-view steps that
260/// every layout crate previously open-coded with its own variants.
261///
262/// # Performance
263///
264/// `perf: unspecified`; errors are returned only from section-binding paths.
265#[derive(Clone, Copy, Debug, Eq, PartialEq)]
266pub enum SectionBindError {
267    /// No section with the requested kind was present.
268    Missing {
269        /// Requested section kind.
270        kind: u32,
271    },
272    /// The section was present but its version did not match.
273    VersionMismatch {
274        /// Requested section kind.
275        kind: u32,
276        /// Version the caller required.
277        expected: u32,
278        /// Version recorded in the section entry.
279        actual: u32,
280    },
281    /// The payload could not be borrowed as the requested little-endian word.
282    View {
283        /// Requested section kind.
284        kind: u32,
285        /// Underlying typed-view failure.
286        error: SectionViewError,
287    },
288}
289
290impl fmt::Display for SectionBindError {
291    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
292        match self {
293            Self::Missing { kind } => write!(formatter, "snapshot section {kind} is missing"),
294            Self::VersionMismatch {
295                kind,
296                expected,
297                actual,
298            } => write!(
299                formatter,
300                "snapshot section {kind} version {actual} does not match expected {expected}"
301            ),
302            Self::View { kind, error } => {
303                write!(
304                    formatter,
305                    "snapshot section {kind} typed view failed: {error}"
306                )
307            }
308        }
309    }
310}
311
312impl core::error::Error for SectionBindError {}
313
314/// Error returned by snapshot writers.
315///
316/// Returned by [`SnapshotPlan::new`](crate::SnapshotPlan::new),
317/// [`SnapshotPlan::write_into`](crate::SnapshotPlan::write_into), and the
318/// alloc-gated [`SnapshotBuilder`](crate::SnapshotBuilder) methods.
319///
320/// # Performance
321///
322/// `perf: unspecified`; errors are returned only from writer paths.
323#[derive(Clone, Copy, Debug, Eq, PartialEq)]
324pub enum PlanError {
325    /// The output buffer is smaller than the snapshot's encoded length.
326    BufferTooSmall {
327        /// Bytes required by the encoded snapshot.
328        needed: usize,
329        /// Bytes provided in the output buffer.
330        actual: usize,
331    },
332    /// A section's `alignment_log2` exceeds [`MAX_ALIGNMENT_LOG2`](crate::MAX_ALIGNMENT_LOG2).
333    AlignmentTooLarge {
334        /// Declared `alignment_log2` value.
335        alignment_log2: u8,
336    },
337    /// Computed offsets or lengths overflowed `u64` or `usize`.
338    PayloadOverflow,
339    /// More sections were supplied than the v1 cap permits.
340    TooManySections {
341        /// Section count actually supplied.
342        count: usize,
343    },
344    /// Two pending sections shared the same kind.
345    DuplicateKind {
346        /// Duplicated section kind.
347        kind: u32,
348    },
349}
350
351impl fmt::Display for PlanError {
352    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
353        match self {
354            Self::BufferTooSmall { needed, actual } => write!(
355                formatter,
356                "output buffer too small: needed {needed} bytes, got {actual}"
357            ),
358            Self::AlignmentTooLarge { alignment_log2 } => write!(
359                formatter,
360                "alignment_log2 {alignment_log2} exceeds the v1 maximum"
361            ),
362            Self::PayloadOverflow => {
363                formatter.write_str("snapshot payload arithmetic overflowed u64 or usize")
364            }
365            Self::TooManySections { count } => {
366                write!(formatter, "section count {count} exceeds the v1 maximum")
367            }
368            Self::DuplicateKind { kind } => write!(formatter, "duplicate section kind {kind}"),
369        }
370    }
371}
372
373impl core::error::Error for PlanError {}