egglog_core_relations/base_values/
unboxed.rs

1//! Special handling for small integer types to avoid lookups in an [`InternTable`].
2//!
3//!
4//! [`InternTable`]: crate::common::InternTable
5
6use crate::numeric_id::NumericId;
7
8use crate::Value;
9
10use super::BaseValue;
11
12/// for small base types we register them as u32 in implementation.
13macro_rules! impl_small_base_value {
14    ($ty:ty) => {
15        impl BaseValue for $ty {
16            const MAY_UNBOX: bool = true;
17            fn try_unbox(val: Value) -> Option<Self> {
18                Some(val.rep() as $ty)
19            }
20            fn try_box(&self) -> Option<Value> {
21                Some(Value::new(*self as u32))
22            }
23        }
24    };
25    ($ty:ty, $($rest:ty),+) => {
26        impl_small_base_value!($ty);
27        impl_small_base_value!($($rest),+);
28    };
29}
30
31impl_small_base_value!(u8, u16, u32, i8, i16, i32);
32
33impl BaseValue for bool {
34    const MAY_UNBOX: bool = true;
35    /// see [`bool::try_box`]
36    fn try_unbox(val: Value) -> Option<Self> {
37        Some(val.rep() != 0)
38    }
39    /// To optimize storage, we map [`Value`] 1 to true and 0 to false in the implementation.
40    /// As an example, the `subsumed` column of a table has type bool.
41    ///
42    /// ```rust
43    /// use egglog_core_relations::BaseValue;
44    /// let true_value = true.try_box().unwrap();
45    /// let false_value = false.try_box().unwrap();
46    /// assert_eq!(bool::try_unbox(true_value).unwrap(), true);
47    /// assert_eq!(bool::try_unbox(false_value).unwrap(), false);
48    /// ```
49    fn try_box(&self) -> Option<Value> {
50        Some(Value::new(if *self { 1 } else { 0 }))
51    }
52}
53
54impl BaseValue for () {
55    const MAY_UNBOX: bool = true;
56    /// To optimize storage, we map [`Value`] 0 to unit type.
57    ///
58    /// ```rust
59    /// use egglog_core_relations::BaseValue;
60    /// let unit_value = ().try_box().unwrap();
61    /// assert_eq!(<()>::try_unbox(unit_value).unwrap(), ());
62    /// ```
63    fn try_unbox(_val: Value) -> Option<Self> {
64        Some(())
65    }
66    fn try_box(&self) -> Option<Value> {
67        Some(Value::new(0))
68    }
69}
70
71const VAL_BITS: u32 = std::mem::size_of::<Value>() as u32 * 8;
72const VAL_MASK: u32 = 1 << (VAL_BITS - 1);
73
74macro_rules! impl_medium_base_value {
75    ($ty:ty) => {
76        impl BaseValue for $ty {
77            const MAY_UNBOX: bool = true;
78            fn try_box(&self) -> Option<Value> {
79                if *self & (VAL_MASK-1) as $ty == *self {
80                    // If the top bit is clear, we can box it directly.
81                    Some(Value::new(*self as u32))
82                } else {
83                    // If the top bit is set, we need to intern it.
84                    None
85                }
86            }
87            fn try_unbox(val: Value) -> Option<Self> {
88                let top_bit_clear = val.rep() & VAL_MASK == 0;
89                if top_bit_clear {
90                    Some(val.rep() as $ty)
91                } else {
92                    // If the top bit is set, look this up in an intern table.
93                    None
94                }
95            }
96        }
97    };
98
99    ($ty:ty, $($rest:ty),+) => {
100        impl_medium_base_value!($ty);
101        impl_medium_base_value!($($rest),+);
102    };
103}
104
105impl_medium_base_value!(u64, i64, usize, isize);