hyper/common/
buf.rs

1use std::collections::VecDeque;
2use std::io::IoSlice;
3
4use bytes::{Buf, BufMut, Bytes, BytesMut};
5
6pub(crate) struct BufList<T> {
7    bufs: VecDeque<T>,
8}
9
10impl<T: Buf> BufList<T> {
11    pub(crate) fn new() -> BufList<T> {
12        BufList {
13            bufs: VecDeque::new(),
14        }
15    }
16
17    #[inline]
18    pub(crate) fn push(&mut self, buf: T) {
19        debug_assert!(buf.has_remaining());
20        self.bufs.push_back(buf);
21    }
22
23    #[inline]
24    #[cfg(feature = "http1")]
25    pub(crate) fn bufs_cnt(&self) -> usize {
26        self.bufs.len()
27    }
28}
29
30impl<T: Buf> Buf for BufList<T> {
31    #[inline]
32    fn remaining(&self) -> usize {
33        self.bufs.iter().map(|buf| buf.remaining()).sum()
34    }
35
36    #[inline]
37    fn chunk(&self) -> &[u8] {
38        self.bufs.front().map(Buf::chunk).unwrap_or_default()
39    }
40
41    #[inline]
42    fn advance(&mut self, mut cnt: usize) {
43        while cnt > 0 {
44            {
45                let front = &mut self.bufs[0];
46                let rem = front.remaining();
47                if rem > cnt {
48                    front.advance(cnt);
49                    return;
50                } else {
51                    front.advance(rem);
52                    cnt -= rem;
53                }
54            }
55            self.bufs.pop_front();
56        }
57    }
58
59    #[inline]
60    fn chunks_vectored<'t>(&'t self, dst: &mut [IoSlice<'t>]) -> usize {
61        if dst.is_empty() {
62            return 0;
63        }
64        let mut vecs = 0;
65        for buf in &self.bufs {
66            vecs += buf.chunks_vectored(&mut dst[vecs..]);
67            if vecs == dst.len() {
68                break;
69            }
70        }
71        vecs
72    }
73
74    #[inline]
75    fn copy_to_bytes(&mut self, len: usize) -> Bytes {
76        // Our inner buffer may have an optimized version of copy_to_bytes, and if the whole
77        // request can be fulfilled by the front buffer, we can take advantage.
78        match self.bufs.front_mut() {
79            Some(front) if front.remaining() == len => {
80                let b = front.copy_to_bytes(len);
81                self.bufs.pop_front();
82                b
83            }
84            Some(front) if front.remaining() > len => front.copy_to_bytes(len),
85            _ => {
86                assert!(len <= self.remaining(), "`len` greater than remaining");
87                let mut bm = BytesMut::with_capacity(len);
88                bm.put(self.take(len));
89                bm.freeze()
90            }
91        }
92    }
93}
94
95#[cfg(test)]
96mod tests {
97    use std::ptr;
98
99    use super::*;
100
101    fn hello_world_buf() -> BufList<Bytes> {
102        BufList {
103            bufs: vec![Bytes::from("Hello"), Bytes::from(" "), Bytes::from("World")].into(),
104        }
105    }
106
107    #[test]
108    fn to_bytes_shorter() {
109        let mut bufs = hello_world_buf();
110        let old_ptr = bufs.chunk().as_ptr();
111        let start = bufs.copy_to_bytes(4);
112        assert_eq!(start, "Hell");
113        assert!(ptr::eq(old_ptr, start.as_ptr()));
114        assert_eq!(bufs.chunk(), b"o");
115        assert!(ptr::eq(old_ptr.wrapping_add(4), bufs.chunk().as_ptr()));
116        assert_eq!(bufs.remaining(), 7);
117    }
118
119    #[test]
120    fn to_bytes_eq() {
121        let mut bufs = hello_world_buf();
122        let old_ptr = bufs.chunk().as_ptr();
123        let start = bufs.copy_to_bytes(5);
124        assert_eq!(start, "Hello");
125        assert!(ptr::eq(old_ptr, start.as_ptr()));
126        assert_eq!(bufs.chunk(), b" ");
127        assert_eq!(bufs.remaining(), 6);
128    }
129
130    #[test]
131    fn to_bytes_longer() {
132        let mut bufs = hello_world_buf();
133        let start = bufs.copy_to_bytes(7);
134        assert_eq!(start, "Hello W");
135        assert_eq!(bufs.remaining(), 4);
136    }
137
138    #[test]
139    fn one_long_buf_to_bytes() {
140        let mut buf = BufList::new();
141        buf.push(b"Hello World" as &[_]);
142        assert_eq!(buf.copy_to_bytes(5), "Hello");
143        assert_eq!(buf.chunk(), b" World");
144    }
145
146    #[test]
147    #[should_panic(expected = "`len` greater than remaining")]
148    fn buf_to_bytes_too_many() {
149        hello_world_buf().copy_to_bytes(42);
150    }
151}