1use crate::slot::ValueSlot;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
14#[repr(u8)]
15pub enum ScalarKind {
16 I8 = 0,
17 U8 = 1,
18 I16 = 2,
19 U16 = 3,
20 I32 = 4,
21 U32 = 5,
22 I64 = 6,
23 U64 = 7,
24 I128 = 8,
25 U128 = 9,
26 F32 = 10,
27 F64 = 11,
28 Bool = 12,
29 None = 13,
30 Unit = 14,
31}
32
33impl ScalarKind {
34 #[inline]
36 pub fn is_integer(self) -> bool {
37 matches!(
38 self,
39 ScalarKind::I8
40 | ScalarKind::U8
41 | ScalarKind::I16
42 | ScalarKind::U16
43 | ScalarKind::I32
44 | ScalarKind::U32
45 | ScalarKind::I64
46 | ScalarKind::U64
47 | ScalarKind::I128
48 | ScalarKind::U128
49 )
50 }
51
52 #[inline]
54 pub fn is_float(self) -> bool {
55 matches!(self, ScalarKind::F32 | ScalarKind::F64)
56 }
57
58 #[inline]
60 pub fn is_numeric(self) -> bool {
61 self.is_integer() || self.is_float()
62 }
63
64 #[inline]
66 pub fn is_unsigned_integer(self) -> bool {
67 matches!(
68 self,
69 ScalarKind::U8 | ScalarKind::U16 | ScalarKind::U32 | ScalarKind::U64 | ScalarKind::U128
70 )
71 }
72
73 #[inline]
75 pub fn is_signed_integer(self) -> bool {
76 matches!(
77 self,
78 ScalarKind::I8 | ScalarKind::I16 | ScalarKind::I32 | ScalarKind::I64 | ScalarKind::I128
79 )
80 }
81}
82
83#[derive(Debug, Clone, Copy, PartialEq)]
91#[repr(C)]
92pub struct TypedScalar {
93 pub kind: ScalarKind,
94 pub payload_lo: u64,
95 pub payload_hi: u64,
97}
98
99impl TypedScalar {
100 #[inline]
102 pub fn i64(v: i64) -> Self {
103 Self {
104 kind: ScalarKind::I64,
105 payload_lo: v as u64,
106 payload_hi: 0,
107 }
108 }
109
110 #[inline]
112 pub fn f64(v: f64) -> Self {
113 Self {
114 kind: ScalarKind::F64,
115 payload_lo: v.to_bits(),
116 payload_hi: 0,
117 }
118 }
119
120 #[inline]
122 pub fn f64_from_bits(bits: u64) -> Self {
123 Self {
124 kind: ScalarKind::F64,
125 payload_lo: bits,
126 payload_hi: 0,
127 }
128 }
129
130 #[inline]
132 pub fn bool(v: bool) -> Self {
133 Self {
134 kind: ScalarKind::Bool,
135 payload_lo: v as u64,
136 payload_hi: 0,
137 }
138 }
139
140 #[inline]
142 pub fn none() -> Self {
143 Self {
144 kind: ScalarKind::None,
145 payload_lo: 0,
146 payload_hi: 0,
147 }
148 }
149
150 #[inline]
152 pub fn unit() -> Self {
153 Self {
154 kind: ScalarKind::Unit,
155 payload_lo: 0,
156 payload_hi: 0,
157 }
158 }
159
160 #[inline]
162 pub fn i8(v: i8) -> Self {
163 Self {
164 kind: ScalarKind::I8,
165 payload_lo: v as i64 as u64,
166 payload_hi: 0,
167 }
168 }
169
170 #[inline]
172 pub fn u8(v: u8) -> Self {
173 Self {
174 kind: ScalarKind::U8,
175 payload_lo: v as u64,
176 payload_hi: 0,
177 }
178 }
179
180 #[inline]
182 pub fn i16(v: i16) -> Self {
183 Self {
184 kind: ScalarKind::I16,
185 payload_lo: v as i64 as u64,
186 payload_hi: 0,
187 }
188 }
189
190 #[inline]
192 pub fn u16(v: u16) -> Self {
193 Self {
194 kind: ScalarKind::U16,
195 payload_lo: v as u64,
196 payload_hi: 0,
197 }
198 }
199
200 #[inline]
202 pub fn i32(v: i32) -> Self {
203 Self {
204 kind: ScalarKind::I32,
205 payload_lo: v as i64 as u64,
206 payload_hi: 0,
207 }
208 }
209
210 #[inline]
212 pub fn u32(v: u32) -> Self {
213 Self {
214 kind: ScalarKind::U32,
215 payload_lo: v as u64,
216 payload_hi: 0,
217 }
218 }
219
220 #[inline]
222 pub fn u64(v: u64) -> Self {
223 Self {
224 kind: ScalarKind::U64,
225 payload_lo: v,
226 payload_hi: 0,
227 }
228 }
229
230 #[inline]
232 pub fn f32(v: f32) -> Self {
233 Self {
234 kind: ScalarKind::F32,
235 payload_lo: f64::from(v).to_bits(),
236 payload_hi: 0,
237 }
238 }
239
240 #[inline]
243 pub fn as_i64(&self) -> Option<i64> {
244 if self.kind == ScalarKind::U64 {
245 i64::try_from(self.payload_lo).ok()
246 } else if self.kind.is_integer() {
247 Some(self.payload_lo as i64)
248 } else {
249 Option::None
250 }
251 }
252
253 #[inline]
255 pub fn as_u64(&self) -> Option<u64> {
256 if self.kind.is_unsigned_integer() {
257 Some(self.payload_lo)
258 } else if self.kind.is_signed_integer() {
259 Some(self.payload_lo)
261 } else {
262 Option::None
263 }
264 }
265
266 #[inline]
268 pub fn as_f64(&self) -> Option<f64> {
269 match self.kind {
270 ScalarKind::F64 => Some(f64::from_bits(self.payload_lo)),
271 ScalarKind::F32 => Some(f64::from_bits(self.payload_lo)),
272 _ => Option::None,
273 }
274 }
275
276 #[inline]
278 pub fn as_bool(&self) -> Option<bool> {
279 if self.kind == ScalarKind::Bool {
280 Some(self.payload_lo != 0)
281 } else {
282 Option::None
283 }
284 }
285
286 #[inline]
289 pub fn to_f64_lossy(&self) -> Option<f64> {
290 match self.kind {
291 ScalarKind::F64 | ScalarKind::F32 => Some(f64::from_bits(self.payload_lo)),
292 k if k.is_unsigned_integer() => Some(self.payload_lo as f64),
293 k if k.is_signed_integer() => Some(self.payload_lo as i64 as f64),
294 _ => Option::None,
295 }
296 }
297}
298
299impl ValueSlot {
311 #[inline]
316 pub fn from_typed_scalar(ts: TypedScalar) -> (Self, bool) {
317 match ts.kind {
318 ScalarKind::I8 | ScalarKind::I16 | ScalarKind::I32 | ScalarKind::I64 => {
319 (ValueSlot::from_int(ts.payload_lo as i64), false)
320 }
321 ScalarKind::U8 | ScalarKind::U16 | ScalarKind::U32 => {
322 (ValueSlot::from_int(ts.payload_lo as i64), false)
323 }
324 ScalarKind::U64 => (ValueSlot::from_u64(ts.payload_lo), false),
325 ScalarKind::I128 | ScalarKind::U128 => {
326 (ValueSlot::from_int(ts.payload_lo as i64), false)
327 }
328 ScalarKind::F64 | ScalarKind::F32 => {
329 (ValueSlot::from_number(f64::from_bits(ts.payload_lo)), false)
330 }
331 ScalarKind::Bool => (ValueSlot::from_bool(ts.payload_lo != 0), false),
332 ScalarKind::None | ScalarKind::Unit => (ValueSlot::none(), false),
333 }
334 }
335}
336
337#[cfg(test)]
342mod tests {
343 use super::*;
344
345 #[test]
346 fn typed_scalar_convenience_constructors() {
347 assert_eq!(TypedScalar::i64(42).kind, ScalarKind::I64);
348 assert_eq!(TypedScalar::i64(42).payload_lo, 42);
349 assert_eq!(TypedScalar::f64(1.5).kind, ScalarKind::F64);
350 assert_eq!(TypedScalar::bool(true).payload_lo, 1);
351 assert_eq!(TypedScalar::none().kind, ScalarKind::None);
352 assert_eq!(TypedScalar::unit().kind, ScalarKind::Unit);
353 }
354
355 #[test]
356 fn scalar_kind_classification() {
357 assert!(ScalarKind::I64.is_integer());
358 assert!(ScalarKind::U32.is_integer());
359 assert!(!ScalarKind::F64.is_integer());
360 assert!(!ScalarKind::Bool.is_integer());
361
362 assert!(ScalarKind::F64.is_float());
363 assert!(ScalarKind::F32.is_float());
364 assert!(!ScalarKind::I64.is_float());
365
366 assert!(ScalarKind::I64.is_numeric());
367 assert!(ScalarKind::F64.is_numeric());
368 assert!(!ScalarKind::Bool.is_numeric());
369 assert!(!ScalarKind::None.is_numeric());
370 }
371
372 #[test]
373 fn value_slot_from_typed_scalar() {
374 let (slot, is_heap) = ValueSlot::from_typed_scalar(TypedScalar::i64(-42));
376 assert!(!is_heap);
377 assert_eq!(slot.as_i64(), -42);
378
379 let (slot, is_heap) = ValueSlot::from_typed_scalar(TypedScalar::f64(3.14));
381 assert!(!is_heap);
382 assert_eq!(slot.as_f64(), 3.14);
383
384 let (slot, is_heap) = ValueSlot::from_typed_scalar(TypedScalar::bool(true));
386 assert!(!is_heap);
387 assert!(slot.as_bool());
388
389 let (slot, is_heap) = ValueSlot::from_typed_scalar(TypedScalar::none());
391 assert!(!is_heap);
392 assert_eq!(slot.raw(), 0);
393 }
394
395 #[test]
396 fn to_f64_lossy_works() {
397 assert_eq!(TypedScalar::f64(3.14).to_f64_lossy(), Some(3.14));
398 assert_eq!(TypedScalar::i64(42).to_f64_lossy(), Some(42.0));
399 assert_eq!(TypedScalar::bool(true).to_f64_lossy(), Option::None);
400 assert_eq!(TypedScalar::none().to_f64_lossy(), Option::None);
401 }
402
403 #[test]
404 fn typed_scalar_extraction_methods() {
405 assert_eq!(TypedScalar::i64(42).as_i64(), Some(42));
406 assert_eq!(TypedScalar::f64(3.14).as_i64(), Option::None);
407 assert_eq!(TypedScalar::f64(3.14).as_f64(), Some(3.14));
408 assert_eq!(TypedScalar::i64(42).as_f64(), Option::None);
409 assert_eq!(TypedScalar::bool(true).as_bool(), Some(true));
410 assert_eq!(TypedScalar::i64(1).as_bool(), Option::None);
411 }
412}