use crate::{
LuaProto, LuaRawFunction, LuaRawTable,
gc::{GcObjectKind, Pooled},
lua_value::{CClosureFunction, LuaString, LuaUpvalue, LuaUserdata, RClosureFunction},
lua_vm::LuaState,
};
use std::cell::Cell;
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;
#[repr(C)]
pub struct GcHeader {
marked: Cell<u8>,
_padding: [u8; 3],
index: Cell<u32>,
size: Cell<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 INDEX_MAX: u32 = u32::MAX;
#[inline(always)]
pub fn marked(&self) -> u8 {
self.marked.get()
}
#[inline(always)]
fn set_marked_bits(&self, m: u8) {
self.marked.set(m);
}
#[inline(always)]
pub fn index(&self) -> usize {
self.index.get() as usize
}
#[inline(always)]
pub fn set_index(&self, idx: usize) {
assert!(
idx <= Self::INDEX_MAX as usize,
"GcList index overflow: {idx} > {}",
Self::INDEX_MAX
);
self.index.set(idx as u32);
}
#[inline(always)]
pub fn size(&self) -> u32 {
self.size.get()
}
#[inline(always)]
pub fn set_size(&self, size: u32) {
self.size.set(size);
}
}
impl Default for GcHeader {
fn default() -> Self {
GcHeader {
marked: Cell::new(G_NEW),
_padding: [0; 3],
index: Cell::new(0),
size: Cell::new(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 {
marked: Cell::new((1 << (WHITE0BIT + current_white)) | G_NEW),
_padding: [0; 3],
index: Cell::new(0),
size: Cell::new(0),
}
}
#[inline(always)]
pub fn age(&self) -> u8 {
self.marked() & AGEBITS
}
#[inline(always)]
pub fn set_age(&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(&self) {
self.set_marked_bits(self.marked() | (1 << FINALIZEDBIT));
}
#[inline(always)]
pub fn clear_finalized(&self) {
self.set_marked_bits(self.marked() & !(1 << FINALIZEDBIT));
}
#[inline(always)]
pub fn make_shared(&self) {
self.set_marked_bits(self.marked() | (1 << SHAREDBIT));
}
#[inline(always)]
pub fn make_white(&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(&self) {
self.set_marked_bits(self.marked() & !MASKCOLORS);
}
#[inline(always)]
pub fn make_black(&self) {
let m = (self.marked() & !WHITEBITS) | (1 << BLACKBIT);
self.set_marked_bits(m);
}
#[inline(always)]
pub fn nw2black(&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(&self) {
self.set_marked_bits(self.marked() ^ WHITEBITS);
}
#[inline(always)]
pub fn make_old0(&self) {
self.set_age(G_OLD0);
}
#[inline(always)]
pub fn make_old1(&self) {
self.set_age(G_OLD1);
}
#[inline(always)]
pub fn make_old(&self) {
self.set_age(G_OLD);
}
#[inline(always)]
pub fn make_touched1(&self) {
self.set_age(G_TOUCHED1);
}
#[inline(always)]
pub fn make_touched2(&self) {
self.set_age(G_TOUCHED2);
}
#[inline(always)]
pub fn make_survival(&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 header = GcHeader::with_white(current_white);
header.set_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<LuaRawTable>;
pub type GcFunction = Gc<LuaRawFunction>;
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<LuaProto>;
#[derive(Debug)]
pub struct GcPtr<T: HasGcHeader> {
ptr: *const T,
_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 as u64).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 as u64) == (other.ptr as u64)
}
}
impl<T: HasGcHeader> GcPtr<T> {
pub fn new(ptr: *const T) -> Self {
Self {
ptr,
_marker: std::marker::PhantomData,
}
}
#[inline(always)]
pub fn from_raw(raw: u64) -> Self {
Self {
ptr: raw as *const T,
_marker: std::marker::PhantomData,
}
}
pub fn null() -> Self {
Self {
ptr: std::ptr::null(),
_marker: std::marker::PhantomData,
}
}
#[inline(always)]
pub fn as_u64(&self) -> u64 {
self.ptr as u64
}
#[inline(always)]
pub fn as_ptr(&self) -> *const T {
self.ptr
}
#[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.ptr as *mut T) }
}
#[inline(always)]
pub fn as_ref(&self) -> &T {
unsafe { &*self.ptr }
}
pub fn is_null(&self) -> bool {
self.ptr.is_null()
}
}
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;
pub const TAG_STRING: u64 = 0;
pub const TAG_TABLE: u64 = 1;
pub const TAG_FUNCTION: u64 = 2;
pub const TAG_CCLOSURE: u64 = 3;
pub const TAG_RCLOSURE: u64 = 4;
pub const TAG_UPVALUE: u64 = 5;
pub const TAG_THREAD: u64 = 6;
pub const TAG_USERDATA: u64 = 7;
pub const TAG_PROTO: u64 = 8;
pub const TAG_NONE: u64 = 9;
#[inline(always)]
pub 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)]
pub fn null() -> Self {
Self(0)
}
#[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<&GcHeader> {
let p = self.raw_ptr();
if p == 0 {
None
} else {
Some(unsafe { &*(p as *const GcHeader) })
}
}
#[inline(always)]
pub fn kind(&self) -> GcObjectKind {
match GcObjectKind::from_u8(self.tag()) {
Some(k) => k,
None => unreachable!(),
}
}
#[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(Pooled<GcString>),
Table(Pooled<GcTable>),
Function(Pooled<GcFunction>),
Upvalue(Pooled<GcUpvalue>),
Thread(Box<GcThread>),
Userdata(Pooled<GcUserdata>),
CClosure(Pooled<GcCClosure>),
RClosure(Pooled<GcRClosure>),
Proto(Pooled<GcProto>),
}
impl GcObjectOwner {
#[inline]
pub fn size(&self) -> usize {
self.header().size() as usize
}
#[inline(always)]
fn raw_header_ptr(&self) -> *mut GcHeader {
match self {
GcObjectOwner::String(s) => s.as_ptr() as *mut GcHeader,
GcObjectOwner::Table(t) => t.as_ptr() as *mut GcHeader,
GcObjectOwner::Function(f) => f.as_ptr() as *mut GcHeader,
GcObjectOwner::CClosure(c) => c.as_ptr() as *mut GcHeader,
GcObjectOwner::RClosure(r) => r.as_ptr() as *mut GcHeader,
GcObjectOwner::Upvalue(u) => u.as_ptr() as *mut GcHeader,
GcObjectOwner::Thread(t) => t.as_ref() as *const _ as *mut GcHeader,
GcObjectOwner::Userdata(u) => u.as_ptr() as *mut GcHeader,
GcObjectOwner::Proto(p) => p.as_ptr() as *mut GcHeader,
}
}
pub fn header(&self) -> &GcHeader {
unsafe { &*self.raw_header_ptr() }
}
pub fn header_mut(&self) -> &GcHeader {
unsafe { &*self.raw_header_ptr() }
}
#[inline(always)]
pub fn as_str_ptr(&self) -> Option<StringPtr> {
match self {
GcObjectOwner::String(s) => Some(StringPtr::new(s.as_ptr())),
_ => None,
}
}
pub fn as_table_ptr(&self) -> Option<TablePtr> {
match self {
GcObjectOwner::Table(t) => Some(TablePtr::new(t.as_ptr())),
_ => None,
}
}
pub fn as_function_ptr(&self) -> Option<FunctionPtr> {
match self {
GcObjectOwner::Function(f) => Some(FunctionPtr::new(f.as_ptr())),
_ => None,
}
}
pub fn as_upvalue_ptr(&self) -> Option<UpvaluePtr> {
match self {
GcObjectOwner::Upvalue(u) => Some(UpvaluePtr::new(u.as_ptr())),
_ => None,
}
}
pub fn as_thread_ptr(&self) -> Option<ThreadPtr> {
match self {
GcObjectOwner::Thread(t) => Some(ThreadPtr::new(t.as_ref() as *const _)),
_ => None,
}
}
pub fn as_userdata_ptr(&self) -> Option<UserdataPtr> {
match self {
GcObjectOwner::Userdata(u) => Some(UserdataPtr::new(u.as_ptr())),
_ => None,
}
}
pub fn as_closure_ptr(&self) -> Option<CClosurePtr> {
match self {
GcObjectOwner::CClosure(c) => Some(CClosurePtr::new(c.as_ptr())),
_ => None,
}
}
pub fn as_rclosure_ptr(&self) -> Option<RClosurePtr> {
match self {
GcObjectOwner::RClosure(r) => Some(RClosurePtr::new(r.as_ptr())),
_ => None,
}
}
pub fn as_proto_ptr(&self) -> Option<ProtoPtr> {
match self {
GcObjectOwner::Proto(p) => Some(ProtoPtr::new(p.as_ptr())),
_ => None,
}
}
pub fn as_gc_ptr(&self) -> GcObjectPtr {
match self {
GcObjectOwner::String(s) => GcObjectPtr::from(StringPtr::new(s.as_ptr())),
GcObjectOwner::Table(t) => GcObjectPtr::from(TablePtr::new(t.as_ptr())),
GcObjectOwner::Function(f) => GcObjectPtr::from(FunctionPtr::new(f.as_ptr())),
GcObjectOwner::Upvalue(u) => GcObjectPtr::from(UpvaluePtr::new(u.as_ptr())),
GcObjectOwner::Thread(t) => GcObjectPtr::from(ThreadPtr::new(t.as_ref() as *const _)),
GcObjectOwner::Userdata(u) => GcObjectPtr::from(UserdataPtr::new(u.as_ptr())),
GcObjectOwner::CClosure(c) => GcObjectPtr::from(CClosurePtr::new(c.as_ptr())),
GcObjectOwner::RClosure(r) => GcObjectPtr::from(RClosurePtr::new(r.as_ptr())),
GcObjectOwner::Proto(p) => GcObjectPtr::from(ProtoPtr::new(p.as_ptr())),
}
}
pub fn as_thread_mut(&mut self) -> Option<&mut LuaState> {
match self {
GcObjectOwner::Thread(t) => Some(&mut t.data),
_ => None,
}
}
pub fn size_of_data(&self) -> usize {
self.header().size() as usize
}
}
pub struct GcList {
gc_list: Vec<GcObjectOwner>,
}
#[allow(unused)]
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 = &self.gc_list[last_index];
moved_obj
.as_gc_ptr()
.header_mut()
.expect("moved object must have a valid GC header")
.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 obj in objects {
self.add(obj);
}
}
}
impl Default for GcList {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn gc_header_uses_u32_index() {
assert_eq!(std::mem::size_of::<GcHeader>(), 12);
assert_eq!(GcHeader::INDEX_MAX, u32::MAX);
}
#[test]
fn gc_header_large_index_round_trip() {
let header = GcHeader::with_white(0);
let idx = (1usize << 24) + 123;
header.set_index(idx);
assert_eq!(header.index(), idx);
}
}