From 143ee062680311ca9c2ed5b7089bb0d741bc17c0 Mon Sep 17 00:00:00 2001 From: Chris Morgan Date: Tue, 24 Mar 2015 13:42:01 +1100 Subject: [PATCH] Substantial refactoring, exposing a raw interface. MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit This is not necessarily the final form, but I think it’s pretty good. The only alteration to the public interface is the removal of the iteration methods from `AnyMap`; they are now attached to `RawAnyMap`. The diff appears considerably more scary than it is in actual fact due to some comparatively unnecessary changes like the field name (from `data` to `raw`). Really, it’s minimal. --- src/lib.rs | 406 ++++++++++++++----------------------------- src/raw.rs | 318 +++++++++++++++++++++++++++++++++ src/unchecked_any.rs | 24 +++ 3 files changed, 473 insertions(+), 275 deletions(-) create mode 100644 src/raw.rs create mode 100644 src/unchecked_any.rs diff --git a/src/lib.rs b/src/lib.rs index a4a5559..097b6bd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,99 +10,83 @@ extern crate test; use std::any::{Any, TypeId}; -use std::mem::forget; -use std::collections::HashMap; -use std::collections::hash_map; -use std::hash::Hasher; -use std::collections::hash_state::HashState; -use std::mem::transmute; -use std::raw::TraitObject; use std::marker::PhantomData; -struct TypeIdHasher { - value: u64, -} - -struct TypeIdState; - -impl HashState for TypeIdState { - type Hasher = TypeIdHasher; - - fn hasher(&self) -> TypeIdHasher { - TypeIdHasher { value: 0 } - } -} - -impl Hasher for TypeIdHasher { - #[inline(always)] - fn write(&mut self, bytes: &[u8]) { - // This expects to receive one and exactly one 64-bit value - debug_assert!(bytes.len() == 8); - unsafe { - std::ptr::copy_nonoverlapping(&mut self.value, transmute(&bytes[0]), 1) +use raw::RawAnyMap; +use unchecked_any::UncheckedAnyExt; + +macro_rules! impl_common_methods { + ( + field: $t:ident.$field:ident; + new() => $new:expr; + with_capacity($with_capacity_arg:ident) => $with_capacity:expr; + ) => { + impl $t { + /// Create an empty collection. + #[inline] + pub fn new() -> $t { + $t { + $field: $new, + } + } + + /// Creates an empty collection with the given initial capacity. + #[inline] + pub fn with_capacity($with_capacity_arg: usize) -> $t { + $t { + $field: $with_capacity, + } + } + + /// Returns the number of elements the collection can hold without reallocating. + #[inline] + pub fn capacity(&self) -> usize { + self.$field.capacity() + } + + /// Reserves capacity for at least `additional` more elements to be inserted + /// in the collection. The collection may reserve more space to avoid + /// frequent reallocations. + /// + /// # Panics + /// + /// Panics if the new allocation size overflows `usize`. + #[inline] + pub fn reserve(&mut self, additional: usize) { + self.$field.reserve(additional) + } + + /// Shrinks the capacity of the collection as much as possible. It will drop + /// down as much as possible while maintaining the internal rules + /// and possibly leaving some space in accordance with the resize policy. + #[inline] + pub fn shrink_to_fit(&mut self) { + self.$field.shrink_to_fit() + } + + /// Returns the number of items in the collection. + #[inline] + pub fn len(&self) -> usize { + self.$field.len() + } + + /// Returns true if there are no items in the collection. + #[inline] + pub fn is_empty(&self) -> bool { + self.$field.is_empty() + } + + /// Removes all items from the collection. Keeps the allocated memory for reuse. + #[inline] + pub fn clear(&mut self) { + self.$field.clear() + } } } - - #[inline(always)] - fn finish(&self) -> u64 { self.value } -} - -/// An extension of `AnyRefExt` allowing unchecked downcasting of trait objects to `&T`. -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 downcast_ref_unchecked(self) -> &'a T; } -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(self); - - // Extract the data pointer - transmute(to.data) - } -} - -/// An extension of `AnyMutRefExt` allowing unchecked downcasting of trait objects to `&mut T`. -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 downcast_mut_unchecked(self) -> &'a mut T; -} - -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(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) - } -} +mod unchecked_any; +pub mod raw; /// A collection containing zero or one values for any given type and allowing convenient, /// type-safe access to those values. @@ -129,98 +113,28 @@ impl UncheckedBoxAny for Box { /// ``` /// /// Values containing non-static references are not permitted. +#[derive(Debug)] pub struct AnyMap { - data: HashMap, TypeIdState>, + raw: RawAnyMap, } -impl AnyMap { - /// Construct a new `AnyMap`. - #[inline] - pub fn new() -> AnyMap { - AnyMap { - data: HashMap::with_hash_state(TypeIdState), - } - } - - /// Creates an empty AnyMap with the given initial capacity. - #[inline] - pub fn with_capcity(capacity: usize) -> AnyMap { - AnyMap { - data: HashMap::with_capacity_and_hash_state(capacity, TypeIdState), - } - } - - /// Returns the number of elements the collection can hold without reallocating. - #[inline] - pub fn capacity(&self) -> usize { - self.data.capacity() - } - - /// Reserves capacity for at least `additional` more elements to be inserted - /// in the `AnyMap`. The collection may reserve more space to avoid - /// frequent reallocations. - /// - /// # Panics - /// - /// Panics if the new allocation size overflows `usize`. - #[inline] - pub fn reserve(&mut self, additional: usize) { - self.data.reserve(additional) - } - - /// Shrinks the capacity of the collection as much as possible. It will drop - /// down as much as possible while maintaining the internal rules - /// and possibly leaving some space in accordance with the resize policy. - #[inline] - pub fn shrink_to_fit(&mut self) { - self.data.shrink_to_fit() - } - - /// An iterator visiting all items in the collection in arbitrary order. - /// Iterator element type is `&Any`. - /// - /// This is probably not a great deal of use. - #[inline] - pub fn iter(&self) -> Iter { - Iter { - inner: self.data.iter(), - } - } - - /// An iterator visiting all items in the collection in arbitrary order. - /// Iterator element type is `&mut Any`. - /// - /// This is probably not a great deal of use. - #[inline] - pub fn iter_mut(&mut self) -> IterMut { - IterMut { - inner: self.data.iter_mut(), - } - } - - /// An iterator visiting all items in the collection in arbitrary order. - /// Creates a consuming iterator, that is, one that moves each item - /// out of the map in arbitrary order. The map cannot be used after - /// calling this. - /// - /// Iterator element type is `Box`. - #[inline] - pub fn into_iter(self) -> IntoIter { - IntoIter { - inner: self.data.into_iter(), - } - } +impl_common_methods! { + field: AnyMap.raw; + new() => RawAnyMap::new(); + with_capacity(capacity) => RawAnyMap::with_capacity(capacity); +} +impl AnyMap { /// Returns a reference to the value stored in the collection for the type `T`, if it exists. pub fn get(&self) -> Option<&T> { - self.data.get(&TypeId::of::()) + self.raw.get(&TypeId::of::()) .map(|any| unsafe { any.downcast_ref_unchecked::() }) } /// Returns a mutable reference to the value stored in the collection for the type `T`, /// if it exists. pub fn get_mut(&mut self) -> Option<&mut T> { - self.data.get_mut(&TypeId::of::()) + self.raw.get_mut(&TypeId::of::()) .map(|any| unsafe { any.downcast_mut_unchecked::() }) } @@ -228,80 +142,91 @@ impl AnyMap { /// If the collection already had a value of type `T`, that value is returned. /// Otherwise, `None` is returned. pub fn insert(&mut self, value: T) -> Option { - self.data.insert(TypeId::of::(), Box::new(value)) - .map(|any| *unsafe { any.downcast_unchecked::() }) + unsafe { + self.raw.insert(TypeId::of::(), Box::new(value)) + .map(|any| *any.downcast_unchecked::()) + } } /// Removes the `T` value from the collection, /// returning it if there was one or `None` if there was not. pub fn remove(&mut self) -> Option { - self.data.remove(&TypeId::of::()) + self.raw.remove(&TypeId::of::()) .map(|any| *unsafe { any.downcast_unchecked::() }) } /// Returns true if the collection contains a value of type `T`. + #[inline] pub fn contains(&self) -> bool { - self.data.contains_key(&TypeId::of::()) + self.raw.contains_key(&TypeId::of::()) } /// Gets the entry for the given type in the collection 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, + match self.raw.entry(TypeId::of::()) { + raw::Entry::Occupied(e) => Entry::Occupied(OccupiedEntry { + inner: e, type_: PhantomData, }), - hash_map::Entry::Vacant(e) => Entry::Vacant(VacantEntry { - entry: e, + raw::Entry::Vacant(e) => Entry::Vacant(VacantEntry { + inner: e, type_: PhantomData, }), } } - /// Returns the number of items in the collection. + /// Get a reference to the raw untyped map underlying the `AnyMap`. + /// + /// Normal users will not need to use this, but generic libraries working with an `AnyMap` may + /// just find a use for it occasionally. #[inline] - pub fn len(&self) -> usize { - self.data.len() + pub fn as_raw(&self) -> &RawAnyMap { + &self.raw } - /// Returns true if there are no items in the collection. + /// Get a mutable reference to the raw untyped map underlying the `AnyMap`. + /// + /// Normal users will not need to use this, but generic libraries working with an `AnyMap` may + /// just find a use for it occasionally. #[inline] - pub fn is_empty(&self) -> bool { - self.data.is_empty() + pub fn as_raw_mut(&mut self) -> &mut RawAnyMap { + &mut self.raw } - /// Clears the map, returning all items as an iterator. - /// - /// Iterator element type is `Box`. + /// Convert the `AnyMap` into the raw untyped map that underlyies it. /// - /// Keeps the allocated memory for reuse. + /// Normal users will not need to use this, but generic libraries working with an `AnyMap` may + /// just find a use for it occasionally. #[inline] - pub fn drain(&mut self) -> Drain { - Drain { - inner: self.data.drain(), - } + pub fn into_raw(self) -> RawAnyMap { + self.raw } - /// Removes all items from the collection. Keeps the allocated memory for reuse. + /// Convert a raw untyped map into an `AnyMap`. + /// + /// Normal users will not need to use this, but generic libraries working with an `AnyMap` may + /// just find a use for it occasionally. #[inline] - pub fn clear(&mut self) { - self.data.clear() + pub fn from_raw(raw: RawAnyMap) -> AnyMap { + AnyMap { + raw: raw, + } } } -/// A view into a single occupied location in an AnyMap +/// A view into a single occupied location in an `AnyMap`. pub struct OccupiedEntry<'a, V: 'a> { - entry: hash_map::OccupiedEntry<'a, TypeId, Box>, + inner: raw::OccupiedEntry<'a>, type_: PhantomData, } -/// A view into a single empty location in an AnyMap +/// A view into a single empty location in an `AnyMap`. pub struct VacantEntry<'a, V: 'a> { - entry: hash_map::VacantEntry<'a, TypeId, Box>, + inner: raw::VacantEntry<'a>, type_: PhantomData, } -/// A view into a single location in an AnyMap, which may be vacant or occupied +/// A view into a single location in an `AnyMap`, which may be vacant or occupied. pub enum Entry<'a, V: 'a> { /// An occupied Entry Occupied(OccupiedEntry<'a, V>), @@ -313,8 +238,8 @@ impl<'a, V: Any + Clone> Entry<'a, V> { /// Returns a mutable reference to the entry if occupied, or the VacantEntry if vacant pub fn get(self) -> Result<&'a mut V, VacantEntry<'a, V>> { match self { - Entry::Occupied(entry) => Ok(entry.into_mut()), - Entry::Vacant(entry) => Err(entry), + Entry::Occupied(inner) => Ok(inner.into_mut()), + Entry::Vacant(inner) => Err(inner), } } } @@ -322,28 +247,28 @@ impl<'a, V: Any + Clone> Entry<'a, V> { impl<'a, V: Any> OccupiedEntry<'a, V> { /// Gets a reference to the value in the entry pub fn get(&self) -> &V { - unsafe { self.entry.get().downcast_ref_unchecked() } + unsafe { self.inner.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() } + unsafe { self.inner.get_mut().downcast_mut_unchecked() } } /// Converts the OccupiedEntry into a mutable reference to the value in the entry /// with a lifetime bound to the collection itself pub fn into_mut(self) -> &'a mut V { - unsafe { self.entry.into_mut().downcast_mut_unchecked() } + unsafe { self.inner.into_mut().downcast_mut_unchecked() } } /// Sets the value of the entry, and returns the entry's old value pub fn insert(&mut self, value: V) -> V { - unsafe { *self.entry.insert(Box::new(value)).downcast_unchecked() } + unsafe { *self.inner.insert(Box::new(value)).downcast_unchecked() } } /// Takes the value out of the entry, and returns it pub fn remove(self) -> V { - unsafe { *self.entry.remove().downcast_unchecked() } + unsafe { *self.inner.remove().downcast_unchecked() } } } @@ -351,79 +276,10 @@ impl<'a, V: Any> VacantEntry<'a, V> { /// Sets the value of the entry with the VacantEntry's key, /// and returns a mutable reference to it pub fn insert(self, value: V) -> &'a mut V { - unsafe { self.entry.insert(Box::new(value)).downcast_mut_unchecked() } + unsafe { self.inner.insert(Box::new(value)).downcast_mut_unchecked() } } } -/// `AnyMap` iterator. -#[derive(Clone)] -pub struct Iter<'a> { - inner: hash_map::Iter<'a, TypeId, Box>, -} - -/// `AnyMap` mutable references iterator. -pub struct IterMut<'a> { - inner: hash_map::IterMut<'a, TypeId, Box>, -} - -/// `AnyMap` draining iterator. -pub struct Drain<'a> { - inner: hash_map::Drain<'a, TypeId, Box>, -} - -/// `AnyMap` move iterator. -pub struct IntoIter { - inner: hash_map::IntoIter>, -} - -impl<'a> Iterator for Iter<'a> { - type Item = &'a Any; - - #[inline] - fn next(&mut self) -> Option<&'a Any> { - self.inner.next().map(|item| &**item.1) - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } -} - -impl<'a> Iterator for IterMut<'a> { - type Item = &'a mut Any; - - #[inline] - fn next(&mut self) -> Option<&'a mut Any> { - self.inner.next().map(|item| &mut **item.1) - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } -} - -impl<'a> Iterator for Drain<'a> { - type Item = Box; - - #[inline] - fn next(&mut self) -> Option> { - self.inner.next().map(|item| item.1) - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } -} - -impl Iterator for IntoIter { - type Item = Box; - - #[inline] - fn next(&mut self) -> Option> { - self.inner.next().map(|item| item.1) - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } -} - #[bench] fn bench_insertion(b: &mut ::test::Bencher) { b.iter(|| { diff --git a/src/raw.rs b/src/raw.rs new file mode 100644 index 0000000..db96a0c --- /dev/null +++ b/src/raw.rs @@ -0,0 +1,318 @@ +//! The raw form of an AnyMap, allowing untyped access. +//! +//! All relevant details are in the `RawAnyMap` struct. + +use std::any::{Any, TypeId}; +use std::borrow::Borrow; +use std::collections::hash_map::{self, HashMap}; +use std::collections::hash_state::HashState; +use std::default::Default; +use std::hash::{Hash, Hasher}; +use std::iter::IntoIterator; +use std::mem; +use std::ops::{Index, IndexMut}; +use std::ptr; + +struct TypeIdHasher { + value: u64, +} + +struct TypeIdState; + +impl HashState for TypeIdState { + type Hasher = TypeIdHasher; + + fn hasher(&self) -> TypeIdHasher { + TypeIdHasher { value: 0 } + } +} + +impl Hasher for TypeIdHasher { + #[inline(always)] + fn write(&mut self, bytes: &[u8]) { + // This expects to receive one and exactly one 64-bit value + debug_assert!(bytes.len() == 8); + unsafe { + ptr::copy_nonoverlapping(&mut self.value, mem::transmute(&bytes[0]), 1) + } + } + + #[inline(always)] + fn finish(&self) -> u64 { self.value } +} + + +/// The raw, underlying form of an AnyMap. +/// +/// At its essence, this is a wrapper around `HashMap>`, with the portions that +/// would be memory-unsafe removed or marked unsafe. Normal people are expected to use the safe +/// `AnyMap` interface instead, but there is the occasional use for this such as iteration over the +/// contents of an `AnyMap`. However, because you will then be dealing with `Any` trait objects, it +/// doesn’t tend to be so very useful. Still, if you need it, it’s here. +#[derive(Debug)] +pub struct RawAnyMap { + inner: HashMap, TypeIdState>, +} + +impl Default for RawAnyMap { + fn default() -> RawAnyMap { + RawAnyMap::new() + } +} + +impl_common_methods! { + field: RawAnyMap.inner; + new() => HashMap::with_hash_state(TypeIdState); + with_capacity(capacity) => HashMap::with_capacity_and_hash_state(capacity, TypeIdState); +} + +/// RawAnyMap iterator. +#[derive(Clone)] +pub struct Iter<'a> { + inner: hash_map::Iter<'a, TypeId, Box>, +} +impl<'a> Iterator for Iter<'a> { + type Item = &'a Any; + #[inline] fn next(&mut self) -> Option<&'a Any> { self.inner.next().map(|x| &**x.1) } + #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } +} +impl<'a> ExactSizeIterator for Iter<'a> { + #[inline] fn len(&self) -> usize { self.inner.len() } +} + +/// RawAnyMap mutable iterator. +pub struct IterMut<'a> { + inner: hash_map::IterMut<'a, TypeId, Box>, +} +impl<'a> Iterator for IterMut<'a> { + type Item = &'a mut Any; + #[inline] fn next(&mut self) -> Option<&'a mut Any> { self.inner.next().map(|x| &mut **x.1) } + #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } +} +impl<'a> ExactSizeIterator for IterMut<'a> { + #[inline] fn len(&self) -> usize { self.inner.len() } +} + +/// RawAnyMap move iterator. +pub struct IntoIter { + inner: hash_map::IntoIter>, +} +impl Iterator for IntoIter { + type Item = Box; + #[inline] fn next(&mut self) -> Option> { self.inner.next().map(|x| x.1) } + #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } +} +impl ExactSizeIterator for IntoIter { + #[inline] fn len(&self) -> usize { self.inner.len() } +} + +/// RawAnyMap drain iterator. +pub struct Drain<'a> { + inner: hash_map::Drain<'a, TypeId, Box>, +} +impl<'a> Iterator for Drain<'a> { + type Item = Box; + #[inline] fn next(&mut self) -> Option> { self.inner.next().map(|x| x.1) } + #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } +} +impl<'a> ExactSizeIterator for Drain<'a> { + #[inline] fn len(&self) -> usize { self.inner.len() } +} + +impl RawAnyMap { + /// An iterator visiting all entries in arbitrary order. + /// + /// Iterator element type is `&Any`. + #[inline] + pub fn iter(&self) -> Iter { + Iter { + inner: self.inner.iter(), + } + } + + /// An iterator visiting all entries in arbitrary order. + /// + /// Iterator element type is `&mut Any`. + #[inline] + pub fn iter_mut(&mut self) -> IterMut { + IterMut { + inner: self.inner.iter_mut(), + } + } + + /// Creates a consuming iterator, that is, one that moves each item + /// out of the map in arbitrary order. The map cannot be used after + /// calling this. + /// + /// Iterator element type is `Box`. + #[inline] + pub fn into_iter(self) -> IntoIter { + IntoIter { + inner: self.inner.into_iter(), + } + } + + /// Clears the map, returning all items as an iterator. + /// + /// Iterator element type is `Box`. + /// + /// Keeps the allocated memory for reuse. + #[inline] + pub fn drain(&mut self) -> Drain { + Drain { + inner: self.inner.drain(), + } + } + + /// Gets the entry for the given type in the collection for in-place manipulation. + pub fn entry(&mut self, key: TypeId) -> Entry { + match self.inner.entry(key) { + hash_map::Entry::Occupied(e) => Entry::Occupied(OccupiedEntry { + inner: e, + }), + hash_map::Entry::Vacant(e) => Entry::Vacant(VacantEntry { + inner: e, + }), + } + } + + /// Returns a reference to the value corresponding to the key. + /// + /// The key may be any borrowed form of the map's key type, but `Hash` and `Eq` on the borrowed + /// form *must* match those for the key type. + pub fn get(&self, k: &Q) -> Option<&Any> + where TypeId: Borrow, Q: Hash + Eq { + self.inner.get(k).map(|x| &**x) + } + + /// Returns true if the map contains a value for the specified key. + /// + /// The key may be any borrowed form of the map's key type, but `Hash` and `Eq` on the borrowed + /// form *must* match those for the key type. + pub fn contains_key(&self, k: &Q) -> bool + where TypeId: Borrow, Q: Hash + Eq { + self.inner.contains_key(k) + } + + /// Returns a mutable reference to the value corresponding to the key. + /// + /// The key may be any borrowed form of the map's key type, but `Hash` and `Eq` on the borrowed + /// form *must* match those for the key type. + pub fn get_mut(&mut self, k: &Q) -> Option<&mut Any> + where TypeId: Borrow, Q: Hash + Eq { + self.inner.get_mut(k).map(|x| &mut **x) + } + + /// Inserts a key-value pair from the map. If the key already had a value present in the map, + /// that value is returned. Otherwise, None is returned. + /// + /// It is the caller’s responsibility to ensure that the key corresponds with the type ID of + /// the value. If they do not, memory safety may be violated. + pub unsafe fn insert(&mut self, key: TypeId, value: Box) -> Option> { + self.inner.insert(key, value) + } + + /// Removes a key from the map, returning the value at the key if the key was previously in the + /// map. + /// + /// The key may be any borrowed form of the map's key type, but `Hash` and `Eq` on the borrowed + /// form *must* match those for the key type. + pub fn remove(&mut self, k: &Q) -> Option> + where TypeId: Borrow, Q: Hash + Eq { + self.inner.remove(k) + } + +} + +impl Index for RawAnyMap where TypeId: Borrow, Q: Eq + Hash { + type Output = Any; + + fn index<'a>(&'a self, index: &Q) -> &'a Any { + self.get(index).expect("no entry found for key") + } +} + +impl IndexMut for RawAnyMap where TypeId: Borrow, Q: Eq + Hash { + fn index_mut<'a>(&'a mut self, index: &Q) -> &'a mut Any { + self.get_mut(index).expect("no entry found for key") + } +} + +impl IntoIterator for RawAnyMap { + type Item = Box; + type IntoIter = IntoIter; + + fn into_iter(self) -> IntoIter { + self.into_iter() + } +} + +/// A view into a single occupied location in a `RawAnyMap`. +pub struct OccupiedEntry<'a> { + inner: hash_map::OccupiedEntry<'a, TypeId, Box>, +} + +/// A view into a single empty location in a `RawAnyMap`. +pub struct VacantEntry<'a> { + inner: hash_map::VacantEntry<'a, TypeId, Box>, +} + +/// A view into a single location in an AnyMap, which may be vacant or occupied. +pub enum Entry<'a> { + /// An occupied Entry + Occupied(OccupiedEntry<'a>), + /// A vacant Entry + Vacant(VacantEntry<'a>), +} + +impl<'a> Entry<'a> { + /// Returns a mutable reference to the entry if occupied, or the VacantEntry if vacant. + pub fn get(self) -> Result<&'a mut Any, VacantEntry<'a>> { + match self { + Entry::Occupied(inner) => Ok(inner.into_mut()), + Entry::Vacant(inner) => Err(inner), + } + } +} + +impl<'a> OccupiedEntry<'a> { + /// Gets a reference to the value in the entry. + pub fn get(&self) -> &Any { + &**self.inner.get() + } + + /// Gets a mutable reference to the value in the entry. + pub fn get_mut(&mut self) -> &mut Any { + &mut **self.inner.get_mut() + } + + /// Converts the OccupiedEntry into a mutable reference to the value in the entry + /// with a lifetime bound to the collection itself. + pub fn into_mut(self) -> &'a mut Any { + &mut **self.inner.into_mut() + } + + /// Sets the value of the entry, and returns the entry's old value. + /// + /// It is the caller’s responsibility to ensure that the key of the entry corresponds with + /// the type ID of `value`. If they do not, memory safety may be violated. + pub unsafe fn insert(&mut self, value: Box) -> Box { + self.inner.insert(value) + } + + /// Takes the value out of the entry, and returns it. + pub fn remove(self) -> Box { + self.inner.remove() + } +} + +impl<'a> VacantEntry<'a> { + /// Sets the value of the entry with the VacantEntry's key, + /// and returns a mutable reference to it + /// + /// It is the caller’s responsibility to ensure that the key of the entry corresponds with + /// the type ID of `value`. If they do not, memory safety may be violated. + pub unsafe fn insert(self, value: Box) -> &'a mut Any { + &mut **self.inner.insert(value) + } +} diff --git a/src/unchecked_any.rs b/src/unchecked_any.rs new file mode 100644 index 0000000..d34f8fe --- /dev/null +++ b/src/unchecked_any.rs @@ -0,0 +1,24 @@ +use std::any::Any; +use std::mem; +use std::raw::TraitObject; + +#[allow(missing_docs)] // Bogus warning (it’s not public outside the crate), ☹ +pub trait UncheckedAnyExt { + unsafe fn downcast_ref_unchecked(&self) -> &T; + unsafe fn downcast_mut_unchecked(&mut self) -> &mut T; + unsafe fn downcast_unchecked(self: Box) -> Box; +} + +impl UncheckedAnyExt for Any { + unsafe fn downcast_ref_unchecked(&self) -> &T { + mem::transmute(mem::transmute::<_, TraitObject>(self).data) + } + + unsafe fn downcast_mut_unchecked(&mut self) -> &mut T { + mem::transmute(mem::transmute::<_, TraitObject>(self).data) + } + + unsafe fn downcast_unchecked(self: Box) -> Box { + mem::transmute(mem::transmute::<_, TraitObject>(self).data) + } +} -- 2.42.0