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