wgpu_core/
storage.rs

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