wgpu_core/
storage.rs

1use alloc::{sync::Arc, vec::Vec};
2use core::mem;
3
4use crate::id::{Id, Marker};
5use crate::resource::ResourceType;
6use crate::{Epoch, Index};
7
8/// An entry in a `Storage::map` table.
9#[derive(Debug)]
10pub(crate) enum Element<T>
11where
12    T: StorageItem,
13{
14    /// There are no live ids with this index.
15    Vacant,
16
17    /// There is one live id with this index, allocated at the given
18    /// epoch.
19    Occupied(T, Epoch),
20}
21
22pub(crate) trait StorageItem: ResourceType {
23    type Marker: Marker;
24}
25
26impl<T: ResourceType> ResourceType for Arc<T> {
27    const TYPE: &'static str = T::TYPE;
28}
29
30impl<T: StorageItem> StorageItem for Arc<T> {
31    type Marker = T::Marker;
32}
33
34#[macro_export]
35macro_rules! impl_storage_item {
36    ($ty:ident) => {
37        impl $crate::storage::StorageItem for $ty {
38            type Marker = $crate::id::markers::$ty;
39        }
40    };
41}
42
43/// A table of `T` values indexed by the id type `I`.
44///
45/// `Storage` implements [`core::ops::Index`], accepting `Id` values as
46/// indices.
47///
48/// The table is represented as a vector indexed by the ids' index
49/// values, so you should use an id allocator like `IdentityManager`
50/// that keeps the index values dense and close to zero.
51#[derive(Debug)]
52pub(crate) struct Storage<T>
53where
54    T: StorageItem,
55{
56    pub(crate) map: Vec<Element<T>>,
57    kind: &'static str,
58}
59
60impl<T> Storage<T>
61where
62    T: StorageItem,
63{
64    pub(crate) fn new() -> Self {
65        Self {
66            map: Vec::new(),
67            kind: T::TYPE,
68        }
69    }
70}
71
72impl<T> Storage<T>
73where
74    T: StorageItem,
75{
76    pub(crate) fn insert(&mut self, id: Id<T::Marker>, value: T) {
77        let (index, epoch) = id.unzip();
78        let index = index as usize;
79        if index >= self.map.len() {
80            self.map.resize_with(index + 1, || Element::Vacant);
81        }
82        match mem::replace(&mut self.map[index], Element::Occupied(value, epoch)) {
83            Element::Vacant => {}
84            Element::Occupied(_, storage_epoch) => {
85                assert_ne!(
86                    epoch,
87                    storage_epoch,
88                    "Index {index:?} of {} is already occupied",
89                    T::TYPE
90                );
91            }
92        }
93    }
94
95    pub(crate) fn remove(&mut self, id: Id<T::Marker>) -> T {
96        let (index, epoch) = id.unzip();
97        let stored = self
98            .map
99            .get_mut(index as usize)
100            .unwrap_or_else(|| panic!("{}[{:?}] does not exist", self.kind, id));
101        match mem::replace(stored, Element::Vacant) {
102            Element::Occupied(value, storage_epoch) => {
103                assert_eq!(epoch, storage_epoch, "id epoch mismatch");
104                value
105            }
106            Element::Vacant => panic!("Cannot remove a vacant resource"),
107        }
108    }
109
110    pub(crate) fn iter(&self) -> impl Iterator<Item = (Id<T::Marker>, &T)> {
111        self.map
112            .iter()
113            .enumerate()
114            .filter_map(move |(index, x)| match *x {
115                Element::Occupied(ref value, storage_epoch) => {
116                    Some((Id::zip(index as Index, storage_epoch), value))
117                }
118                _ => None,
119            })
120    }
121}
122
123impl<T> Storage<T>
124where
125    T: StorageItem + Clone,
126{
127    /// Get an owned reference to an item.
128    /// Panics if there is an epoch mismatch, the entry is empty or in error.
129    pub(crate) fn get(&self, id: Id<T::Marker>) -> T {
130        let (index, epoch) = id.unzip();
131        let (result, storage_epoch) = match self.map.get(index as usize) {
132            Some(&Element::Occupied(ref v, epoch)) => (v.clone(), epoch),
133            None | Some(&Element::Vacant) => panic!("{}[{:?}] does not exist", self.kind, id),
134        };
135        assert_eq!(
136            epoch, storage_epoch,
137            "{}[{:?}] is no longer alive",
138            self.kind, id
139        );
140        result
141    }
142}