block_buffer/
read.rs

1use super::{Array, ArraySize, Error};
2use core::fmt;
3
4/// Buffer for reading block-generated data.
5pub struct ReadBuffer<BS: ArraySize> {
6    /// The first byte of the block is used as cursor position.
7    /// `&buffer[usize::from(buffer[0])..]` is interpreted as unread bytes.
8    /// The cursor position is always bigger than zero and smaller than or equal to block size.
9    buffer: Array<u8, BS>,
10}
11
12impl<BS: ArraySize> fmt::Debug for ReadBuffer<BS> {
13    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
14        f.debug_struct("ReadBuffer")
15            .field("remaining_data", &self.remaining())
16            .finish_non_exhaustive()
17    }
18}
19
20impl<BS: ArraySize> Default for ReadBuffer<BS> {
21    #[inline]
22    fn default() -> Self {
23        assert!(
24            BS::USIZE != 0 && BS::USIZE < 256,
25            "buffer block size must be bigger than zero and smaller than 256"
26        );
27
28        let buffer = Default::default();
29        let mut res = Self { buffer };
30        // SAFETY: `BS::USIZE` satisfies the `set_pos_unchecked` safety contract
31        unsafe { res.set_pos_unchecked(BS::USIZE) };
32        res
33    }
34}
35
36impl<BS: ArraySize> Clone for ReadBuffer<BS> {
37    #[inline]
38    fn clone(&self) -> Self {
39        let buffer = self.buffer.clone();
40        Self { buffer }
41    }
42}
43
44impl<BS: ArraySize> ReadBuffer<BS> {
45    /// Return current cursor position, i.e. how many bytes were read from the buffer.
46    #[inline(always)]
47    pub fn get_pos(&self) -> usize {
48        let pos = self.buffer[0];
49        if pos == 0 || pos > BS::U8 {
50            debug_assert!(false);
51            // SAFETY: `pos` never breaks the invariant
52            unsafe {
53                core::hint::unreachable_unchecked();
54            }
55        }
56        pos as usize
57    }
58
59    /// Return size of the internal buffer in bytes.
60    #[inline(always)]
61    pub fn size(&self) -> usize {
62        BS::USIZE
63    }
64
65    /// Return number of remaining bytes in the internal buffer.
66    #[inline(always)]
67    pub fn remaining(&self) -> usize {
68        self.size() - self.get_pos()
69    }
70
71    /// Set cursor position.
72    ///
73    /// # Safety
74    /// `pos` must be smaller than or equal to the buffer block size and be bigger than zero.
75    #[inline(always)]
76    unsafe fn set_pos_unchecked(&mut self, pos: usize) {
77        debug_assert!(pos != 0 && pos <= BS::USIZE);
78        self.buffer[0] = pos as u8;
79    }
80
81    /// Read up to `len` bytes of remaining data in the buffer.
82    ///
83    /// Returns slice with length of `ret_len = min(len, buffer.remaining())` bytes
84    /// and sets the cursor position to `buffer.get_pos() + ret_len`.
85    #[inline(always)]
86    pub fn read_cached(&mut self, len: usize) -> &[u8] {
87        let rem = self.remaining();
88        let new_len = core::cmp::min(rem, len);
89        let pos = self.get_pos();
90
91        // SAFETY: `pos + new_len` is not equal to zero and not bigger than block size
92        unsafe { self.set_pos_unchecked(pos + new_len) };
93        &self.buffer[pos..][..new_len]
94    }
95
96    /// Write new block and consume `read_len` bytes from it.
97    ///
98    /// If `read_len` is equal to zero, immediately returns without calling the closures.
99    /// Otherwise, the method calls `gen_block` to fill the internal buffer,
100    /// passes to `read_fn` slice with first `read_len` bytes of the block,
101    /// and sets the cursor position to `read_len`.
102    ///
103    /// # Panics
104    /// If `read_len` is bigger than block size.
105    #[inline(always)]
106    pub fn write_block(
107        &mut self,
108        read_len: usize,
109        gen_block: impl FnOnce(&mut Array<u8, BS>),
110        read_fn: impl FnOnce(&[u8]),
111    ) {
112        if read_len == 0 {
113            return;
114        }
115        assert!(read_len < BS::USIZE);
116
117        gen_block(&mut self.buffer);
118        read_fn(&self.buffer[..read_len]);
119
120        // We checked that `read_len` satisfies the `set_pos_unchecked` safety contract
121        unsafe { self.set_pos_unchecked(read_len) };
122    }
123
124    /// Reset buffer into exhausted state.
125    pub fn reset(&mut self) {
126        self.buffer[0] = BS::U8;
127    }
128
129    /// Write remaining data inside buffer into `buf`, fill remaining space
130    /// in `buf` with blocks generated by `gen_block`, and save leftover data
131    /// from the last generated block into the buffer for future use.
132    #[inline]
133    pub fn read(&mut self, buf: &mut [u8], mut gen_block: impl FnMut(&mut Array<u8, BS>)) {
134        let head_ks = self.read_cached(buf.len());
135        let (head, buf) = buf.split_at_mut(head_ks.len());
136        let (blocks, tail) = Array::slice_as_chunks_mut(buf);
137
138        head.copy_from_slice(head_ks);
139        for block in blocks {
140            gen_block(block);
141        }
142
143        self.write_block(tail.len(), gen_block, |tail_ks| {
144            tail.copy_from_slice(tail_ks)
145        });
146    }
147
148    /// Serialize buffer into a byte array.
149    #[inline]
150    pub fn serialize(&self) -> Array<u8, BS> {
151        let pos = self.get_pos();
152        let mut res = self.buffer.clone();
153        // zeroize "garbage" data
154        for b in &mut res[1..pos] {
155            *b = 0;
156        }
157        res
158    }
159
160    /// Deserialize buffer from a byte array.
161    #[inline]
162    pub fn deserialize(buffer: &Array<u8, BS>) -> Result<Self, Error> {
163        let pos = usize::from(buffer[0]);
164        if pos == 0 || pos > BS::USIZE || buffer[1..pos].iter().any(|&b| b != 0) {
165            Err(Error)
166        } else {
167            let buffer = buffer.clone();
168            Ok(Self { buffer })
169        }
170    }
171}
172
173#[cfg(feature = "zeroize")]
174impl<BS: ArraySize> Drop for ReadBuffer<BS> {
175    fn drop(&mut self) {
176        use zeroize::Zeroize;
177        self.buffer.zeroize();
178    }
179}
180
181#[cfg(feature = "zeroize")]
182impl<BS: ArraySize> zeroize::ZeroizeOnDrop for ReadBuffer<BS> {}