use crate::node::State::{Strong, Trace, Unknown};
use crate::node::{Node, NodeHead, NodeTrait};
use crate::raw_type::RawType;
use crate::root_ref::RootRef;
use crate::target::Target;
use std::cell::RefCell;
use std::fmt::{Debug, Formatter};
use std::marker::PhantomData;
use std::mem::{swap, take, transmute, ManuallyDrop};
use std::ops::Deref;
use std::ptr::NonNull;
#[derive(Copy, Clone, Debug)]
pub struct Config {
pub pre_drop: bool,
pub init_cap: usize,
pub forget_cap: usize,
pub stack_factor: f32,
}
impl Default for Config {
fn default() -> Self {
Self {
pre_drop: false,
init_cap: 32,
forget_cap: 0,
stack_factor: 0.1,
}
}
}
#[inline(always)]
pub fn scope_gc<'s, F: for<'gc> FnOnce(Gc<'gc, 's>) -> R, R>(config: Config, f: F) -> R {
unsafe {
let inner = RefCell::new(GcInner::new(config));
let r = f(Gc { inner: &inner });
inner.borrow_mut().clear_all();
r
}
}
#[derive(Copy, Clone, Debug)]
pub struct Gc<'gc, 's: 'gc> {
inner: &'gc RefCell<GcInner<'gc, 's>>,
}
impl<'gc, 's: 'gc> Gc<'gc, 's> {
pub fn new<T: Target + 's>(self, value: T) -> RootRef<'gc, Node<'gc, T>> {
unsafe { self.dangling(value) }
}
pub unsafe fn dangling<T: Target>(self, value: T) -> RootRef<'gc, Node<'gc, T>> {
let node = Box::new(Node::new(value));
let node_ref = transmute::<&'_ Node<'gc, T>, &'gc Node<'gc, T>>(node.deref());
self.inner
.borrow_mut()
.nodes
.push(NonNull::new_unchecked(Box::into_raw(transmute::<
Box<dyn NodeTrait<'gc> + 'gc>,
Box<dyn NodeTrait<'gc> + 's>,
>(node))));
RootRef::new(node_ref)
}
pub fn forget<T: Target>(self, value: T) -> RootRef<'gc, Node<'gc, T>> {
unsafe {
let node = Box::new(ManuallyDrop::new(Node::new(value)));
let node_ref = transmute::<&'_ Node<'gc, T>, &'gc Node<'gc, T>>(node.deref());
self.inner
.borrow_mut()
.forgets
.push(NonNull::new_unchecked(Box::into_raw(transmute::<
Box<ManuallyDrop<dyn NodeTrait<'gc> + 'gc>>,
Box<ManuallyDrop<dyn NodeTrait<'gc> + 's>>,
>(
node
))));
RootRef::new(node_ref)
}
}
pub fn new_raw<T: 's>(self, value: T) -> RootRef<'gc, Node<'gc, RawType<T>>> {
self.new(RawType(value))
}
pub unsafe fn dangling_raw<T>(self, value: T) -> RootRef<'gc, Node<'gc, RawType<T>>> {
self.dangling(RawType(value))
}
pub fn forget_raw<T>(self, value: T) -> RootRef<'gc, Node<'gc, RawType<T>>> {
self.forget(RawType(value))
}
pub fn reserve(self, cap: usize) {
self.inner.borrow_mut().nodes.reserve(cap);
}
pub fn reserve_forgets(self, cap: usize) {
self.inner.borrow_mut().forgets.reserve(cap);
}
pub fn get_node_count(self) -> usize {
self.inner.borrow().nodes.len()
}
pub fn get_forget_count(self) -> usize {
self.inner.borrow().forgets.len()
}
pub fn clear(self) {
unsafe {
let mut inner = self.inner.borrow_mut();
let mut stack = Vec::with_capacity(
((inner.nodes.len() + inner.forgets.len()) as f32 * inner.config.stack_factor)
as usize,
);
for &r in inner.nodes.iter() {
if r.as_ref().root() != 0 {
NodeHead::from_node_trait(r.as_ref()).set_marker(Trace);
stack.push(r.as_ref());
} else {
NodeHead::from_node_trait(r.as_ref()).set_marker(Unknown);
}
}
for &r in inner.forgets.iter() {
if r.as_ref().root() != 0 {
NodeHead::from_node_trait(r.as_ref().deref()).set_marker(Trace);
stack.push(r.as_ref().deref())
} else {
NodeHead::from_node_trait(r.as_ref().deref()).set_marker(Unknown);
}
}
while let Some(r) = stack.pop() {
match NodeHead::from_node_trait(r).get_marker() {
Trace => {
r.mark_and_collect(&mut stack);
}
_ => {
unreachable!();
}
}
}
let nodes = take(&mut inner.nodes);
if inner.config.pre_drop {
let mut drop_count = 0;
for &i in nodes.iter() {
match NodeHead::from_node_trait(i.as_ref()).get_marker() {
Unknown => {
drop_count += 1;
i.as_ref().pre_drop();
}
Strong => {}
Trace => unreachable!(),
}
}
let mut new_nodes =
Vec::with_capacity(nodes.len() - drop_count + inner.config.init_cap);
new_nodes.extend(nodes.into_iter().filter(|x| {
match NodeHead::from_node_trait(x.as_ref()).get_marker() {
Unknown => {
drop(Box::from_raw(x.as_ptr()));
false
}
Strong => true,
Trace => unreachable!(),
}
}));
swap(&mut inner.nodes, &mut new_nodes);
} else {
let mut new_nodes = nodes
.into_iter()
.filter(
|&i| match NodeHead::from_node_trait(i.as_ref()).get_marker() {
Unknown => false,
Strong => true,
Trace => unreachable!(),
},
)
.collect::<Vec<_>>();
new_nodes.reserve(inner.config.init_cap);
swap(&mut inner.nodes, &mut new_nodes);
}
let forgets = take(&mut inner.forgets);
let mut new_forgets = forgets
.into_iter()
.filter(
|&i| match NodeHead::from_node_trait(i.as_ref().deref()).get_marker() {
Unknown => false,
Strong => true,
Trace => unreachable!(),
},
)
.collect::<Vec<_>>();
new_forgets.reserve(inner.config.forget_cap);
swap(&mut inner.forgets, &mut new_forgets);
}
}
}
struct GcInner<'gc, 's: 'gc> {
_marker: PhantomData<*mut &'gc ()>,
config: Config,
nodes: Vec<NonNull<dyn NodeTrait<'gc> + 's>>,
forgets: Vec<NonNull<ManuallyDrop<dyn NodeTrait<'gc> + 's>>>,
}
impl<'gc, 's> GcInner<'gc, 's> {
unsafe fn new(config: Config) -> Self {
Self {
_marker: PhantomData,
config,
nodes: Vec::with_capacity(config.init_cap),
forgets: Vec::with_capacity(config.forget_cap),
}
}
unsafe fn clear_all(&mut self) {
if self.config.pre_drop {
for node in self.nodes.iter() {
node.as_ref().pre_drop();
}
}
for node in self.nodes.iter() {
drop(Box::from_raw(node.as_ptr()));
}
for node in self.forgets.iter() {
drop(Box::from_raw(node.as_ptr()));
}
self.nodes.clear();
self.forgets.clear();
}
}
impl<'gc, 's> Debug for GcInner<'gc, 's> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let mut s = f.debug_struct("GcInner");
s.field("config", &self.config);
struct N<'n, 'gc, 's: 'gc>(&'n (dyn NodeTrait<'gc> + 's));
impl<'n, 'gc, 's: 'gc> Debug for N<'n, 'gc, 's> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
struct D<'n, 'gc, 's: 'gc>(&'n Vec<NonNull<dyn NodeTrait<'gc> + 's>>);
impl<'n, 'gc, 's: 'gc> Debug for D<'n, 'gc, 's> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let mut n = f.debug_list();
for i in self.0 {
unsafe {
n.entry(&N(i.as_ref()));
}
}
n.finish()
}
}
s.field("nodes", &D(&self.nodes));
struct M<'n, 'gc, 's: 'gc>(&'n Vec<NonNull<ManuallyDrop<dyn NodeTrait<'gc> + 's>>>);
impl<'n, 'gc, 's: 'gc> Debug for M<'n, 'gc, 's> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let mut n = f.debug_list();
let p: &'n Vec<NonNull<ManuallyDrop<dyn NodeTrait<'gc> + 's>>> = self.0;
for i in p {
unsafe {
n.entry(&N(i.as_ref().deref()));
}
}
n.finish()
}
}
s.field("forgets", &M(&self.forgets));
s.finish()
}
}