#[derive(Clone)]
pub(crate) struct Blockwise<const N: usize> {
buffer: [u8; N],
used: usize,
}
impl<const N: usize> Blockwise<N> {
pub(crate) const fn new() -> Self {
Self {
buffer: [0u8; N],
used: 0,
}
}
pub(crate) const fn used(&self) -> usize {
self.used
}
pub(crate) fn add(&mut self, bytes: &[u8]) {
if self.used == 0 {
self.add_trailing(bytes);
} else {
self.add_leading(bytes);
}
}
pub(crate) fn update(&mut self, bytes: &[u8], blocks: impl FnMut(&[u8])) {
let used = self.used;
if bytes.len() <= N - used {
self.used = used + bytes.len();
self.buffer[used..used + bytes.len()].copy_from_slice(bytes);
} else {
self.complete_blocks(bytes, blocks);
}
}
#[inline(never)]
fn complete_blocks(&mut self, mut bytes: &[u8], mut blocks: impl FnMut(&[u8])) {
let used = self.used;
if used > 0 {
let (head, rest) = bytes.split_at(N - used);
self.buffer[used..].copy_from_slice(head);
blocks(&self.buffer);
bytes = rest;
}
let (whole, rest) = bytes.split_at((bytes.len() - 1) / N * N);
if !whole.is_empty() {
blocks(whole);
}
self.buffer[..rest.len()].copy_from_slice(rest);
self.used = rest.len();
}
pub(crate) fn add_leading<'a>(&mut self, bytes: &'a [u8]) -> &'a [u8] {
if self.used == 0 {
return bytes;
}
let space = N - self.used;
let take = core::cmp::min(bytes.len(), space);
let (taken, returned) = bytes.split_at(take);
self.buffer[self.used..self.used + take].copy_from_slice(taken);
self.used += take;
returned
}
pub(crate) fn take(&mut self) -> Option<[u8; N]> {
if self.used == N {
self.used = 0;
Some(self.buffer)
} else {
None
}
}
#[cfg(any(target_arch = "x86_64", all(target_arch = "aarch64", test)))]
pub(crate) fn peek_remaining(&self) -> Option<&[u8]> {
if self.used > 0 {
Some(&self.buffer[..self.used])
} else {
None
}
}
pub(crate) fn buffered(&self) -> &[u8] {
&self.buffer[..self.used]
}
pub(crate) fn add_trailing(&mut self, trailing: &[u8]) {
self.buffer[..trailing.len()].copy_from_slice(trailing);
self.used += trailing.len();
}
}
pub(crate) fn compress_padded<const N: usize, const L: usize>(
message: &[u8],
length: [u8; L],
mut compress: impl FnMut(&[u8]),
) {
let tail = message.len() % N;
let start = if tail + 1 + L <= N && message.len() >= N {
message.len() - tail - N
} else {
message.len() - tail
};
let (blocks, rest) = message.split_at(start);
if !blocks.is_empty() {
compress(blocks);
}
let mut last = [[0u8; N]; 2];
compress(pad(rest, length, &mut last));
}
#[cfg(target_arch = "x86_64")]
pub(crate) fn pad_message<'m, 'l, const N: usize, const L: usize>(
message: &'m [u8],
length: [u8; L],
last: &'l mut [[u8; N]; 2],
) -> (&'m [u8], &'l [u8]) {
let (blocks, rest) = message.split_at(message.len() / N * N);
(blocks, pad(rest, length, last))
}
fn pad<'l, const N: usize, const L: usize>(
rest: &[u8],
length: [u8; L],
last: &'l mut [[u8; N]; 2],
) -> &'l [u8] {
let padded = if rest.len() % N + 1 + L <= N {
rest.len() / N * N + N
} else {
rest.len() / N * N + 2 * N
};
let last = &mut last.as_flattened_mut()[..padded];
last[..rest.len()].copy_from_slice(rest);
last[rest.len()] = 0x80;
last[padded - L..].copy_from_slice(&length);
last
}