1use crate::internal_prelude::*;
22
23pub(crate) type RawCount = u32;
25
26macro_rules! slotmap_dec_ref { { $slotmap:expr, $ref_:expr, $refcount:expr } => { { {
39 use $crate::refcount::*;
40 let key: Ref<_> = $ref_;
41 let refcount: &mut Count<_> = $refcount;
42 if let Some(Garbage(key)) = key.dispose(refcount) {
43 let slotmap: &mut SlotMap<_, _> = $slotmap;
44 let removed = slotmap.remove(key).expect("entry vanished or wrong key passed?!");
45 Some(Garbage(removed))
46 } else {
47 None
48 }
49} } } }
50
51#[derive(Default, Educe, Ord, PartialOrd, Eq, PartialEq, Deref)]
53#[educe(Debug)]
54pub(crate) struct Count<K> {
55 #[deref]
57 count: RawCount,
58 #[educe(Debug(ignore))]
61 marker: PhantomData<K>,
62}
63
64#[derive(Deref, Educe)]
75#[educe(Debug, Default, Ord, Eq, PartialEq)]
76pub(crate) struct Ref<K: slotmap_careful::Key> {
77 #[deref]
79 raw_key: K,
80 #[educe(Debug(ignore))]
82 marker: PhantomData<K>,
83 #[educe(Debug(ignore), Ord(ignore), Eq(ignore), PartialEq(ignore))]
87 #[allow(dead_code)]
88 bomb: DropBombCondition,
89}
90
91impl<K: slotmap_careful::Key> PartialOrd for Ref<K> {
93 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
94 Some(self.cmp(other))
95 }
96}
97
98assert_not_impl_any!(DropBombCondition: Clone);
100
101#[derive(Debug, Clone, Error, Eq, PartialEq)]
103#[error("memory tracking refcount overflowed")]
104pub(crate) struct Overflow;
105
106#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
113pub(crate) struct Garbage<K>(pub(crate) K);
114
115impl<K> Count<K> {
116 const fn new_raw(count: RawCount) -> Self {
118 Count {
119 count,
120 marker: PhantomData,
121 }
122 }
123
124 pub(crate) fn as_usize(&self) -> usize {
128 let r: u32 = **self;
131 r as usize
132 }
133}
134
135fn inc_raw(c: &mut RawCount) -> Result<(), Overflow> {
137 *c = c.checked_add(1).ok_or(Overflow)?;
138 Ok(())
139}
140
141fn dec_raw(c: &mut RawCount) -> Option<Garbage<()>> {
145 *c = c
146 .checked_sub(1)
147 .expect("refcount underflow");
149 (*c == 0).then_some(Garbage(()))
150}
151
152impl<K: slotmap_careful::Key> Ref<K> {
153 pub(crate) fn new(key: K, count: &mut Count<K>) -> Result<Self, Overflow> {
155 inc_raw(&mut count.count)?;
156 Ok(Ref::from_raw(key))
157 }
158
159 pub(crate) fn null() -> Self {
161 Ref::from_raw(K::null())
162 }
163
164 fn from_raw(raw_key: K) -> Self {
166 Ref {
167 raw_key,
168 marker: PhantomData,
169 bomb: DropBombCondition::new_armed(),
170 }
171 }
172
173 pub(crate) fn dispose(mut self, refcount: &mut Count<K>) -> Option<Garbage<K>> {
178 let was = mem::take(&mut self.raw_key);
179 assert!(!was.is_null());
180 dec_raw(&mut refcount.count).map(|_: Garbage<()>| Garbage(was))
181 }
182
183 pub(crate) fn dispose_container_destroyed(mut self) {
190 let _: K = mem::take(&mut self.raw_key);
191 }
192}
193
194impl<K: slotmap_careful::Key> DefaultExtTake for Ref<K> {}
195
196pub(crate) fn slotmap_insert<K: slotmap_careful::Key, V>(
207 slotmap: &mut SlotMap<K, V>,
208 value_maker: impl FnOnce(Count<K>) -> V,
209) -> Ref<K> {
210 let (ref_, ()) = slotmap_try_insert(slotmap, move |refcount| {
211 Ok::<_, Void>((value_maker(refcount), ()))
212 })
213 .void_unwrap();
214 ref_
215}
216
217pub(crate) fn slotmap_try_insert<K: slotmap_careful::Key, V, E, RD>(
226 slotmap: &mut SlotMap<K, V>,
227 value_maker: impl FnOnce(Count<K>) -> Result<(V, RD), E>,
228) -> Result<(Ref<K>, RD), E> {
229 let refcount = Count::new_raw(1);
230 let (value, data) = value_maker(refcount)?;
231 let raw_key = slotmap.insert(value);
232 let ref_ = Ref {
233 raw_key,
234 marker: PhantomData,
235 bomb: DropBombCondition::new_armed(),
236 };
237 Ok((ref_, data))
238}
239
240pub(crate) fn slotmap_remove_early<K: slotmap_careful::Key, V>(
244 slotmap: &mut SlotMap<K, V>,
245 key: Ref<K>,
246) -> Option<V> {
247 let r = slotmap.remove(*key);
248 key.dispose_container_destroyed();
250 r
251}
252
253#[cfg(test)]
254impl<K: slotmap_careful::Key> Drop for Ref<K> {
255 fn drop(&mut self) {
256 drop_bomb_disarm_assert!(self.bomb, self.raw_key.is_null(),);
257 }
258}
259
260impl From<Overflow> for Error {
261 fn from(_overflow: Overflow) -> Error {
262 internal!("reference count overflow in memory tracking (out-of-control subsystem?)").into()
263 }
264}
265
266#[cfg(test)]
267mod test {
268 #![allow(clippy::bool_assert_comparison)]
270 #![allow(clippy::clone_on_copy)]
271 #![allow(clippy::dbg_macro)]
272 #![allow(clippy::mixed_attributes_style)]
273 #![allow(clippy::print_stderr)]
274 #![allow(clippy::print_stdout)]
275 #![allow(clippy::single_char_pattern)]
276 #![allow(clippy::unwrap_used)]
277 #![allow(clippy::unchecked_duration_subtraction)]
278 #![allow(clippy::useless_vec)]
279 #![allow(clippy::needless_pass_by_value)]
280 #![allow(clippy::let_and_return)] use super::*;
284
285 slotmap_careful::new_key_type! {
286 struct Id;
287 }
288 #[derive(Eq, PartialEq, Debug)]
289 struct Record {
290 refcount: Count<Id>,
291 }
292 type Map = SlotMap<Id, Record>;
293
294 fn setup() -> (Map, Ref<Id>) {
295 let mut map = Map::default();
296 let ref_ = slotmap_insert(&mut map, |refcount| Record { refcount });
297 (map, ref_)
298 }
299
300 #[test]
301 fn good() {
302 let (mut map, ref1) = setup();
303
304 let ent = map.get_mut(*ref1).unwrap();
305 let ref2 = Ref::new(*ref1, &mut ent.refcount).unwrap();
306
307 let g1: Option<Garbage<Record>> = slotmap_dec_ref!(&mut map, ref1, &mut ent.refcount);
308 assert_eq!(g1, None);
309
310 let ent = map.get_mut(*ref2).unwrap();
311 let g2: Option<Garbage<Record>> = slotmap_dec_ref!(&mut map, ref2, &mut ent.refcount);
312 assert!(g2.is_some());
313 }
314
315 #[test]
316 fn try_insert_fail() {
317 let mut map = Map::default();
318 let () = slotmap_try_insert::<_, _, _, String>(&mut map, |_refcount| Err(())).unwrap_err();
319 }
320
321 #[test]
322 fn drop_ref_without_decrement() {
323 let (_map, mut ref1) = setup();
324 let h = ref1.bomb.make_simulated();
325 drop(ref1);
326 h.expect_exploded();
327 }
328}