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