1pub use shape_value::tags::{
26 CANONICAL_NAN,
28 HEAP_KIND_ARRAY,
30 HEAP_KIND_BIG_INT,
31 HEAP_KIND_BOOL,
32 HEAP_KIND_CLOSURE,
33 HEAP_KIND_COLUMN_REF,
34 HEAP_KIND_DATA_DATETIME_REF,
35 HEAP_KIND_DATA_REFERENCE,
36 HEAP_KIND_DATATABLE,
37 HEAP_KIND_DATETIME_EXPR,
38 HEAP_KIND_DECIMAL,
39 HEAP_KIND_DURATION,
40 HEAP_KIND_ENUM,
41 HEAP_KIND_ERR,
42 HEAP_KIND_EXPR_PROXY,
43 HEAP_KIND_FILTER_EXPR,
44 HEAP_KIND_FUNCTION,
45 HEAP_KIND_FUNCTION_REF,
46 HEAP_KIND_FUTURE,
47 HEAP_KIND_HASHMAP,
48 HEAP_KIND_HOST_CLOSURE,
49 HEAP_KIND_INDEXED_TABLE,
50 HEAP_KIND_MODULE_FUNCTION,
51 HEAP_KIND_NONE,
52 HEAP_KIND_NUMBER,
53 HEAP_KIND_OK,
54 HEAP_KIND_PRINT_RESULT,
55 HEAP_KIND_RANGE,
56 HEAP_KIND_ROW_VIEW,
57 HEAP_KIND_SIMULATION_CALL,
58 HEAP_KIND_SOME,
59 HEAP_KIND_STRING,
60 HEAP_KIND_TASK_GROUP,
61 HEAP_KIND_TIME,
62 HEAP_KIND_TIME_REFERENCE,
63 HEAP_KIND_TIMEFRAME,
64 HEAP_KIND_TIMESPAN,
65 HEAP_KIND_TRAIT_OBJECT,
66 HEAP_KIND_TYPE_ANNOTATED_VALUE,
67 HEAP_KIND_TYPE_ANNOTATION,
68 HEAP_KIND_TYPED_OBJECT,
69 HEAP_KIND_TYPED_TABLE,
70 HEAP_KIND_UNIT,
71 I48_MAX,
72 I48_MIN,
73 PAYLOAD_MASK,
74 TAG_BASE,
75 TAG_SHIFT,
76 make_tagged,
78 sign_extend_i48,
79};
80
81pub const NAN_BASE: u64 = 0x7FF0_0000_0000_0000;
87
88pub const TAG_MASK: u64 = 0xFFFF_0000_0000_0000;
90
91pub const TAG_NULL: u64 =
97 shape_value::tags::TAG_BASE | (shape_value::tags::TAG_NONE << shape_value::tags::TAG_SHIFT);
98
99pub const TAG_BOOL_FALSE: u64 =
101 shape_value::tags::TAG_BASE | (shape_value::tags::TAG_BOOL << shape_value::tags::TAG_SHIFT);
102
103pub const TAG_BOOL_TRUE: u64 =
105 shape_value::tags::TAG_BASE | (shape_value::tags::TAG_BOOL << shape_value::tags::TAG_SHIFT) | 1;
106
107pub const TAG_UNIT: u64 =
109 shape_value::tags::TAG_BASE | (shape_value::tags::TAG_UNIT << shape_value::tags::TAG_SHIFT);
110
111pub const TAG_NONE: u64 = TAG_NULL;
113
114pub const TAG_NUMBER: u64 = 0x0000_0000_0000_0000;
116
117pub const TAG_DATA_ROW: u64 = TAG_BASE | (shape_value::tags::TAG_INT << TAG_SHIFT);
125
126pub const HK_STRING: u16 = HEAP_KIND_STRING as u16;
133pub const HK_ARRAY: u16 = HEAP_KIND_ARRAY as u16;
134pub const HK_TYPED_OBJECT: u16 = HEAP_KIND_TYPED_OBJECT as u16;
135pub const HK_CLOSURE: u16 = HEAP_KIND_CLOSURE as u16;
136pub const HK_DECIMAL: u16 = HEAP_KIND_DECIMAL as u16;
137pub const HK_BIG_INT: u16 = HEAP_KIND_BIG_INT as u16;
138pub const HK_HOST_CLOSURE: u16 = HEAP_KIND_HOST_CLOSURE as u16;
139pub const HK_DATATABLE: u16 = HEAP_KIND_DATATABLE as u16;
140pub const HK_HASHMAP: u16 = HEAP_KIND_HASHMAP as u16;
141pub const HK_TYPED_TABLE: u16 = HEAP_KIND_TYPED_TABLE as u16;
142pub const HK_ROW_VIEW: u16 = HEAP_KIND_ROW_VIEW as u16;
143pub const HK_COLUMN_REF: u16 = HEAP_KIND_COLUMN_REF as u16;
144pub const HK_INDEXED_TABLE: u16 = HEAP_KIND_INDEXED_TABLE as u16;
145pub const HK_RANGE: u16 = HEAP_KIND_RANGE as u16;
146pub const HK_ENUM: u16 = HEAP_KIND_ENUM as u16;
147pub const HK_SOME: u16 = HEAP_KIND_SOME as u16;
148pub const HK_OK: u16 = HEAP_KIND_OK as u16;
149pub const HK_ERR: u16 = HEAP_KIND_ERR as u16;
150pub const HK_FUTURE: u16 = HEAP_KIND_FUTURE as u16;
151pub const HK_TASK_GROUP: u16 = HEAP_KIND_TASK_GROUP as u16;
152pub const HK_TRAIT_OBJECT: u16 = HEAP_KIND_TRAIT_OBJECT as u16;
153pub const HK_EXPR_PROXY: u16 = HEAP_KIND_EXPR_PROXY as u16;
154pub const HK_FILTER_EXPR: u16 = HEAP_KIND_FILTER_EXPR as u16;
155pub const HK_TIME: u16 = HEAP_KIND_TIME as u16;
156pub const HK_DURATION: u16 = HEAP_KIND_DURATION as u16;
157pub const HK_TIMESPAN: u16 = HEAP_KIND_TIMESPAN as u16;
158pub const HK_TIMEFRAME: u16 = HEAP_KIND_TIMEFRAME as u16;
159pub const HK_TIME_REFERENCE: u16 = HEAP_KIND_TIME_REFERENCE as u16;
160pub const HK_DATETIME_EXPR: u16 = HEAP_KIND_DATETIME_EXPR as u16;
161pub const HK_DATA_DATETIME_REF: u16 = HEAP_KIND_DATA_DATETIME_REF as u16;
162pub const HK_TYPE_ANNOTATION: u16 = HEAP_KIND_TYPE_ANNOTATION as u16;
163pub const HK_TYPE_ANNOTATED_VALUE: u16 = HEAP_KIND_TYPE_ANNOTATED_VALUE as u16;
164pub const HK_PRINT_RESULT: u16 = HEAP_KIND_PRINT_RESULT as u16;
165pub const HK_SIMULATION_CALL: u16 = HEAP_KIND_SIMULATION_CALL as u16;
166pub const HK_FUNCTION_REF: u16 = HEAP_KIND_FUNCTION_REF as u16;
167pub const HK_DATA_REFERENCE: u16 = HEAP_KIND_DATA_REFERENCE as u16;
168
169pub const HK_JIT_FUNCTION: u16 = 128;
171pub const HK_JIT_SIGNAL_BUILDER: u16 = 129;
172pub const HK_JIT_TABLE_REF: u16 = 130;
173pub const HK_JIT_OBJECT: u16 = 131;
175
176#[repr(C)]
185pub struct JitAlloc<T> {
186 pub kind: u16,
188 _pad: [u8; 6],
189 pub data: T,
191}
192
193pub const JIT_ALLOC_DATA_OFFSET: usize = 8;
195
196const _: () = {
198 assert!(
200 TAG_NULL & 0x8000_0000_0000_0000 != 0,
201 "TAG_NULL must be in negative NaN space"
202 );
203 assert!(
204 TAG_BOOL_FALSE & 0x8000_0000_0000_0000 != 0,
205 "TAG_BOOL must be in negative NaN space"
206 );
207 assert!(
208 TAG_UNIT & 0x8000_0000_0000_0000 != 0,
209 "TAG_UNIT must be in negative NaN space"
210 );
211 assert!(
213 TAG_DATA_ROW & 0x8000_0000_0000_0000 != 0,
214 "TAG_DATA_ROW must be in negative NaN space"
215 );
216};
217
218#[inline]
225pub fn is_number(bits: u64) -> bool {
226 !shape_value::tags::is_tagged(bits)
227}
228
229#[inline]
231pub fn unbox_number(bits: u64) -> f64 {
232 f64::from_bits(bits)
233}
234
235#[inline]
237pub const fn box_number(n: f64) -> u64 {
238 f64::to_bits(n)
239}
240
241#[inline]
243pub const fn box_bool(b: bool) -> u64 {
244 if b { TAG_BOOL_TRUE } else { TAG_BOOL_FALSE }
245}
246
247#[inline]
249pub fn box_function(fn_id: u16) -> u64 {
250 make_tagged(shape_value::tags::TAG_FUNCTION, fn_id as u64)
251}
252
253#[inline]
255pub fn is_inline_function(bits: u64) -> bool {
256 shape_value::tags::is_tagged(bits)
257 && shape_value::tags::get_tag(bits) == shape_value::tags::TAG_FUNCTION
258}
259
260#[inline]
262pub fn unbox_function_id(bits: u64) -> u16 {
263 (bits & PAYLOAD_MASK) as u16
264}
265
266#[inline]
275pub fn jit_box<T>(kind: u16, data: T) -> u64 {
276 let alloc = Box::new(JitAlloc {
277 kind,
278 _pad: [0; 6],
279 data,
280 });
281 let ptr = Box::into_raw(alloc);
282 TAG_BASE | ((ptr as u64) & PAYLOAD_MASK)
284}
285
286#[inline]
292pub unsafe fn read_heap_kind(bits: u64) -> u16 {
293 let ptr = (bits & PAYLOAD_MASK) as *const u16;
294 unsafe { *ptr }
295}
296
297#[inline]
302pub unsafe fn jit_unbox<T>(bits: u64) -> &'static T {
303 let ptr = (bits & PAYLOAD_MASK) as *const JitAlloc<T>;
304 unsafe { &(*ptr).data }
305}
306
307#[inline]
312pub unsafe fn jit_unbox_mut<T>(bits: u64) -> &'static mut T {
313 let ptr = (bits & PAYLOAD_MASK) as *mut JitAlloc<T>;
314 unsafe { &mut (*ptr).data }
315}
316
317#[inline]
323pub unsafe fn jit_drop<T>(bits: u64) {
324 let ptr = (bits & PAYLOAD_MASK) as *mut JitAlloc<T>;
325 unsafe { drop(Box::from_raw(ptr)) };
326}
327
328#[inline]
330pub fn is_heap(bits: u64) -> bool {
331 shape_value::tags::is_tagged(bits)
332 && shape_value::tags::get_tag(bits) == shape_value::tags::TAG_HEAP
333}
334
335#[inline]
337pub fn heap_kind(bits: u64) -> Option<u16> {
338 if is_heap(bits) {
339 Some(unsafe { read_heap_kind(bits) })
340 } else {
341 None
342 }
343}
344
345#[inline]
347pub fn is_heap_kind(bits: u64, expected_kind: u16) -> bool {
348 is_heap(bits) && unsafe { read_heap_kind(bits) } == expected_kind
349}
350
351#[inline]
353pub fn unbox_heap_pointer(bits: u64) -> *const u8 {
354 (bits & PAYLOAD_MASK) as *const u8
355}
356
357#[inline]
362pub fn is_ok_tag(bits: u64) -> bool {
363 is_heap_kind(bits, HK_OK)
364}
365
366#[inline]
367pub fn is_err_tag(bits: u64) -> bool {
368 is_heap_kind(bits, HK_ERR)
369}
370
371#[inline]
372pub fn is_result_tag(bits: u64) -> bool {
373 is_ok_tag(bits) || is_err_tag(bits)
374}
375
376#[inline]
377pub fn box_ok(inner_bits: u64) -> u64 {
378 jit_box(HK_OK, inner_bits)
379}
380
381#[inline]
382pub fn box_err(inner_bits: u64) -> u64 {
383 jit_box(HK_ERR, inner_bits)
384}
385
386#[inline]
387pub unsafe fn unbox_result_inner(bits: u64) -> u64 {
388 *unsafe { jit_unbox::<u64>(bits) }
389}
390
391#[inline]
392pub fn unbox_result_pointer(bits: u64) -> *const u64 {
393 let ptr = (bits & PAYLOAD_MASK) as *const JitAlloc<u64>;
394 if ptr.is_null() {
395 std::ptr::null()
396 } else {
397 unsafe { &(*ptr).data as *const u64 }
398 }
399}
400
401#[inline]
406pub fn is_some_tag(bits: u64) -> bool {
407 is_heap_kind(bits, HK_SOME)
408}
409
410#[inline]
411pub fn is_none_tag(bits: u64) -> bool {
412 bits == TAG_NONE
413}
414
415#[inline]
416pub fn is_option_tag(bits: u64) -> bool {
417 is_some_tag(bits) || is_none_tag(bits)
418}
419
420#[inline]
421pub fn box_some(inner_bits: u64) -> u64 {
422 jit_box(HK_SOME, inner_bits)
423}
424
425#[inline]
426pub unsafe fn unbox_some_inner(bits: u64) -> u64 {
427 *unsafe { jit_unbox::<u64>(bits) }
428}
429
430#[inline]
436pub const fn box_data_row(row_index: usize) -> u64 {
437 TAG_DATA_ROW | ((row_index as u64) & PAYLOAD_MASK)
438}
439
440#[inline]
442pub const fn unbox_data_row(bits: u64) -> usize {
443 (bits & PAYLOAD_MASK) as usize
444}
445
446#[inline]
449pub fn is_data_row(bits: u64) -> bool {
450 shape_value::tags::is_tagged(bits)
451 && shape_value::tags::get_tag(bits) == shape_value::tags::TAG_INT
452}
453
454#[inline]
459pub fn box_column_ref(ptr: *const f64, len: usize) -> u64 {
460 jit_box(HK_COLUMN_REF, (ptr, len))
461}
462
463#[inline]
464pub unsafe fn unbox_column_ref(bits: u64) -> (*const f64, usize) {
465 *unsafe { jit_unbox::<(*const f64, usize)>(bits) }
466}
467
468#[inline]
469pub fn is_column_ref(bits: u64) -> bool {
470 is_heap_kind(bits, HK_COLUMN_REF)
471}
472
473#[inline]
478pub fn box_typed_object(ptr: *const u8) -> u64 {
479 jit_box(HK_TYPED_OBJECT, ptr)
480}
481
482#[inline]
483pub fn unbox_typed_object(bits: u64) -> *const u8 {
484 *unsafe { jit_unbox::<*const u8>(bits) }
485}
486
487#[inline]
488pub fn is_typed_object(bits: u64) -> bool {
489 is_heap_kind(bits, HK_TYPED_OBJECT)
490}
491
492#[cfg(test)]
497mod tests {
498 use super::*;
499
500 #[test]
501 fn test_inline_types_in_negative_nan_space() {
502 assert!(TAG_NULL & 0x8000_0000_0000_0000 != 0);
503 assert!(TAG_BOOL_FALSE & 0x8000_0000_0000_0000 != 0);
504 assert!(TAG_BOOL_TRUE & 0x8000_0000_0000_0000 != 0);
505 assert!(TAG_UNIT & 0x8000_0000_0000_0000 != 0);
506 }
507
508 #[test]
509 fn test_data_row_in_negative_nan_space() {
510 assert!(TAG_DATA_ROW & 0x8000_0000_0000_0000 != 0);
511 assert!(!is_number(TAG_DATA_ROW));
512 }
513
514 #[test]
515 fn test_nan_base_detects_all_tags() {
516 assert!(!is_number(TAG_NULL), "TAG_NULL should not be a number");
517 assert!(
518 !is_number(TAG_DATA_ROW),
519 "TAG_DATA_ROW should not be a number"
520 );
521 assert!(
522 !is_number(TAG_BOOL_TRUE),
523 "TAG_BOOL_TRUE should not be a number"
524 );
525
526 assert!(is_number(box_number(3.14)));
528 assert!(is_number(box_number(0.0)));
529 assert!(is_number(box_number(-1.0)));
530 assert!(is_number(box_number(f64::MAX)));
531 assert!(is_number(box_number(f64::MIN)));
532 }
533
534 #[test]
535 fn test_inline_constants_match_shared_scheme() {
536 assert_eq!(TAG_NULL, make_tagged(shape_value::tags::TAG_NONE, 0));
537 assert_eq!(TAG_BOOL_FALSE, make_tagged(shape_value::tags::TAG_BOOL, 0));
538 assert_eq!(TAG_BOOL_TRUE, make_tagged(shape_value::tags::TAG_BOOL, 1));
539 assert_eq!(TAG_UNIT, make_tagged(shape_value::tags::TAG_UNIT, 0));
540 }
541
542 #[test]
543 fn test_box_unbox_number() {
544 let n = 3.14f64;
545 let boxed = box_number(n);
546 assert!(is_number(boxed));
547 assert_eq!(unbox_number(boxed), n);
548 }
549
550 #[test]
551 fn test_box_unbox_bool() {
552 assert_eq!(box_bool(true), TAG_BOOL_TRUE);
553 assert_eq!(box_bool(false), TAG_BOOL_FALSE);
554 }
555
556 #[test]
557 fn test_box_function() {
558 let bits = box_function(42);
559 assert!(is_inline_function(bits));
560 assert_eq!(unbox_function_id(bits), 42);
561 assert!(!is_number(bits));
562 assert!(!is_heap(bits));
563 }
564
565 #[test]
566 fn test_jit_alloc_string() {
567 let bits = jit_box(HK_STRING, "hello".to_string());
568 assert!(is_heap(bits));
569 assert!(is_heap_kind(bits, HK_STRING));
570 assert!(!is_number(bits));
571 assert_eq!(heap_kind(bits), Some(HK_STRING));
572 let s = unsafe { jit_unbox::<String>(bits) };
573 assert_eq!(s, "hello");
574 unsafe { jit_drop::<String>(bits) };
575 }
576
577 #[test]
578 fn test_jit_alloc_array() {
579 let bits = jit_box(HK_ARRAY, vec![1u64, 2, 3]);
580 assert!(is_heap(bits));
581 assert!(is_heap_kind(bits, HK_ARRAY));
582 assert_eq!(heap_kind(bits), Some(HK_ARRAY));
583 let arr = unsafe { jit_unbox::<Vec<u64>>(bits) };
584 assert_eq!(arr.len(), 3);
585 unsafe { jit_drop::<Vec<u64>>(bits) };
586 }
587
588 #[test]
589 fn test_heap_kind_none_for_non_heap() {
590 assert_eq!(heap_kind(TAG_NULL), None);
591 assert_eq!(heap_kind(TAG_BOOL_TRUE), None);
592 assert_eq!(heap_kind(box_number(42.0)), None);
593 assert_eq!(heap_kind(TAG_DATA_ROW | 5), None);
594 }
595
596 #[test]
597 fn test_data_row_round_trip() {
598 let bits = box_data_row(999);
599 assert!(is_data_row(bits));
600 assert_eq!(unbox_data_row(bits), 999);
601 assert!(!is_number(bits));
602 assert!(!is_heap(bits));
603 }
604
605 #[test]
606 fn test_result_tag_discrimination() {
607 assert!(!is_ok_tag(TAG_NULL));
608 assert!(!is_err_tag(TAG_NULL));
609 assert!(!is_result_tag(box_number(1.0)));
610
611 let ok_val = box_ok(box_number(1.0));
612 assert!(is_ok_tag(ok_val));
613 assert!(!is_err_tag(ok_val));
614 assert!(is_result_tag(ok_val));
615
616 let err_val = box_err(box_number(42.0));
617 assert!(is_err_tag(err_val));
618 assert!(!is_ok_tag(err_val));
619 assert!(is_result_tag(err_val));
620
621 assert!(!is_err_tag(TAG_BOOL_FALSE));
623 assert!(!is_err_tag(TAG_BOOL_TRUE));
624
625 unsafe { jit_drop::<u64>(ok_val) };
627 unsafe { jit_drop::<u64>(err_val) };
628 }
629
630 #[test]
631 fn test_result_round_trip() {
632 let inner = box_number(99.5);
633 let ok_val = box_ok(inner);
634 assert!(is_ok_tag(ok_val));
635 let recovered = unsafe { unbox_result_inner(ok_val) };
636 assert_eq!(unbox_number(recovered), 99.5);
637 unsafe { jit_drop::<u64>(ok_val) };
638 }
639
640 #[test]
641 fn test_option_tag_discrimination() {
642 assert!(is_none_tag(TAG_NONE));
643 assert!(is_option_tag(TAG_NONE));
644 assert!(!is_some_tag(TAG_NONE));
645
646 let some_val = box_some(box_number(3.14));
647 assert!(is_some_tag(some_val));
648 assert!(is_option_tag(some_val));
649 assert!(!is_none_tag(some_val));
650
651 let inner = unsafe { unbox_some_inner(some_val) };
653 assert_eq!(unbox_number(inner), 3.14);
654
655 unsafe { jit_drop::<u64>(some_val) };
657 }
658
659 #[test]
660 fn test_typed_object_encoding() {
661 let fake_ptr = 0x0000_1234_5678_0000u64 as *const u8;
662 let boxed = box_typed_object(fake_ptr);
663 assert!(is_typed_object(boxed));
664 assert!(!is_number(boxed));
665
666 let recovered = unbox_typed_object(boxed);
668 assert_eq!(recovered, fake_ptr);
669
670 assert!(!is_typed_object(TAG_NULL));
672 assert!(!is_typed_object(box_number(42.0)));
673
674 unsafe { jit_drop::<*const u8>(boxed) };
676 }
677
678 #[test]
679 fn test_column_ref_round_trip() {
680 let data = vec![1.0f64, 2.0, 3.0];
681 let bits = box_column_ref(data.as_ptr(), data.len());
682 assert!(is_column_ref(bits));
683 assert!(!is_number(bits));
684
685 let (ptr, len) = unsafe { unbox_column_ref(bits) };
686 assert_eq!(ptr, data.as_ptr());
687 assert_eq!(len, 3);
688
689 unsafe { jit_drop::<(*const f64, usize)>(bits) };
691 }
692}