perf(header): improve on MemSlice usage in headers
This commit is contained in:
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user