use std::sync::{Arc, Weak};
use super::list::{List, ListNode};
use super::cell::DebugCell;
pub trait GcObject {
fn get_size(&self) -> usize;
fn move_to(&mut self, offset: usize);
}
struct MemInfo {
front: usize,
end: usize,
size: usize,
pointer: Weak<DebugCell<GcObject>>,
}
pub struct Gc {
front: usize,
end: usize,
size: usize,
last_offset: usize,
last_checked: Option<&'static mut ListNode<MemInfo>>,
objects_count: usize,
objects: List,
}
impl Gc {
pub fn new(front: usize, size: usize) -> Self {
Gc {
front: front,
end: front + size,
size: size,
last_offset: front,
last_checked: None,
objects_count: 0,
objects: List::new(),
}
}
pub fn clean(&mut self) {
self.last_checked = None;
let mut obj = self.objects.get_front::<MemInfo>();
self.last_offset = self.front;
while obj.is_some() {
let o = obj.unwrap();
obj = match o.data.pointer.upgrade() {
Some(op) => {
if o.data.front != self.last_offset {
op.borrow_mut().move_to(self.last_offset);
o.data.front = self.last_offset;
o.data.end = self.last_offset + o.data.size;
}
self.last_offset = o.data.end;
o.get_child()
},
None => {
self.objects_count -= 1;
o.remove()
},
};
}
}
pub fn allocate(&mut self, object: &Arc<DebugCell<GcObject>>) {
let obj_size = object.borrow().get_size();
self.objects_count += 1;
if self.size < obj_size {
logf!("The Object you want to allocate is bigger than GC memory!");
}
if self.end - self.last_offset >= obj_size {
object.borrow_mut().move_to(self.last_offset);
self.objects.add_end(
MemInfo {
front: self.last_offset,
end: self.last_offset + obj_size,
size: obj_size,
pointer: Arc::downgrade(object),
}
);
self.last_checked = None;
self.last_offset += obj_size;
return;
}
if self.last_checked.is_none() {
self.last_checked = self.objects.get_front();
}
let mut offset_free = self.last_checked.as_ref().unwrap().data.front;
let obj_count = self.objects_count;
for _ in 0..obj_count {
let obj = self.last_checked.as_ref().unwrap().data.pointer.upgrade();
self.last_checked = match obj {
Some(_) => {
let offset_free_end = self.last_checked.as_ref().unwrap().data.front;
if offset_free_end - offset_free >= obj_size {
object.borrow_mut().move_to(offset_free);
self.last_checked.as_mut().unwrap().add_parent(
MemInfo {
front: offset_free,
end: offset_free + obj_size,
size: obj_size,
pointer: Arc::downgrade(object),
}
);
self.last_checked = self.last_checked.as_mut().unwrap().get_child();
return;
}
offset_free = self.last_checked.as_ref().unwrap().data.end;
self.last_checked.as_mut().unwrap().get_child()
},
None => {
self.objects_count -= 1;
self.last_checked.as_mut().unwrap().remove()
},
};
if self.last_checked.is_none() {
if self.end - offset_free >= obj_size {
object.borrow_mut().move_to(offset_free);
self.objects.add_end(
MemInfo {
front: offset_free,
end: offset_free + obj_size,
size: obj_size,
pointer: Arc::downgrade(object),
}
);
self.last_offset = offset_free + obj_size;
return;
}
self.last_checked = self.objects.get_front();
}
}
loge!("Performance warning, GC called automatically, please do gc cleaning manually for preventing lag in game.");
self.clean();
if self.end - self.last_offset < obj_size {
logf!("Out of GC memory!");
}
object.borrow_mut().move_to(self.last_offset);
self.objects.add_end(
MemInfo {
front: self.last_offset,
end: self.last_offset + obj_size,
size: obj_size,
pointer: Arc::downgrade(object),
}
);
self.last_offset += obj_size;
}
}
impl GcObject for Gc {
fn get_size(&self) -> usize {
self.size
}
fn move_to(&mut self, offset: usize) {
self.front = offset;
self.end = offset + self.size;
self.clean();
}
}