1 use super::Resource;
2 use crate::prelude::*;
3 use alloc::collections::{BTreeMap, BTreeSet};
4 use core::any::Any;
5 use core::fmt;
6 use core::mem;
7 
8 #[derive(Debug)]
9 /// Errors returned by operations on `ResourceTable`
10 pub enum ResourceTableError {
11     /// ResourceTable has no free keys
12     Full,
13     /// Resource not present in table
14     NotPresent,
15     /// Resource present in table, but with a different type
16     WrongType,
17     /// Resource cannot be deleted because child resources exist in the table. Consult wit docs for
18     /// the particular resource to see which methods may return child resources.
19     HasChildren,
20 }
21 
22 impl fmt::Display for ResourceTableError {
23     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
24         match self {
25             Self::Full => write!(f, "resource table has no free keys"),
26             Self::NotPresent => write!(f, "resource not present"),
27             Self::WrongType => write!(f, "resource is of another type"),
28             Self::HasChildren => write!(f, "resource has children"),
29         }
30     }
31 }
32 
33 impl core::error::Error for ResourceTableError {}
34 
35 /// The `ResourceTable` type maps a `Resource<T>` to its `T`.
36 pub struct ResourceTable {
37     entries: Vec<Entry>,
38     free_head: Option<usize>,
39 }
40 
41 #[derive(Debug)]
42 enum Entry {
43     Free { next: Option<usize> },
44     Occupied { entry: TableEntry },
45 }
46 
47 impl Entry {
48     pub fn occupied(&self) -> Option<&TableEntry> {
49         match self {
50             Self::Occupied { entry } => Some(entry),
51             Self::Free { .. } => None,
52         }
53     }
54 
55     pub fn occupied_mut(&mut self) -> Option<&mut TableEntry> {
56         match self {
57             Self::Occupied { entry } => Some(entry),
58             Self::Free { .. } => None,
59         }
60     }
61 }
62 
63 struct Tombstone;
64 
65 /// This structure tracks parent and child relationships for a given table entry.
66 ///
67 /// Parents and children are referred to by table index. We maintain the
68 /// following invariants:
69 /// * the parent must exist when adding a child.
70 /// * whenever a child is created, its index is added to children.
71 /// * whenever a child is deleted, its index is removed from children.
72 /// * an entry with children may not be deleted.
73 #[derive(Debug)]
74 struct TableEntry {
75     /// The entry in the table, as a boxed dynamically-typed object
76     entry: Box<dyn Any + Send>,
77     /// The index of the parent of this entry, if it has one.
78     parent: Option<u32>,
79     /// The indices of any children of this entry.
80     children: BTreeSet<u32>,
81 }
82 
83 impl TableEntry {
84     fn new(entry: Box<dyn Any + Send>, parent: Option<u32>) -> Self {
85         Self {
86             entry,
87             parent,
88             children: BTreeSet::new(),
89         }
90     }
91     fn add_child(&mut self, child: u32) {
92         debug_assert!(!self.children.contains(&child));
93         self.children.insert(child);
94     }
95     fn remove_child(&mut self, child: u32) {
96         let was_removed = self.children.remove(&child);
97         debug_assert!(was_removed);
98     }
99 }
100 
101 impl ResourceTable {
102     /// Create an empty table
103     pub fn new() -> Self {
104         ResourceTable {
105             entries: Vec::new(),
106             free_head: None,
107         }
108     }
109 
110     /// Returns whether or not this table is empty.
111     ///
112     /// Note that this is an `O(n)` operation, where `n` is the number of
113     /// entries in the backing `Vec`.
114     pub fn is_empty(&self) -> bool {
115         self.entries.iter().all(|entry| match entry {
116             Entry::Free { .. } => true,
117             Entry::Occupied { entry } => entry.entry.downcast_ref::<Tombstone>().is_some(),
118         })
119     }
120 
121     /// Create an empty table with at least the specified capacity.
122     pub fn with_capacity(capacity: usize) -> Self {
123         ResourceTable {
124             entries: Vec::with_capacity(capacity),
125             free_head: None,
126         }
127     }
128 
129     /// Inserts a new value `T` into this table, returning a corresponding
130     /// `Resource<T>` which can be used to refer to it after it was inserted.
131     pub fn push<T>(&mut self, entry: T) -> Result<Resource<T>, ResourceTableError>
132     where
133         T: Send + 'static,
134     {
135         let idx = self.push_(TableEntry::new(Box::new(entry), None))?;
136         Ok(Resource::new_own(idx))
137     }
138 
139     /// Pop an index off of the free list, if it's not empty.
140     fn pop_free_list(&mut self) -> Option<usize> {
141         if let Some(ix) = self.free_head {
142             // Advance free_head to the next entry if one is available.
143             match &self.entries[ix] {
144                 Entry::Free { next } => self.free_head = *next,
145                 Entry::Occupied { .. } => unreachable!(),
146             }
147             Some(ix)
148         } else {
149             None
150         }
151     }
152 
153     /// Free an entry in the table, returning its [`TableEntry`]. Add the index to the free list.
154     fn free_entry(&mut self, ix: usize, debug: bool) -> TableEntry {
155         if debug {
156             // Instead of making this entry available for reuse, we leave a
157             // tombstone in debug mode.  This helps detect use-after-delete and
158             // double-delete bugs.
159             match mem::replace(
160                 &mut self.entries[ix],
161                 Entry::Occupied {
162                     entry: TableEntry {
163                         entry: Box::new(Tombstone),
164                         parent: None,
165                         children: BTreeSet::new(),
166                     },
167                 },
168             ) {
169                 Entry::Occupied { entry } => entry,
170                 Entry::Free { .. } => unreachable!(),
171             }
172         } else {
173             let entry = match core::mem::replace(
174                 &mut self.entries[ix],
175                 Entry::Free {
176                     next: self.free_head,
177                 },
178             ) {
179                 Entry::Occupied { entry } => entry,
180                 Entry::Free { .. } => unreachable!(),
181             };
182 
183             self.free_head = Some(ix);
184 
185             entry
186         }
187     }
188 
189     /// Push a new entry into the table, returning its handle. This will prefer to use free entries
190     /// if they exist, falling back on pushing new entries onto the end of the table.
191     fn push_(&mut self, e: TableEntry) -> Result<u32, ResourceTableError> {
192         if let Some(free) = self.pop_free_list() {
193             self.entries[free] = Entry::Occupied { entry: e };
194             Ok(free.try_into().unwrap())
195         } else {
196             let ix = self
197                 .entries
198                 .len()
199                 .try_into()
200                 .map_err(|_| ResourceTableError::Full)?;
201             self.entries.push(Entry::Occupied { entry: e });
202             Ok(ix)
203         }
204     }
205 
206     fn occupied(&self, key: u32) -> Result<&TableEntry, ResourceTableError> {
207         self.entries
208             .get(key as usize)
209             .and_then(Entry::occupied)
210             .ok_or(ResourceTableError::NotPresent)
211     }
212 
213     fn occupied_mut(&mut self, key: u32) -> Result<&mut TableEntry, ResourceTableError> {
214         self.entries
215             .get_mut(key as usize)
216             .and_then(Entry::occupied_mut)
217             .ok_or(ResourceTableError::NotPresent)
218     }
219 
220     /// Insert a resource at the next available index, and track that it has a
221     /// parent resource.
222     ///
223     /// The parent must exist to create a child. All children resources must
224     /// be destroyed before a parent can be destroyed - otherwise
225     /// [`ResourceTable::delete`] will fail with
226     /// [`ResourceTableError::HasChildren`].
227     ///
228     /// Parent-child relationships are tracked inside the table to ensure that
229     /// a parent resource is not deleted while it has live children. This
230     /// allows child resources to hold "references" to a parent by table
231     /// index, to avoid needing e.g. an `Arc<Mutex<parent>>` and the associated
232     /// locking overhead and design issues, such as child existence extending
233     /// lifetime of parent referent even after parent resource is destroyed,
234     /// possibility for deadlocks.
235     pub fn push_child<T, U>(
236         &mut self,
237         entry: T,
238         parent: &Resource<U>,
239     ) -> Result<Resource<T>, ResourceTableError>
240     where
241         T: Send + 'static,
242         U: 'static,
243     {
244         let parent = parent.rep();
245         self.occupied(parent)?;
246         let child = self.push_(TableEntry::new(Box::new(entry), Some(parent)))?;
247         self.occupied_mut(parent)?.add_child(child);
248         Ok(Resource::new_own(child))
249     }
250 
251     /// Add an already-resident child to a resource.
252     pub fn add_child<T: 'static, U: 'static>(
253         &mut self,
254         child: Resource<T>,
255         parent: Resource<U>,
256     ) -> Result<(), ResourceTableError> {
257         let entry = self.occupied_mut(child.rep())?;
258         assert!(entry.parent.is_none());
259         entry.parent = Some(parent.rep());
260         self.occupied_mut(parent.rep())?.add_child(child.rep());
261         Ok(())
262     }
263 
264     /// Remove a child to from a resource (but leave it in the table).
265     pub fn remove_child<T: 'static, U: 'static>(
266         &mut self,
267         child: Resource<T>,
268         parent: Resource<U>,
269     ) -> Result<(), ResourceTableError> {
270         let entry = self.occupied_mut(child.rep())?;
271         assert_eq!(entry.parent, Some(parent.rep()));
272         entry.parent = None;
273         self.occupied_mut(parent.rep())?.remove_child(child.rep());
274         Ok(())
275     }
276 
277     /// Get an immutable reference to a resource of a given type at a given
278     /// index.
279     ///
280     /// Multiple shared references can be borrowed at any given time.
281     pub fn get<T: Any + Sized>(&self, key: &Resource<T>) -> Result<&T, ResourceTableError> {
282         self.get_(key.rep())?
283             .downcast_ref()
284             .ok_or(ResourceTableError::WrongType)
285     }
286 
287     fn get_(&self, key: u32) -> Result<&dyn Any, ResourceTableError> {
288         let r = self.occupied(key)?;
289         Ok(&*r.entry)
290     }
291 
292     /// Get an mutable reference to a resource of a given type at a given
293     /// index.
294     pub fn get_mut<T: Any + Sized>(
295         &mut self,
296         key: &Resource<T>,
297     ) -> Result<&mut T, ResourceTableError> {
298         self.get_any_mut(key.rep())?
299             .downcast_mut()
300             .ok_or(ResourceTableError::WrongType)
301     }
302 
303     /// Returns the raw `Any` at the `key` index provided.
304     pub fn get_any_mut(&mut self, key: u32) -> Result<&mut dyn Any, ResourceTableError> {
305         let r = self.occupied_mut(key)?;
306         Ok(&mut *r.entry)
307     }
308 
309     /// Remove the specified entry from the table.
310     pub fn delete<T>(&mut self, resource: Resource<T>) -> Result<T, ResourceTableError>
311     where
312         T: Any,
313     {
314         self.delete_maybe_debug(resource, cfg!(debug_assertions))
315     }
316 
317     fn delete_maybe_debug<T>(
318         &mut self,
319         resource: Resource<T>,
320         debug: bool,
321     ) -> Result<T, ResourceTableError>
322     where
323         T: Any,
324     {
325         debug_assert!(resource.owned());
326         let entry = self.delete_entry(resource.rep(), debug)?;
327         match entry.entry.downcast() {
328             Ok(t) => Ok(*t),
329             Err(_e) => Err(ResourceTableError::WrongType),
330         }
331     }
332 
333     fn delete_entry(&mut self, key: u32, debug: bool) -> Result<TableEntry, ResourceTableError> {
334         if !self.occupied(key)?.children.is_empty() {
335             return Err(ResourceTableError::HasChildren);
336         }
337         let e = self.free_entry(key as usize, debug);
338         if let Some(parent) = e.parent {
339             // Remove deleted resource from parent's child list.
340             // Parent must still be present because it can't be deleted while still having
341             // children:
342             self.occupied_mut(parent)
343                 .expect("missing parent")
344                 .remove_child(key);
345         }
346         Ok(e)
347     }
348 
349     /// Zip the values of the map with mutable references to table entries corresponding to each
350     /// key. As the keys in the `BTreeMap` are unique, this iterator can give mutable references
351     /// with the same lifetime as the mutable reference to the [ResourceTable].
352     pub fn iter_entries<'a, T>(
353         &'a mut self,
354         map: BTreeMap<u32, T>,
355     ) -> impl Iterator<Item = (Result<&'a mut dyn Any, ResourceTableError>, T)> {
356         map.into_iter().map(move |(k, v)| {
357             let item = self
358                 .occupied_mut(k)
359                 .map(|e| Box::as_mut(&mut e.entry))
360                 // Safety: extending the lifetime of the mutable reference.
361                 .map(|item| unsafe { &mut *(item as *mut dyn Any) });
362             (item, v)
363         })
364     }
365 
366     /// Iterate over all children belonging to the provided parent
367     pub fn iter_children<T>(
368         &self,
369         parent: &Resource<T>,
370     ) -> Result<impl Iterator<Item = &(dyn Any + Send)> + use<'_, T>, ResourceTableError>
371     where
372         T: 'static,
373     {
374         let parent_entry = self.occupied(parent.rep())?;
375         Ok(parent_entry.children.iter().map(|child_index| {
376             let child = self.occupied(*child_index).expect("missing child");
377             child.entry.as_ref()
378         }))
379     }
380 
381     /// Iterate over all the entries in this table.
382     pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut (dyn Any + Send)> {
383         self.entries.iter_mut().filter_map(|entry| match entry {
384             Entry::Occupied { entry } => Some(&mut *entry.entry),
385             Entry::Free { .. } => None,
386         })
387     }
388 }
389 
390 impl Default for ResourceTable {
391     fn default() -> Self {
392         ResourceTable::new()
393     }
394 }
395 
396 impl fmt::Debug for ResourceTable {
397     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
398         write!(f, "[")?;
399         let mut wrote = false;
400         for (index, entry) in self.entries.iter().enumerate() {
401             if let Entry::Occupied { entry } = entry {
402                 if entry.entry.downcast_ref::<Tombstone>().is_none() {
403                     if wrote {
404                         write!(f, ", ")?;
405                     } else {
406                         wrote = true;
407                     }
408                     write!(f, "{index}")?;
409                 }
410             }
411         }
412         write!(f, "]")
413     }
414 }
415 
416 #[test]
417 pub fn test_free_list() {
418     let mut table = ResourceTable::new();
419 
420     let x = table.push(()).unwrap();
421     assert_eq!(x.rep(), 0);
422 
423     let y = table.push(()).unwrap();
424     assert_eq!(y.rep(), 1);
425 
426     // Deleting x should put it on the free list, so the next entry should have the same rep.
427     table.delete_maybe_debug(x, false).unwrap();
428     let x = table.push(()).unwrap();
429     assert_eq!(x.rep(), 0);
430 
431     // Deleting x and then y should yield indices 1 and then 0 for new entries.
432     table.delete_maybe_debug(x, false).unwrap();
433     table.delete_maybe_debug(y, false).unwrap();
434 
435     let y = table.push(()).unwrap();
436     assert_eq!(y.rep(), 1);
437 
438     let x = table.push(()).unwrap();
439     assert_eq!(x.rep(), 0);
440 
441     // As the free list is empty, this entry will have a new id.
442     let x = table.push(()).unwrap();
443     assert_eq!(x.rep(), 2);
444 }
445