perf(header): improve on MemSlice usage in headers

This commit is contained in:
Sean McArthur
2017-01-26 23:49:44 -08:00
parent 1b556389c0
commit 61364d245b
14 changed files with 190 additions and 195 deletions

View File

@@ -141,9 +141,7 @@ impl Header for RetryAfter {
#[cfg(test)]
mod tests {
extern crate httparse;
use header::{Header, Headers};
use header::Header;
use header::shared::HttpDate;
use time::{Duration};
@@ -179,17 +177,15 @@ mod tests {
#[test]
fn hyper_headers_from_raw_delay() {
let headers = make_header!(b"Retry-After", b"300");
let retry_after = headers.get::<RetryAfter>().unwrap();
assert_eq!(retry_after, &RetryAfter::Delay(Duration::seconds(300)));
let retry_after = RetryAfter::parse_header(&b"300".to_vec().into()).unwrap();
assert_eq!(retry_after, RetryAfter::Delay(Duration::seconds(300)));
}
#[test]
fn hyper_headers_from_raw_datetime() {
let headers = make_header!(b"Retry-After", b"Sun, 06 Nov 1994 08:49:37 GMT");
let retry_after = headers.get::<RetryAfter>().unwrap();
let retry_after = RetryAfter::parse_header(&b"Sun, 06 Nov 1994 08:49:37 GMT".to_vec().into()).unwrap();
let expected = "Sun, 06 Nov 1994 08:49:37 GMT".parse::<HttpDate>().unwrap();
assert_eq!(retry_after, &RetryAfter::DateTime(expected.0));
assert_eq!(retry_after, RetryAfter::DateTime(expected.0));
}
}

View File

@@ -4,9 +4,9 @@ pub struct VecMap<K, V> {
}
impl<K: PartialEq, V> VecMap<K, V> {
pub fn new() -> VecMap<K, V> {
pub fn with_capacity(cap: usize) -> VecMap<K, V> {
VecMap {
vec: Vec::new()
vec: Vec::with_capacity(cap)
}
}
@@ -43,6 +43,7 @@ impl<K: PartialEq, V> VecMap<K, V> {
}
pub fn len(&self) -> usize { self.vec.len() }
pub fn iter(&self) -> ::std::slice::Iter<(K, V)> {
self.vec.iter()
}

View File

@@ -80,7 +80,6 @@ use std::borrow::{Cow, ToOwned};
use std::iter::{FromIterator, IntoIterator};
use std::{mem, fmt};
use httparse;
use unicase::UniCase;
use self::internals::{Item, VecMap, Entry};
@@ -347,31 +346,17 @@ literals! {
impl Headers {
/// Creates a new, empty headers map.
#[inline]
pub fn new() -> Headers {
Headers {
data: VecMap::new()
}
Headers::with_capacity(0)
}
#[doc(hidden)]
pub fn from_raw(raw: &[httparse::Header], buf: MemSlice) -> ::Result<Headers> {
let mut headers = Headers::new();
for header in raw {
let name = HeaderName(UniCase(maybe_literal(header.name)));
let trim = header.value.iter().rev().take_while(|&&x| x == b' ').count();
let value_start = header.value.as_ptr() as usize - buf.get().as_ptr() as usize;
let value_end = value_start + header.value.len() - trim;
match headers.data.entry(name) {
Entry::Vacant(entry) => {
entry.insert(Item::new_raw(self::raw::parsed(buf.slice(value_start..value_end))));
}
Entry::Occupied(entry) => {
entry.into_mut().mut_raw().push_slice(buf.slice(value_start..value_end));
}
};
/// Creates a new `Headers` struct with space reserved for `len` headers.
#[inline]
pub fn with_capacity(len: usize) -> Headers {
Headers {
data: VecMap::with_capacity(len)
}
Ok(headers)
}
/// Set a header field to the corresponding value.
@@ -587,6 +572,24 @@ impl<'a> Extend<HeaderView<'a>> for Headers {
}
}
impl<'a> Extend<(&'a str, MemSlice)> for Headers {
fn extend<I: IntoIterator<Item=(&'a str, MemSlice)>>(&mut self, iter: I) {
for (name, value) in iter {
let name = HeaderName(UniCase(maybe_literal(name)));
//let trim = header.value.iter().rev().take_while(|&&x| x == b' ').count();
match self.data.entry(name) {
Entry::Vacant(entry) => {
entry.insert(Item::new_raw(self::raw::parsed(value)));
}
Entry::Occupied(entry) => {
self::raw::push(entry.into_mut().mut_raw(), value);
}
};
}
}
}
impl<'a> FromIterator<HeaderView<'a>> for Headers {
fn from_iter<I: IntoIterator<Item=HeaderView<'a>>>(iter: I) -> Headers {
let mut headers = Headers::new();
@@ -659,33 +662,22 @@ mod tests {
use mime::SubLevel::Plain;
use super::{Headers, Header, Raw, ContentLength, ContentType,
Accept, Host, qitem};
use httparse;
#[cfg(feature = "nightly")]
use test::Bencher;
macro_rules! raw {
($($line:expr),*) => ({
[$({
// Slice.position_elem was unstable
fn index_of(slice: &MemSlice, byte: u8) -> Option<usize> {
for (index, &b) in slice.as_ref().iter().enumerate() {
if b == byte {
return Some(index);
}
}
None
}
let pos = index_of(&$line, b':').expect("raw splits on ':', not found");
httparse::Header {
name: ::std::str::from_utf8(&$line[..pos]).unwrap(),
value: &$line[pos + 2..]
}
}),*]
macro_rules! make_header {
($name:expr, $value:expr) => ({
let mut headers = Headers::new();
headers.set_raw(String::from_utf8($name.to_vec()).unwrap(), $value.to_vec());
headers
});
($text:expr) => ({
let bytes = $text;
let colon = bytes.iter().position(|&x| x == b':').unwrap();
make_header!(&bytes[..colon], &bytes[colon + 2..])
})
}
#[test]
fn test_from_raw() {
let headers = make_header!(b"Content-Length", b"10");
@@ -759,7 +751,9 @@ mod tests {
#[test]
fn test_different_reads() {
let headers = make_header!(b"Content-Length: 10\r\nContent-Type: text/plain");
let mut headers = Headers::new();
headers.set_raw("Content-Length", "10");
headers.set_raw("Content-Type", "text/plain");
let ContentLength(_) = *headers.get::<ContentLength>().unwrap();
let ContentType(_) = *headers.get::<ContentType>().unwrap();
}
@@ -899,17 +893,6 @@ mod tests {
})
}
#[cfg(feature = "nightly")]
#[bench]
fn bench_headers_from_raw(b: &mut Bencher) {
use ::http::buf::MemSlice;
let buf = MemSlice::from(b"Content-Length: 10" as &[u8]);
let buf_clone = buf.clone();
let raw = raw!(buf_clone);
b.iter(|| Headers::from_raw(&raw, buf.clone()).unwrap())
}
#[cfg(feature = "nightly")]
#[bench]
fn bench_headers_get(b: &mut Bencher) {

View File

@@ -21,7 +21,7 @@ pub fn from_one_raw_str<T: str::FromStr>(raw: &Raw) -> ::Result<T> {
/// Reads a raw string into a value.
pub fn from_raw_str<T: str::FromStr>(raw: &[u8]) -> ::Result<T> {
let s = try!(str::from_utf8(raw));
let s = try!(str::from_utf8(raw)).trim();
T::from_str(s).or(Err(::Error::Header))
}
@@ -36,7 +36,7 @@ pub fn from_comma_delimited<T: str::FromStr>(raw: &Raw) -> ::Result<Vec<T>> {
"" => None,
y => Some(y)
})
.filter_map(|x| x.parse().ok()))
.filter_map(|x| x.trim().parse().ok()))
}
Ok(result)
}

View File

@@ -37,27 +37,17 @@ impl Raw {
/// Append a line to this `Raw` header value.
pub fn push(&mut self, val: &[u8]) {
let lines = ::std::mem::replace(&mut self.0, Lines::Many(Vec::new()));
match lines {
Lines::One(line) => {
self.0 = Lines::Many(vec![line, maybe_literal(val.into())]);
}
Lines::Many(mut lines) => {
lines.push(maybe_literal(val.into()));
self.0 = Lines::Many(lines);
}
}
self.push_line(maybe_literal(val.into()));
}
#[doc(hidden)]
pub fn push_slice(&mut self, val: MemSlice) {
fn push_line(&mut self, line: Line) {
let lines = ::std::mem::replace(&mut self.0, Lines::Many(Vec::new()));
match lines {
Lines::One(line) => {
self.0 = Lines::Many(vec![line, Line::Shared(val)]);
Lines::One(one) => {
self.0 = Lines::Many(vec![one, line]);
}
Lines::Many(mut lines) => {
lines.push(Line::Shared(val));
lines.push(line);
self.0 = Lines::Many(lines);
}
}
@@ -190,6 +180,10 @@ pub fn parsed(val: MemSlice) -> Raw {
Raw(Lines::One(From::from(val)))
}
pub fn push(raw: &mut Raw, val: MemSlice) {
raw.push_line(Line::from(val));
}
impl fmt::Debug for Raw {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.0 {