armature_core/
extensions.rs1use smallvec::SmallVec;
38use std::any::{Any, TypeId};
39use std::sync::Arc;
40
41type Slots = SmallVec<[(TypeId, Arc<dyn Any + Send + Sync>); 8]>;
44
45#[derive(Clone, Default)]
50pub struct Extensions {
51 slots: Slots,
52}
53
54impl Extensions {
55 #[inline]
57 pub fn new() -> Self {
58 Self {
59 slots: Slots::new(),
60 }
61 }
62
63 #[inline]
65 pub fn with_capacity(capacity: usize) -> Self {
66 Self {
67 slots: Slots::with_capacity(capacity),
68 }
69 }
70
71 #[inline]
73 pub fn spilled(&self) -> bool {
74 self.slots.spilled()
75 }
76
77 #[inline]
91 pub fn insert<T: Send + Sync + 'static>(&mut self, value: T) {
92 self.insert_arc(Arc::new(value));
93 }
94
95 #[inline]
99 pub fn insert_arc<T: Send + Sync + 'static>(&mut self, value: Arc<T>) {
100 let type_id = TypeId::of::<T>();
101 let erased: Arc<dyn Any + Send + Sync> = value;
102 if let Some(slot) = self.slots.iter_mut().find(|(k, _)| *k == type_id) {
103 slot.1 = erased;
104 return;
105 }
106 self.slots.push((type_id, erased));
107 }
108
109 #[inline]
130 pub fn get<T: Send + Sync + 'static>(&self) -> Option<&T> {
131 let type_id = TypeId::of::<T>();
132 self.slots
133 .iter()
134 .find(|(k, _)| *k == type_id)
135 .and_then(|(_, v)| v.downcast_ref::<T>())
136 }
137
138 #[inline]
142 pub fn get_arc<T: Send + Sync + 'static>(&self) -> Option<Arc<T>> {
143 let type_id = TypeId::of::<T>();
144 self.slots
145 .iter()
146 .find(|(k, _)| *k == type_id)
147 .and_then(|(_, v)| v.clone().downcast::<T>().ok())
148 }
149
150 #[inline]
152 pub fn contains<T: Send + Sync + 'static>(&self) -> bool {
153 let type_id = TypeId::of::<T>();
154 self.slots.iter().any(|(k, _)| *k == type_id)
155 }
156
157 #[inline]
161 pub fn remove<T: Send + Sync + 'static>(&mut self) -> bool {
162 let type_id = TypeId::of::<T>();
163 match self.slots.iter().position(|(k, _)| *k == type_id) {
164 Some(i) => {
165 self.slots.remove(i);
166 true
167 }
168 None => false,
169 }
170 }
171
172 #[inline]
174 pub fn clear(&mut self) {
175 self.slots.clear();
176 }
177
178 #[inline]
180 pub fn len(&self) -> usize {
181 self.slots.len()
182 }
183
184 #[inline]
186 pub fn is_empty(&self) -> bool {
187 self.slots.is_empty()
188 }
189
190 pub fn extend(&mut self, other: Extensions) {
194 for (id, value) in other.slots {
195 if let Some(slot) = self.slots.iter_mut().find(|(k, _)| *k == id) {
196 slot.1 = value;
197 } else {
198 self.slots.push((id, value));
199 }
200 }
201 }
202}
203
204impl std::fmt::Debug for Extensions {
205 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
206 f.debug_struct("Extensions")
207 .field("count", &self.slots.len())
208 .finish()
209 }
210}
211
212#[cfg(test)]
213mod tests {
214 use super::*;
215
216 #[test]
217 fn test_insert_and_get() {
218 let mut ext = Extensions::new();
219
220 ext.insert(42i32);
221 ext.insert("hello".to_string());
222
223 assert_eq!(ext.get::<i32>(), Some(&42));
224 assert_eq!(ext.get::<String>(), Some(&"hello".to_string()));
225 assert_eq!(ext.get::<f64>(), None);
226 }
227
228 #[test]
229 fn test_insert_replaces() {
230 let mut ext = Extensions::new();
231
232 ext.insert(42i32);
233 ext.insert(100i32);
234
235 assert_eq!(ext.get::<i32>(), Some(&100));
236 }
237
238 #[test]
239 fn test_contains() {
240 let mut ext = Extensions::new();
241
242 assert!(!ext.contains::<i32>());
243 ext.insert(42i32);
244 assert!(ext.contains::<i32>());
245 }
246
247 #[test]
248 fn test_remove() {
249 let mut ext = Extensions::new();
250 ext.insert(42i32);
251
252 let removed = ext.remove::<i32>();
253 assert!(removed);
254 assert!(!ext.contains::<i32>());
255 }
256
257 #[test]
258 fn test_arc_insert() {
259 let mut ext = Extensions::new();
260 let arc = Arc::new(42i32);
261
262 ext.insert_arc(arc.clone());
263
264 let retrieved = ext.get_arc::<i32>().unwrap();
265 assert_eq!(*retrieved, 42);
266 }
267
268 #[test]
269 fn eight_extensions_stay_inline() {
270 let mut ext = Extensions::new();
271 ext.insert(1u8);
272 ext.insert(2u16);
273 ext.insert(3u32);
274 ext.insert(4u64);
275 ext.insert(5i8);
276 ext.insert(6i16);
277 ext.insert(7i32);
278 ext.insert(8i64);
279 assert_eq!(ext.len(), 8);
280 assert!(!ext.spilled(), "eight extensions must not allocate a table");
281 assert_eq!(ext.get::<u32>(), Some(&3u32));
282 }
283
284 #[test]
285 fn insert_replaces_the_same_type() {
286 let mut ext = Extensions::new();
287 ext.insert(1u32);
288 ext.insert(2u32);
289 assert_eq!(ext.len(), 1);
290 assert_eq!(ext.get::<u32>(), Some(&2u32));
291 }
292
293 #[test]
294 fn extend_overwrites_colliding_types_and_keeps_the_rest() {
295 let mut a = Extensions::new();
296 a.insert(1u32);
297 a.insert("keep");
298 let mut b = Extensions::new();
299 b.insert(2u32);
300 a.extend(b);
301 assert_eq!(a.get::<u32>(), Some(&2u32));
302 assert_eq!(a.get::<&str>(), Some(&"keep"));
303 }
304
305 #[test]
306 fn test_clone() {
307 let mut ext = Extensions::new();
308 ext.insert(42i32);
309
310 let cloned = ext.clone();
311 assert_eq!(cloned.get::<i32>(), Some(&42));
312 }
313}