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