convert to using Cow

This commit is contained in:
Sean McArthur
2014-11-27 12:02:59 -08:00
parent c77cd3d3da
commit 4263a01f1c
3 changed files with 49 additions and 37 deletions

View File

@@ -95,9 +95,9 @@ impl Reader for Response {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::borrow::Borrowed;
use std::boxed::BoxAny; use std::boxed::BoxAny;
use std::io::BufferedReader; use std::io::BufferedReader;
use std::str::Slice;
use header::Headers; use header::Headers;
use http::HttpReader::EofReader; use http::HttpReader::EofReader;
@@ -117,7 +117,7 @@ mod tests {
headers: Headers::new(), headers: Headers::new(),
version: version::HttpVersion::Http11, version: version::HttpVersion::Http11,
body: EofReader(BufferedReader::new(box MockStream::new() as Box<NetworkStream + Send>)), body: EofReader(BufferedReader::new(box MockStream::new() as Box<NetworkStream + Send>)),
status_raw: RawStatus(200, Slice("OK")) status_raw: RawStatus(200, Borrowed("OK"))
}; };
let b = res.into_inner().downcast::<MockStream>().unwrap(); let b = res.into_inner().downcast::<MockStream>().unwrap();

View File

@@ -6,10 +6,11 @@
//! are already provided, such as `Host`, `ContentType`, `UserAgent`, and others. //! are already provided, such as `Host`, `ContentType`, `UserAgent`, and others.
use std::any::Any; use std::any::Any;
use std::ascii::{AsciiExt, AsciiCast}; use std::ascii::{AsciiExt, AsciiCast};
use std::borrow::{Borrowed, Owned};
use std::fmt::{mod, Show}; use std::fmt::{mod, Show};
use std::intrinsics::TypeId; use std::intrinsics::TypeId;
use std::raw::TraitObject; use std::raw::TraitObject;
use std::str::{SendStr, Slice, Owned}; use std::str::SendStr;
use std::collections::HashMap; use std::collections::HashMap;
use std::collections::hash_map::{Entries, Occupied, Vacant}; use std::collections::hash_map::{Entries, Occupied, Vacant};
use std::sync::RWLock; use std::sync::RWLock;
@@ -95,7 +96,7 @@ fn header_name<T: Header>() -> &'static str {
/// A map of header fields on requests and responses. /// A map of header fields on requests and responses.
#[deriving(Clone)] #[deriving(Clone)]
pub struct Headers { pub struct Headers {
data: HashMap<CaseInsensitive<SendStr>, RWLock<Item>> data: HashMap<CaseInsensitive, RWLock<Item>>
} }
impl Headers { impl Headers {
@@ -136,7 +137,7 @@ impl Headers {
/// ///
/// The field is determined by the type of the value being set. /// The field is determined by the type of the value being set.
pub fn set<H: Header + HeaderFormat>(&mut self, value: H) { pub fn set<H: Header + HeaderFormat>(&mut self, value: H) {
self.data.insert(CaseInsensitive(Slice(header_name::<H>())), self.data.insert(CaseInsensitive(Borrowed(header_name::<H>())),
RWLock::new(Item::typed(box value as Box<HeaderFormat + Send + Sync>))); RWLock::new(Item::typed(box value as Box<HeaderFormat + Send + Sync>)));
} }
@@ -154,7 +155,7 @@ impl Headers {
pub fn get_raw(&self, name: &str) -> Option<&[Vec<u8>]> { pub fn get_raw(&self, name: &str) -> Option<&[Vec<u8>]> {
self.data self.data
// FIXME(reem): Find a better way to do this lookup without find_equiv. // FIXME(reem): Find a better way to do this lookup without find_equiv.
.get(&CaseInsensitive(Slice(unsafe { mem::transmute::<&str, &str>(name) }))) .get(&CaseInsensitive(Borrowed(unsafe { mem::transmute::<&str, &str>(name) })))
.and_then(|item| { .and_then(|item| {
let lock = item.read(); let lock = item.read();
if let Some(ref raw) = lock.raw { if let Some(ref raw) = lock.raw {
@@ -177,8 +178,8 @@ impl Headers {
/// # let mut headers = Headers::new(); /// # let mut headers = Headers::new();
/// headers.set_raw("content-length", vec!["5".as_bytes().to_vec()]); /// headers.set_raw("content-length", vec!["5".as_bytes().to_vec()]);
/// ``` /// ```
pub fn set_raw<K: IntoMaybeOwned<'static>>(&mut self, name: K, value: Vec<Vec<u8>>) { pub fn set_raw<K: IntoCow<'static, String, str>>(&mut self, name: K, value: Vec<Vec<u8>>) {
self.data.insert(CaseInsensitive(name.into_maybe_owned()), RWLock::new(Item::raw(value))); self.data.insert(CaseInsensitive(name.into_cow()), RWLock::new(Item::raw(value)));
} }
/// Get a reference to the header field's value, if it exists. /// Get a reference to the header field's value, if it exists.
@@ -200,7 +201,7 @@ impl Headers {
} }
fn get_or_parse<H: Header + HeaderFormat>(&self) -> Option<&RWLock<Item>> { fn get_or_parse<H: Header + HeaderFormat>(&self) -> Option<&RWLock<Item>> {
self.data.get(&CaseInsensitive(Slice(header_name::<H>()))).and_then(|item| get_or_parse::<H>(item)) self.data.get(&CaseInsensitive(Borrowed(header_name::<H>()))).and_then(|item| get_or_parse::<H>(item))
} }
/// Returns a boolean of whether a certain header is in the map. /// Returns a boolean of whether a certain header is in the map.
@@ -214,13 +215,13 @@ impl Headers {
/// let has_type = headers.has::<ContentType>(); /// let has_type = headers.has::<ContentType>();
/// ``` /// ```
pub fn has<H: Header + HeaderFormat>(&self) -> bool { pub fn has<H: Header + HeaderFormat>(&self) -> bool {
self.data.contains_key(&CaseInsensitive(Slice(header_name::<H>()))) self.data.contains_key(&CaseInsensitive(Borrowed(header_name::<H>())))
} }
/// Removes a header from the map, if one existed. /// Removes a header from the map, if one existed.
/// Returns true if a header has been removed. /// Returns true if a header has been removed.
pub fn remove<H: Header + HeaderFormat>(&mut self) -> bool { pub fn remove<H: Header + HeaderFormat>(&mut self) -> bool {
self.data.remove(&CaseInsensitive(Slice(Header::header_name(None::<H>)))).is_some() self.data.remove(&CaseInsensitive(Borrowed(Header::header_name(None::<H>)))).is_some()
} }
/// Returns an iterator over the header fields. /// Returns an iterator over the header fields.
@@ -252,7 +253,7 @@ impl fmt::Show for Headers {
/// An `Iterator` over the fields in a `Headers` map. /// An `Iterator` over the fields in a `Headers` map.
pub struct HeadersItems<'a> { pub struct HeadersItems<'a> {
inner: Entries<'a, CaseInsensitive<SendStr>, RWLock<Item>> inner: Entries<'a, CaseInsensitive, RWLock<Item>>
} }
impl<'a> Iterator<HeaderView<'a>> for HeadersItems<'a> { impl<'a> Iterator<HeaderView<'a>> for HeadersItems<'a> {
@@ -265,13 +266,13 @@ impl<'a> Iterator<HeaderView<'a>> for HeadersItems<'a> {
} }
/// Returned with the `HeadersItems` iterator. /// Returned with the `HeadersItems` iterator.
pub struct HeaderView<'a>(&'a CaseInsensitive<SendStr>, &'a RWLock<Item>); pub struct HeaderView<'a>(&'a CaseInsensitive, &'a RWLock<Item>);
impl<'a> HeaderView<'a> { impl<'a> HeaderView<'a> {
/// Check if a HeaderView is a certain Header. /// Check if a HeaderView is a certain Header.
#[inline] #[inline]
pub fn is<H: Header>(&self) -> bool { pub fn is<H: Header>(&self) -> bool {
CaseInsensitive(header_name::<H>().into_maybe_owned()) == *self.0 CaseInsensitive(header_name::<H>().into_cow()) == *self.0
} }
/// Get the Header name as a slice. /// Get the Header name as a slice.
@@ -432,10 +433,16 @@ impl fmt::Show for Box<HeaderFormat + Send + Sync> {
} }
} }
#[deriving(Clone)] //#[deriving(Clone)]
struct CaseInsensitive<S: Str>(S); struct CaseInsensitive(SendStr);
impl<S: Str> Str for CaseInsensitive<S> { impl Clone for CaseInsensitive {
fn clone(&self) -> CaseInsensitive {
CaseInsensitive((*self.0).clone().into_cow())
}
}
impl Str for CaseInsensitive {
fn as_slice(&self) -> &str { fn as_slice(&self) -> &str {
let CaseInsensitive(ref s) = *self; let CaseInsensitive(ref s) = *self;
s.as_slice() s.as_slice()
@@ -443,29 +450,27 @@ impl<S: Str> Str for CaseInsensitive<S> {
} }
impl<S: Str> fmt::Show for CaseInsensitive<S> { impl fmt::Show for CaseInsensitive {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
self.as_slice().fmt(fmt) self.as_slice().fmt(fmt)
} }
} }
impl<S: Str> PartialEq for CaseInsensitive<S> { impl PartialEq for CaseInsensitive {
fn eq(&self, other: &CaseInsensitive<S>) -> bool { fn eq(&self, other: &CaseInsensitive) -> bool {
self.as_slice().eq_ignore_ascii_case(other.as_slice()) self.as_slice().eq_ignore_ascii_case(other.as_slice())
} }
} }
impl<S: Str> Eq for CaseInsensitive<S> {} impl Eq for CaseInsensitive {}
impl<S: Str, S2: Str> Equiv<CaseInsensitive<S2>> for CaseInsensitive<S> { impl Equiv<CaseInsensitive> for CaseInsensitive {
fn equiv(&self, other: &CaseInsensitive<S2>) -> bool { fn equiv(&self, other: &CaseInsensitive) -> bool {
let left = CaseInsensitive(self.as_slice()); self == other
let right = CaseInsensitive(other.as_slice());
left == right
} }
} }
impl<S: Str, H: hash::Writer> hash::Hash<H> for CaseInsensitive<S> { impl<H: hash::Writer> hash::Hash<H> for CaseInsensitive {
#[inline] #[inline]
fn hash(&self, hasher: &mut H) { fn hash(&self, hasher: &mut H) {
for b in self.as_slice().bytes() { for b in self.as_slice().bytes() {
@@ -491,7 +496,7 @@ impl<H: HeaderFormat> Show for HeaderFormatter<H> {
mod tests { mod tests {
use std::io::MemReader; use std::io::MemReader;
use std::fmt; use std::fmt;
use std::str::Slice; use std::borrow::Borrowed;
use std::hash::sip::hash; use std::hash::sip::hash;
use mime::{Mime, Text, Plain}; use mime::{Mime, Text, Plain};
use super::CaseInsensitive; use super::CaseInsensitive;
@@ -506,8 +511,8 @@ mod tests {
#[test] #[test]
fn test_case_insensitive() { fn test_case_insensitive() {
let a = CaseInsensitive(Slice("foobar")); let a = CaseInsensitive(Borrowed("foobar"));
let b = CaseInsensitive(Slice("FOOBAR")); let b = CaseInsensitive(Borrowed("FOOBAR"));
assert_eq!(a, b); assert_eq!(a, b);
assert_eq!(hash(&a), hash(&b)); assert_eq!(hash(&a), hash(&b));

View File

@@ -1,9 +1,10 @@
//! Pieces pertaining to the HTTP message protocol. //! Pieces pertaining to the HTTP message protocol.
use std::borrow::{Borrowed, Owned};
use std::cmp::min; use std::cmp::min;
use std::fmt; use std::fmt;
use std::io::{mod, Reader, IoResult, BufWriter}; use std::io::{mod, Reader, IoResult, BufWriter};
use std::num::from_u16; use std::num::from_u16;
use std::str::{mod, SendStr, Slice, Owned}; use std::str::{mod, SendStr};
use url::Url; use url::Url;
@@ -559,9 +560,15 @@ pub fn read_request_line<R: Reader>(stream: &mut R) -> HttpResult<RequestLine> {
pub type StatusLine = (HttpVersion, RawStatus); pub type StatusLine = (HttpVersion, RawStatus);
/// The raw status code and reason-phrase. /// The raw status code and reason-phrase.
#[deriving(PartialEq, Show, Clone)] #[deriving(PartialEq, Show)]
pub struct RawStatus(pub u16, pub SendStr); pub struct RawStatus(pub u16, pub SendStr);
impl Clone for RawStatus {
fn clone(&self) -> RawStatus {
RawStatus(self.0, (*self.1).clone().into_cow())
}
}
/// Read the StatusLine, such as `HTTP/1.1 200 OK`. /// Read the StatusLine, such as `HTTP/1.1 200 OK`.
/// ///
/// > The first line of a response message is the status-line, consisting /// > The first line of a response message is the status-line, consisting
@@ -632,7 +639,7 @@ pub fn read_status<R: Reader>(stream: &mut R) -> HttpResult<RawStatus> {
Some(status) => match status.canonical_reason() { Some(status) => match status.canonical_reason() {
Some(phrase) => { Some(phrase) => {
if phrase == reason { if phrase == reason {
Slice(phrase) Borrowed(phrase)
} else { } else {
Owned(reason.into_string()) Owned(reason.into_string())
} }
@@ -657,7 +664,7 @@ fn expect(r: IoResult<u8>, expected: u8) -> HttpResult<()> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::io::{mod, MemReader, MemWriter}; use std::io::{mod, MemReader, MemWriter};
use std::str::{Slice, Owned}; use std::borrow::{Borrowed, Owned};
use test::Bencher; use test::Bencher;
use uri::RequestUri; use uri::RequestUri;
use uri::RequestUri::{Star, AbsoluteUri, AbsolutePath, Authority}; use uri::RequestUri::{Star, AbsoluteUri, AbsolutePath, Authority};
@@ -727,8 +734,8 @@ mod tests {
assert_eq!(read_status(&mut mem(s)), result); assert_eq!(read_status(&mut mem(s)), result);
} }
read("200 OK\r\n", Ok(RawStatus(200, Slice("OK")))); read("200 OK\r\n", Ok(RawStatus(200, Borrowed("OK"))));
read("404 Not Found\r\n", Ok(RawStatus(404, Slice("Not Found")))); read("404 Not Found\r\n", Ok(RawStatus(404, Borrowed("Not Found"))));
read("200 crazy pants\r\n", Ok(RawStatus(200, Owned("crazy pants".to_string())))); read("200 crazy pants\r\n", Ok(RawStatus(200, Owned("crazy pants".to_string()))));
} }
@@ -774,7 +781,7 @@ mod tests {
#[bench] #[bench]
fn bench_read_status(b: &mut Bencher) { fn bench_read_status(b: &mut Bencher) {
b.bytes = b"404 Not Found\r\n".len() as u64; b.bytes = b"404 Not Found\r\n".len() as u64;
b.iter(|| assert_eq!(read_status(&mut mem("404 Not Found\r\n")), Ok(RawStatus(404, Slice("Not Found"))))); b.iter(|| assert_eq!(read_status(&mut mem("404 Not Found\r\n")), Ok(RawStatus(404, Borrowed("Not Found")))));
} }
} }