Skip to main content

block_buffer/
read.rs

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