Skip to main content

shape_value/
slot.rs

1//! ValueSlot: 8-byte raw value storage for TypedObject fields
2//!
3//! Each slot stores exactly 8 bytes of raw bits. Simple types (f64, i64, bool)
4//! use their native bit representation. Complex types (strings, arrays, objects)
5//! are stored as heap-allocated Box<HeapValue> raw pointers.
6//!
7//! The slot itself does NOT self-describe its type. TypedObject's `heap_mask`
8//! bitmap identifies which slots contain heap pointers (bit N set = slot N is heap).
9
10use crate::heap_value::HeapValue;
11use crate::value_word::ValueWord;
12
13/// A raw 8-byte value slot for TypedObject field storage.
14#[repr(transparent)]
15#[derive(Copy, Clone)]
16pub struct ValueSlot(u64);
17
18impl ValueSlot {
19    /// Store a f64 as raw IEEE 754 bits.
20    pub fn from_number(n: f64) -> Self {
21        Self(n.to_bits())
22    }
23
24    /// Store an i64 as raw two's complement bits. Full 64-bit range, no precision loss.
25    pub fn from_int(i: i64) -> Self {
26        Self(i as u64)
27    }
28
29    /// Store a u64 directly. Only meaningful when the FieldType is known to be U64.
30    pub fn from_u64(v: u64) -> Self {
31        Self(v)
32    }
33
34    /// Read as u64 (caller must know this slot is u64 type).
35    pub fn as_u64(&self) -> u64 {
36        self.0
37    }
38
39    /// Store a bool as 1/0.
40    pub fn from_bool(b: bool) -> Self {
41        Self(if b { 1 } else { 0 })
42    }
43
44    /// Store None as zero bits.
45    pub fn none() -> Self {
46        Self(0)
47    }
48
49    /// Store any HeapValue on the heap. The caller MUST set the corresponding
50    /// bit in `heap_mask` so Drop knows to free this.
51    ///
52    /// Without `gc` feature: allocates via Box (freed by drop_heap).
53    /// With `gc` feature: allocates via GcHeap (freed by garbage collector).
54    #[cfg(not(feature = "gc"))]
55    pub fn from_heap(value: HeapValue) -> Self {
56        let ptr = Box::into_raw(Box::new(value)) as u64;
57        Self(ptr)
58    }
59
60    /// Store any HeapValue on the GC heap.
61    #[cfg(feature = "gc")]
62    pub fn from_heap(value: HeapValue) -> Self {
63        let heap = shape_gc::thread_gc_heap();
64        let ptr = heap.alloc(value) as u64;
65        Self(ptr)
66    }
67
68    /// Read as f64 (caller must know this slot is f64 type).
69    pub fn as_f64(&self) -> f64 {
70        f64::from_bits(self.0)
71    }
72
73    /// Read as i64 (caller must know this slot is i64 type).
74    pub fn as_i64(&self) -> i64 {
75        self.0 as i64
76    }
77
78    /// Read as bool (caller must know this slot is bool type).
79    pub fn as_bool(&self) -> bool {
80        self.0 != 0
81    }
82
83    /// Read as heap HeapValue reference (caller must know this slot is a heap pointer).
84    /// Returns a reference to the pointed-to HeapValue.
85    pub fn as_heap_value(&self) -> &HeapValue {
86        let ptr = self.0 as *const HeapValue;
87        unsafe { &*ptr }
88    }
89
90    /// Create a ValueWord directly from this heap slot (no intermediate conversion).
91    /// Caller must know this slot is a heap pointer.
92    pub fn as_heap_nb(&self) -> ValueWord {
93        ValueWord::from_heap_value(self.as_heap_value().clone())
94    }
95
96    /// Store a ValueWord losslessly. For inline types (f64, i48, bool,
97    /// none, unit, function, module_function), stores the raw NaN-boxed tag bits
98    /// directly. For heap-tagged values, clones the HeapValue into a new Box.
99    /// Returns `(slot, is_heap)` — caller must set the heap_mask bit if `is_heap`.
100    pub fn from_value_word(nb: &ValueWord) -> (Self, bool) {
101        use crate::value_word::NanTag;
102        if nb.tag() == NanTag::Heap {
103            if let Some(hv) = nb.as_heap_ref() {
104                return (Self::from_heap(hv.clone()), true);
105            }
106            return (Self(0), false);
107        }
108        (Self(nb.raw_bits()), false)
109    }
110
111    /// Backward-compatibility alias.
112    pub fn from_nanboxed(nb: &ValueWord) -> (Self, bool) {
113        Self::from_value_word(nb)
114    }
115
116    /// Reconstruct a ValueWord from this slot. `is_heap` must match the value
117    /// returned by `from_value_word` (i.e., whether heap_mask bit is set).
118    pub fn as_value_word(&self, is_heap: bool) -> ValueWord {
119        if is_heap {
120            ValueWord::from_heap_value(self.as_heap_value().clone())
121        } else {
122            // Safety: bits were stored by from_value_word from a valid inline ValueWord.
123            // No heap pointer involved, so no refcount management needed.
124            unsafe { ValueWord::clone_from_bits(self.0) }
125        }
126    }
127
128    /// Backward-compatibility alias.
129    pub fn as_nanboxed(&self, is_heap: bool) -> ValueWord {
130        self.as_value_word(is_heap)
131    }
132
133    /// Raw bits for simple copy.
134    pub fn raw(&self) -> u64 {
135        self.0
136    }
137
138    /// Construct from raw bits.
139    pub fn from_raw(bits: u64) -> Self {
140        Self(bits)
141    }
142
143    /// Drop the heap value. MUST only be called on heap slots.
144    ///
145    /// Without `gc` feature: frees via Box deallocation.
146    /// With `gc` feature: no-op (GC handles deallocation).
147    ///
148    /// # Safety
149    /// Caller must ensure this slot actually contains a valid heap pointer.
150    #[cfg(not(feature = "gc"))]
151    pub unsafe fn drop_heap(&mut self) {
152        if self.0 != 0 {
153            let ptr = self.0 as *mut HeapValue;
154            let _ = unsafe { Box::from_raw(ptr) };
155            self.0 = 0;
156        }
157    }
158
159    /// Drop the heap value (GC path: no-op).
160    #[cfg(feature = "gc")]
161    pub unsafe fn drop_heap(&mut self) {
162        // No-op: garbage collector handles deallocation
163        self.0 = 0;
164    }
165
166    /// Clone a heap slot by cloning the pointed-to HeapValue into a new Box.
167    ///
168    /// Without `gc` feature: deep clones into a new Box allocation.
169    /// With `gc` feature: bitwise copy (GC tracks all references).
170    ///
171    /// # Safety
172    /// Caller must ensure this slot actually contains a valid heap pointer.
173    #[cfg(not(feature = "gc"))]
174    pub unsafe fn clone_heap(&self) -> Self {
175        if self.0 == 0 {
176            return Self(0);
177        }
178        let ptr = self.0 as *const HeapValue;
179        let cloned = unsafe { (*ptr).clone() };
180        Self::from_heap(cloned)
181    }
182
183    /// Clone a heap slot (GC path: bitwise copy).
184    #[cfg(feature = "gc")]
185    pub unsafe fn clone_heap(&self) -> Self {
186        // Under GC, just copy the pointer — GC traces all live references
187        Self(self.0)
188    }
189}
190
191impl std::fmt::Debug for ValueSlot {
192    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
193        write!(f, "ValueSlot(0x{:016x})", self.0)
194    }
195}
196
197#[cfg(test)]
198mod tests {
199    use super::*;
200    use std::sync::Arc;
201
202    #[test]
203    fn test_number_roundtrip() {
204        let slot = ValueSlot::from_number(3.14);
205        assert_eq!(slot.as_f64(), 3.14);
206    }
207
208    #[test]
209    fn test_int_roundtrip() {
210        let slot = ValueSlot::from_int(-42);
211        assert_eq!(slot.as_i64(), -42);
212
213        let slot = ValueSlot::from_int(i64::MAX);
214        assert_eq!(slot.as_i64(), i64::MAX);
215
216        let slot = ValueSlot::from_int(i64::MIN);
217        assert_eq!(slot.as_i64(), i64::MIN);
218    }
219
220    #[test]
221    fn test_bool_roundtrip() {
222        assert!(ValueSlot::from_bool(true).as_bool());
223        assert!(!ValueSlot::from_bool(false).as_bool());
224    }
225
226    #[test]
227    fn test_heap_string_roundtrip() {
228        let original = HeapValue::String(Arc::new("hello".to_string()));
229        let slot = ValueSlot::from_heap(original.clone());
230        let recovered = slot.as_heap_value();
231        match recovered {
232            HeapValue::String(s) => assert_eq!(s.as_str(), "hello"),
233            other => panic!("Expected HeapValue::String, got {:?}", other),
234        }
235        // Clean up
236        unsafe {
237            let mut slot = slot;
238            slot.drop_heap();
239        }
240    }
241}