use std::marker::PhantomData;
use std::collections::HashSet;
use crate::gc::*;
pub struct GcCow<T> {
idx: usize,
phantom_owned: PhantomData<T>,
phantom_not_send_sync: PhantomData<*mut T>,
}
impl<T> Clone for GcCow<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for GcCow<T> {}
impl<T: 'static> GcCompat for GcCow<T> {
fn points_to(&self, buffer: &mut HashSet<usize>) {
buffer.insert(self.idx);
}
}
impl<T: GcCompat> GcCow<T> {
pub fn new(t: T) -> Self {
let idx = with_gc_mut(|st| {
st.alloc(t)
});
GcCow { idx, phantom_owned: PhantomData, phantom_not_send_sync: PhantomData }
}
pub fn extract(self) -> T where T: Clone {
self.call_ref_unchecked(|o| o.clone())
}
pub(crate) fn call_ref_unchecked<O>(self, f: impl FnOnce(&T) -> O) -> O {
with_gc(|st| {
let x = st.get_ref_typed::<T>(self.idx);
f(x)
})
}
pub(crate) fn mutate<O>(&mut self, f: impl FnOnce(&mut T) -> O) -> O where T: Clone {
let mut val = self.extract(); let out = f(&mut val); *self = GcCow::new(val);
out
}
pub(crate) fn call_ref1_unchecked<U: GcCompat, O>(self, arg: GcCow<U>, f: impl FnOnce(&T, &U) -> O) -> O {
with_gc(|st| {
let x = st.get_ref_typed::<T>(self.idx);
let arg = st.get_ref_typed::<U>(arg.idx);
f(x, arg)
})
}
pub(crate) fn call_mut1_unchecked<U: GcCompat, O>(&mut self, arg: GcCow<U>, f: impl FnOnce(&mut T, &U) -> O) -> O where T: Clone {
let mut val = self.extract();
let out = with_gc(|st| {
let arg = st.get_ref_typed::<U>(arg.idx);
f(&mut val, arg)
});
*self = GcCow::new(val);
out
}
}