use crate::objects::map::{InstanceType, Map};
use crate::objects::tagged::TaggedValue;
const FORWARDING_TAG: usize = 0b10;
#[repr(C, align(8))]
pub struct HeapObject {
map_word: TaggedValue,
alloc_size: u32,
age: u8,
_pad: [u8; 3],
}
impl HeapObject {
pub fn new_null() -> Self {
Self {
map_word: TaggedValue(0),
alloc_size: 0,
age: 0,
_pad: [0; 3],
}
}
pub fn alloc_size(&self) -> u32 {
self.alloc_size
}
pub(crate) fn init_alloc_size(&mut self, size: u32) {
self.alloc_size = size;
}
pub fn age(&self) -> u8 {
self.age
}
pub(crate) fn increment_age(&mut self) {
self.age = self.age.saturating_add(1);
}
#[inline]
pub fn is_forwarded(&self) -> bool {
self.map_word.0 & FORWARDING_TAG != 0
}
#[inline]
pub fn forwarding_ptr(&self) -> *mut HeapObject {
(self.map_word.0 & !FORWARDING_TAG) as *mut HeapObject
}
pub(crate) unsafe fn set_forwarding_ptr(&mut self, dest: *mut HeapObject) {
debug_assert!(!dest.is_null(), "forwarding destination must be non-null");
debug_assert!(
dest as usize & FORWARDING_TAG == 0,
"forwarding destination must not have bit 1 set"
);
self.map_word = TaggedValue(dest as usize | FORWARDING_TAG);
}
#[inline]
pub unsafe fn map(&self) -> *mut Map {
self.map_word.0 as *mut Map
}
#[inline]
pub unsafe fn instance_type(&self) -> InstanceType {
unsafe { (*self.map()).instance_type() }
}
#[inline]
pub fn has_map(&self) -> bool {
let raw = self.map_word.0;
raw != 0 && (raw & FORWARDING_TAG) == 0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_heap_object_is_8_byte_aligned() {
assert!(
std::mem::align_of::<HeapObject>() >= 8,
"HeapObject must be at least 8-byte aligned on all targets"
);
}
#[test]
fn test_map_ptr_round_trip() {
let mut map = Map::new(InstanceType::Map, 0);
let obj = HeapObject {
map_word: TaggedValue(&raw mut map as usize),
..HeapObject::new_null()
};
let recovered = unsafe { obj.map() };
assert_eq!(recovered, &raw mut map);
}
#[test]
fn test_instance_type_via_map() {
let mut map = Map::new(InstanceType::Map, 0);
let obj = HeapObject {
map_word: TaggedValue(&raw mut map as usize),
..HeapObject::new_null()
};
let ty = unsafe { obj.instance_type() };
assert_eq!(ty, InstanceType::Map);
}
#[test]
fn test_new_null_has_zero_map_word() {
let obj = HeapObject::new_null();
assert_eq!(obj.map_word.raw(), 0);
}
#[test]
fn test_not_forwarded_by_default() {
let obj = HeapObject::new_null();
assert!(!obj.is_forwarded());
}
#[test]
fn test_forwarding_ptr_round_trip() {
let mut dest = HeapObject::new_null();
let dest_ptr = &raw mut dest;
let mut src = HeapObject::new_null();
unsafe { src.set_forwarding_ptr(dest_ptr) };
assert!(src.is_forwarded());
assert_eq!(src.forwarding_ptr(), dest_ptr);
}
#[test]
fn test_age_increments() {
let mut obj = HeapObject::new_null();
assert_eq!(obj.age(), 0);
obj.increment_age();
assert_eq!(obj.age(), 1);
obj.increment_age();
assert_eq!(obj.age(), 2);
}
#[test]
fn test_alloc_size_init() {
let mut obj = HeapObject::new_null();
assert_eq!(obj.alloc_size(), 0);
obj.init_alloc_size(128);
assert_eq!(obj.alloc_size(), 128);
}
}