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    /// Payload byte length is not an exact multiple of `size_of::<T>()`.
219    LengthNotMultipleOfSize {
220        /// Section payload length in bytes.
221        length: usize,
222        /// `core::mem::size_of::<T>()` for the requested element type.
223        elem_size: usize,
224    },
225    /// Payload base address does not satisfy `align_of::<T>()`.
226    AlignmentMismatch {
227        /// Address of the payload's first byte.
228        ptr_addr: usize,
229        /// `core::mem::align_of::<T>()` for the requested element type.
230        required: usize,
231    },
232}
233
234impl fmt::Display for SectionViewError {
235    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
236        match self {
237            Self::LengthNotMultipleOfSize { length, elem_size } => write!(
238                formatter,
239                "section length {length} is not a multiple of element size {elem_size}"
240            ),
241            Self::AlignmentMismatch { ptr_addr, required } => write!(
242                formatter,
243                "section payload at address {ptr_addr:#x} is not aligned to {required}"
244            ),
245        }
246    }
247}
248
249impl core::error::Error for SectionViewError {}
250
251/// Error returned by snapshot writers.
252///
253/// Returned by [`SnapshotPlan::new`](crate::SnapshotPlan::new),
254/// [`SnapshotPlan::write_into`](crate::SnapshotPlan::write_into), and the
255/// alloc-gated [`SnapshotBuilder`](crate::SnapshotBuilder) methods.
256///
257/// # Performance
258///
259/// `perf: unspecified`; errors are returned only from writer paths.
260#[derive(Clone, Copy, Debug, Eq, PartialEq)]
261pub enum PlanError {
262    /// The output buffer is smaller than the snapshot's encoded length.
263    BufferTooSmall {
264        /// Bytes required by the encoded snapshot.
265        needed: usize,
266        /// Bytes provided in the output buffer.
267        actual: usize,
268    },
269    /// A section's `alignment_log2` exceeds [`MAX_ALIGNMENT_LOG2`](crate::MAX_ALIGNMENT_LOG2).
270    AlignmentTooLarge {
271        /// Declared `alignment_log2` value.
272        alignment_log2: u8,
273    },
274    /// Computed offsets or lengths overflowed `u64` or `usize`.
275    PayloadOverflow,
276    /// More sections were supplied than the v1 cap permits.
277    TooManySections {
278        /// Section count actually supplied.
279        count: usize,
280    },
281    /// Two pending sections shared the same kind.
282    DuplicateKind {
283        /// Duplicated section kind.
284        kind: u32,
285    },
286}
287
288impl fmt::Display for PlanError {
289    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
290        match self {
291            Self::BufferTooSmall { needed, actual } => write!(
292                formatter,
293                "output buffer too small: needed {needed} bytes, got {actual}"
294            ),
295            Self::AlignmentTooLarge { alignment_log2 } => write!(
296                formatter,
297                "alignment_log2 {alignment_log2} exceeds the v1 maximum"
298            ),
299            Self::PayloadOverflow => {
300                formatter.write_str("snapshot payload arithmetic overflowed u64 or usize")
301            }
302            Self::TooManySections { count } => {
303                write!(formatter, "section count {count} exceeds the v1 maximum")
304            }
305            Self::DuplicateKind { kind } => write!(formatter, "duplicate section kind {kind}"),
306        }
307    }
308}
309
310impl core::error::Error for PlanError {}