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