Merge pull request #146 from hyperium/iter

improve Headers.iter
This commit is contained in:
Sean McArthur
2014-11-25 18:45:09 -08:00

View File

@@ -59,13 +59,8 @@ pub trait HeaderFormat: Clone + Any + Send + Sync {
fn clone_box(&self) -> Box<HeaderFormat + Sync + Send> { box self.clone() } fn clone_box(&self) -> Box<HeaderFormat + Sync + Send> { box self.clone() }
} }
#[doc(hidden)] impl HeaderFormat {
trait Is { fn is<T: 'static>(&self) -> bool {
fn is<T: 'static>(self) -> bool;
}
impl<'a> Is for &'a HeaderFormat {
fn is<T: 'static>(self) -> bool {
self.get_type_id() == TypeId::of::<T>() self.get_type_id() == TypeId::of::<T>()
} }
} }
@@ -92,7 +87,7 @@ impl Clone for Box<HeaderFormat + Send + Sync> {
} }
} }
fn header_name<T: Header + HeaderFormat>() -> &'static str { fn header_name<T: Header>() -> &'static str {
let name = Header::header_name(None::<T>); let name = Header::header_name(None::<T>);
name name
} }
@@ -189,74 +184,23 @@ impl Headers {
/// Get a reference to the header field's value, if it exists. /// Get a reference to the header field's value, if it exists.
pub fn get<H: Header + HeaderFormat>(&self) -> Option<&H> { pub fn get<H: Header + HeaderFormat>(&self) -> Option<&H> {
self.get_or_parse::<H>().map(|item| { self.get_or_parse::<H>().map(|item| {
let read = item.read(); unsafe {
debug!("downcasting {}", *read); mem::transmute::<&H, &H>(downcast(&*item.read()))
let ret = match read.typed { }
Some(ref val) => unsafe { val.downcast_ref_unchecked() },
_ => unreachable!()
};
unsafe { mem::transmute::<&H, &H>(ret) }
}) })
} }
/// Get a mutable reference to the header field's value, if it exists. /// Get a mutable reference to the header field's value, if it exists.
pub fn get_mut<H: Header + HeaderFormat>(&mut self) -> Option<&mut H> { pub fn get_mut<H: Header + HeaderFormat>(&mut self) -> Option<&mut H> {
self.get_or_parse::<H>().map(|item| { self.get_or_parse::<H>().map(|item| {
let mut write = item.write(); unsafe {
debug!("downcasting {}", *write); mem::transmute::<&mut H, &mut H>(downcast_mut(&mut *item.write()))
let ret = match *&mut write.typed { }
Some(ref mut val) => unsafe { val.downcast_mut_unchecked() },
_ => unreachable!()
};
unsafe { mem::transmute::<&mut H, &mut H>(ret) }
}) })
} }
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| { self.data.get(&CaseInsensitive(Slice(header_name::<H>()))).and_then(|item| get_or_parse::<H>(item))
match item.read().typed {
Some(ref typed) if typed.is::<H>() => return Some(item),
Some(ref typed) => {
warn!("attempted to access {} as wrong type", typed);
return None;
}
_ => ()
}
// Take out a write lock to do the parsing and mutation.
let mut write = item.write();
// Since this lock can queue, it's possible another thread just
// did the work for us.
match write.typed {
// Check they inserted the correct type and move on.
Some(ref typed) if typed.is::<H>() => return Some(item),
// Wrong type, another thread got here before us and parsed
// as a different representation.
Some(ref typed) => {
debug!("other thread was here first?")
warn!("attempted to access {} as wrong type", typed);
return None;
},
// We are first in the queue or the only ones, so do the actual
// work of parsing and mutation.
_ => ()
}
let header = match write.raw {
Some(ref raw) => match Header::parse_header(raw[]) {
Some::<H>(h) => h,
None => return None
},
None => unreachable!()
};
// Mutate!
write.typed = Some(box header as Box<HeaderFormat + Send + Sync>);
Some(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.
@@ -299,8 +243,8 @@ impl Headers {
impl fmt::Show for Headers { impl fmt::Show for Headers {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
for (k, v) in self.iter() { for header in self.iter() {
try!(write!(fmt, "{}: {}{}", k, v, LineEnding)); try!(write!(fmt, "{}{}", header, LineEnding));
} }
Ok(()) Ok(())
} }
@@ -311,22 +255,67 @@ pub struct HeadersItems<'a> {
inner: Entries<'a, CaseInsensitive<SendStr>, RWLock<Item>> inner: Entries<'a, CaseInsensitive<SendStr>, RWLock<Item>>
} }
impl<'a> Iterator<(&'a str, HeaderView<'a>)> for HeadersItems<'a> { impl<'a> Iterator<HeaderView<'a>> for HeadersItems<'a> {
fn next(&mut self) -> Option<(&'a str, HeaderView<'a>)> { fn next(&mut self) -> Option<HeaderView<'a>> {
match self.inner.next() { match self.inner.next() {
Some((k, v)) => Some((k.as_slice(), HeaderView(v))), Some((k, v)) => Some(HeaderView(k, v)),
None => None None => None
} }
} }
} }
/// Returned with the `HeadersItems` iterator. /// Returned with the `HeadersItems` iterator.
pub struct HeaderView<'a>(&'a RWLock<Item>); pub struct HeaderView<'a>(&'a CaseInsensitive<SendStr>, &'a RWLock<Item>);
impl<'a> HeaderView<'a> {
/// Check if a HeaderView is a certain Header.
#[inline]
pub fn is<H: Header>(&self) -> bool {
CaseInsensitive(header_name::<H>().into_maybe_owned()) == *self.0
}
/// Get the Header name as a slice.
#[inline]
pub fn name(&self) -> &'a str {
self.0.as_slice()
}
/// Cast the value to a certain Header type.
#[inline]
pub fn value<H: Header + HeaderFormat>(&self) -> Option<&'a H> {
get_or_parse::<H>(self.1).map(|item| {
unsafe {
mem::transmute::<&H, &H>(downcast(&*item.read()))
}
})
}
/// Get just the header value as a String.
#[inline]
pub fn value_string(&self) -> String {
(*self.1.read()).to_string()
}
}
impl<'a> fmt::Show for HeaderView<'a> { impl<'a> fmt::Show for HeaderView<'a> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let HeaderView(item) = *self; write!(f, "{}: {}", self.0, *self.1.read())
item.read().fmt(fmt) }
}
impl<'a> Extend<HeaderView<'a>> for Headers {
fn extend<I: Iterator<HeaderView<'a>>>(&mut self, mut iter: I) {
for header in iter {
self.data.insert((*header.0).clone(), (*header.1).clone());
}
}
}
impl<'a> FromIterator<HeaderView<'a>> for Headers {
fn from_iter<I: Iterator<HeaderView<'a>>>(iter: I) -> Headers {
let mut headers = Headers::new();
headers.extend(iter);
headers
} }
} }
@@ -350,6 +339,68 @@ impl Item {
typed: Some(ty), typed: Some(ty),
} }
} }
}
fn get_or_parse<H: Header + HeaderFormat>(item: &RWLock<Item>) -> Option<&RWLock<Item>> {
match item.read().typed {
Some(ref typed) if typed.is::<H>() => return Some(item),
Some(ref typed) => {
warn!("attempted to access {} as wrong type", typed);
return None;
}
_ => ()
}
// Take out a write lock to do the parsing and mutation.
let mut write = item.write();
// Since this lock can queue, it's possible another thread just
// did the work for us.
match write.typed {
// Check they inserted the correct type and move on.
Some(ref typed) if typed.is::<H>() => return Some(item),
// Wrong type, another thread got here before us and parsed
// as a different representation.
Some(ref typed) => {
debug!("other thread was here first?")
warn!("attempted to access {} as wrong type", typed);
return None;
},
// We are first in the queue or the only ones, so do the actual
// work of parsing and mutation.
_ => ()
}
let header = match write.raw {
Some(ref raw) => match Header::parse_header(raw[]) {
Some::<H>(h) => h,
None => return None
},
None => unreachable!()
};
// Mutate!
write.typed = Some(box header as Box<HeaderFormat + Send + Sync>);
Some(item)
}
fn downcast<H: Header + HeaderFormat>(read: &Item) -> &H {
debug!("downcasting {}", *read);
match read.typed {
Some(ref val) => unsafe { val.downcast_ref_unchecked() },
_ => unreachable!()
}
}
fn downcast_mut<H: Header + HeaderFormat>(write: &mut Item) -> &mut H {
debug!("downcasting {}", *write);
match write.typed {
Some(ref mut val) => unsafe { val.downcast_mut_unchecked() },
_ => unreachable!()
}
} }
impl fmt::Show for Item { impl fmt::Show for Item {
@@ -447,6 +498,8 @@ mod tests {
use super::{Headers, Header, HeaderFormat}; use super::{Headers, Header, HeaderFormat};
use super::common::{ContentLength, ContentType, Accept, Host}; use super::common::{ContentLength, ContentType, Accept, Host};
use test::Bencher;
fn mem(s: &str) -> MemReader { fn mem(s: &str) -> MemReader {
MemReader::new(s.as_bytes().to_vec()) MemReader::new(s.as_bytes().to_vec())
} }
@@ -586,4 +639,25 @@ mod tests {
headers.clear(); headers.clear();
assert_eq!(headers.len(), 0); assert_eq!(headers.len(), 0);
} }
#[test]
fn test_iter() {
let mut headers = Headers::new();
headers.set(ContentLength(11));
for header in headers.iter() {
assert!(header.is::<ContentLength>());
assert_eq!(header.name(), Header::header_name(None::<ContentLength>));
assert_eq!(header.value(), Some(&ContentLength(11)));
assert_eq!(header.value_string(), "11".to_string());
}
}
#[bench]
fn bench_header_view_is(b: &mut Bencher) {
let mut headers = Headers::new();
headers.set(ContentLength(11));
let mut iter = headers.iter();
let view = iter.next().unwrap();
b.iter(|| assert!(view.is::<ContentLength>()))
}
} }