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