1 use super::Resource;
2 use crate::prelude::*;
3 use alloc::collections::BTreeSet;
4 use core::any::Any;
5 use core::fmt;
6 
7 #[derive(Debug)]
8 /// Errors returned by operations on `ResourceTable`
9 pub enum ResourceTableError {
10     /// ResourceTable has no free keys
11     Full,
12     /// Resource not present in table
13     NotPresent,
14     /// Resource present in table, but with a different type
15     WrongType,
16     /// Resource cannot be deleted because child resources exist in the table. Consult wit docs for
17     /// the particular resource to see which methods may return child resources.
18     HasChildren,
19 }
20 
21 impl fmt::Display for ResourceTableError {
22     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23         match self {
24             Self::Full => write!(f, "resource table has no free keys"),
25             Self::NotPresent => write!(f, "resource not present"),
26             Self::WrongType => write!(f, "resource is of another type"),
27             Self::HasChildren => write!(f, "resource has children"),
28         }
29     }
30 }
31 
32 #[cfg(feature = "std")]
33 impl std::error::Error for ResourceTableError {}
34 
35 /// The `ResourceTable` type maps a `Resource<T>` to its `T`.
36 #[derive(Debug)]
37 pub struct ResourceTable {
38     entries: Vec<Entry>,
39     free_head: Option<usize>,
40 }
41 
42 #[derive(Debug)]
43 enum Entry {
44     Free { next: Option<usize> },
45     Occupied { entry: TableEntry },
46 }
47 
48 impl Entry {
49     pub fn occupied(&self) -> Option<&TableEntry> {
50         match self {
51             Self::Occupied { entry } => Some(entry),
52             Self::Free { .. } => None,
53         }
54     }
55 
56     pub fn occupied_mut(&mut self) -> Option<&mut TableEntry> {
57         match self {
58             Self::Occupied { entry } => Some(entry),
59             Self::Free { .. } => None,
60         }
61     }
62 }
63 
64 /// This structure tracks parent and child relationships for a given table entry.
65 ///
66 /// Parents and children are referred to by table index. We maintain the
67 /// following invariants to prevent orphans and cycles:
68 /// * parent can only be assigned on creating the entry.
69 /// * parent, if some, must exist when creating the entry.
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     /// Create an empty table with at least the specified capacity.
111     pub fn with_capacity(capacity: usize) -> Self {
112         ResourceTable {
113             entries: Vec::with_capacity(capacity),
114             free_head: None,
115         }
116     }
117 
118     /// Inserts a new value `T` into this table, returning a corresponding
119     /// `Resource<T>` which can be used to refer to it after it was inserted.
120     pub fn push<T>(&mut self, entry: T) -> Result<Resource<T>, ResourceTableError>
121     where
122         T: Send + 'static,
123     {
124         let idx = self.push_(TableEntry::new(Box::new(entry), None))?;
125         Ok(Resource::new_own(idx))
126     }
127 
128     /// Pop an index off of the free list, if it's not empty.
129     fn pop_free_list(&mut self) -> Option<usize> {
130         if let Some(ix) = self.free_head {
131             // Advance free_head to the next entry if one is available.
132             match &self.entries[ix] {
133                 Entry::Free { next } => self.free_head = *next,
134                 Entry::Occupied { .. } => unreachable!(),
135             }
136             Some(ix)
137         } else {
138             None
139         }
140     }
141 
142     /// Free an entry in the table, returning its [`TableEntry`]. Add the index to the free list.
143     fn free_entry(&mut self, ix: usize) -> TableEntry {
144         let entry = match core::mem::replace(
145             &mut self.entries[ix],
146             Entry::Free {
147                 next: self.free_head,
148             },
149         ) {
150             Entry::Occupied { entry } => entry,
151             Entry::Free { .. } => unreachable!(),
152         };
153 
154         self.free_head = Some(ix);
155 
156         entry
157     }
158 
159     /// Push a new entry into the table, returning its handle. This will prefer to use free entries
160     /// if they exist, falling back on pushing new entries onto the end of the table.
161     fn push_(&mut self, e: TableEntry) -> Result<u32, ResourceTableError> {
162         if let Some(free) = self.pop_free_list() {
163             self.entries[free] = Entry::Occupied { entry: e };
164             Ok(free.try_into().unwrap())
165         } else {
166             let ix = self
167                 .entries
168                 .len()
169                 .try_into()
170                 .map_err(|_| ResourceTableError::Full)?;
171             self.entries.push(Entry::Occupied { entry: e });
172             Ok(ix)
173         }
174     }
175 
176     fn occupied(&self, key: u32) -> Result<&TableEntry, ResourceTableError> {
177         self.entries
178             .get(key as usize)
179             .and_then(Entry::occupied)
180             .ok_or(ResourceTableError::NotPresent)
181     }
182 
183     fn occupied_mut(&mut self, key: u32) -> Result<&mut TableEntry, ResourceTableError> {
184         self.entries
185             .get_mut(key as usize)
186             .and_then(Entry::occupied_mut)
187             .ok_or(ResourceTableError::NotPresent)
188     }
189 
190     /// Insert a resource at the next available index, and track that it has a
191     /// parent resource.
192     ///
193     /// The parent must exist to create a child. All children resources must
194     /// be destroyed before a parent can be destroyed - otherwise
195     /// [`ResourceTable::delete`] will fail with
196     /// [`ResourceTableError::HasChildren`].
197     ///
198     /// Parent-child relationships are tracked inside the table to ensure that
199     /// a parent resource is not deleted while it has live children. This
200     /// allows child resources to hold "references" to a parent by table
201     /// index, to avoid needing e.g. an `Arc<Mutex<parent>>` and the associated
202     /// locking overhead and design issues, such as child existence extending
203     /// lifetime of parent referent even after parent resource is destroyed,
204     /// possibility for deadlocks.
205     ///
206     /// Parent-child relationships may not be modified once created. There
207     /// is no way to observe these relationships through the [`ResourceTable`]
208     /// methods except for erroring on deletion, or the [`std::fmt::Debug`]
209     /// impl.
210     pub fn push_child<T, U>(
211         &mut self,
212         entry: T,
213         parent: &Resource<U>,
214     ) -> Result<Resource<T>, ResourceTableError>
215     where
216         T: Send + 'static,
217         U: 'static,
218     {
219         let parent = parent.rep();
220         self.occupied(parent)?;
221         let child = self.push_(TableEntry::new(Box::new(entry), Some(parent)))?;
222         self.occupied_mut(parent)?.add_child(child);
223         Ok(Resource::new_own(child))
224     }
225 
226     /// Get an immutable reference to a resource of a given type at a given
227     /// index.
228     ///
229     /// Multiple shared references can be borrowed at any given time.
230     pub fn get<T: Any + Sized>(&self, key: &Resource<T>) -> Result<&T, ResourceTableError> {
231         self.get_(key.rep())?
232             .downcast_ref()
233             .ok_or(ResourceTableError::WrongType)
234     }
235 
236     fn get_(&self, key: u32) -> Result<&dyn Any, ResourceTableError> {
237         let r = self.occupied(key)?;
238         Ok(&*r.entry)
239     }
240 
241     /// Get an mutable reference to a resource of a given type at a given
242     /// index.
243     pub fn get_mut<T: Any + Sized>(
244         &mut self,
245         key: &Resource<T>,
246     ) -> Result<&mut T, ResourceTableError> {
247         self.get_any_mut(key.rep())?
248             .downcast_mut()
249             .ok_or(ResourceTableError::WrongType)
250     }
251 
252     /// Returns the raw `Any` at the `key` index provided.
253     pub fn get_any_mut(&mut self, key: u32) -> Result<&mut dyn Any, ResourceTableError> {
254         let r = self.occupied_mut(key)?;
255         Ok(&mut *r.entry)
256     }
257 
258     /// Same as `delete`, but typed
259     pub fn delete<T>(&mut self, resource: Resource<T>) -> Result<T, ResourceTableError>
260     where
261         T: Any,
262     {
263         debug_assert!(resource.owned());
264         let entry = self.delete_entry(resource.rep())?;
265         match entry.entry.downcast() {
266             Ok(t) => Ok(*t),
267             Err(_e) => Err(ResourceTableError::WrongType),
268         }
269     }
270 
271     fn delete_entry(&mut self, key: u32) -> Result<TableEntry, ResourceTableError> {
272         if !self.occupied(key)?.children.is_empty() {
273             return Err(ResourceTableError::HasChildren);
274         }
275         let e = self.free_entry(key as usize);
276         if let Some(parent) = e.parent {
277             // Remove deleted resource from parent's child list.
278             // Parent must still be present because it can't be deleted while still having
279             // children:
280             self.occupied_mut(parent)
281                 .expect("missing parent")
282                 .remove_child(key);
283         }
284         Ok(e)
285     }
286 
287     /// Zip the values of the map with mutable references to table entries corresponding to each
288     /// key. As the keys in the `HashMap` are unique, this iterator can give mutable references
289     /// with the same lifetime as the mutable reference to the [ResourceTable].
290     #[cfg(feature = "std")]
291     pub fn iter_entries<'a, T>(
292         &'a mut self,
293         map: std::collections::HashMap<u32, T>,
294     ) -> impl Iterator<Item = (Result<&'a mut dyn Any, ResourceTableError>, T)> {
295         map.into_iter().map(move |(k, v)| {
296             let item = self
297                 .occupied_mut(k)
298                 .map(|e| Box::as_mut(&mut e.entry))
299                 // Safety: extending the lifetime of the mutable reference.
300                 .map(|item| unsafe { &mut *(item as *mut dyn Any) });
301             (item, v)
302         })
303     }
304 
305     /// Iterate over all children belonging to the provided parent
306     pub fn iter_children<T>(
307         &self,
308         parent: &Resource<T>,
309     ) -> Result<impl Iterator<Item = &(dyn Any + Send)>, ResourceTableError>
310     where
311         T: 'static,
312     {
313         let parent_entry = self.occupied(parent.rep())?;
314         Ok(parent_entry.children.iter().map(|child_index| {
315             let child = self.occupied(*child_index).expect("missing child");
316             child.entry.as_ref()
317         }))
318     }
319 }
320 
321 impl Default for ResourceTable {
322     fn default() -> Self {
323         ResourceTable::new()
324     }
325 }
326 
327 #[test]
328 pub fn test_free_list() {
329     let mut table = ResourceTable::new();
330 
331     let x = table.push(()).unwrap();
332     assert_eq!(x.rep(), 0);
333 
334     let y = table.push(()).unwrap();
335     assert_eq!(y.rep(), 1);
336 
337     // Deleting x should put it on the free list, so the next entry should have the same rep.
338     table.delete(x).unwrap();
339     let x = table.push(()).unwrap();
340     assert_eq!(x.rep(), 0);
341 
342     // Deleting x and then y should yield indices 1 and then 0 for new entries.
343     table.delete(x).unwrap();
344     table.delete(y).unwrap();
345 
346     let y = table.push(()).unwrap();
347     assert_eq!(y.rep(), 1);
348 
349     let x = table.push(()).unwrap();
350     assert_eq!(x.rep(), 0);
351 
352     // As the free list is empty, this entry will have a new id.
353     let x = table.push(()).unwrap();
354     assert_eq!(x.rep(), 2);
355 }
356