1use alloc::{sync::Arc, vec::Vec};
2use core::mem;
3
4use crate::id::{Id, Marker};
5use crate::resource::ResourceType;
6use crate::{Epoch, Index};
7use parking_lot::Mutex;
8
9#[derive(Debug)]
11pub(crate) enum Element<T>
12where
13 T: StorageItem,
14{
15 Vacant,
17
18 Occupied(T, Epoch),
21}
22
23#[doc(hidden)]
25pub trait StorageItem: ResourceType {
26 type Marker: Marker;
27}
28
29impl<T: ResourceType> ResourceType for Arc<T> {
30 const TYPE: &'static str = T::TYPE;
31}
32
33impl<T: StorageItem> StorageItem for Arc<T> {
34 type Marker = T::Marker;
35}
36
37impl<T: ResourceType> ResourceType for Mutex<T> {
38 const TYPE: &'static str = T::TYPE;
39}
40
41impl<T: StorageItem> StorageItem for Mutex<T> {
42 type Marker = T::Marker;
43}
44
45#[macro_export]
46macro_rules! impl_storage_item {
47 ($ty:ident) => {
48 impl $crate::storage::StorageItem for $ty {
49 type Marker = $crate::id::markers::$ty;
50 }
51 };
52}
53
54#[derive(Debug)]
63pub(crate) struct Storage<T>
64where
65 T: StorageItem,
66{
67 pub(crate) map: Vec<Element<T>>,
68}
69
70impl<T> Storage<T>
71where
72 T: StorageItem,
73{
74 pub(crate) fn new() -> Self {
75 Self { map: Vec::new() }
76 }
77}
78
79impl<T> Storage<T>
80where
81 T: StorageItem,
82{
83 pub(crate) fn insert(&mut self, id: Id<T::Marker>, value: T) {
84 let (index, epoch) = id.unzip();
85 let index = index as usize;
86 if index >= self.map.len() {
87 self.map.resize_with(index + 1, || Element::Vacant);
88 }
89 match mem::replace(&mut self.map[index], Element::Occupied(value, epoch)) {
90 Element::Vacant => {}
91 Element::Occupied(_, storage_epoch) => {
92 panic!(
93 "Cannot insert {id:?}, found existing resource {other:?}",
94 other = Id::<T::Marker>::zip(index as Index, storage_epoch),
95 );
96 }
97 }
98 }
99
100 pub(crate) fn remove(&mut self, id: Id<T::Marker>) -> T {
101 let (index, epoch) = id.unzip();
102 let stored = self.map.get_mut(index as usize);
103 match stored.map(|stored| mem::replace(stored, Element::Vacant)) {
104 Some(Element::Occupied(value, storage_epoch)) => {
105 assert_eq!(
106 epoch,
107 storage_epoch,
108 "Cannot remove {id:?}, found other resource {other:?}",
109 other = Id::<T::Marker>::zip(index, storage_epoch),
110 );
111 value
112 }
113 None | Some(Element::Vacant) => {
114 panic!("Cannot remove non-existent resource {id:?}");
115 }
116 }
117 }
118
119 #[allow(dead_code)]
120 pub(crate) fn iter(&self) -> impl Iterator<Item = (Id<T::Marker>, &T)> {
121 self.map
122 .iter()
123 .enumerate()
124 .filter_map(move |(index, x)| match *x {
125 Element::Occupied(ref value, storage_epoch) => {
126 Some((Id::zip(index as Index, storage_epoch), value))
127 }
128 _ => None,
129 })
130 }
131}
132
133impl<T> Storage<T>
134where
135 T: StorageItem + Clone,
136{
137 pub(crate) fn get(&self, id: Id<T::Marker>) -> T {
140 let (index, epoch) = id.unzip();
141 let (result, storage_epoch) = match self.map.get(index as usize) {
142 Some(&Element::Occupied(ref v, epoch)) => (v.clone(), epoch),
143 None | Some(&Element::Vacant) => {
144 panic!("Cannot get non-existent resource {id:?}");
145 }
146 };
147 assert_eq!(
148 epoch,
149 storage_epoch,
150 "Cannot get {id:?}, found other resource {other:?}",
151 other = Id::<T::Marker>::zip(index, storage_epoch),
152 );
153 result
154 }
155}
156
157#[cfg(test)]
158mod tests {
159 use super::*;
160
161 #[derive(Clone, Debug)]
162 struct TestItem;
163
164 impl ResourceType for TestItem {
165 const TYPE: &'static str = "TestItem";
166 }
167
168 impl StorageItem for TestItem {
169 type Marker = ();
170 }
171
172 fn id(index: Index, epoch: Epoch) -> Id<()> {
173 Id::zip(index, epoch)
174 }
175
176 #[test]
177 #[should_panic(
178 expected = "Cannot insert UntypedId(0,1), found existing resource UntypedId(0,1)"
179 )]
180 fn insert_occupied_same_epoch() {
181 let mut storage = Storage::new();
182 storage.insert(id(0, 1), TestItem);
183 storage.insert(id(0, 1), TestItem);
184 }
185
186 #[test]
187 #[should_panic(
188 expected = "Cannot insert UntypedId(0,2), found existing resource UntypedId(0,1)"
189 )]
190 fn insert_occupied_different_epoch() {
191 let mut storage = Storage::new();
192 storage.insert(id(0, 1), TestItem);
193 storage.insert(id(0, 2), TestItem);
194 }
195
196 #[test]
197 #[should_panic(expected = "Cannot remove UntypedId(0,2), found other resource UntypedId(0,1)")]
198 fn remove_epoch_mismatch() {
199 let mut storage = Storage::new();
200 storage.insert(id(0, 1), TestItem);
201 storage.remove(id(0, 2));
202 }
203
204 #[test]
205 #[should_panic(expected = "Cannot remove non-existent resource UntypedId(0,1)")]
206 fn remove_vacant() {
207 let mut storage = Storage::<TestItem>::new();
208 storage.remove(id(0, 1));
209 }
210
211 #[test]
212 #[should_panic(expected = "Cannot get non-existent resource UntypedId(0,1)")]
213 fn get_vacant() {
214 let storage = Storage::<TestItem>::new();
215 storage.get(id(0, 1));
216 }
217
218 #[test]
219 #[should_panic(expected = "Cannot get UntypedId(0,2), found other resource UntypedId(0,1)")]
220 fn get_epoch_mismatch() {
221 let mut storage = Storage::new();
222 storage.insert(id(0, 1), TestItem);
223 storage.get(id(0, 2));
224 }
225}