Skip to main content

shape_gc/
header.rs

1//! GC object header — 8 bytes prepended to every GC-managed allocation.
2//!
3//! Layout (8 bytes total):
4//! ```text
5//! Byte 0: [color:2][gen:1][forwarded:1][unused:4]
6//! Byte 1: kind (HeapKind discriminant)
7//! Bytes 2-3: unused (reserved)
8//! Bytes 4-7: size (u32, object size in bytes excluding header)
9//! ```
10
11/// GC tri-color for mark phase.
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13#[repr(u8)]
14pub enum GcColor {
15    /// Unmarked — reclaimable if still white after marking.
16    White = 0,
17    /// Reachable but children not yet scanned.
18    Gray = 1,
19    /// Reachable and all children scanned.
20    Black = 2,
21}
22
23/// Generation identifier.
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25#[repr(u8)]
26pub enum Generation {
27    Young = 0,
28    Old = 1,
29}
30
31/// 8-byte header prepended to every GC-managed object.
32#[repr(C)]
33#[derive(Debug, Clone, Copy)]
34pub struct GcHeader {
35    /// Packed flags: [color:2][gen:1][forwarded:1][unused:4]
36    flags: u8,
37    /// HeapKind discriminant for type identification during tracing.
38    pub kind: u8,
39    /// Reserved for future use.
40    _reserved: u16,
41    /// Object size in bytes (excluding this header).
42    pub size: u32,
43}
44
45impl GcHeader {
46    /// Create a new header for a young-generation white object.
47    pub fn new(kind: u8, size: u32) -> Self {
48        Self {
49            flags: 0, // white, young, not forwarded
50            kind,
51            _reserved: 0,
52            size,
53        }
54    }
55
56    /// Get the object's color.
57    #[inline(always)]
58    pub fn color(&self) -> GcColor {
59        match self.flags & 0b11 {
60            0 => GcColor::White,
61            1 => GcColor::Gray,
62            2 => GcColor::Black,
63            _ => GcColor::White, // shouldn't happen
64        }
65    }
66
67    /// Set the object's color.
68    #[inline(always)]
69    pub fn set_color(&mut self, color: GcColor) {
70        self.flags = (self.flags & !0b11) | (color as u8);
71    }
72
73    /// Get the generation.
74    #[inline(always)]
75    pub fn generation(&self) -> Generation {
76        if (self.flags >> 2) & 1 == 0 {
77            Generation::Young
78        } else {
79            Generation::Old
80        }
81    }
82
83    /// Set the generation.
84    #[inline(always)]
85    pub fn set_generation(&mut self, generation: Generation) {
86        self.flags = (self.flags & !0b100) | ((generation as u8) << 2);
87    }
88
89    /// Check if this object has been forwarded (relocated).
90    #[inline(always)]
91    pub fn is_forwarded(&self) -> bool {
92        (self.flags >> 3) & 1 != 0
93    }
94
95    /// Mark this object as forwarded.
96    #[inline(always)]
97    pub fn set_forwarded(&mut self, forwarded: bool) {
98        if forwarded {
99            self.flags |= 0b1000;
100        } else {
101            self.flags &= !0b1000;
102        }
103    }
104}
105
106#[cfg(test)]
107mod tests {
108    use super::*;
109
110    #[test]
111    fn test_header_size() {
112        assert_eq!(std::mem::size_of::<GcHeader>(), 8);
113    }
114
115    #[test]
116    fn test_color_roundtrip() {
117        let mut h = GcHeader::new(0, 64);
118        assert_eq!(h.color(), GcColor::White);
119
120        h.set_color(GcColor::Gray);
121        assert_eq!(h.color(), GcColor::Gray);
122
123        h.set_color(GcColor::Black);
124        assert_eq!(h.color(), GcColor::Black);
125
126        h.set_color(GcColor::White);
127        assert_eq!(h.color(), GcColor::White);
128    }
129
130    #[test]
131    fn test_generation_roundtrip() {
132        let mut h = GcHeader::new(0, 64);
133        assert_eq!(h.generation(), Generation::Young);
134
135        h.set_generation(Generation::Old);
136        assert_eq!(h.generation(), Generation::Old);
137
138        // Color should be preserved
139        h.set_color(GcColor::Gray);
140        assert_eq!(h.generation(), Generation::Old);
141        assert_eq!(h.color(), GcColor::Gray);
142    }
143
144    #[test]
145    fn test_forwarded_flag() {
146        let mut h = GcHeader::new(0, 64);
147        assert!(!h.is_forwarded());
148
149        h.set_forwarded(true);
150        assert!(h.is_forwarded());
151        // Other flags preserved
152        assert_eq!(h.color(), GcColor::White);
153        assert_eq!(h.generation(), Generation::Young);
154
155        h.set_forwarded(false);
156        assert!(!h.is_forwarded());
157    }
158}