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 {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result23     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     max_capacity: usize,
40 }
41 
42 #[derive(Debug)]
43 enum Entry {
44     Free { next: Option<usize> },
45     Occupied { entry: TableEntry },
46 }
47 
48 impl Entry {
occupied(&self) -> Option<&TableEntry>49     pub fn occupied(&self) -> Option<&TableEntry> {
50         match self {
51             Self::Occupied { entry } => Some(entry),
52             Self::Free { .. } => None,
53         }
54     }
55 
occupied_mut(&mut self) -> Option<&mut TableEntry>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 struct Tombstone;
65 
66 // Change this to `true` to assist with handle debugging in development if
67 // necessary.
68 const DELETE_WITH_TOMBSTONE: bool = false;
69 
70 /// Default setting for `ResourceTable::max_capacity`, chosen to be high
71 /// enough that it doesn't need changing all that often but low enough that
72 /// exhausting it isn't a massive problem for the host.
73 const DEFAULT_MAX_CAPACITY: usize = 1_000_000;
74 
75 /// This structure tracks parent and child relationships for a given table entry.
76 ///
77 /// Parents and children are referred to by table index. We maintain the
78 /// following invariants:
79 /// * the parent must exist when adding a child.
80 /// * whenever a child is created, its index is added to children.
81 /// * whenever a child is deleted, its index is removed from children.
82 /// * an entry with children may not be deleted.
83 #[derive(Debug)]
84 struct TableEntry {
85     /// The entry in the table, as a boxed dynamically-typed object
86     entry: Box<dyn Any + Send>,
87     /// The index of the parent of this entry, if it has one.
88     parent: Option<u32>,
89     /// The indices of any children of this entry.
90     children: BTreeSet<u32>,
91 }
92 
93 impl TableEntry {
new(entry: Box<dyn Any + Send>, parent: Option<u32>) -> Self94     fn new(entry: Box<dyn Any + Send>, parent: Option<u32>) -> Self {
95         Self {
96             entry,
97             parent,
98             children: BTreeSet::new(),
99         }
100     }
add_child(&mut self, child: u32)101     fn add_child(&mut self, child: u32) {
102         debug_assert!(!self.children.contains(&child));
103         self.children.insert(child);
104     }
remove_child(&mut self, child: u32)105     fn remove_child(&mut self, child: u32) {
106         let was_removed = self.children.remove(&child);
107         debug_assert!(was_removed);
108     }
109 }
110 
111 impl ResourceTable {
112     /// Create an empty table
new() -> Self113     pub fn new() -> Self {
114         ResourceTable::with_capacity(0)
115     }
116 
117     /// Returns whether or not this table is empty.
118     ///
119     /// Note that this is an `O(n)` operation, where `n` is the number of
120     /// entries in the backing `Vec`.
is_empty(&self) -> bool121     pub fn is_empty(&self) -> bool {
122         self.entries.iter().all(|entry| match entry {
123             Entry::Free { .. } => true,
124             Entry::Occupied { entry } => entry.entry.downcast_ref::<Tombstone>().is_some(),
125         })
126     }
127 
128     /// Returns the maximum capacity of this table, in elements, before adding
129     /// any more will be refused.
max_capacity(&self) -> usize130     pub fn max_capacity(&self) -> usize {
131         self.max_capacity
132     }
133 
134     /// Configures the maximum number of entries that may be present within this
135     /// table.
136     ///
137     /// Note that this does not retroactively shrink the table nor evict
138     /// existing entries should the maximum be smaller than the current size of
139     /// the entry table.
set_max_capacity(&mut self, max: usize)140     pub fn set_max_capacity(&mut self, max: usize) {
141         self.max_capacity = max;
142     }
143 
144     /// Create an empty table with at least the specified capacity.
with_capacity(capacity: usize) -> Self145     pub fn with_capacity(capacity: usize) -> Self {
146         ResourceTable {
147             entries: Vec::with_capacity(capacity),
148             free_head: None,
149             max_capacity: DEFAULT_MAX_CAPACITY,
150         }
151     }
152 
153     /// Inserts a new value `T` into this table, returning a corresponding
154     /// `Resource<T>` which can be used to refer to it after it was inserted.
push<T>(&mut self, entry: T) -> Result<Resource<T>, ResourceTableError> where T: Send + 'static,155     pub fn push<T>(&mut self, entry: T) -> Result<Resource<T>, ResourceTableError>
156     where
157         T: Send + 'static,
158     {
159         let idx = self.push_(TableEntry::new(Box::new(entry), None))?;
160         Ok(Resource::new_own(idx))
161     }
162 
163     /// Pop an index off of the free list, if it's not empty.
pop_free_list(&mut self) -> Option<usize>164     fn pop_free_list(&mut self) -> Option<usize> {
165         if let Some(ix) = self.free_head {
166             // Advance free_head to the next entry if one is available.
167             match &self.entries[ix] {
168                 Entry::Free { next } => self.free_head = *next,
169                 Entry::Occupied { .. } => unreachable!(),
170             }
171             Some(ix)
172         } else {
173             None
174         }
175     }
176 
177     /// Free an entry in the table, returning its [`TableEntry`]. Add the index to the free list.
free_entry(&mut self, ix: usize, debug: bool) -> TableEntry178     fn free_entry(&mut self, ix: usize, debug: bool) -> TableEntry {
179         if debug {
180             // Instead of making this entry available for reuse, we leave a
181             // tombstone in debug mode.  This helps detect use-after-delete and
182             // double-delete bugs.
183             match mem::replace(
184                 &mut self.entries[ix],
185                 Entry::Occupied {
186                     entry: TableEntry {
187                         entry: Box::new(Tombstone),
188                         parent: None,
189                         children: BTreeSet::new(),
190                     },
191                 },
192             ) {
193                 Entry::Occupied { entry } => entry,
194                 Entry::Free { .. } => unreachable!(),
195             }
196         } else {
197             let entry = match core::mem::replace(
198                 &mut self.entries[ix],
199                 Entry::Free {
200                     next: self.free_head,
201                 },
202             ) {
203                 Entry::Occupied { entry } => entry,
204                 Entry::Free { .. } => unreachable!(),
205             };
206 
207             self.free_head = Some(ix);
208 
209             entry
210         }
211     }
212 
213     /// Push a new entry into the table, returning its handle. This will prefer to use free entries
214     /// if they exist, falling back on pushing new entries onto the end of the table.
push_(&mut self, e: TableEntry) -> Result<u32, ResourceTableError>215     fn push_(&mut self, e: TableEntry) -> Result<u32, ResourceTableError> {
216         if let Some(free) = self.pop_free_list() {
217             self.entries[free] = Entry::Occupied { entry: e };
218             Ok(free.try_into().unwrap())
219         } else {
220             if self.entries.len() >= self.max_capacity {
221                 return Err(ResourceTableError::Full);
222             }
223             let ix = self
224                 .entries
225                 .len()
226                 .try_into()
227                 .map_err(|_| ResourceTableError::Full)?;
228             self.entries.push(Entry::Occupied { entry: e });
229             Ok(ix)
230         }
231     }
232 
occupied(&self, key: u32) -> Result<&TableEntry, ResourceTableError>233     fn occupied(&self, key: u32) -> Result<&TableEntry, ResourceTableError> {
234         self.entries
235             .get(key as usize)
236             .and_then(Entry::occupied)
237             .ok_or(ResourceTableError::NotPresent)
238     }
239 
occupied_mut(&mut self, key: u32) -> Result<&mut TableEntry, ResourceTableError>240     fn occupied_mut(&mut self, key: u32) -> Result<&mut TableEntry, ResourceTableError> {
241         self.entries
242             .get_mut(key as usize)
243             .and_then(Entry::occupied_mut)
244             .ok_or(ResourceTableError::NotPresent)
245     }
246 
247     /// Insert a resource at the next available index, and track that it has a
248     /// parent resource.
249     ///
250     /// The parent must exist to create a child. All children resources must
251     /// be destroyed before a parent can be destroyed - otherwise
252     /// [`ResourceTable::delete`] will fail with
253     /// [`ResourceTableError::HasChildren`].
254     ///
255     /// Parent-child relationships are tracked inside the table to ensure that
256     /// a parent resource is not deleted while it has live children. This
257     /// allows child resources to hold "references" to a parent by table
258     /// index, to avoid needing e.g. an `Arc<Mutex<parent>>` and the associated
259     /// locking overhead and design issues, such as child existence extending
260     /// lifetime of parent referent even after parent resource is destroyed,
261     /// possibility for deadlocks.
push_child<T, U>( &mut self, entry: T, parent: &Resource<U>, ) -> Result<Resource<T>, ResourceTableError> where T: Send + 'static, U: 'static,262     pub fn push_child<T, U>(
263         &mut self,
264         entry: T,
265         parent: &Resource<U>,
266     ) -> Result<Resource<T>, ResourceTableError>
267     where
268         T: Send + 'static,
269         U: 'static,
270     {
271         let parent = parent.rep();
272         self.occupied(parent)?;
273         let child = self.push_(TableEntry::new(Box::new(entry), Some(parent)))?;
274         self.occupied_mut(parent)?.add_child(child);
275         Ok(Resource::new_own(child))
276     }
277 
278     /// Add an already-resident child to a resource.
add_child<T: 'static, U: 'static>( &mut self, child: Resource<T>, parent: Resource<U>, ) -> Result<(), ResourceTableError>279     pub fn add_child<T: 'static, U: 'static>(
280         &mut self,
281         child: Resource<T>,
282         parent: Resource<U>,
283     ) -> Result<(), ResourceTableError> {
284         let entry = self.occupied_mut(child.rep())?;
285         assert!(entry.parent.is_none());
286         entry.parent = Some(parent.rep());
287         self.occupied_mut(parent.rep())?.add_child(child.rep());
288         Ok(())
289     }
290 
291     /// Remove a child to from a resource (but leave it in the table).
remove_child<T: 'static, U: 'static>( &mut self, child: Resource<T>, parent: Resource<U>, ) -> Result<(), ResourceTableError>292     pub fn remove_child<T: 'static, U: 'static>(
293         &mut self,
294         child: Resource<T>,
295         parent: Resource<U>,
296     ) -> Result<(), ResourceTableError> {
297         let entry = self.occupied_mut(child.rep())?;
298         assert_eq!(entry.parent, Some(parent.rep()));
299         entry.parent = None;
300         self.occupied_mut(parent.rep())?.remove_child(child.rep());
301         Ok(())
302     }
303 
304     /// Get an immutable reference to a resource of a given type at a given
305     /// index.
306     ///
307     /// Multiple shared references can be borrowed at any given time.
get<T: Any + Sized>(&self, key: &Resource<T>) -> Result<&T, ResourceTableError>308     pub fn get<T: Any + Sized>(&self, key: &Resource<T>) -> Result<&T, ResourceTableError> {
309         self.get_(key.rep())?
310             .downcast_ref()
311             .ok_or(ResourceTableError::WrongType)
312     }
313 
get_(&self, key: u32) -> Result<&dyn Any, ResourceTableError>314     fn get_(&self, key: u32) -> Result<&dyn Any, ResourceTableError> {
315         let r = self.occupied(key)?;
316         Ok(&*r.entry)
317     }
318 
319     /// Get an mutable reference to a resource of a given type at a given
320     /// index.
get_mut<T: Any + Sized>( &mut self, key: &Resource<T>, ) -> Result<&mut T, ResourceTableError>321     pub fn get_mut<T: Any + Sized>(
322         &mut self,
323         key: &Resource<T>,
324     ) -> Result<&mut T, ResourceTableError> {
325         self.get_any_mut(key.rep())?
326             .downcast_mut()
327             .ok_or(ResourceTableError::WrongType)
328     }
329 
330     /// Returns the raw `Any` at the `key` index provided.
get_any_mut(&mut self, key: u32) -> Result<&mut dyn Any, ResourceTableError>331     pub fn get_any_mut(&mut self, key: u32) -> Result<&mut dyn Any, ResourceTableError> {
332         let r = self.occupied_mut(key)?;
333         Ok(&mut *r.entry)
334     }
335 
336     /// Remove the specified entry from the table.
delete<T>(&mut self, resource: Resource<T>) -> Result<T, ResourceTableError> where T: Any,337     pub fn delete<T>(&mut self, resource: Resource<T>) -> Result<T, ResourceTableError>
338     where
339         T: Any,
340     {
341         self.delete_maybe_debug(resource, DELETE_WITH_TOMBSTONE)
342     }
343 
delete_maybe_debug<T>( &mut self, resource: Resource<T>, debug: bool, ) -> Result<T, ResourceTableError> where T: Any,344     fn delete_maybe_debug<T>(
345         &mut self,
346         resource: Resource<T>,
347         debug: bool,
348     ) -> Result<T, ResourceTableError>
349     where
350         T: Any,
351     {
352         debug_assert!(resource.owned());
353         let entry = self.delete_entry(resource.rep(), debug)?;
354         match entry.entry.downcast() {
355             Ok(t) => Ok(*t),
356             Err(_e) => Err(ResourceTableError::WrongType),
357         }
358     }
359 
delete_entry(&mut self, key: u32, debug: bool) -> Result<TableEntry, ResourceTableError>360     fn delete_entry(&mut self, key: u32, debug: bool) -> Result<TableEntry, ResourceTableError> {
361         if !self.occupied(key)?.children.is_empty() {
362             return Err(ResourceTableError::HasChildren);
363         }
364         let e = self.free_entry(key as usize, debug);
365         if let Some(parent) = e.parent {
366             // Remove deleted resource from parent's child list.
367             // Parent must still be present because it can't be deleted while still having
368             // children:
369             self.occupied_mut(parent)
370                 .expect("missing parent")
371                 .remove_child(key);
372         }
373         Ok(e)
374     }
375 
376     /// Zip the values of the map with mutable references to table entries corresponding to each
377     /// key. As the keys in the `BTreeMap` are unique, this iterator can give mutable references
378     /// with the same lifetime as the mutable reference to the [ResourceTable].
iter_entries<'a, T>( &'a mut self, map: BTreeMap<u32, T>, ) -> impl Iterator<Item = (Result<&'a mut dyn Any, ResourceTableError>, T)>379     pub fn iter_entries<'a, T>(
380         &'a mut self,
381         map: BTreeMap<u32, T>,
382     ) -> impl Iterator<Item = (Result<&'a mut dyn Any, ResourceTableError>, T)> {
383         map.into_iter().map(move |(k, v)| {
384             let item = self
385                 .occupied_mut(k)
386                 .map(|e| Box::as_mut(&mut e.entry))
387                 // Safety: extending the lifetime of the mutable reference.
388                 .map(|item| unsafe { &mut *(item as *mut dyn Any) });
389             (item, v)
390         })
391     }
392 
393     /// Iterate over all children belonging to the provided parent
iter_children<T>( &self, parent: &Resource<T>, ) -> Result<impl Iterator<Item = &(dyn Any + Send)> + use<'_, T>, ResourceTableError> where T: 'static,394     pub fn iter_children<T>(
395         &self,
396         parent: &Resource<T>,
397     ) -> Result<impl Iterator<Item = &(dyn Any + Send)> + use<'_, T>, ResourceTableError>
398     where
399         T: 'static,
400     {
401         let parent_entry = self.occupied(parent.rep())?;
402         Ok(parent_entry.children.iter().map(|child_index| {
403             let child = self.occupied(*child_index).expect("missing child");
404             child.entry.as_ref()
405         }))
406     }
407 
408     /// Iterate over all the entries in this table.
iter_mut(&mut self) -> impl Iterator<Item = &mut (dyn Any + Send)>409     pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut (dyn Any + Send)> {
410         self.entries.iter_mut().filter_map(|entry| match entry {
411             Entry::Occupied { entry } => Some(&mut *entry.entry),
412             Entry::Free { .. } => None,
413         })
414     }
415 }
416 
417 impl Default for ResourceTable {
default() -> Self418     fn default() -> Self {
419         ResourceTable::new()
420     }
421 }
422 
423 impl fmt::Debug for ResourceTable {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result424     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
425         write!(f, "[")?;
426         let mut wrote = false;
427         for (index, entry) in self.entries.iter().enumerate() {
428             if let Entry::Occupied { entry } = entry {
429                 if entry.entry.downcast_ref::<Tombstone>().is_none() {
430                     if wrote {
431                         write!(f, ", ")?;
432                     } else {
433                         wrote = true;
434                     }
435                     write!(f, "{index}")?;
436                 }
437             }
438         }
439         write!(f, "]")
440     }
441 }
442 
443 #[test]
test_free_list()444 pub fn test_free_list() {
445     let mut table = ResourceTable::new();
446 
447     let x = table.push(()).unwrap();
448     assert_eq!(x.rep(), 0);
449 
450     let y = table.push(()).unwrap();
451     assert_eq!(y.rep(), 1);
452 
453     // Deleting x should put it on the free list, so the next entry should have the same rep.
454     table.delete_maybe_debug(x, false).unwrap();
455     let x = table.push(()).unwrap();
456     assert_eq!(x.rep(), 0);
457 
458     // Deleting x and then y should yield indices 1 and then 0 for new entries.
459     table.delete_maybe_debug(x, false).unwrap();
460     table.delete_maybe_debug(y, false).unwrap();
461 
462     let y = table.push(()).unwrap();
463     assert_eq!(y.rep(), 1);
464 
465     let x = table.push(()).unwrap();
466     assert_eq!(x.rep(), 0);
467 
468     // As the free list is empty, this entry will have a new id.
469     let x = table.push(()).unwrap();
470     assert_eq!(x.rep(), 2);
471 }
472 
473 #[test]
test_max_capacity()474 fn test_max_capacity() {
475     let mut table = ResourceTable::new();
476     assert_eq!(table.max_capacity(), DEFAULT_MAX_CAPACITY);
477 
478     table.set_max_capacity(0);
479     assert_eq!(table.max_capacity(), 0);
480     assert!(table.push(()).is_err());
481 
482     table.set_max_capacity(1);
483     assert_eq!(table.max_capacity(), 1);
484     let x = table.push(()).unwrap();
485     assert!(table.push(()).is_err());
486 
487     table.set_max_capacity(0);
488     assert!(table.push(()).is_err());
489     table.delete(x).unwrap();
490     let x = table.push(()).unwrap();
491     table.delete(x).unwrap();
492 
493     table.set_max_capacity(10);
494 
495     let handles = (0..10).map(|_| table.push(()).unwrap()).collect::<Vec<_>>();
496     assert!(table.push(()).is_err());
497     for handle in handles {
498         table.delete(handle).unwrap();
499     }
500 
501     table.push(()).unwrap();
502 }
503