use crate::{
Chunk, GcObjectKind, LuaFunction, LuaTable, LuaValue,
lua_value::{CClosureFunction, LuaString, LuaUpvalue, LuaUserdata, RClosureFunction},
lua_vm::LuaState,
};
pub const G_NEW: u8 = 0; pub const G_SURVIVAL: u8 = 1; pub const G_OLD0: u8 = 2; pub const G_OLD1: u8 = 3; pub const G_OLD: u8 = 4; pub const G_TOUCHED1: u8 = 5; pub const G_TOUCHED2: u8 = 6;
pub const WHITE0BIT: u8 = 3; pub const WHITE1BIT: u8 = 4; pub const BLACKBIT: u8 = 5; pub const FINALIZEDBIT: u8 = 6; pub const SHAREDBIT: u8 = 7;
pub const WHITEBITS: u8 = (1 << WHITE0BIT) | (1 << WHITE1BIT);
pub const AGEBITS: u8 = 0x07; pub const MASKCOLORS: u8 = (1 << BLACKBIT) | WHITEBITS;
pub const MASKGCBITS: u8 = MASKCOLORS | AGEBITS;
#[derive(Clone, Copy, PartialEq, Eq)]
#[repr(C)]
pub struct GcHeader {
packed: u32,
pub size: u32,
}
impl std::fmt::Debug for GcHeader {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("GcHeader")
.field("marked", &self.marked())
.field("index", &self.index())
.field("size", &self.size)
.finish()
}
}
impl GcHeader {
const MARKED_MASK: u32 = 0xFF; const INDEX_SHIFT: u32 = 8;
const INDEX_MAX: u32 = (1 << 24) - 1;
#[inline(always)]
pub fn marked(&self) -> u8 {
self.packed as u8
}
#[inline(always)]
fn set_marked_bits(&mut self, m: u8) {
self.packed = (self.packed & !Self::MARKED_MASK) | (m as u32);
}
#[inline(always)]
pub fn index(&self) -> usize {
(self.packed >> Self::INDEX_SHIFT) as usize
}
#[inline(always)]
pub fn set_index(&mut self, idx: usize) {
debug_assert!(
idx <= Self::INDEX_MAX as usize,
"GcList index overflow: {idx} > {}",
Self::INDEX_MAX
);
self.packed = (self.packed & Self::MARKED_MASK) | ((idx as u32) << Self::INDEX_SHIFT);
}
}
impl Default for GcHeader {
fn default() -> Self {
GcHeader {
packed: G_NEW as u32,
size: 0,
}
}
}
impl GcHeader {
#[inline(always)]
pub fn with_white(current_white: u8) -> Self {
debug_assert!(
current_white == 0 || current_white == 1,
"current_white must be 0 or 1"
);
GcHeader {
packed: ((1 << (WHITE0BIT + current_white)) | G_NEW) as u32,
size: 0,
}
}
#[inline(always)]
pub fn age(&self) -> u8 {
self.marked() & AGEBITS
}
#[inline(always)]
pub fn set_age(&mut self, age: u8) {
debug_assert!(age <= G_TOUCHED2, "Invalid age value");
let m = (self.marked() & !AGEBITS) | (age & AGEBITS);
self.set_marked_bits(m);
}
#[inline(always)]
pub fn is_old(&self) -> bool {
self.age() > G_SURVIVAL
}
#[inline(always)]
pub fn is_white(&self) -> bool {
(self.marked() & WHITEBITS) != 0
}
#[inline(always)]
pub fn is_current_white(&self, current_white: u8) -> bool {
debug_assert!(
current_white == 0 || current_white == 1,
"current_white must be 0 or 1"
);
(self.marked() & (1 << (WHITE0BIT + current_white))) != 0
}
#[inline(always)]
pub fn is_black(&self) -> bool {
(self.marked() & (1 << BLACKBIT)) != 0
}
#[inline(always)]
pub fn is_gray(&self) -> bool {
(self.marked() & (WHITEBITS | (1 << BLACKBIT))) == 0
}
#[inline(always)]
pub fn to_finalize(&self) -> bool {
(self.marked() & (1 << FINALIZEDBIT)) != 0
}
#[inline(always)]
pub fn is_shared(&self) -> bool {
(self.marked() & (1 << SHAREDBIT)) != 0
}
#[inline(always)]
pub fn set_finalized(&mut self) {
self.set_marked_bits(self.marked() | (1 << FINALIZEDBIT));
}
#[inline(always)]
pub fn clear_finalized(&mut self) {
self.set_marked_bits(self.marked() & !(1 << FINALIZEDBIT));
}
#[inline(always)]
pub fn make_shared(&mut self) {
self.set_marked_bits(self.marked() | (1 << SHAREDBIT));
}
#[inline(always)]
pub fn make_white(&mut self, current_white: u8) {
debug_assert!(
current_white == 0 || current_white == 1,
"current_white must be 0 or 1"
);
let m = (self.marked() & !MASKCOLORS) | (1 << (WHITE0BIT + current_white));
self.set_marked_bits(m);
}
#[inline(always)]
pub fn make_gray(&mut self) {
self.set_marked_bits(self.marked() & !MASKCOLORS);
}
#[inline(always)]
pub fn make_black(&mut self) {
let m = (self.marked() & !WHITEBITS) | (1 << BLACKBIT);
self.set_marked_bits(m);
}
#[inline(always)]
pub fn nw2black(&mut self) {
debug_assert!(!self.is_white(), "nw2black called on white object");
self.set_marked_bits(self.marked() | (1 << BLACKBIT));
}
#[inline(always)]
pub fn is_dead(&self, other_white: u8) -> bool {
debug_assert!(
other_white == 0 || other_white == 1,
"other_white must be 0 or 1"
);
if self.is_shared() {
return false;
}
(self.marked() & (1 << (WHITE0BIT + other_white))) != 0
}
#[inline(always)]
pub fn otherwhite(current_white: u8) -> u8 {
current_white ^ 1
}
#[inline(always)]
pub fn change_white(&mut self) {
self.set_marked_bits(self.marked() ^ WHITEBITS);
}
#[inline(always)]
pub fn make_old0(&mut self) {
self.set_age(G_OLD0);
}
#[inline(always)]
pub fn make_old1(&mut self) {
self.set_age(G_OLD1);
}
#[inline(always)]
pub fn make_old(&mut self) {
self.set_age(G_OLD);
}
#[inline(always)]
pub fn make_touched1(&mut self) {
self.set_age(G_TOUCHED1);
}
#[inline(always)]
pub fn make_touched2(&mut self) {
self.set_age(G_TOUCHED2);
}
#[inline(always)]
pub fn make_survival(&mut self) {
self.set_age(G_SURVIVAL);
}
#[inline(always)]
pub fn is_marked(&self) -> bool {
!self.is_white()
}
}
pub trait HasGcHeader {
fn header(&self) -> &GcHeader;
}
#[repr(C)]
pub struct Gc<T> {
pub header: GcHeader,
pub data: T,
}
impl<T: std::fmt::Debug> std::fmt::Debug for Gc<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Gc")
.field("header", &self.header)
.field("data", &self.data)
.finish()
}
}
impl<T> Gc<T> {
pub fn new(data: T, current_white: u8, size: u32) -> Self {
let mut header = GcHeader::with_white(current_white);
header.size = size;
Gc { header, data }
}
}
impl<T> HasGcHeader for Gc<T> {
fn header(&self) -> &GcHeader {
&self.header
}
}
pub type GcString = Gc<LuaString>;
pub type GcTable = Gc<LuaTable>;
pub type GcFunction = Gc<LuaFunction>;
pub type GcCClosure = Gc<CClosureFunction>;
pub type GcRClosure = Gc<RClosureFunction>;
pub type GcUpvalue = Gc<LuaUpvalue>;
pub type GcThread = Gc<LuaState>;
pub type GcUserdata = Gc<LuaUserdata>;
pub type GcProto = Gc<Chunk>;
#[derive(Debug)]
pub struct GcPtr<T: HasGcHeader> {
ptr: u64,
_marker: std::marker::PhantomData<*const T>,
}
impl<T: HasGcHeader> std::hash::Hash for GcPtr<T> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.ptr.hash(state);
}
}
impl<T: HasGcHeader> Clone for GcPtr<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: HasGcHeader> Copy for GcPtr<T> {}
impl<T: HasGcHeader> Eq for GcPtr<T> {}
impl<T: HasGcHeader> PartialEq for GcPtr<T> {
fn eq(&self, other: &Self) -> bool {
self.ptr == other.ptr
}
}
impl<T: HasGcHeader> GcPtr<T> {
pub fn new(ptr: *const T) -> Self {
Self {
ptr: ptr as u64,
_marker: std::marker::PhantomData,
}
}
#[inline(always)]
pub fn from_raw(raw: u64) -> Self {
Self {
ptr: raw,
_marker: std::marker::PhantomData,
}
}
pub fn null() -> Self {
Self {
ptr: 0,
_marker: std::marker::PhantomData,
}
}
#[inline(always)]
pub fn as_u64(&self) -> u64 {
self.ptr
}
#[inline(always)]
pub fn as_ptr(&self) -> *const T {
self.ptr as *const T
}
#[inline(always)]
pub fn as_mut_ptr(&self) -> *mut T {
self.ptr as *mut T
}
#[allow(clippy::mut_from_ref)]
#[inline(always)]
pub fn as_mut_ref(&self) -> &mut T {
unsafe { &mut *(self.as_mut_ptr()) }
}
#[inline(always)]
pub fn as_ref(&self) -> &T {
unsafe { &*(self.as_ptr()) }
}
pub fn is_null(&self) -> bool {
self.ptr == 0
}
}
pub type UpvaluePtr = GcPtr<GcUpvalue>;
pub type TablePtr = GcPtr<GcTable>;
pub type StringPtr = GcPtr<GcString>;
pub type FunctionPtr = GcPtr<GcFunction>;
pub type CClosurePtr = GcPtr<GcCClosure>;
pub type RClosurePtr = GcPtr<GcRClosure>;
pub type UserdataPtr = GcPtr<GcUserdata>;
pub type ThreadPtr = GcPtr<GcThread>;
pub type ProtoPtr = GcPtr<GcProto>;
#[derive(Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
pub struct GcObjectPtr(u64);
impl std::fmt::Debug for GcObjectPtr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"GcObjectPtr({:?}, 0x{:012x})",
self.kind(),
self.raw_ptr()
)
}
}
impl std::hash::Hash for GcObjectPtr {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.0.hash(state);
}
}
impl GcObjectPtr {
const TAG_SHIFT: u32 = 60;
const PTR_MASK: u64 = (1u64 << 48) - 1;
const TAG_STRING: u64 = 0;
const TAG_TABLE: u64 = 1;
const TAG_FUNCTION: u64 = 2;
const TAG_CCLOSURE: u64 = 3;
const TAG_RCLOSURE: u64 = 4;
const TAG_UPVALUE: u64 = 5;
const TAG_THREAD: u64 = 6;
const TAG_USERDATA: u64 = 7;
const TAG_PROTO: u64 = 8;
#[inline(always)]
fn new_tagged(ptr: u64, tag: u64) -> Self {
debug_assert!(
ptr & !Self::PTR_MASK == 0,
"pointer exceeds 48 bits: 0x{ptr:016x}"
);
Self(ptr | (tag << Self::TAG_SHIFT))
}
#[inline(always)]
fn tag(&self) -> u8 {
(self.0 >> Self::TAG_SHIFT) as u8
}
#[inline(always)]
fn raw_ptr(&self) -> u64 {
self.0 & Self::PTR_MASK
}
#[inline(always)]
pub fn header(&self) -> Option<&GcHeader> {
let p = self.raw_ptr();
if p == 0 {
None
} else {
Some(unsafe { &*(p as *const GcHeader) })
}
}
#[inline(always)]
#[allow(clippy::mut_from_ref)]
pub fn header_mut(&self) -> Option<&mut GcHeader> {
let p = self.raw_ptr();
if p == 0 {
None
} else {
Some(unsafe { &mut *(p as *mut GcHeader) })
}
}
#[inline(always)]
pub fn kind(&self) -> GcObjectKind {
unsafe { std::mem::transmute(self.tag()) }
}
#[inline(always)]
pub(crate) fn index(&self) -> usize {
self.header().map(|h| h.index()).unwrap_or(0)
}
pub fn fix_gc_object(&mut self) {
if let Some(header) = self.header_mut() {
header.set_age(G_OLD);
header.make_gray(); }
}
#[inline(always)]
pub fn as_string_ptr(&self) -> StringPtr {
debug_assert!(self.tag() == Self::TAG_STRING as u8);
StringPtr::from_raw(self.raw_ptr())
}
#[inline(always)]
pub fn as_table_ptr(&self) -> TablePtr {
debug_assert!(self.tag() == Self::TAG_TABLE as u8);
TablePtr::from_raw(self.raw_ptr())
}
#[inline(always)]
pub fn as_function_ptr(&self) -> FunctionPtr {
debug_assert!(self.tag() == Self::TAG_FUNCTION as u8);
FunctionPtr::from_raw(self.raw_ptr())
}
#[inline(always)]
pub fn as_cclosure_ptr(&self) -> CClosurePtr {
debug_assert!(self.tag() == Self::TAG_CCLOSURE as u8);
CClosurePtr::from_raw(self.raw_ptr())
}
#[inline(always)]
pub fn as_rclosure_ptr(&self) -> RClosurePtr {
debug_assert!(self.tag() == Self::TAG_RCLOSURE as u8);
RClosurePtr::from_raw(self.raw_ptr())
}
#[inline(always)]
pub fn as_upvalue_ptr(&self) -> UpvaluePtr {
debug_assert!(self.tag() == Self::TAG_UPVALUE as u8);
UpvaluePtr::from_raw(self.raw_ptr())
}
#[inline(always)]
pub fn as_thread_ptr(&self) -> ThreadPtr {
debug_assert!(self.tag() == Self::TAG_THREAD as u8);
ThreadPtr::from_raw(self.raw_ptr())
}
#[inline(always)]
pub fn as_userdata_ptr(&self) -> UserdataPtr {
debug_assert!(self.tag() == Self::TAG_USERDATA as u8);
UserdataPtr::from_raw(self.raw_ptr())
}
#[inline(always)]
pub fn as_proto_ptr(&self) -> ProtoPtr {
debug_assert!(self.tag() == Self::TAG_PROTO as u8);
ProtoPtr::from_raw(self.raw_ptr())
}
#[inline(always)]
pub fn is_string(&self) -> bool {
self.tag() == Self::TAG_STRING as u8
}
#[inline(always)]
pub fn is_table(&self) -> bool {
self.tag() == Self::TAG_TABLE as u8
}
#[inline(always)]
pub fn is_upvalue(&self) -> bool {
self.tag() == Self::TAG_UPVALUE as u8
}
#[inline(always)]
pub fn is_thread(&self) -> bool {
self.tag() == Self::TAG_THREAD as u8
}
#[inline(always)]
pub fn is_function(&self) -> bool {
self.tag() == Self::TAG_FUNCTION as u8
}
#[inline(always)]
pub fn is_cclosure(&self) -> bool {
self.tag() == Self::TAG_CCLOSURE as u8
}
#[inline(always)]
pub fn is_rclosure(&self) -> bool {
self.tag() == Self::TAG_RCLOSURE as u8
}
#[inline(always)]
pub fn is_userdata(&self) -> bool {
self.tag() == Self::TAG_USERDATA as u8
}
#[inline(always)]
pub fn is_proto(&self) -> bool {
self.tag() == Self::TAG_PROTO as u8
}
}
impl From<StringPtr> for GcObjectPtr {
#[inline(always)]
fn from(ptr: StringPtr) -> Self {
Self::new_tagged(ptr.as_u64(), Self::TAG_STRING)
}
}
impl From<TablePtr> for GcObjectPtr {
#[inline(always)]
fn from(ptr: TablePtr) -> Self {
Self::new_tagged(ptr.as_u64(), Self::TAG_TABLE)
}
}
impl From<FunctionPtr> for GcObjectPtr {
#[inline(always)]
fn from(ptr: FunctionPtr) -> Self {
Self::new_tagged(ptr.as_u64(), Self::TAG_FUNCTION)
}
}
impl From<UpvaluePtr> for GcObjectPtr {
#[inline(always)]
fn from(ptr: UpvaluePtr) -> Self {
Self::new_tagged(ptr.as_u64(), Self::TAG_UPVALUE)
}
}
impl From<ThreadPtr> for GcObjectPtr {
#[inline(always)]
fn from(ptr: ThreadPtr) -> Self {
Self::new_tagged(ptr.as_u64(), Self::TAG_THREAD)
}
}
impl From<UserdataPtr> for GcObjectPtr {
#[inline(always)]
fn from(ptr: UserdataPtr) -> Self {
Self::new_tagged(ptr.as_u64(), Self::TAG_USERDATA)
}
}
impl From<CClosurePtr> for GcObjectPtr {
#[inline(always)]
fn from(ptr: CClosurePtr) -> Self {
Self::new_tagged(ptr.as_u64(), Self::TAG_CCLOSURE)
}
}
impl From<RClosurePtr> for GcObjectPtr {
#[inline(always)]
fn from(ptr: RClosurePtr) -> Self {
Self::new_tagged(ptr.as_u64(), Self::TAG_RCLOSURE)
}
}
impl From<ProtoPtr> for GcObjectPtr {
#[inline(always)]
fn from(ptr: ProtoPtr) -> Self {
Self::new_tagged(ptr.as_u64(), Self::TAG_PROTO)
}
}
pub enum GcObjectOwner {
String(Box<GcString>),
Table(Box<GcTable>),
Function(Box<GcFunction>),
Upvalue(Box<GcUpvalue>),
Thread(Box<GcThread>),
Userdata(Box<GcUserdata>),
CClosure(Box<GcCClosure>),
RClosure(Box<GcRClosure>),
Proto(Box<GcProto>),
}
impl GcObjectOwner {
pub fn compute_size(&self) -> usize {
match self {
GcObjectOwner::String(s) => std::mem::size_of::<GcString>() + s.data.str.len(),
GcObjectOwner::Table(t) => {
let base = std::mem::size_of::<GcTable>();
let asize = t.data.impl_table.asize as usize;
let array_bytes = if asize > 0 { asize * 17 + 4 } else { 0 };
let hash_bytes = {
let hs = t.data.hash_size();
if hs > 0 { hs * 24 + 8 } else { 0 }
};
base + array_bytes + hash_bytes
}
GcObjectOwner::Function(f) => {
std::mem::size_of::<GcFunction>() + std::mem::size_of_val(f.data.upvalues())
}
GcObjectOwner::CClosure(c) => {
std::mem::size_of::<GcCClosure>()
+ c.data.upvalues().len() * std::mem::size_of::<LuaValue>()
}
GcObjectOwner::RClosure(r) => {
std::mem::size_of::<GcRClosure>()
+ r.data.upvalues().len() * std::mem::size_of::<LuaValue>()
}
GcObjectOwner::Upvalue(_) => 64, GcObjectOwner::Thread(t) => std::mem::size_of::<GcThread>() + t.data.stack.len() * 16,
GcObjectOwner::Userdata(_) => std::mem::size_of::<GcUserdata>(),
GcObjectOwner::Proto(p) => {
std::mem::size_of::<GcProto>() + p.data.proto_data_size as usize
}
}
}
#[inline]
pub fn size(&self) -> usize {
self.header().size as usize
}
pub fn header(&self) -> &GcHeader {
(match self {
GcObjectOwner::String(s) => &s.header,
GcObjectOwner::Table(t) => &t.header,
GcObjectOwner::Function(f) => &f.header,
GcObjectOwner::CClosure(c) => &c.header,
GcObjectOwner::RClosure(r) => &r.header,
GcObjectOwner::Upvalue(u) => &u.header,
GcObjectOwner::Thread(t) => &t.header,
GcObjectOwner::Userdata(u) => &u.header,
GcObjectOwner::Proto(p) => &p.header,
}) as _
}
pub fn header_mut(&mut self) -> &mut GcHeader {
(match self {
GcObjectOwner::String(s) => &mut s.header,
GcObjectOwner::Table(t) => &mut t.header,
GcObjectOwner::Function(f) => &mut f.header,
GcObjectOwner::CClosure(c) => &mut c.header,
GcObjectOwner::RClosure(r) => &mut r.header,
GcObjectOwner::Upvalue(u) => &mut u.header,
GcObjectOwner::Thread(t) => &mut t.header,
GcObjectOwner::Userdata(u) => &mut u.header,
GcObjectOwner::Proto(p) => &mut p.header,
}) as _
}
#[inline(always)]
pub fn as_str_ptr(&self) -> Option<StringPtr> {
match self {
GcObjectOwner::String(s) => Some(StringPtr::new(s.as_ref() as *const GcString)),
_ => None,
}
}
pub fn as_table_ptr(&self) -> Option<TablePtr> {
match self {
GcObjectOwner::Table(t) => Some(TablePtr::new(t.as_ref() as *const GcTable)),
_ => None,
}
}
pub fn as_function_ptr(&self) -> Option<FunctionPtr> {
match self {
GcObjectOwner::Function(f) => Some(FunctionPtr::new(f.as_ref() as *const GcFunction)),
_ => None,
}
}
pub fn as_upvalue_ptr(&self) -> Option<UpvaluePtr> {
match self {
GcObjectOwner::Upvalue(u) => Some(UpvaluePtr::new(u.as_ref() as *const GcUpvalue)),
_ => None,
}
}
pub fn as_thread_ptr(&self) -> Option<ThreadPtr> {
match self {
GcObjectOwner::Thread(t) => Some(ThreadPtr::new(t.as_ref() as *const GcThread)),
_ => None,
}
}
pub fn as_userdata_ptr(&self) -> Option<UserdataPtr> {
match self {
GcObjectOwner::Userdata(u) => Some(UserdataPtr::new(u.as_ref() as *const GcUserdata)),
_ => None,
}
}
pub fn as_closure_ptr(&self) -> Option<CClosurePtr> {
match self {
GcObjectOwner::CClosure(c) => Some(CClosurePtr::new(c.as_ref() as *const GcCClosure)),
_ => None,
}
}
pub fn as_rclosure_ptr(&self) -> Option<RClosurePtr> {
match self {
GcObjectOwner::RClosure(r) => Some(RClosurePtr::new(r.as_ref() as *const GcRClosure)),
_ => None,
}
}
pub fn as_proto_ptr(&self) -> Option<ProtoPtr> {
match self {
GcObjectOwner::Proto(p) => Some(ProtoPtr::new(p.as_ref() as *const GcProto)),
_ => None,
}
}
pub fn as_gc_ptr(&self) -> GcObjectPtr {
match self {
GcObjectOwner::String(s) => {
GcObjectPtr::from(StringPtr::new(s.as_ref() as *const GcString))
}
GcObjectOwner::Table(t) => {
GcObjectPtr::from(TablePtr::new(t.as_ref() as *const GcTable))
}
GcObjectOwner::Function(f) => {
GcObjectPtr::from(FunctionPtr::new(f.as_ref() as *const GcFunction))
}
GcObjectOwner::Upvalue(u) => {
GcObjectPtr::from(UpvaluePtr::new(u.as_ref() as *const GcUpvalue))
}
GcObjectOwner::Thread(t) => {
GcObjectPtr::from(ThreadPtr::new(t.as_ref() as *const GcThread))
}
GcObjectOwner::Userdata(u) => {
GcObjectPtr::from(UserdataPtr::new(u.as_ref() as *const GcUserdata))
}
GcObjectOwner::CClosure(c) => {
GcObjectPtr::from(CClosurePtr::new(c.as_ref() as *const GcCClosure))
}
GcObjectOwner::RClosure(r) => {
GcObjectPtr::from(RClosurePtr::new(r.as_ref() as *const GcRClosure))
}
GcObjectOwner::Proto(p) => {
GcObjectPtr::from(ProtoPtr::new(p.as_ref() as *const GcProto))
}
}
}
pub fn as_table_mut(&mut self) -> Option<&mut LuaTable> {
match self {
GcObjectOwner::Table(t) => Some(&mut t.data),
_ => None,
}
}
pub fn as_function_mut(&mut self) -> Option<&mut LuaFunction> {
match self {
GcObjectOwner::Function(f) => Some(&mut f.data),
_ => None,
}
}
pub fn as_upvalue_mut(&mut self) -> Option<&mut LuaUpvalue> {
match self {
GcObjectOwner::Upvalue(u) => Some(&mut u.data),
_ => None,
}
}
pub fn as_thread_mut(&mut self) -> Option<&mut LuaState> {
match self {
GcObjectOwner::Thread(t) => Some(&mut t.data),
_ => None,
}
}
pub fn as_userdata_mut(&mut self) -> Option<&mut LuaUserdata> {
match self {
GcObjectOwner::Userdata(u) => Some(&mut u.data),
_ => None,
}
}
pub fn as_cclosure_mut(&mut self) -> Option<&mut CClosureFunction> {
match self {
GcObjectOwner::CClosure(c) => Some(&mut c.data),
_ => None,
}
}
pub fn as_rclosure_mut(&mut self) -> Option<&mut RClosureFunction> {
match self {
GcObjectOwner::RClosure(r) => Some(&mut r.data),
_ => None,
}
}
pub fn as_proto_mut(&mut self) -> Option<&mut Chunk> {
match self {
GcObjectOwner::Proto(p) => Some(&mut p.data),
_ => None,
}
}
pub fn size_of_data(&self) -> usize {
self.header().size as usize
}
}
pub struct GcList {
gc_list: Vec<GcObjectOwner>,
}
impl GcList {
#[inline]
pub fn new() -> Self {
Self {
gc_list: Vec::new(),
}
}
#[inline]
pub fn with_capacity(cap: usize) -> Self {
Self {
gc_list: Vec::with_capacity(cap),
}
}
#[inline]
pub fn add(&mut self, mut value: GcObjectOwner) {
let index = self.gc_list.len();
value.header_mut().set_index(index);
self.gc_list.push(value);
}
#[inline]
pub fn remove(&mut self, gc_ptr: GcObjectPtr) -> GcObjectOwner {
let index = gc_ptr.index();
let last_index = self.gc_list.len() - 1;
if index != last_index {
let moved_obj = &mut self.gc_list[last_index];
moved_obj.header_mut().set_index(index);
}
self.gc_list.swap_remove(index)
}
#[inline]
pub fn len(&self) -> usize {
self.gc_list.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.gc_list.is_empty()
}
pub fn iter(&self) -> impl Iterator<Item = &GcObjectOwner> + '_ {
self.gc_list.iter()
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut GcObjectOwner> + '_ {
self.gc_list.iter_mut()
}
pub fn shrink_to_fit(&mut self) {
self.gc_list.shrink_to_fit();
}
pub fn clear(&mut self) {
self.gc_list.clear();
}
#[inline]
pub fn capacity(&self) -> usize {
self.gc_list.capacity()
}
pub fn get(&self, index: usize) -> Option<&GcObjectOwner> {
self.gc_list.get(index)
}
pub fn get_mut(&mut self, index: usize) -> Option<&mut GcObjectOwner> {
self.gc_list.get_mut(index)
}
pub fn iter_ptrs(&self) -> impl Iterator<Item = GcObjectPtr> + '_ {
self.gc_list.iter().map(|obj| obj.as_gc_ptr())
}
#[inline]
pub fn contains(&self, gc_ptr: GcObjectPtr) -> bool {
let index = gc_ptr.index();
if index < self.gc_list.len() {
self.gc_list[index].as_gc_ptr() == gc_ptr
} else {
false
}
}
#[inline]
pub fn try_remove(&mut self, gc_ptr: GcObjectPtr) -> Option<GcObjectOwner> {
if self.contains(gc_ptr) {
Some(self.remove(gc_ptr))
} else {
None
}
}
#[inline]
pub fn get_owner(&self, index: usize) -> &GcObjectOwner {
&self.gc_list[index]
}
#[inline]
pub fn take_all(&mut self) -> Vec<GcObjectOwner> {
std::mem::take(&mut self.gc_list)
}
#[inline]
pub fn add_all(&mut self, objects: Vec<GcObjectOwner>) {
for mut obj in objects {
let index = self.gc_list.len();
obj.header_mut().set_index(index);
self.gc_list.push(obj);
}
}
}
impl Default for GcList {
fn default() -> Self {
Self::new()
}
}