commonware_cryptography/reed_solomon/engine/
engine_nosimd.rs1use crate::reed_solomon::engine::{
2 tables::{self, Mul16, Skew},
3 utils, Engine, GfElement, ShardsRefMut, GF_MODULUS, SHARD_CHUNK_BYTES,
4};
5use core::iter::zip;
6
7#[derive(Clone, Copy)]
14pub struct NoSimd {
15 mul16: &'static Mul16,
16 skew: &'static Skew,
17}
18
19impl NoSimd {
20 pub fn new() -> Self {
28 let mul16 = tables::get_mul16();
29 let skew = tables::get_skew();
30
31 Self { mul16, skew }
32 }
33}
34
35impl Engine for NoSimd {
36 fn fft(
37 &self,
38 data: &mut ShardsRefMut<'_>,
39 pos: usize,
40 size: usize,
41 truncated_size: usize,
42 skew_delta: usize,
43 ) {
44 self.fft_private(data, pos, size, truncated_size, skew_delta);
45 }
46
47 fn ifft(
48 &self,
49 data: &mut ShardsRefMut<'_>,
50 pos: usize,
51 size: usize,
52 truncated_size: usize,
53 skew_delta: usize,
54 ) {
55 self.ifft_private(data, pos, size, truncated_size, skew_delta);
56 }
57
58 fn mul(&self, x: &mut [[u8; SHARD_CHUNK_BYTES]], log_m: GfElement) {
59 let lut = &self.mul16[log_m as usize];
60
61 for x_chunk in x.iter_mut() {
62 let (x_lo, x_hi) = x_chunk.split_at_mut(SHARD_CHUNK_BYTES / 2);
63
64 for i in 0..SHARD_CHUNK_BYTES / 2 {
65 let lo = x_lo[i];
66 let hi = x_hi[i];
67 let prod = lut[0][usize::from(lo & 15)]
68 ^ lut[1][usize::from(lo >> 4)]
69 ^ lut[2][usize::from(hi & 15)]
70 ^ lut[3][usize::from(hi >> 4)];
71 x_lo[i] = prod as u8;
72 x_hi[i] = (prod >> 8) as u8;
73 }
74 }
75 }
76}
77
78impl Default for NoSimd {
82 fn default() -> Self {
83 Self::new()
84 }
85}
86
87impl NoSimd {
91 fn mul_add(
93 &self,
94 x: &mut [[u8; SHARD_CHUNK_BYTES]],
95 y: &[[u8; SHARD_CHUNK_BYTES]],
96 log_m: GfElement,
97 ) {
98 let lut = &self.mul16[log_m as usize];
99
100 for (x_chunk, y_chunk) in zip(x.iter_mut(), y.iter()) {
101 let (x_lo, x_hi) = x_chunk.split_at_mut(SHARD_CHUNK_BYTES / 2);
102 let (y_lo, y_hi) = y_chunk.split_at(SHARD_CHUNK_BYTES / 2);
103
104 for i in 0..SHARD_CHUNK_BYTES / 2 {
105 let lo = y_lo[i];
106 let hi = y_hi[i];
107 let prod = lut[0][usize::from(lo & 15)]
108 ^ lut[1][usize::from(lo >> 4)]
109 ^ lut[2][usize::from(hi & 15)]
110 ^ lut[3][usize::from(hi >> 4)];
111 x_lo[i] ^= prod as u8;
112 x_hi[i] ^= (prod >> 8) as u8;
113 }
114 }
115 }
116}
117
118impl NoSimd {
122 #[inline(always)]
124 fn fft_butterfly_partial(
125 &self,
126 x: &mut [[u8; SHARD_CHUNK_BYTES]],
127 y: &mut [[u8; SHARD_CHUNK_BYTES]],
128 log_m: GfElement,
129 ) {
130 self.mul_add(x, y, log_m);
131 utils::xor(y, x);
132 }
133
134 #[inline(always)]
135 fn fft_butterfly_two_layers(
136 &self,
137 data: &mut ShardsRefMut<'_>,
138 pos: usize,
139 dist: usize,
140 log_m01: GfElement,
141 log_m23: GfElement,
142 log_m02: GfElement,
143 ) {
144 let (s0, s1, s2, s3) = data.dist4_mut(pos, dist);
145
146 if log_m02 == GF_MODULUS {
149 utils::xor(s2, s0);
150 utils::xor(s3, s1);
151 } else {
152 self.fft_butterfly_partial(s0, s2, log_m02);
153 self.fft_butterfly_partial(s1, s3, log_m02);
154 }
155
156 if log_m01 == GF_MODULUS {
159 utils::xor(s1, s0);
160 } else {
161 self.fft_butterfly_partial(s0, s1, log_m01);
162 }
163
164 if log_m23 == GF_MODULUS {
165 utils::xor(s3, s2);
166 } else {
167 self.fft_butterfly_partial(s2, s3, log_m23);
168 }
169 }
170
171 #[inline(always)]
172 fn fft_private(
173 &self,
174 data: &mut ShardsRefMut<'_>,
175 pos: usize,
176 size: usize,
177 truncated_size: usize,
178 skew_delta: usize,
179 ) {
180 let mut dist4 = size;
183 let mut dist = size >> 2;
184 while dist != 0 {
185 let mut r = 0;
186 while r < truncated_size {
187 let base = r + dist + skew_delta - 1;
188
189 let log_m01 = self.skew[base];
190 let log_m02 = self.skew[base + dist];
191 let log_m23 = self.skew[base + dist * 2];
192
193 for i in r..r + dist {
194 self.fft_butterfly_two_layers(data, pos + i, dist, log_m01, log_m23, log_m02);
195 }
196
197 r += dist4;
198 }
199 dist4 = dist;
200 dist >>= 2;
201 }
202
203 if dist4 == 2 {
206 let mut r = 0;
207 while r < truncated_size {
208 let log_m = self.skew[r + skew_delta];
209
210 let (x, y) = data.dist2_mut(pos + r, 1);
211
212 if log_m == GF_MODULUS {
213 utils::xor(y, x);
214 } else {
215 self.fft_butterfly_partial(x, y, log_m);
216 }
217
218 r += 2;
219 }
220 }
221 }
222}
223
224impl NoSimd {
228 #[inline(always)]
230 fn ifft_butterfly_partial(
231 &self,
232 x: &mut [[u8; SHARD_CHUNK_BYTES]],
233 y: &mut [[u8; SHARD_CHUNK_BYTES]],
234 log_m: GfElement,
235 ) {
236 utils::xor(y, x);
237 self.mul_add(x, y, log_m);
238 }
239
240 #[inline(always)]
241 fn ifft_butterfly_two_layers(
242 &self,
243 data: &mut ShardsRefMut<'_>,
244 pos: usize,
245 dist: usize,
246 log_m01: GfElement,
247 log_m23: GfElement,
248 log_m02: GfElement,
249 ) {
250 let (s0, s1, s2, s3) = data.dist4_mut(pos, dist);
251
252 if log_m01 == GF_MODULUS {
255 utils::xor(s1, s0);
256 } else {
257 self.ifft_butterfly_partial(s0, s1, log_m01);
258 }
259
260 if log_m23 == GF_MODULUS {
261 utils::xor(s3, s2);
262 } else {
263 self.ifft_butterfly_partial(s2, s3, log_m23);
264 }
265
266 if log_m02 == GF_MODULUS {
269 utils::xor(s2, s0);
270 utils::xor(s3, s1);
271 } else {
272 self.ifft_butterfly_partial(s0, s2, log_m02);
273 self.ifft_butterfly_partial(s1, s3, log_m02);
274 }
275 }
276
277 #[inline(always)]
278 fn ifft_private(
279 &self,
280 data: &mut ShardsRefMut<'_>,
281 pos: usize,
282 size: usize,
283 truncated_size: usize,
284 skew_delta: usize,
285 ) {
286 let mut dist = 1;
289 let mut dist4 = 4;
290 while dist4 <= size {
291 let mut r = 0;
292 while r < truncated_size {
293 let base = r + dist + skew_delta - 1;
294
295 let log_m01 = self.skew[base];
296 let log_m02 = self.skew[base + dist];
297 let log_m23 = self.skew[base + dist * 2];
298
299 for i in r..r + dist {
300 self.ifft_butterfly_two_layers(data, pos + i, dist, log_m01, log_m23, log_m02);
301 }
302
303 r += dist4;
304 }
305 dist = dist4;
306 dist4 <<= 2;
307 }
308
309 if dist < size {
312 let log_m = self.skew[dist + skew_delta - 1];
313 if log_m == GF_MODULUS {
314 utils::xor_within(data, pos + dist, pos, dist);
315 } else {
316 let (mut a, mut b) = data.split_at_mut(pos + dist);
317 for i in 0..dist {
318 self.ifft_butterfly_partial(
319 &mut a[pos + i], &mut b[i], log_m,
322 );
323 }
324 }
325 }
326 }
327}
328
329#[cfg(test)]
335mod tests {
336 use crate::reed_solomon::engine::{Engine, Naive, NoSimd, SHARD_CHUNK_BYTES};
337 #[cfg(not(feature = "std"))]
338 use alloc::vec;
339 use rand::{Rng, RngExt as _, SeedableRng};
340 use rand_chacha::ChaCha8Rng;
341
342 #[test]
343 fn mul() {
344 let naive = Naive::default();
345 let nosimd = NoSimd::default();
346
347 let mut rng = ChaCha8Rng::from_seed([0; 32]);
348
349 for shard_chunks in 0..6 {
350 let mut data_nosimd = vec![[0; SHARD_CHUNK_BYTES]; shard_chunks];
351 rng.fill_bytes(data_nosimd.as_flattened_mut());
352 let mut data_naive = data_nosimd.clone();
353
354 let log_m = rng.random();
355
356 nosimd.mul(&mut data_nosimd, log_m);
357 naive.mul(&mut data_naive, log_m);
358
359 assert_eq!(data_nosimd, data_naive);
360 }
361 }
362}