use crate::{ExternRef, ExternalReferenceType}; use alloc::boxed::Box; use core::cell::Cell; use core::mem::forget; struct TemporaryData { use_count: Cell, is_deleted: bool, value: T, } pub struct Temporary { value: *mut TemporaryData, } static mut TEMPORARIES: alloc::collections::BTreeMap = alloc::collections::BTreeMap::new(); impl Temporary where T: ExternalReferenceType, T: Clone, { #[inline] pub fn from_reference(reference: ExternRef) -> Self { let temporaries = unsafe { &mut TEMPORARIES }; let existing = temporaries.get(&reference.get_internal_index()); unsafe { if let Some(v) = existing { let rc = (*v as *mut TemporaryData).as_mut().unwrap(); if !rc.is_deleted { *rc.use_count.get_mut() += 1; return Self { value: rc as *mut TemporaryData, }; } } } let value = reference.get_value().unwrap(); let mut reference_counter = TemporaryData { use_count: Cell::new(1), is_deleted: false, value, }; let ptr = &mut reference_counter as *mut TemporaryData; forget(reference_counter); temporaries.insert(reference.get_internal_index(), ptr as *const u8); Self { value: ptr } } #[inline] pub fn value(&self) -> T { unsafe { self.value.as_ref().unwrap().value.clone() } } #[inline] pub fn value_ref(&self) -> &T { unsafe { &self.value.as_ref().unwrap().value } } pub(crate) fn mark_as_deleted(reference: ExternRef) { let temporaries = unsafe { &mut TEMPORARIES }; let existing = temporaries.get(&reference.get_internal_index()); unsafe { if let Some(v) = existing { crate::utils::print_raw(b"Dropping temporary"); let rc = (*v as *mut TemporaryData).as_mut().unwrap(); rc.is_deleted = true; if rc.use_count.get() == 0 { drop(Box::from(*v as *mut TemporaryData)) } temporaries.remove(&reference.get_internal_index()); } } } } impl Clone for Temporary { fn clone(&self) -> Self { *unsafe { self.value.as_mut() }.unwrap().use_count.get_mut() += 1; Self { value: self.value } } } impl Drop for Temporary { fn drop(&mut self) { let data = unsafe { self.value.as_mut() }.unwrap(); let v = data.use_count.get_mut(); *v -= 1; if *v == 0 && data.is_deleted { drop(Box::from(self.value)) } } }