block_buffer/
sealed.rs

1use hybrid_array::sizes::{U0, U1};
2
3use super::{Array, ArraySize};
4use core::{mem::MaybeUninit, ptr};
5
6type Block<N> = MaybeUninit<Array<u8, N>>;
7
8/// Sealed trait for buffer kinds.
9pub trait Sealed {
10    #[cfg(not(feature = "zeroize"))]
11    type Pos: Default + Clone;
12    #[cfg(feature = "zeroize")]
13    type Pos: Default + Clone + zeroize::Zeroize;
14
15    type Overhead: ArraySize;
16
17    const NAME: &'static str;
18
19    fn get_pos<N: ArraySize>(buf: &Block<N>, pos: &Self::Pos) -> usize;
20
21    fn set_pos<N: ArraySize>(buf: &mut Block<N>, pos: &mut Self::Pos, val: usize);
22
23    /// Invariant guaranteed by a buffer kind, i.e. with correct
24    /// buffer code this function always returns true.
25    fn invariant(pos: usize, block_size: usize) -> bool;
26
27    /// Split input data into slice of blocks and tail.
28    fn split_blocks<N: ArraySize>(data: &[u8]) -> (&[Array<u8, N>], &[u8]);
29}
30
31impl Sealed for super::Eager {
32    type Pos = ();
33    type Overhead = U0;
34    const NAME: &'static str = "BlockBuffer<Eager>";
35
36    fn get_pos<N: ArraySize>(buf: &Block<N>, _pos: &Self::Pos) -> usize {
37        // SAFETY: last byte in `buf` for eager hashes is always properly initialized
38        let pos = unsafe {
39            let buf_ptr = buf.as_ptr().cast::<u8>();
40            let last_byte_ptr = buf_ptr.add(N::USIZE - 1);
41            ptr::read(last_byte_ptr)
42        };
43        pos as usize
44    }
45
46    fn set_pos<N: ArraySize>(buf: &mut Block<N>, _pos: &mut Self::Pos, val: usize) {
47        debug_assert!(val <= u8::MAX as usize);
48        // SAFETY: we write to the last byte of `buf` which is always safe
49        unsafe {
50            let buf_ptr = buf.as_mut_ptr().cast::<u8>();
51            let last_byte_ptr = buf_ptr.add(N::USIZE - 1);
52            ptr::write(last_byte_ptr, val as u8);
53        }
54    }
55
56    #[inline(always)]
57    fn invariant(pos: usize, block_size: usize) -> bool {
58        pos < block_size
59    }
60
61    #[inline(always)]
62    fn split_blocks<N: ArraySize>(data: &[u8]) -> (&[Array<u8, N>], &[u8]) {
63        Array::slice_as_chunks(data)
64    }
65}
66
67impl Sealed for super::Lazy {
68    type Pos = u8;
69    type Overhead = U1;
70    const NAME: &'static str = "BlockBuffer<Lazy>";
71
72    fn get_pos<N: ArraySize>(_buf_val: &Block<N>, pos: &Self::Pos) -> usize {
73        *pos as usize
74    }
75
76    fn set_pos<N: ArraySize>(_: &mut Block<N>, pos: &mut Self::Pos, val: usize) {
77        debug_assert!(val <= u8::MAX as usize);
78        *pos = val as u8;
79    }
80
81    #[inline(always)]
82    fn invariant(pos: usize, block_size: usize) -> bool {
83        pos <= block_size
84    }
85
86    #[inline(always)]
87    fn split_blocks<N: ArraySize>(data: &[u8]) -> (&[Array<u8, N>], &[u8]) {
88        let (blocks, tail) = Array::slice_as_chunks(data);
89        if data.is_empty() || !tail.is_empty() {
90            (blocks, tail)
91        } else {
92            let (tail, blocks) = blocks.split_last().expect("`blocks` can not be empty");
93            (blocks, tail)
94        }
95    }
96}