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 never;
pub(crate) use self::buf::StaticBuf;
pub(crate) use self::exec::Exec;
pub use self::never::Never;

View File

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

View File

@@ -1,10 +1,11 @@
//use std::cmp;
use std::fmt;
use bytes::{Buf, IntoBuf};
use bytes::buf::{Chain, Take};
use iovec::IoVec;
use common::StaticBuf;
/// Encoders to handle different Transfer-Encodings.
#[derive(Debug, Clone)]
pub struct Encoder {
@@ -39,8 +40,8 @@ enum Kind {
enum BufKind<B> {
Exact(B),
Limited(Take<B>),
Chunked(Chain<ChunkSize, Chain<B, CrLf>>),
ChunkedEnd(CrLf),
Chunked(Chain<Chain<ChunkSize, B>, StaticBuf>),
ChunkedEnd(StaticBuf),
}
impl Encoder {
@@ -81,7 +82,7 @@ impl Encoder {
match self.kind {
Kind::Length(0) => Ok(None),
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),
}
@@ -93,13 +94,15 @@ impl Encoder {
{
let msg = msg.into_buf();
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 => {
trace!("encoding chunked {}B", len);
BufKind::Chunked(ChunkSize::new(len)
.chain(msg.chain(CrLf(b"\r\n"))))
let buf = ChunkSize::new(len)
.chain(msg)
.chain(StaticBuf(b"\r\n"));
BufKind::Chunked(buf)
},
Kind::Length(ref mut remaining) => {
trace!("sized write, len = {}", len);
@@ -118,9 +121,52 @@ impl Encoder {
}
};
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>
@@ -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)]
mod tests {
use bytes::{BufMut};