use crate::field::Elem;
use crate::kernel::{FieldKernels, RawDispatch};
use super::check_width;
#[cfg(feature = "alloc")]
use super::CoeffMatrix;
pub fn mul_add_matrix<F: FieldKernels>(
rows: &mut [u8],
row_len: usize,
nrows: usize,
terms: &[(&[Elem<F>], &[u8])],
) {
check_width::<F>("mul_add_matrix", row_len);
let used = nrows
.checked_mul(row_len)
.expect("mul_add_matrix: row geometry overflows");
assert!(
rows.len() >= used,
"mul_add_matrix: rows is {} bytes but {nrows} rows of {row_len} bytes need {used}",
rows.len(),
);
for &(coeffs, src) in terms {
assert_eq!(
coeffs.len(),
nrows,
"mul_add_matrix: term supplies {} coefficients for {nrows} rows",
coeffs.len()
);
assert_eq!(
src.len(),
row_len,
"mul_add_matrix: source is {} bytes, expected {row_len}",
src.len()
);
}
if nrows == 0 || row_len == 0 || terms.is_empty() {
return;
}
F::mul_add_matrix(RawDispatch, &mut rows[..used], row_len, nrows, terms);
}
#[cfg(feature = "alloc")]
pub fn mul_add_matrix_with<F: FieldKernels>(
rows: &mut [u8],
row_len: usize,
coeffs: &CoeffMatrix<F>,
srcs: &[&[u8]],
) {
check_width::<F>("mul_add_matrix_with", row_len);
let nrows = coeffs.output_count();
let used = nrows
.checked_mul(row_len)
.expect("mul_add_matrix_with: row geometry overflows");
assert!(
rows.len() >= used,
"mul_add_matrix_with: rows is {} bytes but {nrows} rows of {row_len} bytes need {used}",
rows.len(),
);
assert_eq!(
coeffs.source_count(),
srcs.len(),
"mul_add_matrix_with: matrix holds coefficients for {} sources but there are {}",
coeffs.source_count(),
srcs.len(),
);
for (index, &src) in srcs.iter().enumerate() {
assert_eq!(
src.len(),
row_len,
"mul_add_matrix_with: source {index} is {} bytes, expected {row_len}",
src.len(),
);
}
if nrows == 0 || row_len == 0 || srcs.is_empty() {
return;
}
F::mul_add_matrix_plan(
RawDispatch,
&mut rows[..used],
row_len,
nrows,
coeffs.values.as_ref(),
coeffs.prepared.as_ref(),
srcs,
);
}
pub fn mul_into_matrix<F: FieldKernels>(
rows: &mut [u8],
row_len: usize,
nrows: usize,
terms: &[(&[Elem<F>], &[u8])],
) {
check_width::<F>("mul_into_matrix", row_len);
let used = nrows
.checked_mul(row_len)
.expect("mul_into_matrix: row geometry overflows");
assert!(
rows.len() >= used,
"mul_into_matrix: rows is {} bytes but {nrows} rows of {row_len} bytes need {used}",
rows.len(),
);
for &(coeffs, src) in terms {
assert_eq!(
coeffs.len(),
nrows,
"mul_into_matrix: term supplies {} coefficients for {nrows} rows",
coeffs.len()
);
assert_eq!(
src.len(),
row_len,
"mul_into_matrix: source is {} bytes, expected {row_len}",
src.len()
);
}
if nrows == 0 || row_len == 0 {
return;
}
if terms.is_empty() {
rows[..used].fill(0);
return;
}
F::mul_into_matrix(RawDispatch, &mut rows[..used], row_len, nrows, terms);
}
#[cfg(feature = "alloc")]
pub fn mul_into_matrix_with<F: FieldKernels>(
rows: &mut [u8],
row_len: usize,
coeffs: &CoeffMatrix<F>,
srcs: &[&[u8]],
) {
check_width::<F>("mul_into_matrix_with", row_len);
let nrows = coeffs.output_count();
let used = nrows
.checked_mul(row_len)
.expect("mul_into_matrix_with: row geometry overflows");
assert!(
rows.len() >= used,
"mul_into_matrix_with: rows is {} bytes but {nrows} rows of {row_len} bytes need {used}",
rows.len(),
);
assert_eq!(
coeffs.source_count(),
srcs.len(),
"mul_into_matrix_with: matrix holds coefficients for {} sources but there are {}",
coeffs.source_count(),
srcs.len(),
);
for (index, &src) in srcs.iter().enumerate() {
assert_eq!(
src.len(),
row_len,
"mul_into_matrix_with: source {index} is {} bytes, expected {row_len}",
src.len(),
);
}
if nrows == 0 || row_len == 0 {
return;
}
if srcs.is_empty() {
rows[..used].fill(0);
return;
}
F::mul_into_matrix_plan(
RawDispatch,
&mut rows[..used],
row_len,
nrows,
coeffs.values.as_ref(),
coeffs.prepared.as_ref(),
srcs,
);
}
pub fn mul_add_matrix_at<F: FieldKernels>(
dst: &mut [u8],
row_len: usize,
row_starts: &[usize],
terms: &[(&[Elem<F>], &[u8])],
) {
check_at::<F>("mul_add_matrix_at", dst.len(), row_len, row_starts);
let nrows = row_starts.len();
for &(coeffs, src) in terms {
assert_eq!(
coeffs.len(),
nrows,
"mul_add_matrix_at: term supplies {} coefficients for {nrows} rows",
coeffs.len(),
);
assert_eq!(
src.len(),
row_len,
"mul_add_matrix_at: source is {} bytes, expected {row_len}",
src.len()
);
}
if nrows == 0 || row_len == 0 || terms.is_empty() {
return;
}
F::mul_add_matrix_at(RawDispatch, dst, row_len, row_starts, terms);
}
#[cfg(feature = "alloc")]
pub fn mul_add_matrix_at_with<F: FieldKernels>(
dst: &mut [u8],
row_len: usize,
row_starts: &[usize],
coeffs: &CoeffMatrix<F>,
srcs: &[&[u8]],
) {
check_at::<F>("mul_add_matrix_at_with", dst.len(), row_len, row_starts);
assert_eq!(
coeffs.source_count(),
srcs.len(),
"mul_add_matrix_at_with: matrix holds coefficients for {} sources but there are {}",
coeffs.source_count(),
srcs.len(),
);
assert_eq!(
coeffs.output_count(),
row_starts.len(),
"mul_add_matrix_at_with: matrix holds coefficients for {} rows but there are {}",
coeffs.output_count(),
row_starts.len(),
);
for (index, &src) in srcs.iter().enumerate() {
assert_eq!(
src.len(),
row_len,
"mul_add_matrix_at_with: source {index} is {} bytes, expected {row_len}",
src.len(),
);
}
if row_starts.is_empty() || row_len == 0 || srcs.is_empty() {
return;
}
F::mul_add_matrix_at_plan(
RawDispatch,
dst,
row_len,
row_starts,
coeffs.values.as_ref(),
coeffs.prepared.as_ref(),
srcs,
);
}
pub fn mul_into_matrix_at<F: FieldKernels>(
dst: &mut [u8],
row_len: usize,
row_starts: &[usize],
terms: &[(&[Elem<F>], &[u8])],
) {
check_at::<F>("mul_into_matrix_at", dst.len(), row_len, row_starts);
let nrows = row_starts.len();
for &(coeffs, src) in terms {
assert_eq!(
coeffs.len(),
nrows,
"mul_into_matrix_at: term supplies {} coefficients for {nrows} rows",
coeffs.len(),
);
assert_eq!(
src.len(),
row_len,
"mul_into_matrix_at: source is {} bytes, expected {row_len}",
src.len()
);
}
if nrows == 0 || row_len == 0 {
return;
}
if terms.is_empty() {
for &start in row_starts {
dst[start..start + row_len].fill(0);
}
return;
}
F::mul_into_matrix_at(RawDispatch, dst, row_len, row_starts, terms);
}
#[cfg(feature = "alloc")]
pub fn mul_into_matrix_at_with<F: FieldKernels>(
dst: &mut [u8],
row_len: usize,
row_starts: &[usize],
coeffs: &CoeffMatrix<F>,
srcs: &[&[u8]],
) {
check_at::<F>("mul_into_matrix_at_with", dst.len(), row_len, row_starts);
assert_eq!(
coeffs.source_count(),
srcs.len(),
"mul_into_matrix_at_with: matrix holds coefficients for {} sources but there are {}",
coeffs.source_count(),
srcs.len(),
);
assert_eq!(
coeffs.output_count(),
row_starts.len(),
"mul_into_matrix_at_with: matrix holds coefficients for {} rows but there are {}",
coeffs.output_count(),
row_starts.len(),
);
for (index, &src) in srcs.iter().enumerate() {
assert_eq!(
src.len(),
row_len,
"mul_into_matrix_at_with: source {index} is {} bytes, expected {row_len}",
src.len(),
);
}
if row_starts.is_empty() || row_len == 0 {
return;
}
if srcs.is_empty() {
for &start in row_starts {
dst[start..start + row_len].fill(0);
}
return;
}
F::mul_into_matrix_at_plan(
RawDispatch,
dst,
row_len,
row_starts,
coeffs.values.as_ref(),
coeffs.prepared.as_ref(),
srcs,
);
}
fn check_at<F: FieldKernels>(name: &str, dst_len: usize, row_len: usize, row_starts: &[usize]) {
check_width::<F>(name, row_len);
for (j, &start) in row_starts.iter().enumerate() {
let end = start
.checked_add(row_len)
.unwrap_or_else(|| panic!("{name}: row offset + length overflows"));
assert!(
end <= dst_len,
"{name}: row {j} spans {start}..{end} but dst is {dst_len} bytes",
);
assert!(
start.is_multiple_of(F::BYTES),
"{name}: row {j} offset {start} is not a whole number of {} elements",
F::NAME,
);
}
for (a, &sa) in row_starts.iter().enumerate() {
for &sb in &row_starts[a + 1..] {
let (lo, hi) = if sa <= sb { (sa, sb) } else { (sb, sa) };
assert!(
hi - lo >= row_len,
"{name}: rows at {sa} and {sb} overlap for {row_len}-byte rows",
);
}
}
}
#[cfg(all(test, feature = "alloc"))]
mod tests {
use super::*;
use crate::field::binary::tower::Rijndael16Field;
use crate::field::poly::AES;
use crate::field::{Elem as E8, FiniteField, Polynomial};
use alloc::vec::Vec;
#[test]
#[should_panic(expected = "term supplies")]
fn zero_row_length_still_checks_term_counts() {
let coeffs = [E8::<FiniteField<2, 8, Polynomial<AES>>>::from_raw(0x07); 2];
let mut rows = [0u8; 8];
mul_add_matrix::<FiniteField<2, 8, Polynomial<AES>>>(&mut rows, 0, 3, &[(&coeffs, &[])]);
}
#[test]
fn prepared_matrix_ops_match_one_shot() {
let values: Vec<Elem<Rijndael16Field>> = (0u16..3 * 4)
.map(|i| Elem::<Rijndael16Field>::from_raw(i * 511 + 3))
.collect();
let matrix = CoeffMatrix::<Rijndael16Field>::from_source_major(3, 4, &values);
let srcs: Vec<Vec<u8>> = (0u8..3).map(|s| alloc::vec![s + 1; 8]).collect();
let src_refs: Vec<&[u8]> = srcs.iter().map(Vec::as_slice).collect();
let terms: Vec<(&[Elem<_>], &[u8])> = (0..3)
.map(|s| (&values[s * 4..s * 4 + 4], src_refs[s]))
.collect();
let mut one_shot = [0x11u8; 32];
mul_add_matrix::<Rijndael16Field>(&mut one_shot, 8, 4, &terms);
let mut prepared = [0x11u8; 32];
mul_add_matrix_with::<Rijndael16Field>(&mut prepared, 8, &matrix, &src_refs);
assert_eq!(one_shot, prepared);
let mut one_shot = [0x22u8; 32];
mul_into_matrix::<Rijndael16Field>(&mut one_shot, 8, 4, &terms);
let mut prepared = [0x22u8; 32];
mul_into_matrix_with::<Rijndael16Field>(&mut prepared, 8, &matrix, &src_refs);
assert_eq!(one_shot, prepared);
}
}