X-Git-Url: https://git.chrismorgan.info/anymap/blobdiff_plain/c29e78c5634f9e6741287e9cfb281a73d4eddcc6..8cc1b0c9f0211c68301d02cff9a490984a6638b9:/src/lib.rs diff --git a/src/lib.rs b/src/lib.rs index 80e5104..0e92822 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,5 @@ //! 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(unused_qualifications, non_upper_case_globals, variant_size_differences, unused_typecasts, @@ -13,8 +11,9 @@ extern crate test; use std::any::Any; 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; @@ -53,11 +52,11 @@ trait UncheckedAnyRefExt<'a> { 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 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) @@ -71,11 +70,11 @@ trait UncheckedAnyMutRefExt<'a> { 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 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) @@ -184,6 +183,14 @@ impl AnyMap { self.data.contains_key(&TypeId::of::()) } + /// 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() @@ -200,6 +207,60 @@ impl AnyMap { } } +/// 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(|| { @@ -231,3 +292,68 @@ fn bench_get_present(b: &mut ::test::Bencher) { } }) } + +#[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); +}