diff --git a/src/header/common/access_control_allow_credentials.rs b/src/header/common/access_control_allow_credentials.rs index 74f64968..34c12cf3 100644 --- a/src/header/common/access_control_allow_credentials.rs +++ b/src/header/common/access_control_allow_credentials.rs @@ -42,7 +42,8 @@ const ACCESS_CONTROL_ALLOW_CREDENTIALS_TRUE: UniCase<&'static str> = UniCase("tr impl Header for AccessControlAllowCredentials { fn header_name() -> &'static str { - "Access-Control-Allow-Credentials" + static NAME: &'static str = "Access-Control-Allow-Credentials"; + NAME } fn parse_header(raw: &[Vec]) -> ::Result { diff --git a/src/header/common/authorization.rs b/src/header/common/authorization.rs index d8997da8..02c75d1a 100644 --- a/src/header/common/authorization.rs +++ b/src/header/common/authorization.rs @@ -73,7 +73,8 @@ impl DerefMut for Authorization { impl Header for Authorization where ::Err: 'static { fn header_name() -> &'static str { - "Authorization" + static NAME: &'static str = "Authorization"; + NAME } fn parse_header(raw: &[Vec]) -> ::Result> { diff --git a/src/header/common/cache_control.rs b/src/header/common/cache_control.rs index 2b40685b..438a3903 100644 --- a/src/header/common/cache_control.rs +++ b/src/header/common/cache_control.rs @@ -51,7 +51,8 @@ __hyper__deref!(CacheControl => Vec); impl Header for CacheControl { fn header_name() -> &'static str { - "Cache-Control" + static NAME: &'static str = "Cache-Control"; + NAME } fn parse_header(raw: &[Vec]) -> ::Result { diff --git a/src/header/common/content_disposition.rs b/src/header/common/content_disposition.rs index cf9d6f09..5b49ac26 100644 --- a/src/header/common/content_disposition.rs +++ b/src/header/common/content_disposition.rs @@ -90,7 +90,8 @@ pub struct ContentDisposition { impl Header for ContentDisposition { fn header_name() -> &'static str { - "Content-Disposition" + static NAME: &'static str = "Content-Disposition"; + NAME } fn parse_header(raw: &[Vec]) -> ::Result { diff --git a/src/header/common/content_length.rs b/src/header/common/content_length.rs index 7ffa3943..c3932cb5 100644 --- a/src/header/common/content_length.rs +++ b/src/header/common/content_length.rs @@ -32,10 +32,13 @@ use header::{Header, parsing}; #[derive(Clone, Copy, Debug, PartialEq)] pub struct ContentLength(pub u64); +//static NAME: &'static str = "Content-Length"; + impl Header for ContentLength { #[inline] fn header_name() -> &'static str { - "Content-Length" + static NAME: &'static str = "Content-Length"; + NAME } fn parse_header(raw: &[Vec]) -> ::Result { // If multiple Content-Length headers were sent, everything can still diff --git a/src/header/common/cookie.rs b/src/header/common/cookie.rs index dbb95c9f..b404d8e8 100644 --- a/src/header/common/cookie.rs +++ b/src/header/common/cookie.rs @@ -39,7 +39,8 @@ __hyper__deref!(Cookie => Vec); impl Header for Cookie { fn header_name() -> &'static str { - "Cookie" + static NAME: &'static str = "Cookie"; + NAME } fn parse_header(raw: &[Vec]) -> ::Result { diff --git a/src/header/common/expect.rs b/src/header/common/expect.rs index 69f1d460..9f27b1b7 100644 --- a/src/header/common/expect.rs +++ b/src/header/common/expect.rs @@ -30,7 +30,8 @@ const EXPECT_CONTINUE: UniCase<&'static str> = UniCase("100-continue"); impl Header for Expect { fn header_name() -> &'static str { - "Expect" + static NAME: &'static str = "Expect"; + NAME } fn parse_header(raw: &[Vec]) -> ::Result { diff --git a/src/header/common/host.rs b/src/header/common/host.rs index 0e180bdb..2d906f0e 100644 --- a/src/header/common/host.rs +++ b/src/header/common/host.rs @@ -43,7 +43,8 @@ pub struct Host { impl Header for Host { fn header_name() -> &'static str { - "Host" + static NAME: &'static str = "Host"; + NAME } fn parse_header(raw: &[Vec]) -> ::Result { diff --git a/src/header/common/if_range.rs b/src/header/common/if_range.rs index 38287056..5b14e5d7 100644 --- a/src/header/common/if_range.rs +++ b/src/header/common/if_range.rs @@ -55,7 +55,8 @@ pub enum IfRange { impl Header for IfRange { fn header_name() -> &'static str { - "If-Range" + static NAME: &'static str = "If-Range"; + NAME } fn parse_header(raw: &[Vec]) -> ::Result { let etag: ::Result = header::parsing::from_one_raw_str(raw); diff --git a/src/header/common/mod.rs b/src/header/common/mod.rs index 6e9ed1d1..bb2e3150 100644 --- a/src/header/common/mod.rs +++ b/src/header/common/mod.rs @@ -217,7 +217,8 @@ macro_rules! header { __hyper__deref!($id => Vec<$item>); impl $crate::header::Header for $id { fn header_name() -> &'static str { - $n + static NAME: &'static str = $n; + NAME } fn parse_header(raw: &[Vec]) -> $crate::Result { $crate::header::parsing::from_comma_delimited(raw).map($id) @@ -243,7 +244,8 @@ macro_rules! header { __hyper__deref!($id => Vec<$item>); impl $crate::header::Header for $id { fn header_name() -> &'static str { - $n + static NAME: &'static str = $n; + NAME } fn parse_header(raw: &[Vec]) -> $crate::Result { $crate::header::parsing::from_comma_delimited(raw).map($id) @@ -268,7 +270,8 @@ macro_rules! header { __hyper__deref!($id => $value); impl $crate::header::Header for $id { fn header_name() -> &'static str { - $n + static NAME: &'static str = $n; + NAME } fn parse_header(raw: &[Vec]) -> $crate::Result { $crate::header::parsing::from_one_raw_str(raw).map($id) @@ -296,7 +299,8 @@ macro_rules! header { } impl $crate::header::Header for $id { fn header_name() -> &'static str { - $n + static NAME: &'static str = $n; + NAME } fn parse_header(raw: &[Vec]) -> $crate::Result { // FIXME: Return None if no item is in $id::Only diff --git a/src/header/common/pragma.rs b/src/header/common/pragma.rs index 20f09227..9e6d0504 100644 --- a/src/header/common/pragma.rs +++ b/src/header/common/pragma.rs @@ -40,7 +40,8 @@ pub enum Pragma { impl Header for Pragma { fn header_name() -> &'static str { - "Pragma" + static NAME: &'static str = "Pragma"; + NAME } fn parse_header(raw: &[Vec]) -> ::Result { diff --git a/src/header/common/prefer.rs b/src/header/common/prefer.rs index 70cba216..b0e81a9a 100644 --- a/src/header/common/prefer.rs +++ b/src/header/common/prefer.rs @@ -52,7 +52,8 @@ __hyper__deref!(Prefer => Vec); impl Header for Prefer { fn header_name() -> &'static str { - "Prefer" + static NAME: &'static str = "Prefer"; + NAME } fn parse_header(raw: &[Vec]) -> ::Result { diff --git a/src/header/common/preference_applied.rs b/src/header/common/preference_applied.rs index bd208c1f..385fa46a 100644 --- a/src/header/common/preference_applied.rs +++ b/src/header/common/preference_applied.rs @@ -50,7 +50,8 @@ __hyper__deref!(PreferenceApplied => Vec); impl Header for PreferenceApplied { fn header_name() -> &'static str { - "Preference-Applied" + static NAME: &'static str = "Preference-Applied"; + NAME } fn parse_header(raw: &[Vec]) -> ::Result { diff --git a/src/header/common/range.rs b/src/header/common/range.rs index 69d68ef4..79edc1a0 100644 --- a/src/header/common/range.rs +++ b/src/header/common/range.rs @@ -176,7 +176,8 @@ impl FromStr for ByteRangeSpec { impl Header for Range { fn header_name() -> &'static str { - "Range" + static NAME: &'static str = "Range"; + NAME } fn parse_header(raw: &[Vec]) -> ::Result { diff --git a/src/header/common/set_cookie.rs b/src/header/common/set_cookie.rs index c0494508..c08a865a 100644 --- a/src/header/common/set_cookie.rs +++ b/src/header/common/set_cookie.rs @@ -84,7 +84,8 @@ __hyper__deref!(SetCookie => Vec); impl Header for SetCookie { fn header_name() -> &'static str { - "Set-Cookie" + static NAME: &'static str = "Set-Cookie"; + NAME } fn parse_header(raw: &[Vec]) -> ::Result { diff --git a/src/header/common/strict_transport_security.rs b/src/header/common/strict_transport_security.rs index 25aa06c2..8023f225 100644 --- a/src/header/common/strict_transport_security.rs +++ b/src/header/common/strict_transport_security.rs @@ -121,7 +121,8 @@ impl FromStr for StrictTransportSecurity { impl Header for StrictTransportSecurity { fn header_name() -> &'static str { - "Strict-Transport-Security" + static NAME: &'static str = "Strict-Transport-Security"; + NAME } fn parse_header(raw: &[Vec]) -> ::Result { diff --git a/src/header/internals/mod.rs b/src/header/internals/mod.rs index 0d0b29ff..89a655d2 100644 --- a/src/header/internals/mod.rs +++ b/src/header/internals/mod.rs @@ -1,4 +1,6 @@ pub use self::item::Item; +pub use self::vec_map::{VecMap, Entry}; mod cell; mod item; +mod vec_map; diff --git a/src/header/internals/vec_map.rs b/src/header/internals/vec_map.rs new file mode 100644 index 00000000..70b30d17 --- /dev/null +++ b/src/header/internals/vec_map.rs @@ -0,0 +1,89 @@ +#[derive(Clone)] +pub struct VecMap { + vec: Vec<(K, V)>, +} + +impl VecMap { + pub fn new() -> VecMap { + VecMap { + vec: Vec::new() + } + } + + pub fn insert(&mut self, key: K, value: V) { + match self.find(&key) { + Some(pos) => self.vec[pos] = (key, value), + None => self.vec.push((key, value)) + } + } + + pub fn entry(&mut self, key: K) -> Entry { + match self.find(&key) { + Some(pos) => Entry::Occupied(OccupiedEntry { + vec: self, + pos: pos, + }), + None => Entry::Vacant(VacantEntry { + vec: self, + key: key, + }) + } + } + + pub fn get(&self, key: &K) -> Option<&V> { + self.find(key).map(move |pos| &self.vec[pos].1) + } + + pub fn get_mut(&mut self, key: &K) -> Option<&mut V> { + self.find(key).map(move |pos| &mut self.vec[pos].1) + } + + pub fn contains_key(&self, key: &K) -> bool { + self.find(key).is_some() + } + + pub fn len(&self) -> usize { self.vec.len() } + pub fn iter(&self) -> ::std::slice::Iter<(K, V)> { + self.vec.iter() + } + pub fn remove(&mut self, key: &K) -> Option { + self.find(key).map(|pos| self.vec.remove(pos)).map(|(_, v)| v) + } + pub fn clear(&mut self) { + self.vec.clear(); + } + + fn find(&self, key: &K) -> Option { + self.vec.iter().position(|entry| key == &entry.0) + } +} + +pub enum Entry<'a, K: 'a, V: 'a> { + Vacant(VacantEntry<'a, K, V>), + Occupied(OccupiedEntry<'a, K, V>) +} + +pub struct VacantEntry<'a, K: 'a, V: 'a> { + vec: &'a mut VecMap, + key: K, +} + +impl<'a, K, V> VacantEntry<'a, K, V> { + pub fn insert(self, val: V) -> &'a mut V { + let mut vec = self.vec; + vec.vec.push((self.key, val)); + let pos = vec.vec.len() - 1; + &mut vec.vec[pos].1 + } +} + +pub struct OccupiedEntry<'a, K: 'a, V: 'a> { + vec: &'a mut VecMap, + pos: usize, +} + +impl<'a, K, V> OccupiedEntry<'a, K, V> { + pub fn into_mut(self) -> &'a mut V { + &mut self.vec.vec[self.pos].1 + } +} diff --git a/src/header/mod.rs b/src/header/mod.rs index baedfb8e..f85da695 100644 --- a/src/header/mod.rs +++ b/src/header/mod.rs @@ -77,17 +77,16 @@ //! ``` use std::any::Any; use std::borrow::{Cow, ToOwned}; -use std::collections::HashMap; -use std::collections::hash_map::{Iter, Entry}; +//use std::collections::HashMap; +//use std::collections::hash_map::{Iter, Entry}; use std::iter::{FromIterator, IntoIterator}; -use std::ops::{Deref, DerefMut}; use std::{mem, fmt}; use {httparse, traitobject}; use typeable::Typeable; use unicase::UniCase; -use self::internals::Item; +use self::internals::{Item, VecMap, Entry}; #[cfg(feature = "serde-serialization")] use serde::{Deserialize, Deserializer, Serialize, Serializer}; @@ -104,7 +103,6 @@ mod internals; mod shared; pub mod parsing; -type HeaderName = UniCase; /// A trait for any object that will represent a header field and value. /// @@ -169,7 +167,8 @@ fn header_name() -> &'static str { /// A map of header fields on requests and responses. #[derive(Clone)] pub struct Headers { - data: HashMap + //data: HashMap + data: VecMap, } impl Default for Headers { @@ -178,12 +177,58 @@ impl Default for Headers { } } +macro_rules! literals { + ($($len:expr => $($header:path),+;)+) => ( + fn maybe_literal(s: &str) -> Cow<'static, str> { + match s.len() { + $($len => { + $( + if UniCase(<$header>::header_name()) == s { + return Cow::Borrowed(<$header>::header_name()); + } + )+ + })+ + + _ => () + } + + Cow::Owned(s.to_owned()) + } + + #[test] + fn test_literal_lens() { + $( + $({ + let s = <$header>::header_name(); + assert!(s.len() == $len, "{:?} has len of {}, listed as {}", s, s.len(), $len); + })+ + )+ + } + ); +} + +literals! { + 4 => Host, Date, ETag; + 5 => Allow, Range; + 6 => Accept, Cookie, Server, Expect; + 7 => Upgrade, Referer, Expires; + 8 => Location, IfMatch, IfRange; + 10 => UserAgent, Connection, SetCookie; + 12 => ContentType; + 13 => Authorization, CacheControl, LastModified, IfNoneMatch, AcceptRanges, ContentRange; + 14 => ContentLength, AcceptCharset; + 15 => AcceptEncoding, AcceptLanguage; + 17 => TransferEncoding; + 25 => StrictTransportSecurity; + 27 => AccessControlAllowOrigin; +} + impl Headers { /// Creates a new, empty headers map. pub fn new() -> Headers { Headers { - data: HashMap::new() + data: VecMap::new() } } @@ -192,7 +237,7 @@ impl Headers { let mut headers = Headers::new(); for header in raw { trace!("raw header: {:?}={:?}", header.name, &header.value[..]); - let name = UniCase(CowStr(Cow::Owned(header.name.to_owned()))); + let name = HeaderName(UniCase(maybe_literal(header.name))); let mut item = match headers.data.entry(name) { Entry::Vacant(entry) => entry.insert(Item::new_raw(vec![])), Entry::Occupied(entry) => entry.into_mut() @@ -209,7 +254,7 @@ impl Headers { /// The field is determined by the type of the value being set. pub fn set(&mut self, value: H) { trace!("Headers.set( {:?}, {:?} )", header_name::(), HeaderFormatter(&value)); - self.data.insert(UniCase(CowStr(Cow::Borrowed(header_name::()))), + self.data.insert(HeaderName(UniCase(Cow::Borrowed(header_name::()))), Item::new_typed(Box::new(value))); } @@ -226,7 +271,7 @@ impl Headers { /// ``` pub fn get_raw(&self, name: &str) -> Option<&[Vec]> { self.data - .get(&UniCase(CowStr(Cow::Borrowed(unsafe { mem::transmute::<&str, &str>(name) })))) + .get(&HeaderName(UniCase(Cow::Borrowed(unsafe { mem::transmute::<&str, &str>(name) })))) .map(Item::raw) } @@ -242,26 +287,26 @@ impl Headers { pub fn set_raw> + fmt::Debug>(&mut self, name: K, value: Vec>) { trace!("Headers.set_raw( {:?}, {:?} )", name, value); - self.data.insert(UniCase(CowStr(name.into())), Item::new_raw(value)); + self.data.insert(HeaderName(UniCase(name.into())), Item::new_raw(value)); } /// Remove a header set by set_raw pub fn remove_raw(&mut self, name: &str) { trace!("Headers.remove_raw( {:?} )", name); self.data.remove( - &UniCase(CowStr(Cow::Borrowed(unsafe { mem::transmute::<&str, &str>(name) }))) + &HeaderName(UniCase(Cow::Borrowed(unsafe { mem::transmute::<&str, &str>(name) }))) ); } /// Get a reference to the header field's value, if it exists. pub fn get(&self) -> Option<&H> { - self.data.get(&UniCase(CowStr(Cow::Borrowed(header_name::())))) + self.data.get(&HeaderName(UniCase(Cow::Borrowed(header_name::())))) .and_then(Item::typed::) } /// Get a mutable reference to the header field's value, if it exists. pub fn get_mut(&mut self) -> Option<&mut H> { - self.data.get_mut(&UniCase(CowStr(Cow::Borrowed(header_name::())))) + self.data.get_mut(&HeaderName(UniCase(Cow::Borrowed(header_name::())))) .and_then(Item::typed_mut::) } @@ -276,14 +321,14 @@ impl Headers { /// let has_type = headers.has::(); /// ``` pub fn has(&self) -> bool { - self.data.contains_key(&UniCase(CowStr(Cow::Borrowed(header_name::())))) + self.data.contains_key(&HeaderName(UniCase(Cow::Borrowed(header_name::())))) } /// Removes a header from the map, if one existed. /// Returns true if a header has been removed. pub fn remove(&mut self) -> bool { trace!("Headers.remove( {:?} )", header_name::()); - self.data.remove(&UniCase(CowStr(Cow::Borrowed(header_name::())))).is_some() + self.data.remove(&HeaderName(UniCase(Cow::Borrowed(header_name::())))).is_some() } /// Returns an iterator over the header fields. @@ -377,14 +422,14 @@ impl Deserialize for Headers { /// An `Iterator` over the fields in a `Headers` map. #[allow(missing_debug_implementations)] pub struct HeadersItems<'a> { - inner: Iter<'a, HeaderName, Item> + inner: ::std::slice::Iter<'a, (HeaderName, Item)> } impl<'a> Iterator for HeadersItems<'a> { type Item = HeaderView<'a>; fn next(&mut self) -> Option> { - self.inner.next().map(|(k, v)| HeaderView(k, v)) + self.inner.next().map(|&(ref k, ref v)| HeaderView(k, v)) } } @@ -395,7 +440,7 @@ impl<'a> HeaderView<'a> { /// Check if a HeaderView is a certain Header. #[inline] pub fn is(&self) -> bool { - UniCase(CowStr(Cow::Borrowed(header_name::()))) == *self.0 + HeaderName(UniCase(Cow::Borrowed(header_name::()))) == *self.0 } /// Get the Header name as a slice. @@ -473,38 +518,30 @@ impl<'a, H: Header> fmt::Debug for HeaderFormatter<'a, H> { } } -#[derive(Clone, Hash, Eq, PartialEq, PartialOrd, Ord)] -struct CowStr(Cow<'static, str>); +#[derive(Clone, Debug)] +struct HeaderName(UniCase>); -impl Deref for CowStr { - type Target = Cow<'static, str>; - - fn deref(&self) -> &Cow<'static, str> { - &self.0 - } -} - -impl fmt::Debug for CowStr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Debug::fmt(&self.0, f) - } -} - -impl fmt::Display for CowStr { +impl fmt::Display for HeaderName { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) } } -impl DerefMut for CowStr { - fn deref_mut(&mut self) -> &mut Cow<'static, str> { - &mut self.0 +impl AsRef for HeaderName { + fn as_ref(&self) -> &str { + ((self.0).0).as_ref() } } -impl AsRef for CowStr { - fn as_ref(&self) -> &str { - self +impl PartialEq for HeaderName { + fn eq(&self, other: &HeaderName) -> bool { + let s = self.as_ref(); + let k = other.as_ref(); + if s.len() == k.len() && s.as_ptr() == k.as_ptr() { + true + } else { + self.0 == other.0 + } } }