use core::fmt::Debug;
use core::hash::Hash;
use core::ops::{BitAnd, BitOr, BitXor, Not, Shl, Shr};
use core::slice;
use rand::{Rng, RngExt};
pub(crate) mod private {
pub trait Sealed {}
}
use private::Sealed;
impl Sealed for u8 {}
impl Sealed for u16 {}
impl Sealed for u32 {}
impl Sealed for u64 {}
pub trait Word:
Sealed
+ Copy
+ Default
+ Eq
+ Ord
+ Hash
+ Debug
+ Send
+ Sync
+ 'static
+ BitAnd<Output = Self>
+ BitOr<Output = Self>
+ BitXor<Output = Self>
+ Not<Output = Self>
+ Shl<usize, Output = Self>
+ Shr<usize, Output = Self>
{
const BITS: usize;
const ZERO: Self;
const ONES: Self;
fn count_ones(self) -> u32;
fn to_u64(self) -> u64;
fn from_u64(value: u64) -> Self;
}
macro_rules! impl_word {
($word:ty) => {
impl Word for $word {
const BITS: usize = <$word>::BITS as usize;
const ZERO: Self = 0;
const ONES: Self = <$word>::MAX;
#[inline]
fn count_ones(self) -> u32 {
Self::count_ones(self)
}
#[inline]
fn to_u64(self) -> u64 {
u64::from(self)
}
#[inline]
fn from_u64(value: u64) -> Self {
value as Self
}
}
};
}
impl_word!(u8);
impl_word!(u16);
impl_word!(u32);
impl_word!(u64);
pub unsafe trait Underlier:
Copy + Default + Eq + Hash + Debug + Send + Sync + 'static
{
type Word: Word;
const WORDS: usize;
const BITS: usize = Self::WORDS * <Self::Word as Word>::BITS;
const ZERO: Self;
const ONES: Self;
fn words(&self) -> &[Self::Word];
fn words_mut(&mut self) -> &mut [Self::Word];
fn from_words_fn(f: impl FnMut(usize) -> Self::Word) -> Self;
fn random<R: Rng + ?Sized>(rng: &mut R) -> Self;
#[inline]
fn zip(self, other: Self, op: impl Fn(Self::Word, Self::Word) -> Self::Word) -> Self {
Self::from_words_fn(|i| op(self.words()[i], other.words()[i]))
}
}
macro_rules! impl_scalar_underlier {
($word:ty) => {
unsafe impl Underlier for $word {
type Word = Self;
const WORDS: usize = 1;
const ZERO: Self = 0;
const ONES: Self = <$word>::MAX;
#[inline]
fn words(&self) -> &[Self::Word] {
slice::from_ref(self)
}
#[inline]
fn words_mut(&mut self) -> &mut [Self::Word] {
slice::from_mut(self)
}
#[inline]
fn from_words_fn(mut f: impl FnMut(usize) -> Self::Word) -> Self {
f(0)
}
#[inline]
fn random<R: Rng + ?Sized>(rng: &mut R) -> Self {
rng.random()
}
}
};
}
impl_scalar_underlier!(u8);
impl_scalar_underlier!(u16);
impl_scalar_underlier!(u32);
impl_scalar_underlier!(u64);
macro_rules! wide_underlier {
($name:ident, $words:literal, $align:literal, $doc:literal) => {
#[doc = $doc]
#[derive(Clone, Copy, Default, PartialEq, Eq, Hash, Debug)]
#[repr(C, align($align))]
pub struct $name([u64; $words]);
impl Sealed for $name {}
impl $name {
#[inline]
#[must_use]
pub const fn from_words(words: [u64; $words]) -> Self {
Self(words)
}
#[inline]
#[must_use]
pub const fn to_words(self) -> [u64; $words] {
self.0
}
}
unsafe impl Underlier for $name {
type Word = u64;
const WORDS: usize = $words;
const ZERO: Self = Self([0; $words]);
const ONES: Self = Self([u64::MAX; $words]);
#[inline]
fn words(&self) -> &[Self::Word] {
&self.0
}
#[inline]
fn words_mut(&mut self) -> &mut [Self::Word] {
&mut self.0
}
#[inline]
fn from_words_fn(f: impl FnMut(usize) -> Self::Word) -> Self {
Self(core::array::from_fn(f))
}
#[inline]
fn random<R: Rng + ?Sized>(rng: &mut R) -> Self {
Self(rng.random())
}
}
};
}
wide_underlier!(M128, 2, 16, "A 128-bit block of bits.");
wide_underlier!(M256, 4, 32, "A 256-bit block of bits.");
wide_underlier!(M512, 8, 64, "A 512-bit block of bits.");
pub unsafe trait Divisible<Narrow: Underlier>: Underlier {
const RATIO: usize = Self::BITS / Narrow::BITS;
#[inline]
fn parts(&self) -> &[Narrow] {
const { Self::CHECK_LAYOUT };
unsafe { slice::from_raw_parts(core::ptr::from_ref(self).cast(), Self::RATIO) }
}
#[inline]
fn parts_mut(&mut self) -> &mut [Narrow] {
const { Self::CHECK_LAYOUT };
unsafe { slice::from_raw_parts_mut(core::ptr::from_mut(self).cast(), Self::RATIO) }
}
#[inline]
fn split_slice(slice: &[Self]) -> &[Narrow] {
const { Self::CHECK_LAYOUT };
unsafe { slice::from_raw_parts(slice.as_ptr().cast(), slice.len() * Self::RATIO) }
}
#[inline]
fn split_slice_mut(slice: &mut [Self]) -> &mut [Narrow] {
const { Self::CHECK_LAYOUT };
let len = slice.len() * Self::RATIO;
unsafe { slice::from_raw_parts_mut(slice.as_mut_ptr().cast(), len) }
}
const CHECK_LAYOUT: () = {
assert!(size_of::<Self>() == Self::RATIO * size_of::<Narrow>());
assert!(align_of::<Self>() >= align_of::<Narrow>());
assert!(
cfg!(target_endian = "little") || size_of::<Narrow::Word>() == size_of::<Self::Word>(),
"reading a block at a narrower word needs a little-endian target"
);
};
}
macro_rules! divisible {
($wide:ty, $narrow:ty) => {
unsafe impl Divisible<$narrow> for $wide {
const RATIO: usize = <$wide as Underlier>::BITS / <$narrow as Underlier>::BITS;
}
};
}
divisible!(M512, M256);
divisible!(M512, M128);
divisible!(M512, u64);
divisible!(M512, u32);
divisible!(M512, u16);
divisible!(M512, u8);
divisible!(M256, M128);
divisible!(M256, u64);
divisible!(M256, u32);
divisible!(M256, u16);
divisible!(M256, u8);
divisible!(M128, u64);
divisible!(M128, u32);
divisible!(M128, u16);
divisible!(M128, u8);
divisible!(u64, u32);
divisible!(u64, u16);
divisible!(u64, u8);
divisible!(u32, u16);
divisible!(u32, u8);
divisible!(u16, u8);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_block_is_its_words() {
assert_eq!(M512::ZERO.words(), &[0u64; 8]);
assert_eq!(M512::ONES.words(), &[u64::MAX; 8]);
assert_eq!(M512::BITS, 512);
assert_eq!(M256::BITS, 256);
assert_eq!(M128::BITS, 128);
assert_eq!(<u8 as Underlier>::BITS, 8);
let block = M256::from_words_fn(|i| i as u64);
assert_eq!(block.words(), &[0u64, 1, 2, 3]);
assert_eq!(block.to_words(), [0u64, 1, 2, 3]);
assert_eq!(M256::from_words([0, 1, 2, 3]), block);
}
#[test]
fn a_wide_block_is_an_array_of_narrow_ones() {
let block = M512::from_words_fn(|i| i as u64);
let words: &[u64] = block.parts();
assert_eq!(words, &[0u64, 1, 2, 3, 4, 5, 6, 7]);
let halves: &[M128] = block.parts();
assert_eq!(halves.len(), 4);
assert_eq!(halves[0].to_words(), [0, 1]);
assert_eq!(halves[3].to_words(), [6, 7]);
let quarters: &[M256] = block.parts();
assert_eq!(quarters.len(), 2);
assert_eq!(quarters[0].to_words(), [0, 1, 2, 3]);
assert_eq!(quarters[1].to_words(), [4, 5, 6, 7]);
let bytes: &[u8] = block.parts();
assert_eq!(bytes.len(), 64);
assert_eq!(bytes[0], 0);
assert_eq!(bytes[8], 1);
assert_eq!(bytes[16], 2);
}
#[test]
fn writing_through_a_narrow_view_changes_the_wide_block() {
let mut block = M128::ZERO;
{
let bytes: &mut [u8] = block.parts_mut();
bytes[0] = 0xff;
bytes[15] = 1;
}
assert_eq!(block.to_words(), [0xff, 1 << 56]);
}
#[test]
fn a_word_widens_and_truncates() {
assert_eq!(<u8 as Word>::to_u64(0xff), 255);
assert_eq!(<u8 as Word>::from_u64(0x1234), 0x34);
assert_eq!(<u16 as Word>::from_u64(0x1_2345), 0x2345);
assert_eq!(<u64 as Word>::ONES, u64::MAX);
assert_eq!(<u32 as Word>::count_ones(0b1011), 3);
}
#[test]
fn combining_runs_word_by_word() {
let a = M128::from_words([0b1100, 0b1010]);
let b = M128::from_words([0b1010, 0b0110]);
assert_eq!(
a.zip(b, |x, y| x ^ y).to_words(),
[0b1100 ^ 0b1010, 0b1010 ^ 0b0110]
);
assert_eq!(
a.zip(b, |x, y| x & y).to_words(),
[0b1100 & 0b1010, 0b1010 & 0b0110]
);
}
}