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