Skip to main content

shape_value/v2/
typed_option.rs

1//! Typed Option representations for v2 runtime.
2//!
3//! For heap types: `Option<*const T>` = nullable pointer.
4//! `None` = null (0x0), `Some(v)` = non-null pointer. Zero overhead.
5//!
6//! For primitive types: tagged struct with `has_value` discriminant.
7
8/// Option for primitive (non-pointer) types.
9///
10/// ## Memory layout (16 bytes for T=f64/i64, 8 bytes could suffice for smaller T)
11///
12/// ```text
13/// Offset  Size  Field
14/// ------  ----  -----
15///   0       1   has_value (0 = None, 1 = Some)
16///   1       7   _pad (align to 8)
17///   8    sz(T)  value
18/// ```
19#[repr(C)]
20pub struct PrimitiveOption<T: Copy> {
21    /// 0 = None, 1 = Some.
22    pub has_value: u8,
23    /// Padding to align value to 8 bytes.
24    pub _pad: [u8; 7],
25    /// The value (only valid when has_value == 1).
26    pub value: T,
27}
28
29impl<T: Copy> PrimitiveOption<T> {
30    /// Create a `Some` variant.
31    #[inline]
32    pub fn some(value: T) -> Self {
33        Self {
34            has_value: 1,
35            _pad: [0; 7],
36            value,
37        }
38    }
39
40    /// Create a `None` variant.
41    ///
42    /// # Safety
43    /// The `value` field is left zeroed and must not be read.
44    #[inline]
45    pub fn none() -> Self
46    where
47        T: Default,
48    {
49        Self {
50            has_value: 0,
51            _pad: [0; 7],
52            value: T::default(),
53        }
54    }
55
56    /// Whether this is `Some`.
57    #[inline]
58    pub fn is_some(&self) -> bool {
59        self.has_value != 0
60    }
61
62    /// Whether this is `None`.
63    #[inline]
64    pub fn is_none(&self) -> bool {
65        self.has_value == 0
66    }
67
68    /// Get the value, if present.
69    #[inline]
70    pub fn get(&self) -> Option<T> {
71        if self.is_some() {
72            Some(self.value)
73        } else {
74            None
75        }
76    }
77}
78
79/// Option for heap pointer types — just a nullable pointer.
80/// `None` = null (0x0), `Some` = non-null pointer. Zero overhead.
81///
82/// This is a type alias to make intent clear in signatures.
83/// The actual representation is just `*const T`.
84pub type HeapOption<T> = *const T;
85
86/// Check if a heap option is None (null pointer).
87#[inline]
88pub fn heap_option_is_none<T>(ptr: *const T) -> bool {
89    ptr.is_null()
90}
91
92/// Check if a heap option is Some (non-null pointer).
93#[inline]
94pub fn heap_option_is_some<T>(ptr: *const T) -> bool {
95    !ptr.is_null()
96}
97
98/// Byte offset constants for JIT codegen.
99pub const PRIMITIVE_OPTION_OFFSET_HAS_VALUE: usize = 0;
100pub const PRIMITIVE_OPTION_OFFSET_VALUE: usize = 8;
101
102#[cfg(test)]
103mod tests {
104    use super::*;
105
106    #[test]
107    fn test_primitive_option_f64_some() {
108        let opt = PrimitiveOption::some(3.14f64);
109        assert!(opt.is_some());
110        assert!(!opt.is_none());
111        assert_eq!(opt.get(), Some(3.14f64));
112    }
113
114    #[test]
115    fn test_primitive_option_f64_none() {
116        let opt = PrimitiveOption::<f64>::none();
117        assert!(opt.is_none());
118        assert!(!opt.is_some());
119        assert_eq!(opt.get(), None);
120    }
121
122    #[test]
123    fn test_primitive_option_i64_some() {
124        let opt = PrimitiveOption::some(42i64);
125        assert!(opt.is_some());
126        assert_eq!(opt.get(), Some(42i64));
127    }
128
129    #[test]
130    fn test_primitive_option_i64_none() {
131        let opt = PrimitiveOption::<i64>::none();
132        assert!(opt.is_none());
133        assert_eq!(opt.get(), None);
134    }
135
136    #[test]
137    fn test_primitive_option_i32_some() {
138        let opt = PrimitiveOption::some(99i32);
139        assert!(opt.is_some());
140        assert_eq!(opt.get(), Some(99i32));
141    }
142
143    #[test]
144    fn test_primitive_option_i32_none() {
145        let opt = PrimitiveOption::<i32>::none();
146        assert!(opt.is_none());
147        assert_eq!(opt.get(), None);
148    }
149
150    #[test]
151    fn test_primitive_option_bool_some() {
152        let opt = PrimitiveOption::some(true);
153        assert!(opt.is_some());
154        assert_eq!(opt.get(), Some(true));
155
156        let opt_false = PrimitiveOption::some(false);
157        assert!(opt_false.is_some());
158        assert_eq!(opt_false.get(), Some(false));
159    }
160
161    #[test]
162    fn test_primitive_option_bool_none() {
163        let opt = PrimitiveOption::<bool>::none();
164        assert!(opt.is_none());
165        assert_eq!(opt.get(), None);
166    }
167
168    #[test]
169    fn test_size_of_primitive_option_f64() {
170        assert_eq!(std::mem::size_of::<PrimitiveOption<f64>>(), 16);
171    }
172
173    #[test]
174    fn test_size_of_primitive_option_i64() {
175        assert_eq!(std::mem::size_of::<PrimitiveOption<i64>>(), 16);
176    }
177
178    #[test]
179    fn test_size_of_primitive_option_i32() {
180        // 8 (tag+pad) + 4 (i32) = 12, but repr(C) pads to alignment of largest field
181        // Largest field alignment is 4 (i32), so 12 is valid (no extra padding needed)
182        assert_eq!(std::mem::size_of::<PrimitiveOption<i32>>(), 12);
183    }
184
185    #[test]
186    fn test_size_of_primitive_option_bool() {
187        // 8 (tag+pad) + 1 (bool) = 9, padded to alignment of largest = 1, so 9
188        assert_eq!(std::mem::size_of::<PrimitiveOption<bool>>(), 9);
189    }
190
191    #[test]
192    fn test_field_offsets_f64() {
193        let opt = PrimitiveOption::some(1.0f64);
194        let base = &opt as *const _ as usize;
195        let has_value_offset = &opt.has_value as *const _ as usize - base;
196        let value_offset = &opt.value as *const _ as usize - base;
197
198        assert_eq!(has_value_offset, PRIMITIVE_OPTION_OFFSET_HAS_VALUE);
199        assert_eq!(value_offset, PRIMITIVE_OPTION_OFFSET_VALUE);
200    }
201
202    #[test]
203    fn test_heap_option_none() {
204        let ptr: HeapOption<u8> = std::ptr::null();
205        assert!(heap_option_is_none(ptr));
206        assert!(!heap_option_is_some(ptr));
207    }
208
209    #[test]
210    fn test_heap_option_some() {
211        let val: u8 = 42;
212        let ptr: HeapOption<u8> = &val as *const u8;
213        assert!(heap_option_is_some(ptr));
214        assert!(!heap_option_is_none(ptr));
215    }
216
217    #[test]
218    fn test_heap_option_is_pointer_sized() {
219        // HeapOption<T> is just *const T, should be 8 bytes
220        assert_eq!(std::mem::size_of::<HeapOption<u8>>(), 8);
221        assert_eq!(std::mem::size_of::<HeapOption<f64>>(), 8);
222    }
223}