use crate::field::{Elem, FiniteField, Polynomial};
use crate::kernel;
#[must_use]
pub const fn bytes_for(bits: usize) -> usize {
bits.div_ceil(8)
}
#[inline]
fn check_pair(name: &str, left_name: &str, left: usize, right_name: &str, right: usize) {
assert_eq!(
left, right,
"{name}: {left_name} is {left} bytes but {right_name} is {right} bytes"
);
}
#[inline]
fn check_range(name: &str, bits: usize, from: usize, to: usize) {
assert!(from <= to, "{name}: range start {from} exceeds end {to}");
assert!(
to <= bits,
"{name}: range end {to} exceeds the {bits} live bits"
);
}
#[inline]
fn check_holds(name: &str, operand: &str, len: usize, bits: usize) {
assert!(
bytes_for(bits) <= len,
"{name}: {bits} bits need at least {} bytes but {operand} holds {len}",
bytes_for(bits)
);
}
pub fn xor_assign(dst: &mut [u8], src: &[u8]) {
check_pair("bits::xor_assign", "dst", dst.len(), "src", src.len());
kernel::gf1::xor(dst, src);
}
#[inline]
pub fn xor_range(dst: &mut [u8], src: &[u8], bits: usize, from: usize, to: usize) {
check_pair("bits::xor_range", "dst", dst.len(), "src", src.len());
check_range("bits::xor_range", bits, from, to);
check_holds("bits::xor_range", "dst", dst.len(), bits);
xor_range_with(dst, src, &XorRange::new(bits, from, to));
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct XorRange {
window: kernel::gf1::Window,
}
impl XorRange {
#[must_use]
pub fn new(bits: usize, from: usize, to: usize) -> Self {
check_range("XorRange::new", bits, from, to);
Self {
window: kernel::gf1::window(from, to),
}
}
}
#[inline]
pub fn xor_range_with(dst: &mut [u8], src: &[u8], range: &XorRange) {
let end = range.window.end;
if end == 0 {
return;
}
assert!(
end <= dst.len() && end <= src.len(),
"bits::xor_range_with: the planned range needs {end} bytes but dst holds {} and src holds {}",
dst.len(),
src.len()
);
kernel::gf1::xor_window(dst, src, &range.window);
}
pub fn and_into(dst: &mut [u8], a: &[u8], b: &[u8]) {
check_pair("bits::and_into", "dst", dst.len(), "a", a.len());
check_pair("bits::and_into", "a", a.len(), "b", b.len());
kernel::gf1::and_into(dst, a, b);
}
pub fn and_assign(dst: &mut [u8], src: &[u8]) {
check_pair("bits::and_assign", "dst", dst.len(), "src", src.len());
kernel::gf1::and_assign(dst, src);
}
pub fn andnot_assign(dst: &mut [u8], mask: &[u8]) {
check_pair("bits::andnot_assign", "dst", dst.len(), "mask", mask.len());
kernel::gf1::andnot_assign(dst, mask);
}
#[inline]
pub fn clear_range(dst: &mut [u8], bits: usize, from: usize, to: usize) {
check_range("bits::clear_range", bits, from, to);
check_holds("bits::clear_range", "dst", dst.len(), bits);
kernel::gf1::clear_range(dst, from, to);
}
pub fn set_range(dst: &mut [u8], bits: usize, from: usize, to: usize) {
check_range("bits::set_range", bits, from, to);
check_holds("bits::set_range", "dst", dst.len(), bits);
kernel::gf1::set_range(dst, from, to);
}
#[must_use]
pub fn weight(buf: &[u8], bits: usize) -> usize {
const SEGMENT_BITS: usize = (u32::MAX as usize / 8) * 8;
check_holds("bits::weight", "buf", buf.len(), bits);
let mut total = 0usize;
let mut done = 0;
while done < bits {
let segment = (bits - done).min(SEGMENT_BITS);
total += kernel::gf1::weight(&buf[done / 8..], segment) as usize;
done += segment;
}
total
}
#[must_use]
pub fn dot_product(a: &[u8], b: &[u8], bits: usize) -> Elem<FiniteField<2, 1, Polynomial<3>>> {
check_pair("bits::dot_product", "a", a.len(), "b", b.len());
check_holds("bits::dot_product", "a", a.len(), bits);
#[allow(clippy::cast_possible_truncation)] Elem::<FiniteField<2, 1, Polynomial<3>>>::from_raw(kernel::gf1::parity(a, b, bits) as u8)
}
pub fn xor_gather(dst: &mut [u8], bits: usize, selector: u64, srcs: &[&[u8]]) {
check_holds("bits::xor_gather", "dst", dst.len(), bits);
for (index, &src) in srcs.iter().enumerate() {
assert_eq!(
src.len(),
dst.len(),
"bits::xor_gather: source {index} is {} bytes but dst is {} bytes",
src.len(),
dst.len()
);
}
if srcs.len() < 64 {
let excess = selector >> srcs.len();
let count = srcs.len();
assert!(
excess == 0,
"bits::xor_gather: selector names source {} but only {count} sources exist",
count + excess.trailing_zeros() as usize
);
}
kernel::gf1::xor_gather(dst, srcs, selector);
}