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