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