use std::cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd};
use std::collections::HashMap;
use std::default::Default;
use std::fmt;
use std::hash::{Hash, Hasher};
use super::{Symbol, SymbolId, Table};
#[derive(Clone, Eq, Ord, Hash, PartialEq, PartialOrd)]
pub enum Insertion<T> {
Present(T),
New(T),
}
impl<T> Insertion<T> {
pub fn map<F, X>(&self, f: F) -> Insertion<X> where F: FnOnce(&T) -> X {
match *self {
Insertion::Present(ref s) => Insertion::Present(f(s)),
Insertion::New(ref s) => Insertion::New(f(s)),
}
}
pub fn unwrap(self) -> T {
match self {
Insertion::Present(s) => s,
Insertion::New(s) => s,
}
}
}
pub struct Ref<T> { ptr: *const T, }
unsafe impl<T> Send for Ref<T> where T: Send { }
unsafe impl<T> Sync for Ref<T> where T: Sync { }
impl<T> Ref<T> {
fn new(data: &T) -> Self {
Ref { ptr: data as *const T, }
}
unsafe fn deref<'a>(&self) -> &'a T {
&*self.ptr
}
}
impl<T> Clone for Ref<T> {
fn clone(&self) -> Self {
Ref { ptr: self.ptr, }
}
}
impl<T> Copy for Ref<T> { }
impl<T> fmt::Pointer for Ref<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Pointer::fmt(&self.ptr, f)
}
}
impl<T> fmt::Debug for Ref<T> where T: fmt::Debug {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Ref({:?})", unsafe { &(*self.ptr) })
}
}
impl<T> Eq for Ref<T> where T: Eq { }
impl<T> Hash for Ref<T> where T: Hash {
fn hash<H>(&self, h: &mut H) where H: Hasher {
unsafe { (*self.ptr).hash(h) }
}
}
impl<T> Ord for Ref<T> where T: Ord {
fn cmp(&self, other: &Self) -> Ordering {
unsafe { (*self.ptr).cmp(&(*other.ptr)) }
}
}
impl<T> PartialEq for Ref<T> where T: PartialEq {
fn eq(&self, other: &Self) -> bool {
unsafe { (*self.ptr).eq(&(*other.ptr)) }
}
}
impl<T> PartialOrd for Ref<T> where T: PartialOrd {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
unsafe { (*self.ptr).partial_cmp(&(*other.ptr)) }
}
}
pub trait Indexing: Default {
type Data;
type Id: SymbolId;
fn from_table(table: Table<Self::Data, Self::Id>) -> Self;
fn table(&self) -> &Table<Self::Data, Self::Id>;
fn to_table(self) -> Table<Self::Data, Self::Id>;
fn get(&self, data: &Self::Data) -> Option<&Symbol<Self::Data, Self::Id>>;
fn get_or_insert<'s>(&'s mut self, data: Self::Data)
-> Insertion<&'s Symbol<Self::Data, Self::Id>>;
fn get_symbol<'s>(&'s self, id: &Self::Id) -> Option<&'s Symbol<Self::Data, Self::Id>>;
}
#[derive(Debug)]
pub struct HashIndexing<T, D> where T: Eq + Hash, D: SymbolId {
table: Table<T, D>,
by_symbol: HashMap<Ref<T>, Ref<Symbol<T, D>>>,
by_id: Vec<Ref<Symbol<T, D>>>,
}
impl<T, D> Default for HashIndexing<T, D> where T: Eq + Hash, D: SymbolId {
fn default() -> Self {
HashIndexing {
table: Table::new(),
by_symbol: HashMap::new(),
by_id: Vec::new(),
}
}
}
impl<T, D> Indexing for HashIndexing<T, D> where T: Eq + Hash, D: SymbolId {
type Data = T;
type Id = D;
fn from_table(table: Table<T, D>) -> Self {
let mut by_symbol = HashMap::with_capacity(table.len());
let mut by_id =
match table.iter().next() {
Some(symbol) => vec![Ref::new(symbol); table.len()],
None => Vec::new(),
};
for symbol in table.iter() {
by_symbol.insert(Ref::new(symbol.data()), Ref::new(symbol));
by_id[symbol.id().as_usize()] = Ref::new(symbol);
}
HashIndexing {
table: table,
by_symbol: by_symbol,
by_id: by_id,
}
}
fn table(&self) -> &Table<Self::Data, Self::Id> { &self.table }
fn to_table(self) -> Table<Self::Data, Self::Id> { self.table }
fn get<'s>(&'s self, data: &T) -> Option<&'s Symbol<T, D>> {
self.by_symbol.get(&Ref::new(data)).map(|x| unsafe { x.deref() })
}
fn get_or_insert<'s>(&'s mut self, data: T) -> Insertion<&'s Symbol<T, D>> {
use std::collections::hash_map::Entry;
if let Entry::Occupied(e) = self.by_symbol.entry(Ref::new(&data)) {
return Insertion::Present(unsafe { e.get().deref() })
}
let symbol = self.table.insert(data);
self.by_symbol.insert(Ref::new(symbol.data()), Ref::new(symbol));
self.by_id.push(Ref::new(symbol));
Insertion::New(symbol)
}
fn get_symbol<'s>(&'s self, id: &D) -> Option<&'s Symbol<T, D>> {
self.by_id.get(id.as_usize()).map(|x| unsafe { x.deref() })
}
}
#[cfg(test)]
mod test {
use super::{HashIndexing, Indexing, Insertion, Ref};
use ::{SymbolId, Table};
use std::collections::hash_map::DefaultHasher;
use std::cmp::Ordering;
use std::hash::{Hash, Hasher};
use std::str::FromStr;
const VALUES: &'static [usize] = &[101, 203, 500, 30, 0, 1];
#[test]
fn ref_impls_ok() {
let x1 = String::from_str("foo").unwrap();
let x2 = String::from_str("foo").unwrap();
let x3 = String::from_str("fo").unwrap();
let x4 = String::from_str("fox").unwrap();
assert!(x1 == x2);
assert!(&x1 as *const String != &x2 as *const String);
let r1 = Ref::new(&x1);
let r2 = Ref::new(&x2);
let r3 = Ref::new(&x3);
let r4 = Ref::new(&x4);
assert_eq!(r1, r2);
assert!(r1 != r3);
assert!(r1 != r4);
assert_eq!(r1.cmp(&r2), Ordering::Equal);
assert_eq!(r1.cmp(&r3), Ordering::Greater);
assert_eq!(r1.cmp(&r4), Ordering::Less);
let mut rh1 = DefaultHasher::new();
let mut rh2 = DefaultHasher::new();
let mut rh3 = DefaultHasher::new();
let mut rh4 = DefaultHasher::new();
r1.hash(&mut rh1);
r2.hash(&mut rh2);
r3.hash(&mut rh3);
r4.hash(&mut rh4);
let rh1 = rh1.finish();
let rh2 = rh2.finish();
let rh3 = rh3.finish();
let rh4 = rh4.finish();
let mut xh1 = DefaultHasher::new();
let mut xh2 = DefaultHasher::new();
let mut xh3 = DefaultHasher::new();
let mut xh4 = DefaultHasher::new();
x1.hash(&mut xh1);
x2.hash(&mut xh2);
x3.hash(&mut xh3);
x4.hash(&mut xh4);
let xh1 = xh1.finish();
let xh2 = xh2.finish();
let xh3 = xh3.finish();
let xh4 = xh4.finish();
assert_eq!(rh1, xh1);
assert_eq!(rh2, xh2);
assert_eq!(rh3, xh3);
assert_eq!(rh4, xh4);
}
#[test]
fn hash_indexing_empty_ok() {
let t = Table::<usize, usize>::new();
assert_eq!(t.len(), 0);
let i = HashIndexing::from_table(t);
assert!(i.by_symbol.is_empty());
assert!(i.by_id.is_empty());
}
#[test]
fn hash_indexing_from_table_ok() {
let mut t = Table::<usize, usize>::new();
for v in VALUES.iter() {
t.insert(*v);
}
let expected_len = t.len();
let expected_values: Vec<(usize, usize)> =
t.iter().map(|s| (*s.data(), *s.id())).collect();
let i = HashIndexing::from_table(t);
assert_eq!(i.by_symbol.len(), expected_len);
assert_eq!(i.by_id.len(), expected_len);
for (data, id) in expected_values.into_iter() {
let data_ref = Ref::new(&data);
unsafe {
assert_eq!(i.by_symbol.get(&data_ref).unwrap().deref().data(), &data);
assert_eq!(i.by_symbol.get(&data_ref).unwrap().deref().id(), &id);
assert_eq!(i.by_id[id.as_usize()].deref().data(), &data);
}
}
}
#[test]
fn hash_indexing_empty_insertion_ok() {
let mut i = HashIndexing::<usize, usize>::default();
for v in VALUES.iter() {
assert!(i.get(v).is_none());
let id = match i.get_or_insert(*v) {
Insertion::Present(_) => panic!(),
Insertion::New(symbol) => {
assert_eq!(symbol.data(), v);
*symbol.id()
},
};
assert_eq!(i.get_symbol(&id).unwrap().data(), v);
}
}
#[test]
fn indexed_present_ok() {
let mut t = Table::<usize, usize>::new();
for v in VALUES.iter() {
t.insert(*v);
}
let mut i = HashIndexing::from_table(t);
for v in VALUES.iter() {
assert_eq!(i.get(v).unwrap().data(), v);
let id = match i.get_or_insert(*v) {
Insertion::New(_) => panic!(),
Insertion::Present(symbol) => {
assert_eq!(symbol.data(), v);
*symbol.id()
},
};
assert_eq!(i.get_symbol(&id).unwrap().data(), v);
}
}
#[test]
fn send_to_thread_safe_ok() {
use std::sync::Arc;
use std::thread;
let mut t = Table::<usize, usize>::new();
for v in VALUES.iter() {
t.insert(*v);
}
let index = Arc::new(HashIndexing::from_table(t));
{
let id1 = index.get(&VALUES[0]).unwrap().id().clone();
let id2 = index.get(&VALUES[1]).unwrap().id().clone();
let t1 = {
let index = index.clone();
thread::spawn(move || index.get_symbol(&id1).map(|x| (*x.data(), x.id().clone())))
};
let t2 = {
let index = index.clone();
thread::spawn(move || index.get_symbol(&id2).map(|x| (*x.data(), x.id().clone())))
};
let v1 = index.get(&VALUES[0]).unwrap();
let v2 = index.get(&VALUES[1]).unwrap();
match t1.join() {
Ok(Some((data, id))) => {
assert_eq!(&id, v1.id());
assert_eq!(data, *v1.data());
},
_ => panic!(),
}
match t2.join() {
Ok(Some((data, id))) => {
assert_eq!(&id, v2.id());
assert_eq!(data, *v2.data());
},
_ => panic!(),
}
}
}
#[test]
fn sync_to_thread_ok() {
use ::crossbeam;
let mut t = Table::<usize, usize>::new();
for v in VALUES.iter() {
t.insert(*v);
}
let index = HashIndexing::from_table(t);
let id1 = *index.get(&VALUES[0]).unwrap().id();
let id2 = *index.get(&VALUES[1]).unwrap().id();
let index = &index;
let t1 =
crossbeam::scope(move |scope| scope.spawn(move || index.get_symbol(&id1).map(|x| (x.data(), x.id()))));
let t2 =
crossbeam::scope(move |scope| scope.spawn(move || index.get_symbol(&id2).map(|x| (x.data(), x.id()))));
let v1 = index.get(&VALUES[0]).unwrap();
let v2 = index.get(&VALUES[1]).unwrap();
match t1.join() {
Some((data, id)) => {
assert_eq!(id, v1.id());
assert_eq!(data, v1.data());
},
_ => panic!(),
}
match t2.join() {
Some((data, id)) => {
assert_eq!(id, v2.id());
assert_eq!(data, v2.data());
},
_ => panic!(),
}
}
}