Skip to main content

rustls/
vecbuf.rs

1use alloc::collections::VecDeque;
2use alloc::vec::Vec;
3use core::{cmp, mem};
4#[cfg(feature = "std")]
5use std::io;
6#[cfg(feature = "std")]
7use std::io::Read;
8
9#[cfg(feature = "std")]
10use crate::msgs::message::OutboundChunks;
11
12/// This is a byte buffer that is built from a deque of byte vectors.
13///
14/// This avoids extra copies when appending a new byte vector,
15/// at the expense of more complexity when reading out.
16pub(crate) struct ChunkVecBuffer {
17    /// How many bytes have been consumed in the first chunk.
18    ///
19    /// Invariant: zero if `chunks.is_empty()`
20    /// Invariant: 0 <= `prefix_used` < `chunks[0].len()`
21    prefix_used: usize,
22
23    chunks: VecDeque<Vec<u8>>,
24
25    /// The total upper limit (in bytes) of this object.
26    limit: Option<usize>,
27}
28
29impl ChunkVecBuffer {
30    pub(crate) fn new(limit: Option<usize>) -> Self {
31        Self {
32            prefix_used: 0,
33            chunks: VecDeque::new(),
34            limit,
35        }
36    }
37
38    /// Sets the upper limit on how many bytes this
39    /// object can store.
40    ///
41    /// Setting a lower limit than the currently stored
42    /// data is not an error.
43    ///
44    /// A [`None`] limit is interpreted as no limit.
45    pub(crate) fn set_limit(&mut self, new_limit: Option<usize>) {
46        self.limit = new_limit;
47    }
48
49    /// If we're empty
50    pub(crate) fn is_empty(&self) -> bool {
51        self.chunks.is_empty()
52    }
53
54    /// How many bytes we're storing
55    pub(crate) fn len(&self) -> usize {
56        self.chunks
57            .iter()
58            .fold(0usize, |acc, chunk| acc + chunk.len())
59            - self.prefix_used
60    }
61
62    /// For a proposed append of `len` bytes, how many
63    /// bytes should we actually append to adhere to the
64    /// currently set `limit`?
65    pub(crate) fn apply_limit(&self, len: usize) -> usize {
66        if let Some(limit) = self.limit {
67            let space = limit.saturating_sub(self.len());
68            cmp::min(len, space)
69        } else {
70            len
71        }
72    }
73
74    /// Take and append the given `bytes`.
75    pub(crate) fn append(&mut self, bytes: Vec<u8>) -> usize {
76        let len = bytes.len();
77
78        if !bytes.is_empty() {
79            if self.chunks.is_empty() {
80                debug_assert_eq!(self.prefix_used, 0);
81            }
82
83            self.chunks.push_back(bytes);
84        }
85
86        len
87    }
88
89    /// Take one of the chunks from this object.
90    ///
91    /// This function returns `None` if the object `is_empty`.
92    pub(crate) fn pop(&mut self) -> Option<Vec<u8>> {
93        let mut first = self.chunks.pop_front();
94
95        if let Some(first) = &mut first {
96            // slice off `prefix_used` if needed (uncommon)
97            let prefix = mem::take(&mut self.prefix_used);
98            first.drain(0..prefix);
99        }
100
101        first
102    }
103
104    #[cfg(read_buf)]
105    /// Read data out of this object, writing it into `cursor`.
106    pub(crate) fn read_buf(
107        &mut self,
108        mut cursor: core::io::BorrowedCursor<'_, u8>,
109    ) -> io::Result<()> {
110        while !self.is_empty() && cursor.capacity() > 0 {
111            let chunk = &self.chunks[0][self.prefix_used..];
112            let used = cmp::min(chunk.len(), cursor.capacity());
113            cursor.append(&chunk[..used]);
114            self.consume(used);
115        }
116
117        Ok(())
118    }
119
120    /// Inspect the first chunk from this object.
121    pub(crate) fn peek(&self) -> Option<&[u8]> {
122        self.chunks
123            .front()
124            .map(|ch| ch.as_slice())
125    }
126}
127
128#[cfg(feature = "std")]
129impl ChunkVecBuffer {
130    pub(crate) fn is_full(&self) -> bool {
131        self.limit
132            .map(|limit| self.len() > limit)
133            .unwrap_or_default()
134    }
135
136    /// Append a copy of `bytes`, perhaps a prefix if
137    /// we're near the limit.
138    pub(crate) fn append_limited_copy(&mut self, payload: OutboundChunks<'_>) -> usize {
139        let take = self.apply_limit(payload.len());
140        self.append(payload.split_at(take).0.to_vec());
141        take
142    }
143
144    /// Read data out of this object, writing it into `buf`
145    /// and returning how many bytes were written there.
146    pub(crate) fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
147        let mut offs = 0;
148
149        while offs < buf.len() && !self.is_empty() {
150            let used = (&self.chunks[0][self.prefix_used..]).read(&mut buf[offs..])?;
151
152            self.consume(used);
153            offs += used;
154        }
155
156        Ok(offs)
157    }
158
159    pub(crate) fn consume_first_chunk(&mut self, used: usize) {
160        // this backs (infallible) `BufRead::consume`, where `used` is
161        // user-supplied.
162        assert!(
163            used <= self
164                .chunk()
165                .map(|ch| ch.len())
166                .unwrap_or_default(),
167            "illegal `BufRead::consume` usage",
168        );
169        self.consume(used);
170    }
171
172    fn consume(&mut self, used: usize) {
173        // first, mark the rightmost extent of the used buffer
174        self.prefix_used += used;
175
176        // then reduce `prefix_used` by discarding wholly-covered
177        // buffers
178        while let Some(buf) = self.chunks.front() {
179            if self.prefix_used < buf.len() {
180                return;
181            } else {
182                self.prefix_used -= buf.len();
183                self.chunks.pop_front();
184            }
185        }
186
187        debug_assert_eq!(
188            self.prefix_used, 0,
189            "attempted to `ChunkVecBuffer::consume` more than available"
190        );
191    }
192
193    /// Read data out of this object, passing it `wr`
194    pub(crate) fn write_to(&mut self, wr: &mut dyn io::Write) -> io::Result<usize> {
195        if self.is_empty() {
196            return Ok(0);
197        }
198
199        let mut prefix = self.prefix_used;
200        let mut bufs = [io::IoSlice::new(&[]); 64];
201        for (iov, chunk) in bufs.iter_mut().zip(self.chunks.iter()) {
202            *iov = io::IoSlice::new(&chunk[prefix..]);
203            prefix = 0;
204        }
205        let len = cmp::min(bufs.len(), self.chunks.len());
206        let bufs = &bufs[..len];
207        let used = wr.write_vectored(bufs)?;
208        let available_bytes = bufs.iter().map(|ch| ch.len()).sum();
209
210        if used > available_bytes {
211            // This is really unrecoverable, since the amount of data written
212            // is now unknown.  Consume all the potentially-written data in
213            // case the caller ignores the error.
214            // See <https://github.com/rustls/rustls/issues/2316> for background.
215            self.consume(available_bytes);
216            return Err(io::Error::new(
217                io::ErrorKind::Other,
218                std::format!("illegal write_vectored return value ({used} > {available_bytes})"),
219            ));
220        }
221        self.consume(used);
222        Ok(used)
223    }
224
225    /// Returns the first contiguous chunk of data, or None if empty.
226    pub(crate) fn chunk(&self) -> Option<&[u8]> {
227        self.chunks
228            .front()
229            .map(|chunk| &chunk[self.prefix_used..])
230    }
231}
232
233#[cfg(all(test, feature = "std"))]
234mod tests {
235    use alloc::vec;
236    use alloc::vec::Vec;
237
238    use super::ChunkVecBuffer;
239
240    #[test]
241    fn short_append_copy_with_limit() {
242        let mut cvb = ChunkVecBuffer::new(Some(12));
243        assert_eq!(cvb.append_limited_copy(b"hello"[..].into()), 5);
244        assert_eq!(cvb.append_limited_copy(b"world"[..].into()), 5);
245        assert_eq!(cvb.append_limited_copy(b"hello"[..].into()), 2);
246        assert_eq!(cvb.append_limited_copy(b"world"[..].into()), 0);
247
248        let mut buf = [0u8; 12];
249        assert_eq!(cvb.read(&mut buf).unwrap(), 12);
250        assert_eq!(buf.to_vec(), b"helloworldhe".to_vec());
251    }
252
253    #[test]
254    fn read_byte_by_byte() {
255        let mut cvb = ChunkVecBuffer::new(None);
256        cvb.append(b"test fixture data".to_vec());
257        assert!(!cvb.is_empty());
258        for expect in b"test fixture data" {
259            let mut byte = [0];
260            assert_eq!(cvb.read(&mut byte).unwrap(), 1);
261            assert_eq!(byte[0], *expect);
262        }
263
264        assert_eq!(cvb.read(&mut [0]).unwrap(), 0);
265    }
266
267    #[test]
268    fn every_possible_chunk_interleaving() {
269        let input = (0..=0xffu8)
270            .cycle()
271            .take(4096)
272            .collect::<Vec<u8>>();
273
274        for input_chunk_len in 1..64usize {
275            for output_chunk_len in 1..65usize {
276                std::println!("check input={input_chunk_len} output={output_chunk_len}");
277                let mut cvb = ChunkVecBuffer::new(None);
278                for chunk in input.chunks(input_chunk_len) {
279                    cvb.append(chunk.to_vec());
280                }
281
282                assert_eq!(cvb.len(), input.len());
283                let mut buf = vec![0u8; output_chunk_len];
284
285                for expect in input.chunks(output_chunk_len) {
286                    assert_eq!(expect.len(), cvb.read(&mut buf).unwrap());
287                    assert_eq!(expect, &buf[..expect.len()]);
288                }
289
290                assert_eq!(cvb.read(&mut [0]).unwrap(), 0);
291            }
292        }
293    }
294
295    #[cfg(read_buf)]
296    #[test]
297    fn read_buf() {
298        use core::io::BorrowedBuf;
299        use core::mem::MaybeUninit;
300
301        {
302            let mut cvb = ChunkVecBuffer::new(None);
303            cvb.append(b"test ".to_vec());
304            cvb.append(b"fixture ".to_vec());
305            cvb.append(b"data".to_vec());
306
307            let mut buf = [MaybeUninit::<u8>::uninit(); 8];
308            let mut buf: BorrowedBuf<'_, u8> = buf.as_mut_slice().into();
309            cvb.read_buf(buf.unfilled()).unwrap();
310            assert_eq!(buf.filled(), b"test fix");
311            buf.clear();
312            cvb.read_buf(buf.unfilled()).unwrap();
313            assert_eq!(buf.filled(), b"ture dat");
314            buf.clear();
315            cvb.read_buf(buf.unfilled()).unwrap();
316            assert_eq!(buf.filled(), b"a");
317        }
318
319        {
320            let mut cvb = ChunkVecBuffer::new(None);
321            cvb.append(b"short message".to_vec());
322
323            let mut buf = [MaybeUninit::<u8>::uninit(); 1024];
324            let mut buf: BorrowedBuf<'_, u8> = buf.as_mut_slice().into();
325            cvb.read_buf(buf.unfilled()).unwrap();
326            assert_eq!(buf.filled(), b"short message");
327        }
328    }
329}
330
331#[cfg(bench)]
332mod benchmarks {
333    use alloc::vec;
334
335    use super::ChunkVecBuffer;
336
337    #[bench]
338    fn read_one_byte_from_large_message(b: &mut test::Bencher) {
339        b.iter(|| {
340            let mut cvb = ChunkVecBuffer::new(None);
341            cvb.append(vec![0u8; 16_384]);
342            assert_eq!(1, cvb.read(&mut [0u8]).unwrap());
343        });
344    }
345
346    #[bench]
347    fn read_all_individual_from_large_message(b: &mut test::Bencher) {
348        b.iter(|| {
349            let mut cvb = ChunkVecBuffer::new(None);
350            cvb.append(vec![0u8; 16_384]);
351            loop {
352                if let Ok(0) = cvb.read(&mut [0u8]) {
353                    break;
354                }
355            }
356        });
357    }
358
359    #[bench]
360    fn read_half_bytes_from_large_message(b: &mut test::Bencher) {
361        b.iter(|| {
362            let mut cvb = ChunkVecBuffer::new(None);
363            cvb.append(vec![0u8; 16_384]);
364            assert_eq!(8192, cvb.read(&mut [0u8; 8192]).unwrap());
365            assert_eq!(8192, cvb.read(&mut [0u8; 8192]).unwrap());
366        });
367    }
368
369    #[bench]
370    fn read_entire_large_message(b: &mut test::Bencher) {
371        b.iter(|| {
372            let mut cvb = ChunkVecBuffer::new(None);
373            cvb.append(vec![0u8; 16_384]);
374            assert_eq!(16_384, cvb.read(&mut [0u8; 16_384]).unwrap());
375        });
376    }
377}