e7669130fd89b615c2472e07d714d9c8d3fd13a4
[anymap] / src / lib.rs
1 //! This crate provides the `AnyMap` type, a safe and convenient store for one value of each type.
2
3 #![crate_name = "anymap"]
4 #![crate_type = "lib"]
5 #![feature(default_type_params)]
6 #![warn(unused_qualifications, non_upper_case_globals,
7 variant_size_differences, unused_typecasts,
8 missing_docs, unused_results)]
9
10 #[cfg(test)]
11 extern crate test;
12
13 use std::any::Any;
14 use std::intrinsics::TypeId;
15 use std::collections::HashMap;
16 use std::hash::{Hash, Hasher, Writer};
17 use std::mem::{transmute, transmute_copy};
18 use std::raw::TraitObject;
19
20 struct TypeIdHasher;
21
22 struct TypeIdState {
23 value: u64,
24 }
25
26 impl Writer for TypeIdState {
27 #[inline(always)]
28 fn write(&mut self, bytes: &[u8]) {
29 // This expects to receive one and exactly one 64-bit value
30 debug_assert!(bytes.len() == 8);
31 unsafe {
32 std::ptr::copy_nonoverlapping_memory(&mut self.value,
33 transmute(&bytes[0]),
34 1)
35 }
36 }
37 }
38
39 impl Hasher<TypeIdState> for TypeIdHasher {
40 fn hash<Sized? T: Hash<TypeIdState>>(&self, value: &T) -> u64 {
41 let mut state = TypeIdState {
42 value: 0,
43 };
44 value.hash(&mut state);
45 state.value
46 }
47 }
48
49 /// An extension of `AnyRefExt` allowing unchecked downcasting of trait objects to `&T`.
50 trait UncheckedAnyRefExt<'a> {
51 /// Returns a reference to the boxed value, assuming that it is of type `T`. This should only be
52 /// called if you are ABSOLUTELY CERTAIN of `T` as you will get really wacky output if it’s not.
53 unsafe fn as_ref_unchecked<T: 'static>(self) -> &'a T;
54 }
55
56 impl<'a> UncheckedAnyRefExt<'a> for &'a Any + 'a {
57 #[inline]
58 unsafe fn as_ref_unchecked<T: 'static>(self) -> &'a T {
59 // Get the raw representation of the trait object
60 let to: TraitObject = transmute_copy(&self);
61
62 // Extract the data pointer
63 transmute(to.data)
64 }
65 }
66
67 /// An extension of `AnyMutRefExt` allowing unchecked downcasting of trait objects to `&mut T`.
68 trait UncheckedAnyMutRefExt<'a> {
69 /// Returns a reference to the boxed value, assuming that it is of type `T`. This should only be
70 /// called if you are ABSOLUTELY CERTAIN of `T` as you will get really wacky output if it’s not.
71 unsafe fn as_mut_unchecked<T: 'static>(self) -> &'a mut T;
72 }
73
74 impl<'a> UncheckedAnyMutRefExt<'a> for &'a mut Any + 'a {
75 #[inline]
76 unsafe fn as_mut_unchecked<T: 'static>(self) -> &'a mut T {
77 // Get the raw representation of the trait object
78 let to: TraitObject = transmute_copy(&self);
79
80 // Extract the data pointer
81 transmute(to.data)
82 }
83 }
84
85 /// A map containing zero or one values for any given type and allowing convenient,
86 /// type-safe access to those values.
87 ///
88 /// ```rust
89 /// # use anymap::AnyMap;
90 /// let mut data = AnyMap::new();
91 /// assert_eq!(data.find(), None::<&int>);
92 /// data.insert(42i);
93 /// assert_eq!(data.find(), Some(&42i));
94 /// data.remove::<int>();
95 /// assert_eq!(data.find::<int>(), None);
96 ///
97 /// #[deriving(PartialEq, Show)]
98 /// struct Foo {
99 /// str: String,
100 /// }
101 ///
102 /// assert_eq!(data.find::<Foo>(), None);
103 /// data.insert(Foo { str: "foo".to_string() });
104 /// assert_eq!(data.find(), Some(&Foo { str: "foo".to_string() }));
105 /// data.find_mut::<Foo>().map(|foo| foo.str.push('t'));
106 /// assert_eq!(data.find::<Foo>().unwrap().str.as_slice(), "foot");
107 /// ```
108 ///
109 /// Values containing non-static references are not permitted.
110 pub struct AnyMap {
111 data: HashMap<TypeId, Box<Any + 'static>, TypeIdHasher>,
112 }
113
114 impl AnyMap {
115 /// Construct a new `AnyMap`.
116 pub fn new() -> AnyMap {
117 AnyMap {
118 data: HashMap::with_hasher(TypeIdHasher),
119 }
120 }
121 }
122
123 impl AnyMap {
124 /// Retrieve the value stored in the map for the type `T`, if it exists.
125 pub fn find<T: 'static>(&self) -> Option<&T> {
126 self.data.find(&TypeId::of::<T>()).map(|any| unsafe { any.as_ref_unchecked::<T>() })
127 }
128
129 /// Retrieve a mutable reference to the value stored in the map for the type `T`, if it exists.
130 pub fn find_mut<T: 'static>(&mut self) -> Option<&mut T> {
131 self.data.find_mut(&TypeId::of::<T>()).map(|any| unsafe { any.as_mut_unchecked::<T>() })
132 }
133
134 /// Set the value contained in the map for the type `T`.
135 /// This will override any previous value stored.
136 pub fn insert<T: 'static>(&mut self, value: T) {
137 self.data.insert(TypeId::of::<T>(), box value as Box<Any>);
138 }
139
140 /// Remove the value for the type `T` if it existed.
141 pub fn remove<T: 'static>(&mut self) {
142 self.data.remove(&TypeId::of::<T>());
143 }
144
145 /// Does a value of type `T` exist?
146 pub fn contains<T: 'static>(&self) -> bool {
147 self.data.contains_key(&TypeId::of::<T>())
148 }
149
150 /// Returns the number of items in the collection.
151 pub fn len(&self) -> uint {
152 self.data.len()
153 }
154
155 /// Returns true if there are no items in the collection.
156 pub fn is_empty(&self) -> bool {
157 self.data.is_empty()
158 }
159
160 /// Removes all items from the collection.
161 pub fn clear(&mut self) {
162 self.data.clear();
163 }
164 }
165
166 #[bench]
167 fn bench_insertion(b: &mut ::test::Bencher) {
168 b.iter(|| {
169 let mut data = AnyMap::new();
170 for _ in range(0u, 100) {
171 data.insert(42i);
172 }
173 })
174 }
175
176 #[bench]
177 fn bench_find_missing(b: &mut ::test::Bencher) {
178 b.iter(|| {
179 let data = AnyMap::new();
180 for _ in range(0u, 100) {
181 assert_eq!(data.find(), None::<&int>);
182 }
183 })
184 }
185
186 #[bench]
187 fn bench_find_present(b: &mut ::test::Bencher) {
188 b.iter(|| {
189 let mut data = AnyMap::new();
190 data.insert(42i);
191 // These inner loops are a feeble attempt to drown the other factors.
192 for _ in range(0u, 100) {
193 assert_eq!(data.find(), Some(&42i));
194 }
195 })
196 }