use crate::{
Kt,
consts::{CHUNK_SIZE, CHUNK_SIZE_U64, ROUNDS, S0_DELIM},
node_turbo_shake,
};
use digest::typenum::Unsigned;
use keccak::{Backend, BackendClosure};
const BUFFER_LEN: usize = 512;
pub(crate) struct Closure<'a, const RATE: usize> {
pub(crate) data: &'a [u8],
pub(crate) kt: &'a mut Kt<RATE>,
}
impl<const RATE: usize> BackendClosure for Closure<'_, RATE> {
#[inline(always)]
fn call_once<B: Backend>(self) {
let Kt {
accum_tshk,
node_tshk,
consumed_len,
..
} = self.kt;
let mut data = self.data;
let par_p1600 = B::get_par_p1600::<ROUNDS>();
let p1600 = B::get_p1600::<ROUNDS>();
let par_size = B::ParSize1600::USIZE;
let cv_len = 200 - RATE;
let mut cv_buf = [0u8; BUFFER_LEN];
if let Some(s0_rem_len) = CHUNK_SIZE_U64.checked_sub(*consumed_len) {
let s0_rem_len = usize::try_from(s0_rem_len)
.expect("the value is smaller or equal to CHUNK_SIZE_U64");
let (part_data, rem_data) = data.split_at_checked(s0_rem_len).unwrap_or((data, &[]));
accum_tshk.absorb(p1600, part_data);
*consumed_len += u64::try_from(part_data.len()).expect("length fits into `u64`");
if rem_data.is_empty() {
return;
}
debug_assert_eq!(*consumed_len, CHUNK_SIZE_U64);
accum_tshk.absorb(p1600, &S0_DELIM.to_le_bytes());
data = rem_data;
}
let partial_chunk_len = usize::try_from(*consumed_len % CHUNK_SIZE_U64)
.expect("the remainder is always smaller than CHUNK_SIZE");
*consumed_len += u64::try_from(data.len()).expect("`data.len()` fits into `u64`");
if partial_chunk_len != 0 {
let rem_len = CHUNK_SIZE - partial_chunk_len;
let Some((part_data, rem_data)) = data.split_at_checked(rem_len) else {
node_tshk.absorb(p1600, data);
return;
};
node_tshk.absorb(p1600, part_data);
let cv_dst = &mut cv_buf[..cv_len];
node_tshk.finalize_intermediate_node(p1600, cv_dst);
accum_tshk.absorb(p1600, cv_dst);
node_tshk.reset();
data = rem_data;
}
if data.is_empty() {
return;
}
if par_size > 1 {
let cvs_dst = &mut cv_buf[..par_size * cv_len];
let mut par_data_chunks = data.chunks_exact(par_size * CHUNK_SIZE);
for par_data_chunk in &mut par_data_chunks {
node_turbo_shake::parallel::<_, RATE>(par_p1600, par_data_chunk, cvs_dst);
accum_tshk.absorb(p1600, cvs_dst);
}
data = par_data_chunks.remainder();
}
let cv_dst = &mut cv_buf[..cv_len];
let mut data_chunks = data.chunks_exact(CHUNK_SIZE);
for data_chunk in &mut data_chunks {
node_turbo_shake::scalar::<RATE>(p1600, data_chunk, cv_dst);
accum_tshk.absorb(p1600, cv_dst);
}
data = data_chunks.remainder();
node_tshk.absorb(p1600, data);
}
}