Skip to main content

rustfs_erasure_codec/core/
shard_by_shard.rs

1use crate::Field;
2use crate::errors::{Error, SBSError};
3
4use super::ReedSolomon;
5
6/// Bookkeeper for shard by shard encoding.
7///
8/// This is useful for avoiding incorrect use of
9/// `encode_single` and `encode_single_sep`
10///
11/// # Use cases
12///
13/// Shard by shard encoding is useful for streamed data encoding
14/// where you do not have all the needed data shards immediately,
15/// but you want to spread out the encoding workload rather than
16/// doing the encoding after everything is ready.
17///
18/// A concrete example would be network packets encoding,
19/// where encoding packet by packet as you receive them may be more efficient
20/// than waiting for N packets then encode them all at once.
21///
22/// # Example
23///
24/// ```
25/// # #[macro_use] extern crate rustfs_erasure_codec;
26/// # use rustfs_erasure_codec::*;
27/// # fn main () {
28/// use rustfs_erasure_codec::galois_8::Field;
29/// let r: ReedSolomon<Field> = ReedSolomon::new(3, 2).unwrap();
30///
31/// let mut sbs = ShardByShard::new(&r);
32///
33/// let mut shards = shards!([0u8,  1,  2,  3,  4],
34///                          [5,  6,  7,  8,  9],
35///                          // say we don't have the 3rd data shard yet
36///                          // and we want to fill it in later
37///                          [0,  0,  0,  0,  0],
38///                          [0,  0,  0,  0,  0],
39///                          [0,  0,  0,  0,  0]);
40///
41/// // encode 1st and 2nd data shard
42/// sbs.encode(&mut shards).unwrap();
43/// sbs.encode(&mut shards).unwrap();
44///
45/// // fill in 3rd data shard
46/// shards[2][0] = 10.into();
47/// shards[2][1] = 11.into();
48/// shards[2][2] = 12.into();
49/// shards[2][3] = 13.into();
50/// shards[2][4] = 14.into();
51///
52/// // now do the encoding
53/// sbs.encode(&mut shards).unwrap();
54///
55/// assert!(r.verify(&shards).unwrap());
56/// # }
57/// ```
58#[derive(PartialEq, Debug)]
59pub struct ShardByShard<'a, F: 'a + Field> {
60    codec: &'a ReedSolomon<F>,
61    cur_input: usize,
62}
63
64impl<'a, F: 'a + Field> ShardByShard<'a, F> {
65    /// Create a new shard-by-shard encoder.
66    pub fn new(codec: &'a ReedSolomon<F>) -> ShardByShard<'a, F> {
67        ShardByShard {
68            codec,
69            cur_input: 0,
70        }
71    }
72
73    /// Returns `true` if all data shards have been encoded and parity is ready.
74    pub fn parity_ready(&self) -> bool {
75        self.cur_input == self.codec.data_shard_count
76    }
77
78    /// Reset to accept a new batch of data shards.
79    ///
80    /// Returns [`SBSError::LeftoverShards`] if the previous batch was incomplete.
81    pub fn reset(&mut self) -> Result<(), SBSError> {
82        if self.cur_input > 0 && !self.parity_ready() {
83            return Err(SBSError::LeftoverShards);
84        }
85
86        self.cur_input = 0;
87
88        Ok(())
89    }
90
91    /// Force-reset without checking for leftover shards.
92    pub fn reset_force(&mut self) {
93        self.cur_input = 0;
94    }
95
96    /// Returns the index of the next data shard expected.
97    pub fn cur_input_index(&self) -> usize {
98        self.cur_input
99    }
100
101    fn return_ok_and_incre_cur_input(&mut self) -> Result<(), SBSError> {
102        self.cur_input += 1;
103        Ok(())
104    }
105
106    fn sbs_encode_checks<U: AsRef<[F::Elem]> + AsMut<[F::Elem]>>(
107        &mut self,
108        slices: &mut [U],
109    ) -> Result<(), SBSError> {
110        let internal_checks = |codec: &ReedSolomon<F>, data: &mut [U]| -> Result<(), Error> {
111            check_piece_count!(all => codec, data);
112            check_slices!(multi => data);
113
114            Ok(())
115        };
116
117        if self.parity_ready() {
118            return Err(SBSError::TooManyCalls);
119        }
120
121        match internal_checks(self.codec, slices) {
122            Ok(()) => Ok(()),
123            Err(e) => Err(SBSError::RSError(e)),
124        }
125    }
126
127    fn sbs_encode_sep_checks<T: AsRef<[F::Elem]>, U: AsRef<[F::Elem]> + AsMut<[F::Elem]>>(
128        &mut self,
129        data: &[T],
130        parity: &mut [U],
131    ) -> Result<(), SBSError> {
132        let internal_checks =
133            |codec: &ReedSolomon<F>, data: &[T], parity: &mut [U]| -> Result<(), Error> {
134                check_piece_count!(data => codec, data);
135                check_piece_count!(parity => codec, parity);
136                check_slices!(multi => data, multi => parity);
137
138                Ok(())
139            };
140
141        if self.parity_ready() {
142            return Err(SBSError::TooManyCalls);
143        }
144
145        match internal_checks(self.codec, data, parity) {
146            Ok(()) => Ok(()),
147            Err(e) => Err(SBSError::RSError(e)),
148        }
149    }
150
151    /// Encode the next data shard in the batch.
152    pub fn encode<T, U>(&mut self, mut shards: T) -> Result<(), SBSError>
153    where
154        T: AsRef<[U]> + AsMut<[U]>,
155        U: AsRef<[F::Elem]> + AsMut<[F::Elem]>,
156    {
157        let shards = shards.as_mut();
158        self.sbs_encode_checks(shards)?;
159
160        self.codec
161            .encode_single(self.cur_input, shards)
162            .map_err(SBSError::RSError)?;
163
164        self.return_ok_and_incre_cur_input()
165    }
166
167    /// Encode the next data shard using separate data and parity slices.
168    pub fn encode_sep<T: AsRef<[F::Elem]>, U: AsRef<[F::Elem]> + AsMut<[F::Elem]>>(
169        &mut self,
170        data: &[T],
171        parity: &mut [U],
172    ) -> Result<(), SBSError> {
173        self.sbs_encode_sep_checks(data, parity)?;
174
175        self.codec
176            .encode_single_sep(self.cur_input, data[self.cur_input].as_ref(), parity)
177            .map_err(SBSError::RSError)?;
178
179        self.return_ok_and_incre_cur_input()
180    }
181}