Skip to main content

reed_solomon_simd/engine/
shards.rs

1#[cfg(not(feature = "std"))]
2use alloc::vec::Vec;
3use core::ops::{Bound, Index, IndexMut, Range, RangeBounds};
4
5// ======================================================================
6// Shards - CRATE
7
8pub(crate) struct Shards {
9    shard_count: usize,
10    // Shard length in 64 byte chunks
11    shard_len_64: usize,
12
13    // Flat Vec of `shard_count * shard_len_64 * 64` bytes.
14    data: Vec<[u8; 64]>,
15}
16
17impl Shards {
18    pub(crate) fn as_ref_mut(&mut self) -> ShardsRefMut<'_> {
19        ShardsRefMut::new(self.shard_count, self.shard_len_64, self.data.as_mut())
20    }
21
22    pub(crate) fn new() -> Self {
23        Self {
24            shard_count: 0,
25            shard_len_64: 0,
26            data: Vec::new(),
27        }
28    }
29
30    pub(crate) fn resize(&mut self, shard_count: usize, shard_len_64: usize) {
31        self.shard_count = shard_count;
32        self.shard_len_64 = shard_len_64;
33
34        self.data
35            .resize(self.shard_count * self.shard_len_64, [0; 64]);
36    }
37
38    pub(crate) fn insert(&mut self, index: usize, shard: &[u8]) {
39        debug_assert_eq!(shard.len() % 2, 0);
40
41        let whole_chunk_count = shard.len() / 64;
42        let tail_len = shard.len() % 64;
43
44        let (src_chunks, src_tail) = shard.split_at(shard.len() - tail_len);
45
46        let dst = &mut self[index];
47        dst[..whole_chunk_count]
48            .as_flattened_mut()
49            .copy_from_slice(src_chunks);
50
51        // Last chunk is special if shard.len() % 64 != 0.
52        // See src/algorithm.md for an explanation.
53        if tail_len > 0 {
54            let (src_lo, src_hi) = src_tail.split_at(tail_len / 2);
55            let (dst_lo, dst_hi) = dst[whole_chunk_count].split_at_mut(32);
56            dst_lo[..src_lo.len()].copy_from_slice(src_lo);
57            dst_hi[..src_hi.len()].copy_from_slice(src_hi);
58        }
59    }
60
61    // Undoes the encoding of the last chunk for the given range of shards
62    pub(crate) fn undo_last_chunk_encoding(&mut self, shard_bytes: usize, range: Range<usize>) {
63        let whole_chunk_count = shard_bytes / 64;
64        let tail_len = shard_bytes % 64;
65
66        if tail_len == 0 {
67            return;
68        }
69
70        for idx in range {
71            let last_chunk = &mut self[idx][whole_chunk_count];
72            last_chunk.copy_within(32..32 + tail_len / 2, tail_len / 2);
73        }
74    }
75}
76
77// ======================================================================
78// Shards - IMPL Index
79
80impl Index<usize> for Shards {
81    type Output = [[u8; 64]];
82    fn index(&self, index: usize) -> &Self::Output {
83        &self.data[index * self.shard_len_64..(index + 1) * self.shard_len_64]
84    }
85}
86
87// ======================================================================
88// Shards - IMPL IndexMut
89
90impl IndexMut<usize> for Shards {
91    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
92        &mut self.data[index * self.shard_len_64..(index + 1) * self.shard_len_64]
93    }
94}
95
96// ======================================================================
97// ShardsRefMut - PUBLIC
98
99/// Mutable reference to a shard array.
100pub struct ShardsRefMut<'a> {
101    shard_count: usize,
102    shard_len_64: usize,
103
104    data: &'a mut [[u8; 64]],
105}
106
107impl<'a> ShardsRefMut<'a> {
108    /// Returns mutable references to shards at `pos` and `pos + dist`.
109    ///
110    /// See source code of [`Naive::fft`] for an example.
111    ///
112    /// # Panics
113    ///
114    /// If `dist` is `0`.
115    ///
116    /// [`Naive::fft`]: crate::engine::Naive#method.fft
117    pub fn dist2_mut(
118        &mut self,
119        mut pos: usize,
120        mut dist: usize,
121    ) -> (&mut [[u8; 64]], &mut [[u8; 64]]) {
122        pos *= self.shard_len_64;
123        dist *= self.shard_len_64;
124
125        let (a, b) = self.data[pos..].split_at_mut(dist);
126        (&mut a[..self.shard_len_64], &mut b[..self.shard_len_64])
127    }
128
129    /// Returns mutable references to shards at
130    /// `pos`, `pos + dist`, `pos + dist * 2` and `pos + dist * 3`.
131    ///
132    /// See source code of [`NoSimd::fft`] for an example
133    /// (specifically the private method `fft_butterfly_two_layers`).
134    ///
135    /// # Panics
136    ///
137    /// If `dist` is `0`.
138    ///
139    /// [`NoSimd::fft`]: crate::engine::NoSimd#method.fft
140    #[allow(clippy::type_complexity)]
141    pub fn dist4_mut(
142        &mut self,
143        mut pos: usize,
144        mut dist: usize,
145    ) -> (
146        &mut [[u8; 64]],
147        &mut [[u8; 64]],
148        &mut [[u8; 64]],
149        &mut [[u8; 64]],
150    ) {
151        pos *= self.shard_len_64;
152        dist *= self.shard_len_64;
153
154        let (ab, cd) = self.data[pos..].split_at_mut(dist * 2);
155        let (a, b) = ab.split_at_mut(dist);
156        let (c, d) = cd.split_at_mut(dist);
157
158        (
159            &mut a[..self.shard_len_64],
160            &mut b[..self.shard_len_64],
161            &mut c[..self.shard_len_64],
162            &mut d[..self.shard_len_64],
163        )
164    }
165
166    /// Returns `true` if this contains no shards.
167    pub fn is_empty(&self) -> bool {
168        self.shard_count == 0
169    }
170
171    /// Returns number of shards.
172    pub fn len(&self) -> usize {
173        self.shard_count
174    }
175
176    /// Creates new [`ShardsRefMut`] that references given `data`.
177    ///
178    /// # Panics
179    ///
180    /// If `data.len() < shard_count * shard_len_64`.
181    pub fn new(shard_count: usize, shard_len_64: usize, data: &'a mut [[u8; 64]]) -> Self {
182        assert!(data.len() >= shard_count * shard_len_64);
183
184        Self {
185            shard_count,
186            shard_len_64,
187            data: &mut data[..shard_count * shard_len_64],
188        }
189    }
190
191    /// Splits this [`ShardsRefMut`] into two so that
192    /// first includes shards `0..mid` and second includes shards `mid..`.
193    pub fn split_at_mut(&mut self, mid: usize) -> (ShardsRefMut<'_>, ShardsRefMut<'_>) {
194        let (a, b) = self.data.split_at_mut(mid * self.shard_len_64);
195
196        (
197            ShardsRefMut::new(mid, self.shard_len_64, a),
198            ShardsRefMut::new(self.shard_count - mid, self.shard_len_64, b),
199        )
200    }
201
202    /// Fills the given shard-range with `0u8`:s.
203    pub fn zero<R: RangeBounds<usize>>(&mut self, range: R) {
204        let start = match range.start_bound() {
205            Bound::Included(start) => start * self.shard_len_64,
206            Bound::Excluded(start) => (start + 1) * self.shard_len_64,
207            Bound::Unbounded => 0,
208        };
209
210        let end = match range.end_bound() {
211            Bound::Included(end) => (end + 1) * self.shard_len_64,
212            Bound::Excluded(end) => end * self.shard_len_64,
213            Bound::Unbounded => self.shard_count * self.shard_len_64,
214        };
215
216        self.data[start..end].fill([0; 64]);
217    }
218}
219
220// ======================================================================
221// ShardsRefMut - IMPL Index
222
223impl Index<usize> for ShardsRefMut<'_> {
224    type Output = [[u8; 64]];
225    fn index(&self, index: usize) -> &Self::Output {
226        &self.data[index * self.shard_len_64..(index + 1) * self.shard_len_64]
227    }
228}
229
230// ======================================================================
231// ShardsRefMut - IMPL IndexMut
232
233impl IndexMut<usize> for ShardsRefMut<'_> {
234    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
235        &mut self.data[index * self.shard_len_64..(index + 1) * self.shard_len_64]
236    }
237}
238
239// ======================================================================
240// ShardsRefMut - CRATE
241
242impl ShardsRefMut<'_> {
243    pub(crate) fn copy_within(&mut self, mut src: usize, mut dest: usize, mut count: usize) {
244        src *= self.shard_len_64;
245        dest *= self.shard_len_64;
246        count *= self.shard_len_64;
247
248        self.data.copy_within(src..src + count, dest);
249    }
250
251    // Returns mutable references to flat-arrays of shard-ranges
252    // `x .. x + count` and `y .. y + count`.
253    //
254    // Ranges must not overlap.
255    pub(crate) fn flat2_mut(
256        &mut self,
257        mut x: usize,
258        mut y: usize,
259        mut count: usize,
260    ) -> (&mut [[u8; 64]], &mut [[u8; 64]]) {
261        x *= self.shard_len_64;
262        y *= self.shard_len_64;
263        count *= self.shard_len_64;
264
265        if x < y {
266            let (head, tail) = self.data.split_at_mut(y);
267            (&mut head[x..x + count], &mut tail[..count])
268        } else {
269            let (head, tail) = self.data.split_at_mut(x);
270            (&mut tail[..count], &mut head[y..y + count])
271        }
272    }
273}