X-Git-Url: https://git.chrismorgan.info/anymap/blobdiff_plain/183e9ff248661db509a4196bdd586e3a78bae0c9..f1710353a039c1d5e95d15bb54effd7cab8e047f:/src/lib.rs diff --git a/src/lib.rs b/src/lib.rs index 0a1a692..0e92822 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,20 +1,19 @@ //! This crate provides the `AnyMap` type, a safe and convenient store for one value of each type. -#![crate_name = "anymap"] -#![crate_type = "lib"] #![feature(default_type_params)] -#![warn(unnecessary_qualification, non_uppercase_statics, - variant_size_difference, managed_heap_memory, unnecessary_typecast, - missing_doc, unused_result)] +#![warn(unused_qualifications, non_upper_case_globals, + variant_size_differences, unused_typecasts, + missing_docs, unused_results)] #[cfg(test)] extern crate test; use std::any::Any; -use std::intrinsics::TypeId; -use std::collections::{Collection, HashMap, Mutable}; +use std::intrinsics::{forget, TypeId}; +use std::collections::HashMap; +use std::collections::hash_map; use std::hash::{Hash, Hasher, Writer}; -use std::mem::{transmute, transmute_copy}; +use std::mem::transmute; use std::raw::TraitObject; struct TypeIdHasher; @@ -37,7 +36,7 @@ impl Writer for TypeIdState { } impl Hasher for TypeIdHasher { - fn hash>(&self, value: &T) -> u64 { + fn hash>(&self, value: &T) -> u64 { let mut state = TypeIdState { value: 0, }; @@ -50,14 +49,14 @@ impl Hasher for TypeIdHasher { trait UncheckedAnyRefExt<'a> { /// Returns a reference to the boxed value, assuming that it is of type `T`. This should only be /// called if you are ABSOLUTELY CERTAIN of `T` as you will get really wacky output if it’s not. - unsafe fn as_ref_unchecked(self) -> &'a T; + unsafe fn downcast_ref_unchecked(self) -> &'a T; } -impl<'a> UncheckedAnyRefExt<'a> for &'a Any + 'a { +impl<'a> UncheckedAnyRefExt<'a> for &'a Any { #[inline] - unsafe fn as_ref_unchecked(self) -> &'a T { + unsafe fn downcast_ref_unchecked(self) -> &'a T { // Get the raw representation of the trait object - let to: TraitObject = transmute_copy(&self); + let to: TraitObject = transmute(self); // Extract the data pointer transmute(to.data) @@ -68,14 +67,35 @@ impl<'a> UncheckedAnyRefExt<'a> for &'a Any + 'a { trait UncheckedAnyMutRefExt<'a> { /// Returns a reference to the boxed value, assuming that it is of type `T`. This should only be /// called if you are ABSOLUTELY CERTAIN of `T` as you will get really wacky output if it’s not. - unsafe fn as_mut_unchecked(self) -> &'a mut T; + unsafe fn downcast_mut_unchecked(self) -> &'a mut T; } -impl<'a> UncheckedAnyMutRefExt<'a> for &'a mut Any + 'a { +impl<'a> UncheckedAnyMutRefExt<'a> for &'a mut Any { #[inline] - unsafe fn as_mut_unchecked(self) -> &'a mut T { + unsafe fn downcast_mut_unchecked(self) -> &'a mut T { // Get the raw representation of the trait object - let to: TraitObject = transmute_copy(&self); + let to: TraitObject = transmute(self); + + // Extract the data pointer + transmute(to.data) + } +} + +/// An extension of `BoxAny` allowing unchecked downcasting of trait objects to `Box`. +trait UncheckedBoxAny { + /// Returns the boxed value, assuming that it is of type `T`. This should only be called if you + /// are ABSOLUTELY CERTAIN of `T` as you will get really wacky output if it’s not. + unsafe fn downcast_unchecked(self) -> Box; +} + +impl UncheckedBoxAny for Box { + #[inline] + unsafe fn downcast_unchecked(self) -> Box { + // Get the raw representation of the trait object + let to: TraitObject = *transmute::<&Box, &TraitObject>(&self); + + // Prevent destructor on self being run + forget(self); // Extract the data pointer transmute(to.data) @@ -88,22 +108,22 @@ impl<'a> UncheckedAnyMutRefExt<'a> for &'a mut Any + 'a { /// ```rust /// # use anymap::AnyMap; /// let mut data = AnyMap::new(); -/// assert_eq!(data.find(), None::<&int>); +/// assert_eq!(data.get(), None::<&int>); /// data.insert(42i); -/// assert_eq!(data.find(), Some(&42i)); +/// assert_eq!(data.get(), Some(&42i)); /// data.remove::(); -/// assert_eq!(data.find::(), None); +/// assert_eq!(data.get::(), None); /// /// #[deriving(PartialEq, Show)] /// struct Foo { /// str: String, /// } /// -/// assert_eq!(data.find::(), None); +/// assert_eq!(data.get::(), None); /// data.insert(Foo { str: "foo".to_string() }); -/// assert_eq!(data.find(), Some(&Foo { str: "foo".to_string() })); -/// data.find_mut::().map(|foo| foo.str.push('t')); -/// assert_eq!(data.find::().unwrap().str.as_slice(), "foot"); +/// assert_eq!(data.get(), Some(&Foo { str: "foo".to_string() })); +/// data.get_mut::().map(|foo| foo.str.push('t')); +/// assert_eq!(data.get::().unwrap().str.as_slice(), "foot"); /// ``` /// /// Values containing non-static references are not permitted. @@ -121,77 +141,219 @@ impl AnyMap { } impl AnyMap { + /// Deprecated: Renamed to `get`. + #[deprecated = "Renamed to `get`"] + pub fn find(&self) -> Option<&T> { + self.get::() + } + + /// Deprecated: Renamed to `get_mut`. + #[deprecated = "Renamed to `get_mut`"] + pub fn find_mut(&mut self) -> Option<&mut T> { + self.get_mut::() + } + /// Retrieve the value stored in the map for the type `T`, if it exists. - pub fn find<'a, T: 'static>(&'a self) -> Option<&'a T> { - self.data.find(&TypeId::of::()).map(|any| unsafe { any.as_ref_unchecked::() }) + pub fn get(&self) -> Option<&T> { + self.data.get(&TypeId::of::()) + .map(|any| unsafe { any.downcast_ref_unchecked::() }) } /// Retrieve a mutable reference to the value stored in the map for the type `T`, if it exists. - pub fn find_mut<'a, T: 'static>(&'a mut self) -> Option<&'a mut T> { - self.data.find_mut(&TypeId::of::()).map(|any| unsafe { any.as_mut_unchecked::() }) + pub fn get_mut(&mut self) -> Option<&mut T> { + self.data.get_mut(&TypeId::of::()) + .map(|any| unsafe { any.downcast_mut_unchecked::() }) } /// Set the value contained in the map for the type `T`. - /// This will override any previous value stored. - pub fn insert(&mut self, value: T) { - self.data.insert(TypeId::of::(), box value as Box); + /// If there is a previous value stored, it will be returned. + pub fn insert(&mut self, value: T) -> Option { + self.data.insert(TypeId::of::(), box value as Box) + .map(|any| *unsafe { any.downcast_unchecked::() }) } - /// Remove the value for the type `T` if it existed. - pub fn remove(&mut self) { - self.data.remove(&TypeId::of::()); + /// Remove and return the value for the type `T` if it existed. + pub fn remove(&mut self) -> Option { + self.data.remove(&TypeId::of::()) + .map(|any| *unsafe { any.downcast_unchecked::() }) } /// Does a value of type `T` exist? - pub fn contains(&self) -> bool { + pub fn contains(&self) -> bool { self.data.contains_key(&TypeId::of::()) } -} -impl Collection for AnyMap { - fn len(&self) -> uint { + /// Gets the given key's corresponding entry in the map for in-place manipulation + pub fn entry(&mut self) -> Entry { + match self.data.entry(TypeId::of::()) { + hash_map::Entry::Occupied(e) => Entry::Occupied(OccupiedEntry { entry: e }), + hash_map::Entry::Vacant(e) => Entry::Vacant(VacantEntry { entry: e }), + } + } + + /// Returns the number of items in the collection. + pub fn len(&self) -> uint { self.data.len() } - fn is_empty(&self) -> bool { + /// Returns true if there are no items in the collection. + pub fn is_empty(&self) -> bool { self.data.is_empty() } -} -impl Mutable for AnyMap { - fn clear(&mut self) { + /// Removes all items from the collection. + pub fn clear(&mut self) { self.data.clear(); } } +/// A view into a single occupied location in an AnyMap +pub struct OccupiedEntry<'a, V: 'a> { + entry: hash_map::OccupiedEntry<'a, TypeId, Box>, +} + +/// A view into a single empty location in an AnyMap +pub struct VacantEntry<'a, V: 'a> { + entry: hash_map::VacantEntry<'a, TypeId, Box>, +} + +/// A view into a single location in a map, which may be vacant or occupied +pub enum Entry<'a, V: 'a> { + /// An occupied Entry + Occupied(OccupiedEntry<'a, V>), + /// A vacant Entry + Vacant(VacantEntry<'a, V>), +} + +impl<'a, V: 'static> OccupiedEntry<'a, V> { + /// Gets a reference to the value in the entry + pub fn get(&self) -> &V { + unsafe { self.entry.get().downcast_ref_unchecked() } + } + + /// Gets a mutable reference to the value in the entry + pub fn get_mut(&mut self) -> &mut V { + unsafe { self.entry.get_mut().downcast_mut_unchecked() } + } + + /// Converts the OccupiedEntry into a mutable reference to the value in the entry + /// with a lifetime bound to the map itself + pub fn into_mut(self) -> &'a mut V { + unsafe { self.entry.into_mut().downcast_mut_unchecked() } + } + + /// Sets the value of the entry, and returns the entry's old value + pub fn set(&mut self, value: V) -> V { + unsafe { *self.entry.set(box value as Box).downcast_unchecked() } + } + + /// Takes the value out of the entry, and returns it + pub fn take(self) -> V { + unsafe { *self.entry.take().downcast_unchecked() } + } +} + +impl<'a, V: 'static> VacantEntry<'a, V> { + /// Sets the value of the entry with the VacantEntry's key, + /// and returns a mutable reference to it + pub fn set(self, value: V) -> &'a mut V { + unsafe { self.entry.set(box value as Box).downcast_mut_unchecked() } + } +} + #[bench] fn bench_insertion(b: &mut ::test::Bencher) { b.iter(|| { let mut data = AnyMap::new(); for _ in range(0u, 100) { - data.insert(42i); + let _ = data.insert(42i); } }) } #[bench] -fn bench_find_missing(b: &mut ::test::Bencher) { +fn bench_get_missing(b: &mut ::test::Bencher) { b.iter(|| { let data = AnyMap::new(); for _ in range(0u, 100) { - assert_eq!(data.find(), None::<&int>); + assert_eq!(data.get(), None::<&int>); } }) } #[bench] -fn bench_find_present(b: &mut ::test::Bencher) { +fn bench_get_present(b: &mut ::test::Bencher) { b.iter(|| { let mut data = AnyMap::new(); - data.insert(42i); + let _ = data.insert(42i); // These inner loops are a feeble attempt to drown the other factors. for _ in range(0u, 100) { - assert_eq!(data.find(), Some(&42i)); + assert_eq!(data.get(), Some(&42i)); } }) } + +#[test] +fn test_entry() { + #[deriving(Show, PartialEq)] struct A(int); + #[deriving(Show, PartialEq)] struct B(int); + #[deriving(Show, PartialEq)] struct C(int); + #[deriving(Show, PartialEq)] struct D(int); + #[deriving(Show, PartialEq)] struct E(int); + #[deriving(Show, PartialEq)] struct F(int); + #[deriving(Show, PartialEq)] struct J(int); + + let mut map: AnyMap = AnyMap::new(); + assert_eq!(map.insert(A(10)), None); + assert_eq!(map.insert(B(20)), None); + assert_eq!(map.insert(C(30)), None); + assert_eq!(map.insert(D(40)), None); + assert_eq!(map.insert(E(50)), None); + assert_eq!(map.insert(F(60)), None); + + // Existing key (insert) + match map.entry::() { + Entry::Vacant(_) => unreachable!(), + Entry::Occupied(mut view) => { + assert_eq!(view.get(), &A(10)); + assert_eq!(view.set(A(100)), A(10)); + } + } + assert_eq!(map.get::().unwrap(), &A(100)); + assert_eq!(map.len(), 6); + + + // Existing key (update) + match map.entry::() { + Entry::Vacant(_) => unreachable!(), + Entry::Occupied(mut view) => { + let v = view.get_mut(); + let new_v = B(v.0 * 10); + *v = new_v; + } + } + assert_eq!(map.get().unwrap(), &B(200)); + assert_eq!(map.len(), 6); + + + // Existing key (take) + match map.entry::() { + Entry::Vacant(_) => unreachable!(), + Entry::Occupied(view) => { + assert_eq!(view.take(), C(30)); + } + } + assert_eq!(map.get::(), None); + assert_eq!(map.len(), 5); + + + // Inexistent key (insert) + match map.entry::() { + Entry::Occupied(_) => unreachable!(), + Entry::Vacant(view) => { + assert_eq!(*view.set(J(1000)), J(1000)); + } + } + assert_eq!(map.get::().unwrap(), &J(1000)); + assert_eq!(map.len(), 6); +}