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