-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(headers): adds re-parsing ability when getting typed headers
BREAKING CHANGE: added requirement that all HeaderFormat implementations must also be fmt::Debug. This likely as easy as slapping #[derive(Debug)] on to any custom headers.
- Loading branch information
1 parent
9e07708
commit df75687
Showing
10 changed files
with
258 additions
and
240 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,5 +19,4 @@ openssl = "*" | |
rustc-serialize = "*" | ||
time = "*" | ||
unicase = "*" | ||
unsafe-any = "*" | ||
url = "*" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
use std::any::{Any, TypeId}; | ||
use std::cell::UnsafeCell; | ||
use std::collections::HashMap; | ||
use std::fmt; | ||
use std::mem; | ||
use std::ops::Deref; | ||
|
||
pub struct OptCell<T>(UnsafeCell<Option<T>>); | ||
|
||
impl<T> OptCell<T> { | ||
#[inline] | ||
pub fn new(val: Option<T>) -> OptCell<T> { | ||
OptCell(UnsafeCell::new(val)) | ||
} | ||
|
||
#[inline] | ||
pub fn set(&self, val: T) { | ||
unsafe { | ||
let opt = self.0.get(); | ||
debug_assert!((*opt).is_none()); | ||
*opt = Some(val) | ||
} | ||
} | ||
|
||
#[inline] | ||
pub unsafe fn get_mut(&mut self) -> &mut T { | ||
let opt = &mut *self.0.get(); | ||
opt.as_mut().unwrap() | ||
} | ||
} | ||
|
||
impl<T> Deref for OptCell<T> { | ||
type Target = Option<T>; | ||
#[inline] | ||
fn deref<'a>(&'a self) -> &'a Option<T> { | ||
unsafe { &*self.0.get() } | ||
} | ||
} | ||
|
||
impl<T: Clone> Clone for OptCell<T> { | ||
#[inline] | ||
fn clone(&self) -> OptCell<T> { | ||
OptCell::new((**self).clone()) | ||
} | ||
} | ||
|
||
pub struct PtrMapCell<V: ?Sized>(UnsafeCell<PtrMap<Box<V>>>); | ||
|
||
#[derive(Clone, Debug)] | ||
enum PtrMap<T> { | ||
Empty, | ||
One(TypeId, T), | ||
Many(HashMap<TypeId, T>) | ||
} | ||
|
||
impl<V: ?Sized + fmt::Debug + Any + 'static> PtrMapCell<V> { | ||
#[inline] | ||
pub fn new() -> PtrMapCell<V> { | ||
PtrMapCell(UnsafeCell::new(PtrMap::Empty)) | ||
} | ||
|
||
#[inline] | ||
pub fn get(&self, key: TypeId) -> Option<&V> { | ||
let map = unsafe { &*self.0.get() }; | ||
match *map { | ||
PtrMap::Empty => None, | ||
PtrMap::One(id, ref v) => if id == key { | ||
Some(v) | ||
} else { | ||
None | ||
}, | ||
PtrMap::Many(ref hm) => hm.get(&key) | ||
}.map(|val| &**val) | ||
} | ||
|
||
#[inline] | ||
pub fn get_mut(&mut self, key: TypeId) -> Option<&mut V> { | ||
let mut map = unsafe { &mut *self.0.get() }; | ||
match *map { | ||
PtrMap::Empty => None, | ||
PtrMap::One(id, ref mut v) => if id == key { | ||
Some(v) | ||
} else { | ||
None | ||
}, | ||
PtrMap::Many(ref mut hm) => hm.get_mut(&key) | ||
}.map(|val| &mut **val) | ||
} | ||
|
||
#[inline] | ||
pub unsafe fn insert(&self, key: TypeId, val: Box<V>) { | ||
let mut map = &mut *self.0.get(); | ||
match *map { | ||
PtrMap::Empty => *map = PtrMap::One(key, val), | ||
PtrMap::One(..) => { | ||
let one = mem::replace(map, PtrMap::Empty); | ||
match one { | ||
PtrMap::One(id, one) => { | ||
debug_assert!(id != key); | ||
let mut hm = HashMap::with_capacity(2); | ||
hm.insert(id, one); | ||
hm.insert(key, val); | ||
mem::replace(map, PtrMap::Many(hm)); | ||
}, | ||
_ => unreachable!() | ||
} | ||
}, | ||
PtrMap::Many(ref mut hm) => { hm.insert(key, val); } | ||
} | ||
} | ||
|
||
#[inline] | ||
pub unsafe fn one(&self) -> &V { | ||
let map = &*self.0.get(); | ||
match *map { | ||
PtrMap::One(_, ref one) => one, | ||
_ => panic!("not PtrMap::One value, {:?}", *map) | ||
} | ||
} | ||
} | ||
|
||
impl<V: ?Sized + fmt::Debug + Any + 'static> Clone for PtrMapCell<V> where Box<V>: Clone { | ||
#[inline] | ||
fn clone(&self) -> PtrMapCell<V> { | ||
let cell = PtrMapCell::new(); | ||
unsafe { | ||
*cell.0.get() = (&*self.0.get()).clone() | ||
} | ||
cell | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
use std::any::TypeId; | ||
use std::fmt; | ||
use std::str::from_utf8; | ||
|
||
use super::cell::{OptCell, PtrMapCell}; | ||
use header::{Header, HeaderFormat}; | ||
|
||
|
||
#[derive(Clone)] | ||
pub struct Item { | ||
raw: OptCell<Vec<Vec<u8>>>, | ||
typed: PtrMapCell<HeaderFormat + Send + Sync> | ||
} | ||
|
||
impl Item { | ||
#[inline] | ||
pub fn new_raw(data: Vec<Vec<u8>>) -> Item { | ||
Item { | ||
raw: OptCell::new(Some(data)), | ||
typed: PtrMapCell::new(), | ||
} | ||
} | ||
|
||
#[inline] | ||
pub fn new_typed(ty: Box<HeaderFormat + Send + Sync>) -> Item { | ||
let map = PtrMapCell::new(); | ||
unsafe { map.insert((&*ty).get_type_id(), ty); } | ||
Item { | ||
raw: OptCell::new(None), | ||
typed: map, | ||
} | ||
} | ||
|
||
#[inline] | ||
pub fn mut_raw(&mut self) -> &mut Vec<Vec<u8>> { | ||
self.typed = PtrMapCell::new(); | ||
unsafe { | ||
self.raw.get_mut() | ||
} | ||
} | ||
|
||
pub fn raw(&self) -> &[Vec<u8>] { | ||
if let Some(ref raw) = *self.raw { | ||
return &raw[..]; | ||
} | ||
|
||
let raw = vec![unsafe { self.typed.one() }.to_string().into_bytes()]; | ||
self.raw.set(raw); | ||
|
||
let raw = self.raw.as_ref().unwrap(); | ||
&raw[..] | ||
} | ||
|
||
pub fn typed<H: Header + HeaderFormat>(&self) -> Option<&H> { | ||
let tid = TypeId::of::<H>(); | ||
match self.typed.get(tid) { | ||
Some(val) => Some(val), | ||
None => { | ||
match parse::<H>(self.raw.as_ref().expect("item.raw must exist")) { | ||
Some(typed) => { | ||
unsafe { self.typed.insert(tid, typed); } | ||
self.typed.get(tid) | ||
}, | ||
None => None | ||
} | ||
} | ||
}.map(|typed| unsafe { typed.downcast_ref_unchecked() }) | ||
} | ||
|
||
pub fn typed_mut<H: Header + HeaderFormat>(&mut self) -> Option<&mut H> { | ||
let tid = TypeId::of::<H>(); | ||
if self.typed.get_mut(tid).is_none() { | ||
match parse::<H>(self.raw.as_ref().expect("item.raw must exist")) { | ||
Some(typed) => { | ||
unsafe { self.typed.insert(tid, typed); } | ||
}, | ||
None => () | ||
} | ||
} | ||
self.typed.get_mut(tid).map(|typed| unsafe { typed.downcast_mut_unchecked() }) | ||
} | ||
} | ||
|
||
#[inline] | ||
fn parse<H: Header + HeaderFormat>(raw: &Vec<Vec<u8>>) -> Option<Box<HeaderFormat + Send + Sync>> { | ||
Header::parse_header(&raw[..]).map(|h: H| box h as Box<HeaderFormat + Send + Sync>) | ||
} | ||
|
||
impl fmt::Display for Item { | ||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { | ||
match *self.raw { | ||
Some(ref raw) => { | ||
for part in raw.iter() { | ||
match from_utf8(&part[..]) { | ||
Ok(s) => try!(fmt.write_str(s)), | ||
Err(e) => { | ||
error!("raw header value is not utf8. header={:?}, error={:?}", part, e); | ||
return Err(fmt::Error); | ||
} | ||
} | ||
} | ||
Ok(()) | ||
}, | ||
None => fmt::Display::fmt(&unsafe { self.typed.one() }, fmt) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
pub use self::item::Item; | ||
|
||
mod cell; | ||
mod item; |
Oops, something went wrong.