use crate::buffer::BufferRuntime;
use crate::call::ProtectedCall;
use crate::class::ClassRuntime;
use crate::function::FunctionRuntime;
use crate::gc::GcObject;
use crate::handle::RawHandle;
use crate::memory::LuaPage;
use crate::state::ThreadLifecycle;
use crate::state::ThreadState;
use crate::string::LUA_MIN_STRING_TABLE_SIZE;
use crate::string::StringRuntime;
use crate::table::TableRuntime;
use crate::thread::Thread;
use crate::types;
use crate::userdata::UserdataRuntime;
use crate::{VmError, VmExit, VmResult};
impl Thread {
unsafe fn free_obj(&self, object: GcObject, page: LuaPage) {
unsafe {
match object.as_ptr().as_ref().unwrap_unchecked().tt as i32 {
x if x == types::LUA_TPROTO => self.free_proto(object.to_proto(), page),
x if x == types::LUA_TFUNCTION => self.free_closure(object.to_closure(), page),
x if x == types::LUA_TUPVALUE => self.free_upvalue(object.to_upvalue(), page),
x if x == types::LUA_TTABLE => self.free_table(object.to_table(), page),
x if x == types::LUA_TTHREAD => {
let state = object.to_state();
let global = self.global();
debug_assert!(state != *self);
debug_assert!(state != global.main_thread());
self.free_thread(&state, page);
}
x if x == types::LUA_TSTRING => self.free_string(object.to_string(), page),
x if x == types::LUA_TUSERDATA => self.free_userdata(object.to_userdata(), page),
x if x == types::LUA_TBUFFER => self.free_buffer(object.to_buffer(), page),
x if x == types::LUA_TCLASS => self.free_class(object.to_class(), page),
x if x == types::LUA_TOBJECT => self.free_object(object.to_object(), page),
other => unreachable!("unsupported sweep object {}", other),
}
}
}
unsafe fn resize_string_protected(&self, new_size: i32) {
#[repr(C)]
struct ResizeStringContext {
new_size: i32,
}
unsafe fn resize_string_trampoline(
thread: &Thread,
context: &mut ResizeStringContext,
) -> VmResult {
unsafe { thread.resize(context.new_size) }?;
Ok(())
}
let mut context = ResizeStringContext { new_size };
let result = unsafe { self.raw_run_protected(resize_string_trampoline, &mut context) };
debug_assert!(matches!(
result,
Ok(()) | Err(VmExit::Error(VmError::Memory))
));
}
pub(super) unsafe fn shrink_buffers(&self) {
unsafe {
let global = self.global();
let string_table = &global.as_ptr().as_ref().unwrap_unchecked().string_table;
if string_table.n_use < (string_table.size / 4) as u32
&& string_table.size > (LUA_MIN_STRING_TABLE_SIZE as i32) * 2
{
self.resize_string_protected(string_table.size / 2);
}
}
}
pub(super) unsafe fn shrink_buffers_full(&self) {
unsafe {
let global = self.global();
let string_table = &global.as_ptr().as_ref().unwrap_unchecked().string_table;
let mut hash_size = string_table.size;
while string_table.n_use < (hash_size / 4) as u32
&& hash_size > (LUA_MIN_STRING_TABLE_SIZE as i32) * 2
{
hash_size /= 2;
}
if hash_size != string_table.size {
self.resize_string_protected(hash_size);
}
}
}
}
pub(super) unsafe fn delete_gco(context: *mut (), page: LuaPage, object: GcObject) -> bool {
unsafe {
let thread = Thread::from_raw(NonNull::new_unchecked(context.cast()));
thread.free_obj(object, page);
}
true
}
impl LuaPage {
pub(super) unsafe fn sweep_gco(&self, thread: &Thread) -> i32 {
unsafe {
let (mut walk, mut busy_blocks) = self.gco_walk();
debug_assert!(busy_blocks > 0);
let global = thread.global();
let dead_mask =
global.as_ptr().as_ref().unwrap_unchecked().current_white ^ crate::gc::WHITE_BITS;
let new_white = global.white();
let mask_marks = !(crate::gc::bit_mask(crate::gc::BLACK_BIT) | crate::gc::WHITE_BITS);
while let Some(object) = walk.next() {
let marked = object.as_ptr().as_ref().unwrap_unchecked().marked;
if ((marked ^ crate::gc::WHITE_BITS) & dead_mask) != 0 {
debug_assert!(!global.is_dead(object));
object.as_ptr().as_mut().unwrap_unchecked().marked =
(marked & mask_marks) | new_white;
} else {
debug_assert!(global.is_dead(object));
thread.free_obj(object, *self);
busy_blocks -= 1;
if busy_blocks == 0 {
return walk.steps();
}
}
}
walk.steps()
}
}
}
use core::ptr::NonNull;