p3_binary_field/bitslice/
underlier.rs1use core::fmt::Debug;
27use core::hash::Hash;
28use core::ops::{BitAnd, BitOr, BitXor, Not, Shl, Shr};
29use core::slice;
30
31use rand::{Rng, RngExt};
32
33pub(crate) mod private {
38 pub trait Sealed {}
39}
40
41use private::Sealed;
42
43impl Sealed for u8 {}
44impl Sealed for u16 {}
45impl Sealed for u32 {}
46impl Sealed for u64 {}
47
48pub trait Word:
52 Sealed
53 + Copy
54 + Default
55 + Eq
56 + Ord
57 + Hash
58 + Debug
59 + Send
60 + Sync
61 + 'static
62 + BitAnd<Output = Self>
63 + BitOr<Output = Self>
64 + BitXor<Output = Self>
65 + Not<Output = Self>
66 + Shl<usize, Output = Self>
67 + Shr<usize, Output = Self>
68{
69 const BITS: usize;
71
72 const ZERO: Self;
74
75 const ONES: Self;
77
78 fn count_ones(self) -> u32;
80
81 fn to_u64(self) -> u64;
83
84 fn from_u64(value: u64) -> Self;
86}
87
88macro_rules! impl_word {
90 ($word:ty) => {
91 impl Word for $word {
92 const BITS: usize = <$word>::BITS as usize;
93 const ZERO: Self = 0;
94 const ONES: Self = <$word>::MAX;
95
96 #[inline]
97 fn count_ones(self) -> u32 {
98 Self::count_ones(self)
99 }
100
101 #[inline]
102 fn to_u64(self) -> u64 {
103 u64::from(self)
104 }
105
106 #[inline]
107 fn from_u64(value: u64) -> Self {
108 value as Self
109 }
110 }
111 };
112}
113
114impl_word!(u8);
115impl_word!(u16);
116impl_word!(u32);
117impl_word!(u64);
118
119pub unsafe trait Underlier:
130 Copy + Default + Eq + Hash + Debug + Send + Sync + 'static
131{
132 type Word: Word;
134
135 const WORDS: usize;
137
138 const BITS: usize = Self::WORDS * <Self::Word as Word>::BITS;
140
141 const ZERO: Self;
143
144 const ONES: Self;
146
147 fn words(&self) -> &[Self::Word];
149
150 fn words_mut(&mut self) -> &mut [Self::Word];
152
153 fn from_words_fn(f: impl FnMut(usize) -> Self::Word) -> Self;
155
156 fn random<R: Rng + ?Sized>(rng: &mut R) -> Self;
158
159 #[inline]
161 fn zip(self, other: Self, op: impl Fn(Self::Word, Self::Word) -> Self::Word) -> Self {
162 Self::from_words_fn(|i| op(self.words()[i], other.words()[i]))
163 }
164}
165
166macro_rules! impl_scalar_underlier {
168 ($word:ty) => {
169 unsafe impl Underlier for $word {
171 type Word = Self;
172
173 const WORDS: usize = 1;
174 const ZERO: Self = 0;
175 const ONES: Self = <$word>::MAX;
176
177 #[inline]
178 fn words(&self) -> &[Self::Word] {
179 slice::from_ref(self)
180 }
181
182 #[inline]
183 fn words_mut(&mut self) -> &mut [Self::Word] {
184 slice::from_mut(self)
185 }
186
187 #[inline]
188 fn from_words_fn(mut f: impl FnMut(usize) -> Self::Word) -> Self {
189 f(0)
190 }
191
192 #[inline]
193 fn random<R: Rng + ?Sized>(rng: &mut R) -> Self {
194 rng.random()
195 }
196 }
197 };
198}
199
200impl_scalar_underlier!(u8);
201impl_scalar_underlier!(u16);
202impl_scalar_underlier!(u32);
203impl_scalar_underlier!(u64);
204
205macro_rules! wide_underlier {
209 ($name:ident, $words:literal, $align:literal, $doc:literal) => {
210 #[doc = $doc]
211 #[derive(Clone, Copy, Default, PartialEq, Eq, Hash, Debug)]
214 #[repr(C, align($align))]
215 pub struct $name([u64; $words]);
216
217 impl Sealed for $name {}
218
219 impl $name {
220 #[inline]
222 #[must_use]
223 pub const fn from_words(words: [u64; $words]) -> Self {
224 Self(words)
225 }
226
227 #[inline]
229 #[must_use]
230 pub const fn to_words(self) -> [u64; $words] {
231 self.0
232 }
233 }
234
235 unsafe impl Underlier for $name {
237 type Word = u64;
238
239 const WORDS: usize = $words;
240 const ZERO: Self = Self([0; $words]);
241 const ONES: Self = Self([u64::MAX; $words]);
242
243 #[inline]
244 fn words(&self) -> &[Self::Word] {
245 &self.0
246 }
247
248 #[inline]
249 fn words_mut(&mut self) -> &mut [Self::Word] {
250 &mut self.0
251 }
252
253 #[inline]
254 fn from_words_fn(f: impl FnMut(usize) -> Self::Word) -> Self {
255 Self(core::array::from_fn(f))
256 }
257
258 #[inline]
259 fn random<R: Rng + ?Sized>(rng: &mut R) -> Self {
260 Self(rng.random())
261 }
262 }
263 };
264}
265
266wide_underlier!(M128, 2, 16, "A 128-bit block of bits.");
267wide_underlier!(M256, 4, 32, "A 256-bit block of bits.");
268wide_underlier!(M512, 8, 64, "A 512-bit block of bits.");
269
270pub unsafe trait Divisible<Narrow: Underlier>: Underlier {
280 const RATIO: usize = Self::BITS / Narrow::BITS;
282
283 #[inline]
285 fn parts(&self) -> &[Narrow] {
286 const { Self::CHECK_LAYOUT };
287
288 unsafe { slice::from_raw_parts(core::ptr::from_ref(self).cast(), Self::RATIO) }
291 }
292
293 #[inline]
295 fn parts_mut(&mut self) -> &mut [Narrow] {
296 const { Self::CHECK_LAYOUT };
297
298 unsafe { slice::from_raw_parts_mut(core::ptr::from_mut(self).cast(), Self::RATIO) }
300 }
301
302 #[inline]
304 fn split_slice(slice: &[Self]) -> &[Narrow] {
305 const { Self::CHECK_LAYOUT };
306
307 unsafe { slice::from_raw_parts(slice.as_ptr().cast(), slice.len() * Self::RATIO) }
309 }
310
311 #[inline]
313 fn split_slice_mut(slice: &mut [Self]) -> &mut [Narrow] {
314 const { Self::CHECK_LAYOUT };
315
316 let len = slice.len() * Self::RATIO;
318 unsafe { slice::from_raw_parts_mut(slice.as_mut_ptr().cast(), len) }
319 }
320
321 const CHECK_LAYOUT: () = {
328 assert!(size_of::<Self>() == Self::RATIO * size_of::<Narrow>());
329 assert!(align_of::<Self>() >= align_of::<Narrow>());
330 assert!(
331 cfg!(target_endian = "little") || size_of::<Narrow::Word>() == size_of::<Self::Word>(),
332 "reading a block at a narrower word needs a little-endian target"
333 );
334 };
335}
336
337macro_rules! divisible {
341 ($wide:ty, $narrow:ty) => {
342 unsafe impl Divisible<$narrow> for $wide {
344 const RATIO: usize = <$wide as Underlier>::BITS / <$narrow as Underlier>::BITS;
345 }
346 };
347}
348
349divisible!(M512, M256);
350divisible!(M512, M128);
351divisible!(M512, u64);
352divisible!(M512, u32);
353divisible!(M512, u16);
354divisible!(M512, u8);
355
356divisible!(M256, M128);
357divisible!(M256, u64);
358divisible!(M256, u32);
359divisible!(M256, u16);
360divisible!(M256, u8);
361
362divisible!(M128, u64);
363divisible!(M128, u32);
364divisible!(M128, u16);
365divisible!(M128, u8);
366
367divisible!(u64, u32);
368divisible!(u64, u16);
369divisible!(u64, u8);
370
371divisible!(u32, u16);
372divisible!(u32, u8);
373
374divisible!(u16, u8);
375
376#[cfg(test)]
377mod tests {
378 use super::*;
379
380 #[test]
381 fn a_block_is_its_words() {
382 assert_eq!(M512::ZERO.words(), &[0u64; 8]);
384 assert_eq!(M512::ONES.words(), &[u64::MAX; 8]);
385 assert_eq!(M512::BITS, 512);
386 assert_eq!(M256::BITS, 256);
387 assert_eq!(M128::BITS, 128);
388 assert_eq!(<u8 as Underlier>::BITS, 8);
389
390 let block = M256::from_words_fn(|i| i as u64);
392 assert_eq!(block.words(), &[0u64, 1, 2, 3]);
393 assert_eq!(block.to_words(), [0u64, 1, 2, 3]);
394 assert_eq!(M256::from_words([0, 1, 2, 3]), block);
395 }
396
397 #[test]
398 fn a_wide_block_is_an_array_of_narrow_ones() {
399 let block = M512::from_words_fn(|i| i as u64);
401
402 let words: &[u64] = block.parts();
404 assert_eq!(words, &[0u64, 1, 2, 3, 4, 5, 6, 7]);
405
406 let halves: &[M128] = block.parts();
408 assert_eq!(halves.len(), 4);
409 assert_eq!(halves[0].to_words(), [0, 1]);
410 assert_eq!(halves[3].to_words(), [6, 7]);
411
412 let quarters: &[M256] = block.parts();
414 assert_eq!(quarters.len(), 2);
415 assert_eq!(quarters[0].to_words(), [0, 1, 2, 3]);
416 assert_eq!(quarters[1].to_words(), [4, 5, 6, 7]);
417
418 let bytes: &[u8] = block.parts();
420 assert_eq!(bytes.len(), 64);
421 assert_eq!(bytes[0], 0);
422 assert_eq!(bytes[8], 1);
423 assert_eq!(bytes[16], 2);
424 }
425
426 #[test]
427 fn writing_through_a_narrow_view_changes_the_wide_block() {
428 let mut block = M128::ZERO;
430 {
431 let bytes: &mut [u8] = block.parts_mut();
432 bytes[0] = 0xff;
433 bytes[15] = 1;
434 }
435
436 assert_eq!(block.to_words(), [0xff, 1 << 56]);
438 }
439
440 #[test]
441 fn a_word_widens_and_truncates() {
442 assert_eq!(<u8 as Word>::to_u64(0xff), 255);
444 assert_eq!(<u8 as Word>::from_u64(0x1234), 0x34);
445 assert_eq!(<u16 as Word>::from_u64(0x1_2345), 0x2345);
446 assert_eq!(<u64 as Word>::ONES, u64::MAX);
447 assert_eq!(<u32 as Word>::count_ones(0b1011), 3);
448 }
449
450 #[test]
451 fn combining_runs_word_by_word() {
452 let a = M128::from_words([0b1100, 0b1010]);
454 let b = M128::from_words([0b1010, 0b0110]);
455
456 assert_eq!(
457 a.zip(b, |x, y| x ^ y).to_words(),
458 [0b1100 ^ 0b1010, 0b1010 ^ 0b0110]
459 );
460 assert_eq!(
461 a.zip(b, |x, y| x & y).to_words(),
462 [0b1100 & 0b1010, 0b1010 & 0b0110]
463 );
464 }
465}