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.
This commit is contained in:
131
src/header/internals/cell.rs
Normal file
131
src/header/internals/cell.rs
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
107
src/header/internals/item.rs
Normal file
107
src/header/internals/item.rs
Normal file
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
4
src/header/internals/mod.rs
Normal file
4
src/header/internals/mod.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
pub use self::item::Item;
|
||||
|
||||
mod cell;
|
||||
mod item;
|
||||
Reference in New Issue
Block a user