Skip to main content

gizmo_core/
asset.rs

1use std::collections::HashMap;
2use std::hash::{Hash, Hasher};
3use std::marker::PhantomData;
4use std::sync::atomic::{AtomicUsize, Ordering};
5use std::sync::Arc;
6use crossbeam_queue::SegQueue;
7
8static NEXT_HANDLE_ID: AtomicUsize = AtomicUsize::new(1);
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
11pub struct HandleId(pub usize);
12
13impl Default for HandleId {
14    fn default() -> Self {
15        Self::new()
16    }
17}
18
19impl HandleId {
20    pub fn new() -> Self {
21        HandleId(NEXT_HANDLE_ID.fetch_add(1, Ordering::Relaxed))
22    }
23}
24
25pub struct HandleIdTracker {
26    pub id: usize,
27    pub drop_queue: Arc<SegQueue<usize>>,
28}
29
30impl Drop for HandleIdTracker {
31    fn drop(&mut self) {
32        self.drop_queue.push(self.id);
33    }
34}
35
36pub struct Handle<T> {
37    pub id: HandleId,
38    pub tracker: Option<Arc<HandleIdTracker>>,
39    _marker: PhantomData<T>,
40}
41
42impl<T> Default for Handle<T> {
43    fn default() -> Self {
44        Self::weak(HandleId::new())
45    }
46}
47
48impl<T> Handle<T> {
49    pub fn new(id: HandleId, drop_queue: Arc<SegQueue<usize>>) -> Self {
50        Self {
51            id,
52            tracker: Some(Arc::new(HandleIdTracker {
53                id: id.0,
54                drop_queue,
55            })),
56            _marker: PhantomData,
57        }
58    }
59
60    pub fn weak(id: HandleId) -> Self {
61        Self {
62            id,
63            tracker: None,
64            _marker: PhantomData,
65        }
66    }
67    
68    pub fn is_weak(&self) -> bool {
69        self.tracker.is_none()
70    }
71    
72    pub fn make_strong(&mut self, drop_queue: Arc<SegQueue<usize>>) {
73        if self.tracker.is_none() {
74            self.tracker = Some(Arc::new(HandleIdTracker {
75                id: self.id.0,
76                drop_queue,
77            }));
78        }
79    }
80}
81
82impl<T> Clone for Handle<T> {
83    fn clone(&self) -> Self {
84        Self {
85            id: self.id,
86            tracker: self.tracker.clone(),
87            _marker: PhantomData,
88        }
89    }
90}
91
92impl<T> PartialEq for Handle<T> {
93    fn eq(&self, other: &Self) -> bool {
94        self.id == other.id
95    }
96}
97
98impl<T> Eq for Handle<T> {}
99
100impl<T> Hash for Handle<T> {
101    fn hash<H: Hasher>(&self, state: &mut H) {
102        self.id.hash(state);
103    }
104}
105
106impl<T: 'static + Send + Sync> crate::component::Component for Handle<T> {}
107
108impl<T> std::fmt::Debug for Handle<T> {
109    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
110        if self.is_weak() {
111            write!(f, "WeakHandle({:?})", self.id)
112        } else {
113            write!(f, "StrongHandle({:?})", self.id)
114        }
115    }
116}
117
118/// Generic resource to store assets by their handle ID.
119pub struct Assets<T> {
120    pub data: HashMap<HandleId, T>,
121    drop_queue: Arc<SegQueue<usize>>,
122}
123
124impl<T> Default for Assets<T> {
125    fn default() -> Self {
126        Self {
127            data: HashMap::new(),
128            drop_queue: Arc::new(SegQueue::new()),
129        }
130    }
131}
132
133impl<T> Assets<T> {
134    pub fn new() -> Self {
135        Self::default()
136    }
137
138    pub fn add(&mut self, asset: T) -> Handle<T> {
139        let id = HandleId::new();
140        self.data.insert(id, asset);
141        Handle::new(id, self.drop_queue.clone())
142    }
143
144    pub fn get(&self, handle: &Handle<T>) -> Option<&T> {
145        self.data.get(&handle.id)
146    }
147
148    pub fn get_mut(&mut self, handle: &Handle<T>) -> Option<&mut T> {
149        self.data.get_mut(&handle.id)
150    }
151
152    pub fn insert(&mut self, handle: &Handle<T>, asset: T) {
153        self.data.insert(handle.id, asset);
154    }
155
156    pub fn remove(&mut self, handle: &Handle<T>) -> Option<T> {
157        self.data.remove(&handle.id)
158    }
159    
160    /// Collects dropped handles and removes their corresponding assets.
161    pub fn process_drops(&mut self) {
162        while let Some(dropped_id) = self.drop_queue.pop() {
163            self.data.remove(&HandleId(dropped_id));
164        }
165    }
166}