commonware_cryptography/reed_solomon/engine/
shards.rs1use crate::reed_solomon::engine::SHARD_CHUNK_BYTES;
4#[cfg(not(feature = "std"))]
5use alloc::vec::Vec;
6use core::ops::{Bound, Index, IndexMut, Range, RangeBounds};
7
8pub(crate) struct Shards {
12 shard_count: usize,
13 shard_chunk_count: usize,
15
16 data: Vec<[u8; SHARD_CHUNK_BYTES]>,
18}
19
20impl Shards {
21 pub(crate) fn as_ref_mut(&mut self) -> ShardsRefMut<'_> {
23 ShardsRefMut::new(self.shard_count, self.shard_chunk_count, self.data.as_mut())
24 }
25
26 pub(crate) const fn new() -> Self {
28 Self {
29 shard_count: 0,
30 shard_chunk_count: 0,
31 data: Vec::new(),
32 }
33 }
34
35 pub(crate) fn resize(&mut self, shard_count: usize, shard_chunk_count: usize) {
40 self.shard_count = shard_count;
41 self.shard_chunk_count = shard_chunk_count;
42
43 self.data.resize(
44 self.shard_count * self.shard_chunk_count,
45 [0; SHARD_CHUNK_BYTES],
46 );
47 }
48
49 pub(crate) fn insert(&mut self, index: usize, shard: &[u8]) {
61 assert_eq!(shard.len() % 2, 0);
62
63 let whole_chunk_count = shard.len() / SHARD_CHUNK_BYTES;
64 let tail_len = shard.len() % SHARD_CHUNK_BYTES;
65
66 let (src_chunks, src_tail) = shard.split_at(shard.len() - tail_len);
67
68 let dst = &mut self[index];
69 dst[..whole_chunk_count]
70 .as_flattened_mut()
71 .copy_from_slice(src_chunks);
72
73 if tail_len > 0 {
76 let (src_lo, src_hi) = src_tail.split_at(tail_len / 2);
77 let (dst_lo, dst_hi) = dst[whole_chunk_count].split_at_mut(SHARD_CHUNK_BYTES / 2);
78 dst_lo[..src_lo.len()].copy_from_slice(src_lo);
79 dst_hi[..src_hi.len()].copy_from_slice(src_hi);
80 }
81 }
82
83 pub(crate) fn undo_last_chunk_encoding(&mut self, shard_bytes: usize, range: Range<usize>) {
87 let whole_chunk_count = shard_bytes / SHARD_CHUNK_BYTES;
88 let tail_len = shard_bytes % SHARD_CHUNK_BYTES;
89
90 if tail_len == 0 {
91 return;
92 }
93
94 for idx in range {
95 let last_chunk = &mut self[idx][whole_chunk_count];
96 last_chunk.copy_within(
97 SHARD_CHUNK_BYTES / 2..SHARD_CHUNK_BYTES / 2 + tail_len / 2,
98 tail_len / 2,
99 );
100 }
101 }
102}
103
104impl Index<usize> for Shards {
105 type Output = [[u8; SHARD_CHUNK_BYTES]];
106 fn index(&self, index: usize) -> &Self::Output {
107 &self.data[index * self.shard_chunk_count..(index + 1) * self.shard_chunk_count]
108 }
109}
110
111impl IndexMut<usize> for Shards {
112 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
113 &mut self.data[index * self.shard_chunk_count..(index + 1) * self.shard_chunk_count]
114 }
115}
116
117pub struct ShardsRefMut<'a> {
119 shard_count: usize,
120 shard_chunk_count: usize,
122
123 data: &'a mut [[u8; SHARD_CHUNK_BYTES]],
126}
127
128type FourShardsMut<'a> = (
130 &'a mut [[u8; SHARD_CHUNK_BYTES]],
131 &'a mut [[u8; SHARD_CHUNK_BYTES]],
132 &'a mut [[u8; SHARD_CHUNK_BYTES]],
133 &'a mut [[u8; SHARD_CHUNK_BYTES]],
134);
135
136impl<'a> ShardsRefMut<'a> {
137 pub fn dist2_mut(
147 &mut self,
148 mut pos: usize,
149 mut dist: usize,
150 ) -> (
151 &mut [[u8; SHARD_CHUNK_BYTES]],
152 &mut [[u8; SHARD_CHUNK_BYTES]],
153 ) {
154 assert!(pos < self.shard_count && dist > 0 && dist < self.shard_count - pos);
155 pos *= self.shard_chunk_count;
156 dist *= self.shard_chunk_count;
157
158 let (a, b) = self.data[pos..].split_at_mut(dist);
159 (
160 &mut a[..self.shard_chunk_count],
161 &mut b[..self.shard_chunk_count],
162 )
163 }
164
165 pub fn dist4_mut(&mut self, mut pos: usize, mut dist: usize) -> FourShardsMut<'_> {
177 assert!(pos < self.shard_count && dist > 0 && dist <= (self.shard_count - 1 - pos) / 3);
178 pos *= self.shard_chunk_count;
179 dist *= self.shard_chunk_count;
180
181 let (ab, cd) = self.data[pos..].split_at_mut(dist * 2);
182 let (a, b) = ab.split_at_mut(dist);
183 let (c, d) = cd.split_at_mut(dist);
184
185 (
186 &mut a[..self.shard_chunk_count],
187 &mut b[..self.shard_chunk_count],
188 &mut c[..self.shard_chunk_count],
189 &mut d[..self.shard_chunk_count],
190 )
191 }
192
193 pub const fn is_empty(&self) -> bool {
195 self.shard_count == 0
196 }
197
198 pub const fn len(&self) -> usize {
200 self.shard_count
201 }
202
203 pub fn new(
211 shard_count: usize,
212 shard_chunk_count: usize,
213 data: &'a mut [[u8; SHARD_CHUNK_BYTES]],
214 ) -> Self {
215 let chunk_count = shard_count
216 .checked_mul(shard_chunk_count)
217 .expect("shard dimensions overflow");
218 assert!(data.len() >= chunk_count);
219
220 Self {
221 shard_count,
222 shard_chunk_count,
223 data: &mut data[..chunk_count],
224 }
225 }
226
227 pub fn split_at_mut(&mut self, mid: usize) -> (ShardsRefMut<'_>, ShardsRefMut<'_>) {
234 assert!(mid <= self.shard_count);
235 let (a, b) = self.data.split_at_mut(mid * self.shard_chunk_count);
236
237 (
238 ShardsRefMut::new(mid, self.shard_chunk_count, a),
239 ShardsRefMut::new(self.shard_count - mid, self.shard_chunk_count, b),
240 )
241 }
242
243 pub fn zero<R: RangeBounds<usize>>(&mut self, range: R) {
249 let start = match range.start_bound() {
250 Bound::Included(start) => *start,
251 Bound::Excluded(start) => start.checked_add(1).expect("shard range start overflow"),
252 Bound::Unbounded => 0,
253 };
254
255 let end = match range.end_bound() {
256 Bound::Included(end) => end.checked_add(1).expect("shard range end overflow"),
257 Bound::Excluded(end) => *end,
258 Bound::Unbounded => self.shard_count,
259 };
260
261 assert!(start <= end && end <= self.shard_count);
262 self.data[start * self.shard_chunk_count..end * self.shard_chunk_count]
263 .fill([0; SHARD_CHUNK_BYTES]);
264 }
265}
266
267impl Index<usize> for ShardsRefMut<'_> {
268 type Output = [[u8; SHARD_CHUNK_BYTES]];
269 fn index(&self, index: usize) -> &Self::Output {
270 assert!(index < self.shard_count);
271 &self.data[index * self.shard_chunk_count..(index + 1) * self.shard_chunk_count]
272 }
273}
274
275impl IndexMut<usize> for ShardsRefMut<'_> {
276 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
277 assert!(index < self.shard_count);
278 &mut self.data[index * self.shard_chunk_count..(index + 1) * self.shard_chunk_count]
279 }
280}
281
282impl ShardsRefMut<'_> {
283 pub(crate) fn copy_within(&mut self, mut src: usize, mut dest: usize, mut count: usize) {
287 src *= self.shard_chunk_count;
288 dest *= self.shard_chunk_count;
289 count *= self.shard_chunk_count;
290
291 self.data.copy_within(src..src + count, dest);
292 }
293
294 pub(crate) fn flat2_mut(
300 &mut self,
301 mut x: usize,
302 mut y: usize,
303 mut count: usize,
304 ) -> (
305 &mut [[u8; SHARD_CHUNK_BYTES]],
306 &mut [[u8; SHARD_CHUNK_BYTES]],
307 ) {
308 assert!(x <= self.shard_count && count <= self.shard_count - x);
309 assert!(y <= self.shard_count && count <= self.shard_count - y);
310 assert!(x.abs_diff(y) >= count);
311 x *= self.shard_chunk_count;
312 y *= self.shard_chunk_count;
313 count *= self.shard_chunk_count;
314
315 if x < y {
316 let (head, tail) = self.data.split_at_mut(y);
317 (&mut head[x..x + count], &mut tail[..count])
318 } else {
319 let (head, tail) = self.data.split_at_mut(x);
320 (&mut tail[..count], &mut head[y..y + count])
321 }
322 }
323}
324
325#[cfg(test)]
326mod tests {
327 use super::*;
328 use crate::reed_solomon::engine::utils::xor_within;
329
330 #[test]
331 #[should_panic]
332 fn dimensions_overflow() {
333 ShardsRefMut::new(usize::MAX / 2 + 1, 2, &mut []);
334 }
335
336 #[test]
337 #[should_panic]
338 fn empty_shard_index_out_of_bounds() {
339 let shards = ShardsRefMut::new(2, 0, &mut []);
340 let _ = &shards[2];
341 }
342
343 #[test]
344 #[should_panic]
345 fn empty_shard_mutable_index_out_of_bounds() {
346 let mut shards = ShardsRefMut::new(2, 0, &mut []);
347 let _ = &mut shards[2];
348 }
349
350 #[test]
351 #[should_panic]
352 fn empty_shard_split_out_of_bounds() {
353 ShardsRefMut::new(2, 0, &mut []).split_at_mut(3);
354 }
355
356 #[test]
357 #[should_panic]
358 fn empty_shard_range_out_of_bounds() {
359 ShardsRefMut::new(2, 0, &mut []).zero(0..3);
360 }
361
362 #[test]
363 #[should_panic]
364 fn empty_shard_range_reversed() {
365 ShardsRefMut::new(2, 0, &mut []).zero((Bound::Included(2), Bound::Excluded(1)));
366 }
367
368 #[test]
369 #[should_panic]
370 fn empty_shard_range_start_overflow() {
371 ShardsRefMut::new(2, 0, &mut []).zero((Bound::Excluded(usize::MAX), Bound::Unbounded));
372 }
373
374 #[test]
375 #[should_panic]
376 fn empty_shard_range_end_overflow() {
377 ShardsRefMut::new(2, 0, &mut []).zero(..=usize::MAX);
378 }
379
380 #[test]
381 #[should_panic]
382 fn empty_shard_xor_out_of_bounds() {
383 xor_within(&mut ShardsRefMut::new(4, 0, &mut []), 0, 3, 2);
384 }
385
386 #[test]
387 #[should_panic]
388 fn empty_shard_xor_overlapping() {
389 xor_within(&mut ShardsRefMut::new(4, 0, &mut []), 0, 1, 2);
390 }
391
392 #[test]
393 #[should_panic]
394 fn empty_shard_dist2_zero() {
395 ShardsRefMut::new(2, 0, &mut []).dist2_mut(0, 0);
396 }
397
398 #[test]
399 #[should_panic]
400 fn empty_shard_dist4_zero() {
401 ShardsRefMut::new(4, 0, &mut []).dist4_mut(0, 0);
402 }
403
404 #[test]
405 #[should_panic]
406 fn empty_shard_dist2_out_of_bounds() {
407 ShardsRefMut::new(2, 0, &mut []).dist2_mut(1, 1);
408 }
409
410 #[test]
411 #[should_panic]
412 fn empty_shard_dist4_out_of_bounds() {
413 ShardsRefMut::new(4, 0, &mut []).dist4_mut(1, 1);
414 }
415
416 #[test]
417 fn empty_shards() {
418 let mut shards = ShardsRefMut::new(4, 0, &mut []);
419 assert_eq!(shards.len(), 4);
420 assert!(!shards.is_empty());
421 assert!(shards[3].is_empty());
422 shards.dist2_mut(1, 2);
423 shards.dist4_mut(0, 1);
424 shards.zero(1..=3);
425 let (left, right) = shards.split_at_mut(4);
426 assert_eq!(left.len(), 4);
427 assert!(right.is_empty());
428 }
429
430 #[test]
431 fn views_preserve_shard_boundaries() {
432 let mut data = [[9; SHARD_CHUNK_BYTES]; 14];
433 let mut shards = ShardsRefMut::new(6, 2, &mut data);
434 shards.zero(2..4);
435 assert_eq!(shards[2], [[0; SHARD_CHUNK_BYTES]; 2]);
436 assert_eq!(shards[3], [[0; SHARD_CHUNK_BYTES]; 2]);
437 let (first, last) = shards.dist2_mut(0, 5);
438 first.fill([1; SHARD_CHUNK_BYTES]);
439 last.fill([6; SHARD_CHUNK_BYTES]);
440 let (a, b, c, d) = shards.dist4_mut(0, 1);
441 for (index, shard) in [a, b, c, d].into_iter().enumerate() {
442 shard.fill([index as u8 + 1; SHARD_CHUNK_BYTES]);
443 }
444 let (_, mut right) = shards.split_at_mut(3);
445 right[1].fill([5; SHARD_CHUNK_BYTES]);
446 for (index, shard) in data.as_chunks::<2>().0.iter().enumerate() {
447 let expected = if index < 6 { index as u8 + 1 } else { 9 };
448 assert_eq!(shard, &[[expected; SHARD_CHUNK_BYTES]; 2]);
449 }
450 }
451}