wasmtime/runtime/component/
resource_table.rs1use super::Resource;
2use crate::prelude::*;
3use alloc::collections::{BTreeMap, BTreeSet};
4use core::any::Any;
5use core::fmt;
6use core::mem;
7
8#[derive(Debug)]
9pub enum ResourceTableError {
11 Full,
13 NotPresent,
15 WrongType,
17 HasChildren,
20}
21
22impl 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
33impl core::error::Error for ResourceTableError {}
34
35pub struct ResourceTable {
37 entries: Vec<Entry>,
38 free_head: Option<usize>,
39}
40
41#[derive(Debug)]
42enum Entry {
43 Free { next: Option<usize> },
44 Occupied { entry: TableEntry },
45}
46
47impl 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
63struct Tombstone;
64
65const DELETE_WITH_TOMBSTONE: bool = false;
68
69#[derive(Debug)]
78struct TableEntry {
79 entry: Box<dyn Any + Send>,
81 parent: Option<u32>,
83 children: BTreeSet<u32>,
85}
86
87impl 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
105impl ResourceTable {
106 pub fn new() -> Self {
108 ResourceTable {
109 entries: Vec::new(),
110 free_head: None,
111 }
112 }
113
114 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 pub fn with_capacity(capacity: usize) -> Self {
127 ResourceTable {
128 entries: Vec::with_capacity(capacity),
129 free_head: None,
130 }
131 }
132
133 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 fn pop_free_list(&mut self) -> Option<usize> {
145 if let Some(ix) = self.free_head {
146 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 fn free_entry(&mut self, ix: usize, debug: bool) -> TableEntry {
159 if debug {
160 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 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 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 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 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 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 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 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 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 self.occupied_mut(parent)
347 .expect("missing parent")
348 .remove_child(key);
349 }
350 Ok(e)
351 }
352
353 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 .map(|item| unsafe { &mut *(item as *mut dyn Any) });
366 (item, v)
367 })
368 }
369
370 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 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
394impl Default for ResourceTable {
395 fn default() -> Self {
396 ResourceTable::new()
397 }
398}
399
400impl 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]
421pub 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 table.delete_maybe_debug(x, false).unwrap();
432 let x = table.push(()).unwrap();
433 assert_eq!(x.rep(), 0);
434
435 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 let x = table.push(()).unwrap();
447 assert_eq!(x.rep(), 2);
448}