commonware_cryptography/reed_solomon/
decoder_result.rs1use crate::reed_solomon::rate::DecoderWork;
2
3pub struct DecoderResult<'a> {
13 work: &'a mut DecoderWork,
14}
15
16impl DecoderResult<'_> {
17 pub fn original(&self, index: usize) -> Option<&[u8]> {
21 self.work.original(index)
22 }
23
24 pub const fn original_iter(&self) -> Originals<'_> {
27 Originals::new(self.work)
28 }
29}
30
31impl<'a> DecoderResult<'a> {
35 pub(crate) const fn new(work: &'a mut DecoderWork) -> Self {
36 Self { work }
37 }
38}
39
40impl Drop for DecoderResult<'_> {
44 fn drop(&mut self) {
45 self.work.reset_received();
46 }
47}
48
49pub struct RecoveryDecoderResult<'a> {
57 inner: DecoderResult<'a>,
58}
59
60impl RecoveryDecoderResult<'_> {
61 pub fn original(&self, index: usize) -> Option<&[u8]> {
64 self.inner.original(index)
65 }
66
67 pub const fn original_iter(&self) -> Originals<'_> {
70 self.inner.original_iter()
71 }
72
73 pub fn recovery(&self, index: usize) -> Option<&[u8]> {
76 self.inner.work.recovery(index)
77 }
78
79 pub const fn recovery_iter(&self) -> Recoveries<'_> {
82 Recoveries::new(self.inner.work)
83 }
84}
85
86impl<'a> RecoveryDecoderResult<'a> {
90 pub(crate) const fn new(inner: DecoderResult<'a>) -> Self {
91 Self { inner }
92 }
93}
94
95pub struct Originals<'a> {
102 remaining: usize,
103 next_index: usize,
104 work: &'a DecoderWork,
105}
106
107impl<'a> Iterator for Originals<'a> {
111 type Item = (usize, &'a [u8]);
112 fn next(&mut self) -> Option<(usize, &'a [u8])> {
113 if self.remaining == 0 {
114 return None;
115 }
116
117 let mut index = self.next_index;
118 while index < self.work.original_count() {
119 if let Some(original) = self.work.original(index) {
120 self.next_index = index + 1;
121 self.remaining -= 1;
122 return Some((index, original));
123 }
124 index += 1;
125 }
126
127 unreachable!("Inconsistency in internal data structures. Please report.");
128 }
129
130 fn size_hint(&self) -> (usize, Option<usize>) {
131 (self.remaining, Some(self.remaining))
132 }
133}
134
135impl ExactSizeIterator for Originals<'_> {}
139
140impl<'a> Originals<'a> {
144 pub(crate) const fn new(work: &'a DecoderWork) -> Self {
145 Self {
146 remaining: work.missing_original_count(),
147 next_index: 0,
148 work,
149 }
150 }
151}
152
153pub struct Recoveries<'a> {
160 remaining: usize,
161 next_index: usize,
162 work: &'a DecoderWork,
163}
164
165impl<'a> Iterator for Recoveries<'a> {
169 type Item = (usize, &'a [u8]);
170 fn next(&mut self) -> Option<(usize, &'a [u8])> {
171 if self.remaining == 0 {
172 return None;
173 }
174
175 let mut index = self.next_index;
176 while index < self.work.recovery_count() {
177 if let Some(recovery) = self.work.recovery(index) {
178 self.next_index = index + 1;
179 self.remaining -= 1;
180 return Some((index, recovery));
181 }
182 index += 1;
183 }
184
185 unreachable!("Inconsistency in internal data structures. Please report.");
186 }
187
188 fn size_hint(&self) -> (usize, Option<usize>) {
189 (self.remaining, Some(self.remaining))
190 }
191}
192
193impl ExactSizeIterator for Recoveries<'_> {}
197
198impl<'a> Recoveries<'a> {
202 pub(crate) const fn new(work: &'a DecoderWork) -> Self {
203 Self {
204 remaining: work.missing_recovery_count(),
205 next_index: 0,
206 work,
207 }
208 }
209}
210
211#[cfg(test)]
215mod tests {
216 use super::*;
217 use crate::reed_solomon::{test_util, Decoder, Encoder, SHARD_CHUNK_BYTES};
218 #[cfg(not(feature = "std"))]
219 use alloc::vec::Vec;
220
221 fn simple_roundtrip(shard_size: usize) {
222 let original = test_util::generate_original(3, shard_size, 0);
223
224 let mut encoder = Encoder::new(3, 2, shard_size).unwrap();
225 let mut decoder = Decoder::new(3, 2, shard_size).unwrap();
226
227 for original in &original {
228 encoder.add_original_shard(original).unwrap();
229 }
230
231 let result = encoder.encode().unwrap();
232 let recovery: Vec<_> = result.recovery_iter().collect();
233
234 assert!(recovery.iter().all(|slice| slice.len() == shard_size));
235
236 decoder.add_original_shard(1, &original[1]).unwrap();
237 decoder.add_recovery_shard(0, recovery[0]).unwrap();
238 decoder.add_recovery_shard(1, recovery[1]).unwrap();
239
240 let result = decoder.decode().unwrap().unwrap();
241
242 assert_eq!(result.original(0).unwrap(), original[0]);
243 assert!(result.original(1).is_none());
244 assert_eq!(result.original(2).unwrap(), original[2]);
245 assert!(result.original(3).is_none());
246
247 let mut iter: Originals<'_> = result.original_iter();
248 assert_eq!(iter.next(), Some((0, original[0].as_slice())));
249 assert_eq!(iter.next(), Some((2, original[2].as_slice())));
250 assert_eq!(iter.next(), None);
251 assert_eq!(iter.next(), None);
252 }
253
254 #[test]
255 fn decoder_result() {
259 simple_roundtrip(1024);
260 }
261
262 #[test]
263 fn shard_size_not_divisible_by_chunk_size() {
264 for shard_size in [
265 2,
266 4,
267 6,
268 30,
269 32,
270 34,
271 62,
272 SHARD_CHUNK_BYTES,
273 66,
274 126,
275 128,
276 130,
277 ] {
278 simple_roundtrip(shard_size);
279 }
280 }
281
282 #[test]
283 fn decoder_result_size_hint() {
284 let shard_size = SHARD_CHUNK_BYTES;
285 let original = test_util::generate_original(3, shard_size, 0);
286
287 let mut encoder = Encoder::new(3, 2, shard_size).unwrap();
288 let mut decoder = Decoder::new(3, 2, shard_size).unwrap();
289
290 for original in &original {
291 encoder.add_original_shard(original).unwrap();
292 }
293
294 let result = encoder.encode().unwrap();
295 let recovery: Vec<_> = result.recovery_iter().collect();
296
297 decoder.add_original_shard(1, &original[1]).unwrap();
298 decoder.add_recovery_shard(0, recovery[0]).unwrap();
299 decoder.add_recovery_shard(1, recovery[1]).unwrap();
300
301 let result = decoder.decode().unwrap().unwrap();
302
303 let mut iter: Originals<'_> = result.original_iter();
304
305 assert_eq!(iter.len(), 2);
306
307 assert!(iter.next().is_some());
308 assert_eq!(iter.len(), 1);
309
310 assert!(iter.next().is_some());
311 assert_eq!(iter.len(), 0);
312
313 assert!(iter.next().is_none());
314 assert_eq!(iter.len(), 0);
315 }
316
317 fn recovery_roundtrip(original_count: usize, recovery_count: usize, shard_size: usize) {
323 let original = test_util::generate_original(original_count, shard_size, 0);
324
325 let mut encoder = Encoder::new(original_count, recovery_count, shard_size).unwrap();
326 for original in &original {
327 encoder.add_original_shard(original).unwrap();
328 }
329 let encoding = encoder.encode().unwrap();
330 let recovery: Vec<Vec<u8>> = encoding.recovery_iter().map(<[u8]>::to_vec).collect();
331
332 let mut decoder = Decoder::new(original_count, recovery_count, shard_size).unwrap();
335 for (i, original) in original.iter().enumerate().skip(1) {
336 decoder.add_original_shard(i, original).unwrap();
337 }
338 decoder.add_recovery_shard(1, &recovery[1]).unwrap();
339 let decoding = decoder.decode_with_recovery().unwrap().unwrap();
340
341 assert_eq!(decoding.original(0).unwrap(), original[0].as_slice());
342 for (i, recovery) in recovery.iter().enumerate() {
343 let label = format!("oc={original_count} rc={recovery_count} ss={shard_size} rec={i}");
344 if i == 1 {
345 assert!(decoding.recovery(i).is_none(), "provided recovery: {label}");
346 } else {
347 assert_eq!(
348 decoding.recovery(i).unwrap(),
349 recovery.as_slice(),
350 "restored recovery mismatch: {label}"
351 );
352 }
353 }
354
355 let via_iter: Vec<(usize, Vec<u8>)> = decoding
356 .recovery_iter()
357 .map(|(i, s)| (i, s.to_vec()))
358 .collect();
359 let expected: Vec<(usize, Vec<u8>)> = (0..recovery_count)
360 .filter(|&i| i != 1)
361 .map(|i| (i, recovery[i].clone()))
362 .collect();
363 assert_eq!(via_iter, expected);
364 }
365
366 #[test]
367 fn recovery_matches_encoder() {
368 for shard_size in [2, 34, 62, SHARD_CHUNK_BYTES, 66, 130, 1024] {
370 recovery_roundtrip(3, 2, shard_size);
372 recovery_roundtrip(16, 4, shard_size);
373 recovery_roundtrip(4, 8, shard_size);
376 recovery_roundtrip(83, 167, shard_size);
377 }
378 }
379
380 fn assert_decode_none(original_count: usize, recovery_count: usize) {
383 let shard_size = SHARD_CHUNK_BYTES;
384 let original = test_util::generate_original(original_count, shard_size, 0);
385
386 let mut decoder = Decoder::new(original_count, recovery_count, shard_size).unwrap();
387 for (i, shard) in original.iter().enumerate() {
388 decoder.add_original_shard(i, shard).unwrap();
389 }
390 assert!(decoder.decode().unwrap().is_none());
391
392 let mut decoder = Decoder::new(original_count, recovery_count, shard_size).unwrap();
393 for (i, shard) in original.iter().enumerate() {
394 decoder.add_original_shard(i, shard).unwrap();
395 }
396 assert!(decoder.decode_with_recovery().unwrap().is_none());
397 }
398
399 #[test]
400 fn decode_none_when_all_originals_present() {
401 assert_decode_none(3, 2); assert_decode_none(4, 8); }
404}