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