catallaxy_runtime/
value.rs1use catallaxy_gc::Header;
16
17#[derive(Copy, Clone, PartialEq, Eq, Hash)]
18#[repr(transparent)]
19pub struct Value(pub u64);
20
21const TAG_SHIFT: u32 = 48;
22const TAG_PTR: u64 = 0xFFF9;
23const TAG_INT: u64 = 0xFFFA;
24const TAG_SPECIAL: u64 = 0xFFFB;
25const PAYLOAD_MASK: u64 = (1 << 48) - 1;
26const CANONICAL_NAN: u64 = 0x7FF8_0000_0000_0000;
27
28pub const SMALL_INT_MIN: i64 = -(1 << 47);
29pub const SMALL_INT_MAX: i64 = (1 << 47) - 1;
30
31impl Value {
32 pub const NIL: Value = Value(TAG_SPECIAL << TAG_SHIFT);
33 pub const FALSE: Value = Value((TAG_SPECIAL << TAG_SHIFT) | 2);
34 pub const TRUE: Value = Value((TAG_SPECIAL << TAG_SHIFT) | 3);
35 pub const EMPTY: Value = Value((TAG_SPECIAL << TAG_SHIFT) | 4);
37 pub const TOMBSTONE: Value = Value((TAG_SPECIAL << TAG_SHIFT) | 5);
39
40 #[inline(always)]
41 pub fn from_f64(f: f64) -> Value {
42 if f.is_nan() {
43 Value(CANONICAL_NAN)
44 } else {
45 Value(f.to_bits())
46 }
47 }
48
49 #[inline(always)]
50 pub fn bool(b: bool) -> Value {
51 if b {
52 Value::TRUE
53 } else {
54 Value::FALSE
55 }
56 }
57
58 #[inline(always)]
60 pub fn small_int(i: i64) -> Value {
61 debug_assert!(fits_small(i));
62 Value((TAG_INT << TAG_SHIFT) | (i as u64 & PAYLOAD_MASK))
63 }
64
65 #[inline(always)]
66 pub fn from_ptr(p: *const Header) -> Value {
67 debug_assert!((p as u64) & !PAYLOAD_MASK == 0);
68 Value((TAG_PTR << TAG_SHIFT) | p as u64)
69 }
70
71 #[inline(always)]
72 fn tag(self) -> u64 {
73 self.0 >> TAG_SHIFT
74 }
75
76 #[inline(always)]
77 pub fn is_float(self) -> bool {
78 self.tag() < TAG_PTR || self.tag() > 0xFFFC
79 }
80 #[inline(always)]
81 pub fn is_small_int(self) -> bool {
82 self.tag() == TAG_INT
83 }
84 #[inline(always)]
85 pub fn is_ptr(self) -> bool {
86 self.tag() == TAG_PTR
87 }
88 #[inline(always)]
89 pub fn is_nil(self) -> bool {
90 self.0 == Value::NIL.0
91 }
92 #[inline(always)]
93 pub fn is_bool(self) -> bool {
94 self.0 == Value::TRUE.0 || self.0 == Value::FALSE.0
95 }
96 #[inline(always)]
97 pub fn is_true(self) -> bool {
98 self.0 == Value::TRUE.0
99 }
100 #[inline(always)]
101 pub fn is_empty_marker(self) -> bool {
102 self.0 == Value::EMPTY.0
103 }
104
105 #[inline(always)]
106 pub fn as_f64(self) -> f64 {
107 f64::from_bits(self.0)
108 }
109 #[inline(always)]
110 pub fn as_small_int(self) -> i64 {
111 ((self.0 << 16) as i64) >> 16
112 }
113 #[inline(always)]
114 pub fn as_ptr(self) -> *mut Header {
115 (self.0 & PAYLOAD_MASK) as *mut Header
116 }
117 #[inline(always)]
119 pub fn both_small_ints(a: Value, b: Value) -> bool {
120 (a.tag() == TAG_INT) & (b.tag() == TAG_INT)
121 }
122
123 #[inline(always)]
125 pub fn header(self) -> Option<&'static Header> {
126 if self.is_ptr() {
127 Some(unsafe { &*self.as_ptr() })
129 } else {
130 None
131 }
132 }
133}
134
135#[inline(always)]
136pub fn fits_small(i: i64) -> bool {
137 ((i << 16) >> 16) == i
138}
139
140impl std::fmt::Debug for Value {
141 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
142 if self.is_float() {
143 write!(f, "{}f", self.as_f64())
144 } else if self.is_small_int() {
145 write!(f, "{}i", self.as_small_int())
146 } else if self.is_nil() {
147 write!(f, "nil")
148 } else if self.is_bool() {
149 write!(f, "{}", self.is_true())
150 } else if self.is_ptr() {
151 let cls = crate::class::class_of(*self);
152 write!(f, "<{} @{:p}>", cls.name, self.as_ptr())
153 } else {
154 write!(f, "<special {:x}>", self.0)
155 }
156 }
157}
158
159#[cfg(test)]
160mod tests {
161 use super::*;
162
163 #[test]
164 fn encodings_round_trip() {
165 for i in [0i64, 1, -1, SMALL_INT_MAX, SMALL_INT_MIN, 123456789] {
166 let v = Value::small_int(i);
167 assert!(v.is_small_int() && !v.is_float() && !v.is_ptr());
168 assert_eq!(v.as_small_int(), i);
169 }
170 for f in [0.0, -0.0, 1.5, f64::INFINITY, f64::NEG_INFINITY, f64::MIN_POSITIVE, -1e300] {
171 let v = Value::from_f64(f);
172 assert!(v.is_float());
173 assert_eq!(v.as_f64().to_bits(), f.to_bits());
174 }
175 let nan = Value::from_f64(f64::NAN);
176 assert!(nan.is_float() && nan.as_f64().is_nan());
177 let neg_nan = Value::from_f64(-f64::NAN);
178 assert!(neg_nan.is_float());
179 assert!(!Value::NIL.is_float() && Value::NIL.is_nil());
180 assert!(Value::TRUE.is_bool() && Value::TRUE.is_true());
181 assert!(!fits_small(SMALL_INT_MAX + 1));
182 assert!(!fits_small(SMALL_INT_MIN - 1));
183 }
184}