Skip to main content

rusty_erasure_core/
error.rs

1//! Typed errors for every public operation.
2//!
3//! ISA-L's own README states that parameters passed to its functions are *not*
4//! validated — callers are responsible for argument validity. This crate's
5//! contract is the opposite (mission plan §3): every public entry point checks
6//! its inputs and returns one of these errors instead of reading out of bounds
7//! or panicking. Fuzzers hold that line from M1 onward.
8
9use core::fmt;
10
11/// Errors from coding-matrix construction and inversion.
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13#[non_exhaustive]
14pub enum MatrixError {
15    /// The requested dimensions are unusable: `k` (source shards) or `p`
16    /// (parity shards) is zero, or `k + p` exceeds 255 — the Vandermonde and
17    /// Cauchy constructions run out of distinct GF(2^8) elements past that.
18    Dimensions {
19        /// Requested source-shard count.
20        k: usize,
21        /// Requested parity-shard count.
22        p: usize,
23    },
24    /// The requested `(k, p)` falls outside ISA-L's documented safe region for
25    /// Vandermonde matrices (mission plan §3), where some recovery submatrices
26    /// are singular. Use a Cauchy matrix instead — every submatrix inverts.
27    VandermondeUnsafe {
28        /// Requested source-shard count.
29        k: usize,
30        /// Requested parity-shard count.
31        p: usize,
32    },
33    /// The submatrix selected for recovery is singular and cannot be inverted.
34    Singular,
35}
36
37impl fmt::Display for MatrixError {
38    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39        match *self {
40            Self::Dimensions { k, p } => write!(
41                f,
42                "unusable matrix dimensions k={k}, p={p}: need k >= 1, p >= 1, k + p <= 255"
43            ),
44            Self::VandermondeUnsafe { k, p } => write!(
45                f,
46                "k={k}, p={p} is outside the safe Vandermonde region; use Matrix::cauchy"
47            ),
48            Self::Singular => f.write_str("recovery submatrix is singular"),
49        }
50    }
51}
52
53impl core::error::Error for MatrixError {}
54
55/// Errors from encode and update operations.
56#[derive(Debug, Clone, Copy, PartialEq, Eq)]
57#[non_exhaustive]
58pub enum CodeError {
59    /// The number of shard buffers supplied does not match the coder's matrix.
60    ShardCount {
61        /// Shard count the coder's matrix requires.
62        expected: usize,
63        /// Shard count actually supplied.
64        got: usize,
65    },
66    /// A shard buffer's length disagrees with the stripe's shard length.
67    ShardLength {
68        /// Index of the offending shard.
69        index: usize,
70        /// Length every shard in this call must have.
71        expected: usize,
72        /// Length actually supplied.
73        got: usize,
74    },
75    /// A shard index argument is out of range for the coder's matrix.
76    ShardIndex {
77        /// The out-of-range index.
78        index: usize,
79        /// Number of source shards in the coder's matrix.
80        k: usize,
81    },
82}
83
84impl fmt::Display for CodeError {
85    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86        match *self {
87            Self::ShardCount { expected, got } => {
88                write!(f, "wrong shard count: expected {expected}, got {got}")
89            }
90            Self::ShardLength {
91                index,
92                expected,
93                got,
94            } => write!(
95                f,
96                "shard {index} has length {got}, but this stripe's shard length is {expected}"
97            ),
98            Self::ShardIndex { index, k } => {
99                write!(f, "shard index {index} out of range for k={k} sources")
100            }
101        }
102    }
103}
104
105impl core::error::Error for CodeError {}
106
107/// Errors from the recovery path.
108#[derive(Debug, Clone, Copy, PartialEq, Eq)]
109#[non_exhaustive]
110pub enum RecoverError {
111    /// More shards are missing than the code can repair.
112    TooManyMissing {
113        /// Number of missing shards.
114        missing: usize,
115        /// Number of parity shards (the repair capacity).
116        p: usize,
117    },
118    /// The surviving-rows submatrix could not be inverted.
119    Matrix(MatrixError),
120    /// A shard buffer failed validation.
121    Code(CodeError),
122}
123
124impl fmt::Display for RecoverError {
125    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
126        match *self {
127            Self::TooManyMissing { missing, p } => {
128                write!(
129                    f,
130                    "{missing} shards missing, but only {p} parity shards exist"
131                )
132            }
133            Self::Matrix(e) => write!(f, "recovery matrix error: {e}"),
134            Self::Code(e) => write!(f, "recovery shard error: {e}"),
135        }
136    }
137}
138
139impl core::error::Error for RecoverError {
140    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
141        match self {
142            Self::Matrix(e) => Some(e),
143            Self::Code(e) => Some(e),
144            Self::TooManyMissing { .. } => None,
145        }
146    }
147}
148
149impl From<MatrixError> for RecoverError {
150    fn from(e: MatrixError) -> Self {
151        Self::Matrix(e)
152    }
153}
154
155impl From<CodeError> for RecoverError {
156    fn from(e: CodeError) -> Self {
157        Self::Code(e)
158    }
159}