Skip to main content

rustfs_erasure_codec/core/
mod.rs

1extern crate alloc;
2
3#[cfg(feature = "std")]
4pub(crate) mod cache_detect;
5mod codec;
6mod encode;
7mod leopard;
8pub(crate) mod leopard_gf16;
9pub(crate) mod leopard_gf8;
10mod metrics;
11mod options;
12#[cfg(feature = "std")]
13mod parallel;
14mod reconstruct;
15mod shard_by_shard;
16#[cfg(feature = "std")]
17pub mod stream;
18mod verify;
19mod workspace;
20
21use alloc::sync::Arc;
22use alloc::vec::Vec;
23
24use hashlink::LruCache;
25#[cfg(feature = "std")]
26use parking_lot::Mutex;
27#[cfg(not(feature = "std"))]
28use spin::Mutex;
29
30use crate::Field;
31use crate::matrix::Matrix;
32
33use leopard::FamilyState;
34
35pub use leopard::{LEOPARD_SHARD_MULTIPLE, leopard_aligned_shard_len};
36#[cfg(feature = "std")]
37pub use leopard_gf8::LeopardGf8ProfileStats;
38#[cfg(feature = "std")]
39pub(crate) use leopard_gf8::{leopard_gf8_profile_stats, reset_leopard_gf8_profile_stats};
40#[cfg(feature = "std")]
41pub use metrics::{ReconstructionCacheAnalysis, ReconstructionCacheStats, RuntimeProfileStats};
42pub use options::{CodecFamily, CodecOptions, LeopardMode, MatrixMode};
43#[cfg(feature = "std")]
44pub(crate) use parallel::RuntimeParallelPolicyCache;
45#[cfg(feature = "std")]
46pub use parallel::{PARALLEL_POLICY_VERSION, ParallelDecision, ParallelPolicy};
47pub use shard_by_shard::ShardByShard;
48pub use workspace::VerifyWorkspace;
49
50#[cfg(feature = "std")]
51use metrics::{ReconstructionCacheMetrics, RuntimeProfileMetrics};
52
53pub(crate) const DATA_DECODE_MATRIX_CACHE_MIN_CAPACITY: usize = 128;
54pub(crate) const DATA_DECODE_MATRIX_CACHE_MAX_CAPACITY: usize = 4096;
55pub(crate) const CODE_SLICE_MIN_CHUNK_BYTES: usize = 16 * 1024;
56pub(crate) const CODE_SLICE_DEFAULT_CHUNK_BYTES: usize = 64 * 1024;
57pub(crate) const CODE_SLICE_LARGE_CHUNK_BYTES: usize = 256 * 1024;
58pub(crate) const VERIFY_INLINE_SCRATCH_ELEMS: usize = 4 * 1024;
59#[cfg(feature = "std")]
60pub(crate) const PARALLEL_MIN_SHARD_BYTES: usize = 256 * 1024;
61
62#[derive(Debug)]
63pub struct ReedSolomon<F: Field> {
64    data_shard_count: usize,
65    parity_shard_count: usize,
66    total_shard_count: usize,
67    codec_family: CodecFamily,
68    pub(crate) family_state: FamilyState<F>,
69    matrix: Matrix<F>,
70    options: CodecOptions,
71    #[cfg(feature = "std")]
72    pub(crate) policy_cache: RuntimeParallelPolicyCache,
73    data_decode_matrix_cache: Mutex<LruCache<Vec<usize>, Arc<Matrix<F>>>>,
74    #[cfg(feature = "std")]
75    reconstruction_cache_metrics: ReconstructionCacheMetrics,
76    #[cfg(feature = "std")]
77    runtime_profile_metrics: RuntimeProfileMetrics,
78}
79
80impl<F: Field> Clone for ReedSolomon<F> {
81    fn clone(&self) -> ReedSolomon<F> {
82        match ReedSolomon::with_options(
83            self.data_shard_count,
84            self.parity_shard_count,
85            self.options,
86        ) {
87            Ok(codec) => codec,
88            Err(err) => {
89                debug_assert!(
90                    false,
91                    "existing codec invariants must produce a valid clone, but got error: {err:?}"
92                );
93                let mut matrix = Matrix::new(self.matrix.row_count(), self.matrix.col_count());
94                for row in 0..self.matrix.row_count() {
95                    for col in 0..self.matrix.col_count() {
96                        matrix.set(row, col, self.matrix.get(row, col));
97                    }
98                }
99
100                let family_state = crate::core::leopard::build_family_state(
101                    self.codec_family,
102                    self.data_shard_count,
103                    self.parity_shard_count,
104                    &matrix,
105                )
106                .unwrap_or_else(|_| {
107                    debug_assert!(
108                        false,
109                        "fallback clone path could not rebuild family state from stored matrix"
110                    );
111                    match self.codec_family {
112                        super::CodecFamily::Classic => FamilyState::Classic,
113                        super::CodecFamily::LeopardGF16 => FamilyState::LeopardGF16,
114                        super::CodecFamily::LeopardGF8 => {
115                            debug_assert!(
116                                false,
117                                "LeopardGF8 should have a recoverable family state"
118                            );
119                            FamilyState::Classic
120                        }
121                    }
122                });
123
124                let options = self.options;
125                #[cfg(feature = "std")]
126                let policy_cache = self.policy_cache;
127
128                ReedSolomon {
129                    data_shard_count: self.data_shard_count,
130                    parity_shard_count: self.parity_shard_count,
131                    total_shard_count: self.total_shard_count,
132                    codec_family: self.codec_family,
133                    family_state,
134                    matrix,
135                    options,
136                    #[cfg(feature = "std")]
137                    policy_cache,
138                    data_decode_matrix_cache: Mutex::new(LruCache::new(
139                        options.inversion_cache_capacity,
140                    )),
141                    #[cfg(feature = "std")]
142                    reconstruction_cache_metrics: ReconstructionCacheMetrics::default(),
143                    #[cfg(feature = "std")]
144                    runtime_profile_metrics: RuntimeProfileMetrics::default(),
145                }
146            }
147        }
148    }
149}
150
151impl<F: Field> PartialEq for ReedSolomon<F> {
152    fn eq(&self, rhs: &ReedSolomon<F>) -> bool {
153        self.data_shard_count == rhs.data_shard_count
154            && self.parity_shard_count == rhs.parity_shard_count
155            && self.codec_family == rhs.codec_family
156    }
157}
158
159impl<F: Field> ReedSolomon<F> {
160    /// Returns `Ok(())` for Classic, LeopardGF8, and LeopardGF16 families.
161    ///
162    /// Methods that are genuinely Classic-only (e.g., `update`, `decode_idx`) should
163    /// check `is_leopard_gf8_family()` / `is_leopard_gf16_family()` separately and
164    /// return an appropriate error.
165    pub(crate) fn ensure_classic_family_execution(&self) -> Result<(), crate::Error> {
166        match self.family_state {
167            FamilyState::Classic | FamilyState::LeopardGF8(_) | FamilyState::LeopardGF16 => Ok(()),
168        }
169    }
170
171    pub(crate) fn is_leopard_gf8_family(&self) -> bool {
172        matches!(self.family_state, FamilyState::LeopardGF8(_))
173    }
174
175    pub(crate) fn is_leopard_gf16_family(&self) -> bool {
176        matches!(self.family_state, FamilyState::LeopardGF16)
177    }
178
179    /// True for any Leopard codec family (GF8 or GF16). Both decode through the
180    /// Leopard FFT reconstruct path in [`ReedSolomon::reconstruct`], never the
181    /// Classic inversion-matrix machinery — so the `*_opt` entry points must
182    /// route either family to that serial dispatch rather than the Classic
183    /// parallel plan.
184    pub(crate) fn is_leopard_family(&self) -> bool {
185        self.is_leopard_gf8_family() || self.is_leopard_gf16_family()
186    }
187}