Skip to content

Commit 4d2125c

Browse files
authored
perf(body): specialize BufList::copy_to_bytes (#2413)
Some implementations of the Buf trait have an optimized version (for example Bytes) of copy_to_bytes, opportunistically use that one.
1 parent 5e8238c commit 4d2125c

File tree

1 file changed

+79
-1
lines changed

1 file changed

+79
-1
lines changed

src/common/buf.rs

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use std::collections::VecDeque;
22
use std::io::IoSlice;
33

4-
use bytes::Buf;
4+
use bytes::{Buf, BufMut, Bytes, BytesMut};
55

66
pub(crate) struct BufList<T> {
77
bufs: VecDeque<T>,
@@ -70,4 +70,82 @@ impl<T: Buf> Buf for BufList<T> {
7070
}
7171
vecs
7272
}
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)]
96+
mod 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+
}
73151
}

0 commit comments

Comments
 (0)