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