perf(h1): remove book keeping on final body writes

This commit is contained in:
Sean McArthur
2018-05-07 12:48:21 -07:00
parent 8f0e01f853
commit bf4fe7c515
4 changed files with 98 additions and 53 deletions

34
src/common/buf.rs Normal file
View File

@@ -0,0 +1,34 @@
use bytes::Buf;
use iovec::IoVec;
/// A `Buf` wrapping a static byte slice.
#[derive(Debug)]
pub(crate) struct StaticBuf(pub(crate) &'static [u8]);
impl Buf for StaticBuf {
#[inline]
fn remaining(&self) -> usize {
self.0.len()
}
#[inline]
fn bytes(&self) -> &[u8] {
self.0
}
#[inline]
fn advance(&mut self, cnt: usize) {
self.0 = &self.0[cnt..];
}
#[inline]
fn bytes_vec<'t>(&'t self, dst: &mut [&'t IoVec]) -> usize {
if dst.is_empty() {
return 0;
} else {
dst[0] = self.0.into();
return 1;
}
}
}

View File

@@ -1,5 +1,7 @@
mod buf;
mod exec; mod exec;
mod never; mod never;
pub(crate) use self::buf::StaticBuf;
pub(crate) use self::exec::Exec; pub(crate) use self::exec::Exec;
pub use self::never::Never; pub use self::never::Never;

View File

@@ -482,20 +482,13 @@ where I: AsyncRead + AsyncWrite,
debug_assert!(chunk.remaining() != 0); debug_assert!(chunk.remaining() != 0);
let state = match self.state.writing { let state = match self.state.writing {
Writing::Body(ref mut encoder) => { Writing::Body(ref encoder) => {
self.io.buffer(encoder.encode(chunk)); let (encoded, can_keep_alive) = encoder.encode_and_end(chunk);
match encoder.end() { self.io.buffer(encoded);
Ok(end) => { if can_keep_alive {
if let Some(end) = end { Writing::KeepAlive
self.io.buffer(end); } else {
} Writing::Closed
if encoder.is_last() {
Writing::Closed
} else {
Writing::KeepAlive
}
},
Err(_not_eof) => Writing::Closed,
} }
}, },
_ => unreachable!("write_body invalid state: {:?}", self.state.writing), _ => unreachable!("write_body invalid state: {:?}", self.state.writing),

View File

@@ -1,10 +1,11 @@
//use std::cmp;
use std::fmt; use std::fmt;
use bytes::{Buf, IntoBuf}; use bytes::{Buf, IntoBuf};
use bytes::buf::{Chain, Take}; use bytes::buf::{Chain, Take};
use iovec::IoVec; use iovec::IoVec;
use common::StaticBuf;
/// Encoders to handle different Transfer-Encodings. /// Encoders to handle different Transfer-Encodings.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Encoder { pub struct Encoder {
@@ -39,8 +40,8 @@ enum Kind {
enum BufKind<B> { enum BufKind<B> {
Exact(B), Exact(B),
Limited(Take<B>), Limited(Take<B>),
Chunked(Chain<ChunkSize, Chain<B, CrLf>>), Chunked(Chain<Chain<ChunkSize, B>, StaticBuf>),
ChunkedEnd(CrLf), ChunkedEnd(StaticBuf),
} }
impl Encoder { impl Encoder {
@@ -81,7 +82,7 @@ impl Encoder {
match self.kind { match self.kind {
Kind::Length(0) => Ok(None), Kind::Length(0) => Ok(None),
Kind::Chunked => Ok(Some(EncodedBuf { Kind::Chunked => Ok(Some(EncodedBuf {
kind: BufKind::ChunkedEnd(CrLf(b"0\r\n\r\n")), kind: BufKind::ChunkedEnd(StaticBuf(b"0\r\n\r\n")),
})), })),
_ => Err(NotEof), _ => Err(NotEof),
} }
@@ -93,13 +94,15 @@ impl Encoder {
{ {
let msg = msg.into_buf(); let msg = msg.into_buf();
let len = msg.remaining(); let len = msg.remaining();
assert!(len > 0, "encode() called with empty buf"); debug_assert!(len > 0, "encode() called with empty buf");
let buf = match self.kind { let kind = match self.kind {
Kind::Chunked => { Kind::Chunked => {
trace!("encoding chunked {}B", len); trace!("encoding chunked {}B", len);
BufKind::Chunked(ChunkSize::new(len) let buf = ChunkSize::new(len)
.chain(msg.chain(CrLf(b"\r\n")))) .chain(msg)
.chain(StaticBuf(b"\r\n"));
BufKind::Chunked(buf)
}, },
Kind::Length(ref mut remaining) => { Kind::Length(ref mut remaining) => {
trace!("sized write, len = {}", len); trace!("sized write, len = {}", len);
@@ -118,9 +121,52 @@ impl Encoder {
} }
}; };
EncodedBuf { EncodedBuf {
kind: buf, kind,
} }
} }
pub fn encode_and_end<B>(&self, msg: B) -> (EncodedBuf<B::Buf>, bool)
where
B: IntoBuf,
{
let msg = msg.into_buf();
let len = msg.remaining();
debug_assert!(len > 0, "encode() called with empty buf");
let (kind, eof) = match self.kind {
Kind::Chunked => {
trace!("encoding chunked {}B", len);
let buf = ChunkSize::new(len)
.chain(msg)
.chain(StaticBuf(b"\r\n0\r\n\r\n"));
(BufKind::Chunked(buf), !self.is_last)
},
Kind::Length(remaining) => {
use std::cmp::Ordering;
trace!("sized write, len = {}", len);
match (len as u64).cmp(&remaining) {
Ordering::Greater => {
(BufKind::Limited(msg.take(remaining as usize)), !self.is_last)
},
Ordering::Equal => {
(BufKind::Exact(msg), !self.is_last)
},
Ordering::Less => {
(BufKind::Exact(msg), false)
}
}
},
Kind::CloseDelimited => {
trace!("close delimited write {}B", len);
(BufKind::Exact(msg), false)
}
};
(EncodedBuf {
kind,
}, eof)
}
} }
impl<B> Buf for EncodedBuf<B> impl<B> Buf for EncodedBuf<B>
@@ -236,36 +282,6 @@ impl fmt::Write for ChunkSize {
} }
} }
#[derive(Debug)]
struct CrLf(&'static [u8]);
impl Buf for CrLf {
#[inline]
fn remaining(&self) -> usize {
self.0.len()
}
#[inline]
fn bytes(&self) -> &[u8] {
self.0
}
#[inline]
fn advance(&mut self, cnt: usize) {
self.0 = &self.0[cnt..];
}
#[inline]
fn bytes_vec<'t>(&'t self, dst: &mut [&'t IoVec]) -> usize {
if dst.is_empty() {
return 0;
} else {
dst[0] = self.0.into();
return 1;
}
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use bytes::{BufMut}; use bytes::{BufMut};