block_buffer/
sealed.rs

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