perf(h1): optimize write buffer when flattening
This commit is contained in:
@@ -168,7 +168,7 @@ where I: AsyncRead + AsyncWrite,
|
|||||||
self.state.busy();
|
self.state.busy();
|
||||||
if msg.expect_continue {
|
if msg.expect_continue {
|
||||||
let cont = b"HTTP/1.1 100 Continue\r\n\r\n";
|
let cont = b"HTTP/1.1 100 Continue\r\n\r\n";
|
||||||
self.io.write_buf_mut().extend_from_slice(cont);
|
self.io.headers_buf().extend_from_slice(cont);
|
||||||
}
|
}
|
||||||
let wants_keep_alive = msg.keep_alive;
|
let wants_keep_alive = msg.keep_alive;
|
||||||
self.state.keep_alive &= wants_keep_alive;
|
self.state.keep_alive &= wants_keep_alive;
|
||||||
@@ -397,7 +397,7 @@ where I: AsyncRead + AsyncWrite,
|
|||||||
|
|
||||||
self.enforce_version(&mut head);
|
self.enforce_version(&mut head);
|
||||||
|
|
||||||
let buf = self.io.write_buf_mut();
|
let buf = self.io.headers_buf();
|
||||||
self.state.writing = match T::encode(Encode {
|
self.state.writing = match T::encode(Encode {
|
||||||
head: &mut head,
|
head: &mut head,
|
||||||
body,
|
body,
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ where
|
|||||||
pub fn set_write_strategy_flatten(&mut self) {
|
pub fn set_write_strategy_flatten(&mut self) {
|
||||||
// this should always be called only at construction time,
|
// this should always be called only at construction time,
|
||||||
// so this assert is here to catch myself
|
// so this assert is here to catch myself
|
||||||
debug_assert!(self.write_buf.buf.bufs.is_empty());
|
debug_assert!(self.write_buf.queue.bufs.is_empty());
|
||||||
self.write_buf.set_strategy(Strategy::Flatten);
|
self.write_buf.set_strategy(Strategy::Flatten);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,14 +94,8 @@ where
|
|||||||
self.read_buf.as_ref()
|
self.read_buf.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO(perf): don't return a `&mut Vec<u8>`, but a wrapper
|
pub fn headers_buf(&mut self) -> &mut Vec<u8> {
|
||||||
//that protects the Vec when growing. Specifically, if this
|
let buf = self.write_buf.headers_mut();
|
||||||
//Vec couldn't be reset, as it's position isn't at the end,
|
|
||||||
//any new reserves will copy the bytes before the position,
|
|
||||||
//which is unnecessary.
|
|
||||||
pub fn write_buf_mut(&mut self) -> &mut Vec<u8> {
|
|
||||||
let buf = self.write_buf.head_mut();
|
|
||||||
buf.maybe_reset();
|
|
||||||
&mut buf.bytes
|
&mut buf.bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,6 +186,10 @@ where
|
|||||||
} else if self.write_buf.remaining() == 0 {
|
} else if self.write_buf.remaining() == 0 {
|
||||||
try_nb!(self.io.flush());
|
try_nb!(self.io.flush());
|
||||||
} else {
|
} else {
|
||||||
|
match self.write_buf.strategy {
|
||||||
|
Strategy::Flatten => return self.flush_flattened(),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
loop {
|
loop {
|
||||||
let n = try_ready!(self.io.write_buf(&mut self.write_buf.auto()));
|
let n = try_ready!(self.io.write_buf(&mut self.write_buf.auto()));
|
||||||
debug!("flushed {} bytes", n);
|
debug!("flushed {} bytes", n);
|
||||||
@@ -206,6 +204,27 @@ where
|
|||||||
}
|
}
|
||||||
Ok(Async::Ready(()))
|
Ok(Async::Ready(()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Specialized version of `flush` when strategy is Flatten.
|
||||||
|
///
|
||||||
|
/// Since all buffered bytes are flattened into the single headers buffer,
|
||||||
|
/// that skips some bookkeeping around using multiple buffers.
|
||||||
|
fn flush_flattened(&mut self) -> Poll<(), io::Error> {
|
||||||
|
loop {
|
||||||
|
let n = try_nb!(self.io.write(self.write_buf.headers.bytes()));
|
||||||
|
debug!("flushed {} bytes", n);
|
||||||
|
self.write_buf.headers.advance(n);
|
||||||
|
if self.write_buf.headers.remaining() == 0 {
|
||||||
|
self.write_buf.headers.reset();
|
||||||
|
break;
|
||||||
|
} else if n == 0 {
|
||||||
|
trace!("write returned zero, but {} bytes remaining", self.write_buf.remaining());
|
||||||
|
return Err(io::ErrorKind::WriteZero.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try_nb!(self.io.flush());
|
||||||
|
Ok(Async::Ready(()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait MemRead {
|
pub trait MemRead {
|
||||||
@@ -249,17 +268,16 @@ impl<T: AsRef<[u8]>> Cursor<T> {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn consume(&mut self, num: usize) {
|
pub fn consume(&mut self, num: usize) {
|
||||||
|
debug_assert!(self.pos + num <= self.bytes.as_ref().len());
|
||||||
self.pos += num;
|
self.pos += num;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Cursor<Vec<u8>> {
|
impl Cursor<Vec<u8>> {
|
||||||
fn maybe_reset(&mut self) {
|
fn reset(&mut self) {
|
||||||
if self.pos != 0 && self.remaining() == 0 {
|
self.pos = 0;
|
||||||
self.pos = 0;
|
unsafe {
|
||||||
unsafe {
|
self.bytes.set_len(0);
|
||||||
self.bytes.set_len(0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -292,16 +310,20 @@ impl<T: AsRef<[u8]>> Buf for Cursor<T> {
|
|||||||
|
|
||||||
// an internal buffer to collect writes before flushes
|
// an internal buffer to collect writes before flushes
|
||||||
struct WriteBuf<B> {
|
struct WriteBuf<B> {
|
||||||
buf: BufDeque<B>,
|
/// Re-usable buffer that holds message headers
|
||||||
|
headers: Cursor<Vec<u8>>,
|
||||||
max_buf_size: usize,
|
max_buf_size: usize,
|
||||||
|
/// Deque of user buffers if strategy is Queue
|
||||||
|
queue: BufDeque<B>,
|
||||||
strategy: Strategy,
|
strategy: Strategy,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<B> WriteBuf<B> {
|
impl<B> WriteBuf<B> {
|
||||||
fn new() -> WriteBuf<B> {
|
fn new() -> WriteBuf<B> {
|
||||||
WriteBuf {
|
WriteBuf {
|
||||||
buf: BufDeque::new(),
|
headers: Cursor::new(Vec::with_capacity(INIT_BUFFER_SIZE)),
|
||||||
max_buf_size: DEFAULT_MAX_BUFFER_SIZE,
|
max_buf_size: DEFAULT_MAX_BUFFER_SIZE,
|
||||||
|
queue: BufDeque::new(),
|
||||||
strategy: Strategy::Auto,
|
strategy: Strategy::Auto,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -322,14 +344,14 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn buffer(&mut self, buf: B) {
|
fn buffer(&mut self, buf: B) {
|
||||||
|
debug_assert!(buf.has_remaining());
|
||||||
match self.strategy {
|
match self.strategy {
|
||||||
Strategy::Flatten => {
|
Strategy::Flatten => {
|
||||||
let head = self.head_mut();
|
let head = self.headers_mut();
|
||||||
head.maybe_reset();
|
|
||||||
head.bytes.put(buf);
|
head.bytes.put(buf);
|
||||||
},
|
},
|
||||||
Strategy::Auto | Strategy::Queue => {
|
Strategy::Auto | Strategy::Queue => {
|
||||||
self.buf.bufs.push_back(VecOrBuf::Buf(buf));
|
self.queue.bufs.push_back(buf);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -340,30 +362,15 @@ where
|
|||||||
self.remaining() < self.max_buf_size
|
self.remaining() < self.max_buf_size
|
||||||
},
|
},
|
||||||
Strategy::Auto | Strategy::Queue => {
|
Strategy::Auto | Strategy::Queue => {
|
||||||
self.buf.bufs.len() < MAX_BUF_LIST_BUFFERS
|
self.queue.bufs.len() < MAX_BUF_LIST_BUFFERS
|
||||||
&& self.remaining() < self.max_buf_size
|
&& self.remaining() < self.max_buf_size
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn head_mut(&mut self) -> &mut Cursor<Vec<u8>> {
|
fn headers_mut(&mut self) -> &mut Cursor<Vec<u8>> {
|
||||||
// this dance is brought to you, The Borrow Checker!
|
debug_assert!(!self.queue.has_remaining());
|
||||||
|
&mut self.headers
|
||||||
let reuse_back = if let Some(&VecOrBuf::Vec(_)) = self.buf.bufs.back() {
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
};
|
|
||||||
|
|
||||||
if !reuse_back {
|
|
||||||
let head_buf = Cursor::new(Vec::with_capacity(INIT_BUFFER_SIZE));
|
|
||||||
self.buf.bufs.push_back(VecOrBuf::Vec(head_buf));
|
|
||||||
}
|
|
||||||
if let Some(&mut VecOrBuf::Vec(ref mut v)) = self.buf.bufs.back_mut() {
|
|
||||||
v
|
|
||||||
} else {
|
|
||||||
unreachable!("head_buf just pushed on back");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -379,22 +386,37 @@ impl<B: Buf> fmt::Debug for WriteBuf<B> {
|
|||||||
impl<B: Buf> Buf for WriteBuf<B> {
|
impl<B: Buf> Buf for WriteBuf<B> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn remaining(&self) -> usize {
|
fn remaining(&self) -> usize {
|
||||||
self.buf.remaining()
|
self.headers.remaining() + self.queue.remaining()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn bytes(&self) -> &[u8] {
|
fn bytes(&self) -> &[u8] {
|
||||||
self.buf.bytes()
|
let headers = self.headers.bytes();
|
||||||
|
if !headers.is_empty() {
|
||||||
|
headers
|
||||||
|
} else {
|
||||||
|
self.queue.bytes()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn advance(&mut self, cnt: usize) {
|
fn advance(&mut self, cnt: usize) {
|
||||||
self.buf.advance(cnt)
|
let hrem = self.headers.remaining();
|
||||||
|
if hrem == cnt {
|
||||||
|
self.headers.reset();
|
||||||
|
} else if hrem > cnt {
|
||||||
|
self.headers.advance(cnt);
|
||||||
|
} else {
|
||||||
|
let qcnt = cnt - hrem;
|
||||||
|
self.headers.reset();
|
||||||
|
self.queue.advance(qcnt);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn bytes_vec<'t>(&'t self, dst: &mut [&'t IoVec]) -> usize {
|
fn bytes_vec<'t>(&'t self, dst: &mut [&'t IoVec]) -> usize {
|
||||||
self.buf.bytes_vec(dst)
|
let n = self.headers.bytes_vec(dst);
|
||||||
|
self.queue.bytes_vec(&mut dst[n..]) + n
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -448,9 +470,7 @@ impl<'a, B: Buf + 'a> Drop for WriteBufAuto<'a, B> {
|
|||||||
} else if self.bytes_called.get() {
|
} else if self.bytes_called.get() {
|
||||||
trace!("detected no usage of vectored write, flattening");
|
trace!("detected no usage of vectored write, flattening");
|
||||||
self.inner.strategy = Strategy::Flatten;
|
self.inner.strategy = Strategy::Flatten;
|
||||||
let mut vec = Vec::new();
|
self.inner.headers.bytes.put(&mut self.inner.queue);
|
||||||
vec.put(&mut self.inner.buf);
|
|
||||||
self.inner.buf.bufs.push_back(VecOrBuf::Vec(Cursor::new(vec)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -464,59 +484,8 @@ enum Strategy {
|
|||||||
Queue,
|
Queue,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum VecOrBuf<B> {
|
|
||||||
Vec(Cursor<Vec<u8>>),
|
|
||||||
Buf(B),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<B: Buf> Buf for VecOrBuf<B> {
|
|
||||||
#[inline]
|
|
||||||
fn remaining(&self) -> usize {
|
|
||||||
match *self {
|
|
||||||
VecOrBuf::Vec(ref v) => v.remaining(),
|
|
||||||
VecOrBuf::Buf(ref b) => b.remaining(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn bytes(&self) -> &[u8] {
|
|
||||||
match *self {
|
|
||||||
VecOrBuf::Vec(ref v) => v.bytes(),
|
|
||||||
VecOrBuf::Buf(ref b) => b.bytes(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn advance(&mut self, cnt: usize) {
|
|
||||||
match *self {
|
|
||||||
VecOrBuf::Vec(ref mut v) => v.advance(cnt),
|
|
||||||
VecOrBuf::Buf(ref mut b) => b.advance(cnt),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn bytes_vec<'t>(&'t self, dst: &mut [&'t IoVec]) -> usize {
|
|
||||||
match *self {
|
|
||||||
VecOrBuf::Vec(ref v) => {
|
|
||||||
if v.has_remaining() {
|
|
||||||
v.bytes_vec(dst)
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
VecOrBuf::Buf(ref b) => {
|
|
||||||
if b.has_remaining() {
|
|
||||||
b.bytes_vec(dst)
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct BufDeque<T> {
|
struct BufDeque<T> {
|
||||||
bufs: VecDeque<VecOrBuf<T>>,
|
bufs: VecDeque<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -539,16 +508,13 @@ impl<T: Buf> Buf for BufDeque<T> {
|
|||||||
#[inline]
|
#[inline]
|
||||||
fn bytes(&self) -> &[u8] {
|
fn bytes(&self) -> &[u8] {
|
||||||
for buf in &self.bufs {
|
for buf in &self.bufs {
|
||||||
if buf.has_remaining() {
|
return buf.bytes();
|
||||||
return buf.bytes();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
&[]
|
&[]
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn advance(&mut self, mut cnt: usize) {
|
fn advance(&mut self, mut cnt: usize) {
|
||||||
let mut maybe_reclaim = None;
|
|
||||||
while cnt > 0 {
|
while cnt > 0 {
|
||||||
{
|
{
|
||||||
let front = &mut self.bufs[0];
|
let front = &mut self.bufs[0];
|
||||||
@@ -561,12 +527,7 @@ impl<T: Buf> Buf for BufDeque<T> {
|
|||||||
cnt -= rem;
|
cnt -= rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
maybe_reclaim = self.bufs.pop_front();
|
self.bufs.pop_front();
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(VecOrBuf::Vec(v)) = maybe_reclaim {
|
|
||||||
trace!("reclaiming write buf Vec");
|
|
||||||
self.bufs.push_back(VecOrBuf::Vec(v));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -630,15 +591,12 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn write_buf_skips_empty_bufs() {
|
#[should_panic]
|
||||||
let mut mock = AsyncIo::new_buf(vec![], 1024);
|
fn write_buf_requires_non_empty_bufs() {
|
||||||
mock.max_read_vecs(0); // disable vectored IO
|
let mock = AsyncIo::new_buf(vec![], 1024);
|
||||||
let mut buffered = Buffered::<_, Cursor<Vec<u8>>>::new(mock);
|
let mut buffered = Buffered::<_, Cursor<Vec<u8>>>::new(mock);
|
||||||
|
|
||||||
buffered.buffer(Cursor::new(Vec::new()));
|
buffered.buffer(Cursor::new(Vec::new()));
|
||||||
buffered.buffer(Cursor::new(b"hello".to_vec()));
|
|
||||||
buffered.flush().unwrap();
|
|
||||||
assert_eq!(buffered.io, b"hello");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -649,42 +607,17 @@ mod tests {
|
|||||||
let mock = AsyncIo::new_buf(vec![], 1024);
|
let mock = AsyncIo::new_buf(vec![], 1024);
|
||||||
let mut buffered = Buffered::<_, Cursor<Vec<u8>>>::new(mock);
|
let mut buffered = Buffered::<_, Cursor<Vec<u8>>>::new(mock);
|
||||||
|
|
||||||
buffered.write_buf_mut().extend(b"hello ");
|
|
||||||
|
buffered.headers_buf().extend(b"hello ");
|
||||||
buffered.buffer(Cursor::new(b"world, ".to_vec()));
|
buffered.buffer(Cursor::new(b"world, ".to_vec()));
|
||||||
buffered.write_buf_mut().extend(b"it's ");
|
buffered.buffer(Cursor::new(b"it's ".to_vec()));
|
||||||
buffered.buffer(Cursor::new(b"hyper!".to_vec()));
|
buffered.buffer(Cursor::new(b"hyper!".to_vec()));
|
||||||
|
assert_eq!(buffered.write_buf.queue.bufs.len(), 3);
|
||||||
buffered.flush().unwrap();
|
buffered.flush().unwrap();
|
||||||
|
|
||||||
assert_eq!(buffered.io, b"hello world, it's hyper!");
|
assert_eq!(buffered.io, b"hello world, it's hyper!");
|
||||||
assert_eq!(buffered.io.num_writes(), 1);
|
assert_eq!(buffered.io.num_writes(), 1);
|
||||||
}
|
assert_eq!(buffered.write_buf.queue.bufs.len(), 0);
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn write_buf_reclaim_vec() {
|
|
||||||
extern crate pretty_env_logger;
|
|
||||||
let _ = pretty_env_logger::try_init();
|
|
||||||
|
|
||||||
let mock = AsyncIo::new_buf(vec![], 1024);
|
|
||||||
let mut buffered = Buffered::<_, Cursor<Vec<u8>>>::new(mock);
|
|
||||||
|
|
||||||
buffered.write_buf_mut().extend(b"hello ");
|
|
||||||
assert_eq!(buffered.write_buf.buf.bufs.len(), 1);
|
|
||||||
buffered.write_buf_mut().extend(b"world, ");
|
|
||||||
assert_eq!(buffered.write_buf.buf.bufs.len(), 1);
|
|
||||||
|
|
||||||
// after flushing, reclaim the Vec
|
|
||||||
buffered.flush().unwrap();
|
|
||||||
assert_eq!(buffered.write_buf.remaining(), 0);
|
|
||||||
assert_eq!(buffered.write_buf.buf.bufs.len(), 1);
|
|
||||||
|
|
||||||
// add a user buf in the way
|
|
||||||
buffered.buffer(Cursor::new(b"it's ".to_vec()));
|
|
||||||
// and then add more hyper bytes
|
|
||||||
buffered.write_buf_mut().extend(b"hyper!");
|
|
||||||
buffered.flush().unwrap();
|
|
||||||
assert_eq!(buffered.write_buf.buf.bufs.len(), 1);
|
|
||||||
|
|
||||||
assert_eq!(buffered.io, b"hello world, it's hyper!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -696,17 +629,16 @@ mod tests {
|
|||||||
let mut buffered = Buffered::<_, Cursor<Vec<u8>>>::new(mock);
|
let mut buffered = Buffered::<_, Cursor<Vec<u8>>>::new(mock);
|
||||||
buffered.write_buf.set_strategy(Strategy::Flatten);
|
buffered.write_buf.set_strategy(Strategy::Flatten);
|
||||||
|
|
||||||
buffered.write_buf_mut().extend(b"hello ");
|
buffered.headers_buf().extend(b"hello ");
|
||||||
buffered.buffer(Cursor::new(b"world, ".to_vec()));
|
buffered.buffer(Cursor::new(b"world, ".to_vec()));
|
||||||
buffered.write_buf_mut().extend(b"it's ");
|
buffered.buffer(Cursor::new(b"it's ".to_vec()));
|
||||||
buffered.buffer(Cursor::new(b"hyper!".to_vec()));
|
buffered.buffer(Cursor::new(b"hyper!".to_vec()));
|
||||||
assert_eq!(buffered.write_buf.buf.bufs.len(), 1);
|
assert_eq!(buffered.write_buf.queue.bufs.len(), 0);
|
||||||
|
|
||||||
buffered.flush().unwrap();
|
buffered.flush().unwrap();
|
||||||
|
|
||||||
assert_eq!(buffered.io, b"hello world, it's hyper!");
|
assert_eq!(buffered.io, b"hello world, it's hyper!");
|
||||||
assert_eq!(buffered.io.num_writes(), 1);
|
assert_eq!(buffered.io.num_writes(), 1);
|
||||||
assert_eq!(buffered.write_buf.buf.bufs.len(), 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -721,19 +653,20 @@ mod tests {
|
|||||||
// we have 4 buffers, but hope to detect that vectored IO isn't
|
// we have 4 buffers, but hope to detect that vectored IO isn't
|
||||||
// being used, and switch to flattening automatically,
|
// being used, and switch to flattening automatically,
|
||||||
// resulting in only 2 writes
|
// resulting in only 2 writes
|
||||||
buffered.write_buf_mut().extend(b"hello ");
|
buffered.headers_buf().extend(b"hello ");
|
||||||
buffered.buffer(Cursor::new(b"world, ".to_vec()));
|
buffered.buffer(Cursor::new(b"world, ".to_vec()));
|
||||||
buffered.write_buf_mut().extend(b"it's hyper!");
|
buffered.buffer(Cursor::new(b"it's ".to_vec()));
|
||||||
//buffered.buffer(Cursor::new(b"hyper!".to_vec()));
|
buffered.buffer(Cursor::new(b"hyper!".to_vec()));
|
||||||
|
assert_eq!(buffered.write_buf.queue.bufs.len(), 3);
|
||||||
buffered.flush().unwrap();
|
buffered.flush().unwrap();
|
||||||
|
|
||||||
assert_eq!(buffered.io, b"hello world, it's hyper!");
|
assert_eq!(buffered.io, b"hello world, it's hyper!");
|
||||||
assert_eq!(buffered.io.num_writes(), 2);
|
assert_eq!(buffered.io.num_writes(), 2);
|
||||||
assert_eq!(buffered.write_buf.buf.bufs.len(), 1);
|
assert_eq!(buffered.write_buf.queue.bufs.len(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn write_buf_queue_does_not_auto() {
|
fn write_buf_queue_disable_auto() {
|
||||||
extern crate pretty_env_logger;
|
extern crate pretty_env_logger;
|
||||||
let _ = pretty_env_logger::try_init();
|
let _ = pretty_env_logger::try_init();
|
||||||
|
|
||||||
@@ -744,13 +677,16 @@ mod tests {
|
|||||||
|
|
||||||
// we have 4 buffers, and vec IO disabled, but explicitly said
|
// we have 4 buffers, and vec IO disabled, but explicitly said
|
||||||
// don't try to auto detect (via setting strategy above)
|
// don't try to auto detect (via setting strategy above)
|
||||||
buffered.write_buf_mut().extend(b"hello ");
|
|
||||||
|
buffered.headers_buf().extend(b"hello ");
|
||||||
buffered.buffer(Cursor::new(b"world, ".to_vec()));
|
buffered.buffer(Cursor::new(b"world, ".to_vec()));
|
||||||
buffered.write_buf_mut().extend(b"it's ");
|
buffered.buffer(Cursor::new(b"it's ".to_vec()));
|
||||||
buffered.buffer(Cursor::new(b"hyper!".to_vec()));
|
buffered.buffer(Cursor::new(b"hyper!".to_vec()));
|
||||||
|
assert_eq!(buffered.write_buf.queue.bufs.len(), 3);
|
||||||
buffered.flush().unwrap();
|
buffered.flush().unwrap();
|
||||||
|
|
||||||
assert_eq!(buffered.io, b"hello world, it's hyper!");
|
assert_eq!(buffered.io, b"hello world, it's hyper!");
|
||||||
assert_eq!(buffered.io.num_writes(), 4);
|
assert_eq!(buffered.io.num_writes(), 4);
|
||||||
|
assert_eq!(buffered.write_buf.queue.bufs.len(), 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user