use super::{Array, BlockSizes, Error};
use core::fmt;
pub struct ReadBuffer<BS: BlockSizes> {
buffer: Array<u8, BS>,
}
impl<BS: BlockSizes> fmt::Debug for ReadBuffer<BS> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ReadBuffer")
.field("remaining_data", &self.remaining())
.finish_non_exhaustive()
}
}
impl<BS: BlockSizes> Default for ReadBuffer<BS> {
#[inline]
fn default() -> Self {
let buffer = Default::default();
let mut res = Self { buffer };
unsafe { res.set_pos_unchecked(BS::USIZE) };
res
}
}
impl<BS: BlockSizes> Clone for ReadBuffer<BS> {
#[inline]
fn clone(&self) -> Self {
let buffer = self.buffer.clone();
Self { buffer }
}
}
impl<BS: BlockSizes> ReadBuffer<BS> {
#[inline(always)]
pub fn get_pos(&self) -> usize {
let pos = self.buffer[0];
if pos == 0 || pos > BS::U8 {
debug_assert!(false);
unsafe {
core::hint::unreachable_unchecked();
}
}
pos as usize
}
#[inline(always)]
pub fn size(&self) -> usize {
BS::USIZE
}
#[inline(always)]
pub fn remaining(&self) -> usize {
self.size() - self.get_pos()
}
#[inline(always)]
#[allow(clippy::cast_possible_truncation)]
unsafe fn set_pos_unchecked(&mut self, pos: usize) {
debug_assert!(pos != 0 && pos <= BS::USIZE);
self.buffer[0] = pos as u8;
}
#[inline(always)]
pub fn read_cached(&mut self, len: usize) -> &[u8] {
let rem = self.remaining();
let new_len = core::cmp::min(rem, len);
let pos = self.get_pos();
unsafe { self.set_pos_unchecked(pos + new_len) };
&self.buffer[pos..][..new_len]
}
#[inline(always)]
pub fn write_block(
&mut self,
read_len: usize,
gen_block: impl FnOnce(&mut Array<u8, BS>),
read_fn: impl FnOnce(&[u8]),
) {
if read_len == 0 {
return;
}
assert!(read_len < BS::USIZE);
let g = ResetGuard(self);
let buf = &mut g.0.buffer;
gen_block(buf);
read_fn(&buf[..read_len]);
core::mem::forget(g);
unsafe { self.set_pos_unchecked(read_len) };
}
pub fn reset(&mut self) {
self.buffer[0] = BS::U8;
}
#[inline]
pub fn read(&mut self, buf: &mut [u8], mut gen_block: impl FnMut(&mut Array<u8, BS>)) {
let head_ks = self.read_cached(buf.len());
let (head, buf) = buf.split_at_mut(head_ks.len());
let (blocks, tail) = Array::slice_as_chunks_mut(buf);
head.copy_from_slice(head_ks);
for block in blocks {
gen_block(block);
}
self.write_block(tail.len(), gen_block, |tail_ks| {
tail.copy_from_slice(tail_ks);
});
}
#[inline]
pub fn serialize(&self) -> Array<u8, BS> {
let pos = self.get_pos();
let mut res = self.buffer.clone();
for b in &mut res[1..pos] {
*b = 0;
}
res
}
#[inline]
pub fn deserialize(buffer: &Array<u8, BS>) -> Result<Self, Error> {
let pos = usize::from(buffer[0]);
if pos == 0 || pos > BS::USIZE || buffer[1..pos].iter().any(|&b| b != 0) {
Err(Error)
} else {
let buffer = buffer.clone();
Ok(Self { buffer })
}
}
}
#[cfg(feature = "zeroize")]
impl<BS: BlockSizes> Drop for ReadBuffer<BS> {
fn drop(&mut self) {
use zeroize::Zeroize;
self.buffer.zeroize();
}
}
#[cfg(feature = "zeroize")]
impl<BS: BlockSizes> zeroize::ZeroizeOnDrop for ReadBuffer<BS> {}
struct ResetGuard<'a, BS: BlockSizes>(&'a mut ReadBuffer<BS>);
impl<BS: BlockSizes> Drop for ResetGuard<'_, BS> {
fn drop(&mut self) {
self.0.reset();
}
}