reed_solomon_novelpoly/novel_poly_basis/
mod.rs1use crate::errors::*;
9use crate::f2e16::*;
10use crate::Shard;
11
12mod encode;
13mod reconstruct;
14
15pub use self::encode::*;
16pub use self::reconstruct::*;
17pub use super::util::*;
18
19use super::field::f2e16;
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24pub struct CodeParams {
25 n: usize,
28 k: usize,
31 wanted_n: usize,
33}
34
35impl CodeParams {
36 pub fn derive_parameters(n: usize, k: usize) -> Result<Self> {
44 if n < 2 {
45 return Err(Error::WantedShardCountTooLow(n));
46 }
47 if k < 1 {
48 return Err(Error::WantedPayloadShardCountTooLow(k));
49 }
50 let k_po2 = next_lower_power_of_2(k);
51 let n_po2 = next_higher_power_of_2(n);
52 assert!(n * k_po2 <= n_po2 * k);
56
57 if n_po2 > FIELD_SIZE {
58 return Err(Error::WantedShardCountTooHigh(n));
59 }
60 Ok(Self { n: n_po2, k: k_po2, wanted_n: n })
61 }
62
63 pub fn is_faster8(&self) -> bool {
65 #[cfg(all(target_feature = "avx", feature = "avx"))]
66 {
67 self.k >= (Additive8x::LANE << 1) && self.n % Additive8x::LANE == 0
68 }
69 #[cfg(not(all(target_feature = "avx", feature = "avx")))]
70 false
71 }
72
73 pub fn make_encoder(&self) -> ReedSolomon {
75 ReedSolomon::new(self.n, self.k, self.wanted_n)
76 .expect("this struct is not created with invalid shard number; qed")
77 }
78
79 pub fn n(&self) -> usize {
81 self.n
82 }
83
84 pub fn k(&self) -> usize {
86 self.k
87 }
88}
89
90#[derive(Debug, Clone, PartialEq, Eq)]
91pub struct ReedSolomon {
92 n: usize,
94 k: usize,
96 wanted_n: usize,
98}
99
100impl ReedSolomon {
101 pub fn shard_len(&self, payload_size: usize) -> usize {
103 let payload_symbols = (payload_size + 1) / 2;
104 let shard_symbols_ceil = (payload_symbols + self.k - 1) / self.k;
105
106 shard_symbols_ceil * 2
107 }
108
109 pub(crate) fn new(n: usize, k: usize, wanted_n: usize) -> Result<Self> {
110 if !is_power_of_2(n) && !is_power_of_2(k) {
111 Err(Error::ParamterMustBePowerOf2 { n, k })
112 } else {
113 Ok(Self { wanted_n, n, k })
114 }
115 }
116
117 pub fn encode<S: Shard>(&self, bytes: &[u8]) -> Result<Vec<S>> {
118 if bytes.is_empty() {
119 return Err(Error::PayloadSizeIsZero);
120 }
121
122 let shard_len = self.shard_len(bytes.len());
126 assert!(shard_len > 0);
127 let validator_count = self.wanted_n;
130 let k2 = self.k * 2;
131 let mut shards = vec![
133 <S as From<Vec<u8>>>::from(
134 #[allow(clippy::uninit_vec)]
135 {
136 let mut v = Vec::<u8>::with_capacity(shard_len);
137 unsafe { v.set_len(shard_len) }
138 v
139 }
140 );
141 validator_count
142 ];
143
144 for (chunk_idx, i) in (0..bytes.len()).step_by(k2).enumerate() {
145 let end = std::cmp::min(i + k2, bytes.len());
146 assert_ne!(i, end);
147 let data_piece = &bytes[i..end];
148 assert!(!data_piece.is_empty());
149 assert!(data_piece.len() <= k2);
150 let encoding_run = f2e16::encode_sub(data_piece, self.n, self.k)?;
151 for val_idx in 0..validator_count {
152 AsMut::<[[u8; 2]]>::as_mut(&mut shards[val_idx])[chunk_idx] = encoding_run[val_idx].0.to_be_bytes();
153 }
154 }
155
156 Ok(shards)
157 }
158
159 pub fn reconstruct<S: Shard>(&self, received_shards: Vec<Option<S>>) -> Result<Vec<u8>> {
163 let gap = self.n.saturating_sub(received_shards.len());
164
165 let received_shards =
166 received_shards.into_iter().take(self.n).chain(std::iter::repeat(None).take(gap)).collect::<Vec<_>>();
167
168 assert_eq!(received_shards.len(), self.n);
169
170 let mut existential_count = 0_usize;
172 let erasures = received_shards
173 .iter()
174 .map(|x| x.is_none())
175 .inspect(|erased| existential_count += !*erased as usize)
176 .collect::<Vec<bool>>();
177
178 if existential_count < self.k {
179 return Err(Error::NeedMoreShards { have: existential_count, min: self.k, all: self.n });
180 }
181
182 let shard_len_in_syms = {
184 let (first_shard_idx, first_shard_len) = received_shards
185 .iter()
186 .enumerate()
187 .find_map(|(idx, shard)| {
188 shard.as_ref().map(|shard| {
189 let shard = AsRef::<[[u8; 2]]>::as_ref(shard);
190 (idx, shard.len())
191 })
192 })
193 .expect("Existential shard count is at least k shards. qed");
194
195 if first_shard_len == 0 {
196 return Err(Error::EmptyShard);
197 }
198
199 if let Some(other_shard_len) = received_shards[(first_shard_idx + 1)..].iter().find_map(|shard| {
201 shard.as_ref().and_then(|shard| {
202 let shard = AsRef::<[[u8; 2]]>::as_ref(shard);
203 if first_shard_len != shard.len() {
204 Some(shard.len())
205 } else {
206 None
207 }
208 })
209 }) {
210 return Err(Error::InconsistentShardLengths { first: first_shard_len, other: other_shard_len });
211 }
212
213 first_shard_len
214 };
215
216 let mut error_poly_in_log = [Multiplier(0); FIELD_SIZE];
218 f2e16::eval_error_polynomial(&erasures[..], &mut error_poly_in_log[..], FIELD_SIZE);
219
220 let mut acc = Vec::<u8>::with_capacity(shard_len_in_syms * 2 * self.k);
221 for i in 0..shard_len_in_syms {
222 let decoding_run = Vec::<Option<Additive>>::from_iter(received_shards.iter().map(|x| {
224 x.as_ref().map(|x| {
225 let z = AsRef::<[[u8; 2]]>::as_ref(&x)[i];
226 Additive(u16::from_be_bytes(z))
227 })
228 }));
229
230 assert_eq!(decoding_run.len(), self.n);
231
232 let piece =
234 f2e16::reconstruct_sub(&decoding_run[..], &erasures, self.n, self.k, &error_poly_in_log).unwrap();
235 acc.extend_from_slice(&piece[..]);
236 }
237
238 Ok(acc)
239 }
240
241 pub fn reconstruct_from_systematic<S: Shard>(&self, chunks: Vec<S>) -> Result<Vec<u8>> {
248 let Some(first_shard) = chunks.first() else {
249 return Err(Error::NeedMoreShards { have: 0, min: self.k, all: self.n });
250 };
251 if chunks.len() < self.k {
252 return Err(Error::NeedMoreShards { have: chunks.len(), min: self.k, all: self.n });
253 }
254
255 let shard_len = AsRef::<[[u8; 2]]>::as_ref(first_shard).len();
256
257 if shard_len == 0 {
258 return Err(Error::EmptyShard);
259 }
260
261 if let Some(length) = chunks.iter().find_map(|c| {
262 let length = AsRef::<[[u8; 2]]>::as_ref(c).len();
263 if length != shard_len {
264 Some(length)
265 } else {
266 None
267 }
268 }) {
269 return Err(Error::InconsistentShardLengths { first: shard_len, other: length });
270 }
271
272 let mut systematic_bytes = Vec::with_capacity(shard_len * 2 * self.k);
273
274 for i in 0..shard_len {
275 for chunk in chunks.iter().take(self.k) {
276 let chunk = AsRef::<[[u8; 2]]>::as_ref(chunk)[i];
279 systematic_bytes.push(chunk[0]);
280 systematic_bytes.push(chunk[1]);
281 }
282 }
283
284 Ok(systematic_bytes)
285 }
286}
287
288#[cfg(test)]
289mod tests;