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(UnsafeCell>); impl OptCell { #[inline] pub fn new(val: Option) -> OptCell { 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 Deref for OptCell { type Target = Option; #[inline] fn deref(&self) -> &Option { unsafe { &*self.0.get() } } } impl Clone for OptCell { #[inline] fn clone(&self) -> OptCell { OptCell::new((**self).clone()) } } pub struct PtrMapCell(UnsafeCell>>); #[derive(Clone, Debug)] enum PtrMap { Empty, One(TypeId, T), Many(HashMap) } impl PtrMapCell { #[inline] pub fn new() -> PtrMapCell { 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) { 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 Clone for PtrMapCell where Box: Clone { #[inline] fn clone(&self) -> PtrMapCell { let cell = PtrMapCell::new(); unsafe { *cell.0.get() = (&*self.0.get()).clone() } cell } } #[cfg(test)] mod test { use std::any::TypeId; use super::*; #[test] fn test_opt_cell_set() { let one:OptCell = OptCell::new(None); one.set(1); assert_eq!(*one,Some(1)); } #[test] fn test_opt_cell_clone() { let one:OptCell = OptCell::new(Some(3)); let stored = *one.clone(); assert_eq!(stored,Some(3)); } #[test] fn test_ptr_map_cell_none() { let type_id = TypeId::of::(); let pm:PtrMapCell = PtrMapCell::new(); assert_eq!(pm.get(type_id),None); } #[test] fn test_ptr_map_cell_one() { let type_id = TypeId::of::(); let pm:PtrMapCell = PtrMapCell::new(); unsafe { pm.insert(type_id, Box::new("a".to_string())); } assert_eq!(pm.get(type_id), Some(&"a".to_string())); assert_eq!(unsafe {pm.one()}, "a"); } #[test] fn test_ptr_map_cell_two() { let type_id = TypeId::of::(); let type_id2 = TypeId::of::>(); let pm:PtrMapCell = PtrMapCell::new(); unsafe { pm.insert(type_id, Box::new("a".to_string())); } unsafe { pm.insert(type_id2, Box::new("b".to_string())); } assert_eq!(pm.get(type_id), Some(&"a".to_string())); assert_eq!(pm.get(type_id2), Some(&"b".to_string())); } #[test] fn test_ptr_map_cell_many() { let id1 = TypeId::of::(); let id2 = TypeId::of::>(); let id3 = TypeId::of::>(); let pm:PtrMapCell = PtrMapCell::new(); unsafe { pm.insert(id1, Box::new("a".to_string())); } unsafe { pm.insert(id2, Box::new("b".to_string())); } unsafe { pm.insert(id3, Box::new("c".to_string())); } assert_eq!(pm.get(id1), Some(&"a".to_string())); assert_eq!(pm.get(id2), Some(&"b".to_string())); assert_eq!(pm.get(id3), Some(&"c".to_string())); } #[test] fn test_ptr_map_cell_clone() { let type_id = TypeId::of::(); let pm:PtrMapCell = PtrMapCell::new(); unsafe { pm.insert(type_id, Box::new("a".to_string())); } let cloned = pm.clone(); assert_eq!(cloned.get(type_id), Some(&"a".to_string())); } }