use luau_common::BString;
use crate::VmErrorResult;
use crate::gc::{GCS_PAUSE, GcCategoryNamer, GcRuntime};
use crate::gc::{
LUA_GC_COLLECT, LUA_GC_COUNT, LUA_GC_COUNT_B, LUA_GC_IS_PAUSED, LUA_GC_IS_RUNNING,
LUA_GC_RESTART, LUA_GC_SET_GOAL, LUA_GC_SET_STEP_MUL, LUA_GC_SET_STEP_SIZE, LUA_GC_STEP,
LUA_GC_STOP,
};
use crate::handle::RawHandle;
use crate::thread::Thread;
impl Thread {
pub unsafe fn gc(&self, what: i32, data: i32) -> VmErrorResult<i32> {
let global = unsafe { self.global() };
let mut result = 0;
unsafe {
match what {
LUA_GC_STOP => {
global.as_ptr().as_mut().unwrap_unchecked().gc_threshold = usize::MAX;
}
LUA_GC_RESTART => {
let global_mut = global.as_ptr().as_mut().unwrap_unchecked();
global_mut.gc_threshold = global_mut.total_bytes;
}
LUA_GC_COLLECT => {
self.full_gc();
}
LUA_GC_COUNT => {
result = (global.as_ptr().as_ref().unwrap_unchecked().total_bytes >> 10) as i32;
}
LUA_GC_COUNT_B => {
result =
(global.as_ptr().as_ref().unwrap_unchecked().total_bytes & 1023) as i32;
}
LUA_GC_IS_RUNNING => {
result = i32::from(
global.as_ptr().as_ref().unwrap_unchecked().gc_threshold != usize::MAX,
);
}
LUA_GC_STEP => {
let amount = (data as usize) << 10;
let old_credit = if global.gc_state() == GCS_PAUSE {
0
} else {
global.as_ptr().as_ref().unwrap_unchecked().gc_threshold as isize
- global.as_ptr().as_ref().unwrap_unchecked().total_bytes as isize
};
if amount <= global.as_ptr().as_ref().unwrap_unchecked().total_bytes {
let global_mut = global.as_ptr().as_mut().unwrap_unchecked();
global_mut.gc_threshold = global_mut.total_bytes - amount;
} else {
global.as_ptr().as_mut().unwrap_unchecked().gc_threshold = 0;
}
let mut actual_work = 0usize;
while global.as_ptr().as_ref().unwrap_unchecked().gc_threshold
<= global.as_ptr().as_ref().unwrap_unchecked().total_bytes
{
actual_work += self.step(false)?;
if global.gc_state() == GCS_PAUSE {
result = 1;
break;
}
}
if global.gc_state() != GCS_PAUSE {
let total_bytes = global.as_ptr().as_ref().unwrap_unchecked().total_bytes;
let new_threshold =
total_bytes as isize + actual_work as isize + old_credit;
global.as_ptr().as_mut().unwrap_unchecked().gc_threshold =
if new_threshold < 0 {
0
} else {
new_threshold as usize
};
}
}
LUA_GC_SET_GOAL => {
result = global.as_ptr().as_ref().unwrap_unchecked().gc_goal;
global.as_ptr().as_mut().unwrap_unchecked().gc_goal = data;
}
LUA_GC_SET_STEP_MUL => {
result = global.as_ptr().as_ref().unwrap_unchecked().gc_step_mul;
global.as_ptr().as_mut().unwrap_unchecked().gc_step_mul = data;
}
LUA_GC_SET_STEP_SIZE => {
result = global.as_ptr().as_ref().unwrap_unchecked().gc_step_size >> 10;
global.as_ptr().as_mut().unwrap_unchecked().gc_step_size = data << 10;
}
LUA_GC_IS_PAUSED => {
result = i32::from(global.gc_state() == GCS_PAUSE);
}
_ => result = -1,
}
}
Ok(result)
}
pub unsafe fn memory_dump(
&self,
output: &mut BString,
category_name: Option<&mut dyn GcCategoryNamer>,
) {
unsafe { <Self as GcRuntime>::dump(self, (output as *mut BString).cast(), category_name) };
}
pub unsafe fn allocation_rate(&self) -> i64 {
unsafe { <Self as GcRuntime>::allocation_rate(self) }
}
pub unsafe fn set_mem_cat(&self, category: i32) {
debug_assert!((category as u32) < crate::memory::LUA_MEMORY_CATEGORIES as u32);
unsafe {
self.as_ptr().as_mut().unwrap_unchecked().active_memcat = category as u8;
}
}
pub unsafe fn total_bytes(&self, category: i32) -> usize {
debug_assert!(category < crate::memory::LUA_MEMORY_CATEGORIES as i32);
let global_handle = unsafe { self.global() };
let global = unsafe { global_handle.as_ptr().as_ref().unwrap_unchecked() };
if category < 0 {
global.total_bytes
} else {
global.memcat_bytes[category as usize]
}
}
}