intuicio_framework_value/
lib.rs

1use intuicio_data::{
2    is_copy,
3    lifetime::{ValueReadAccess, ValueWriteAccess},
4    managed::gc::DynamicManagedGc,
5    type_hash::TypeHash,
6};
7use std::{cmp::Ordering, collections::BTreeMap};
8
9const SIZE: usize = std::mem::size_of::<DynamicManagedGc>();
10
11#[derive(Default)]
12enum ValueContent {
13    #[default]
14    Null,
15    Gc(DynamicManagedGc),
16    Primitive {
17        type_hash: TypeHash,
18        data: [u8; SIZE],
19    },
20    String(String),
21    Array(Vec<Value>),
22    Map(BTreeMap<Value, Value>),
23}
24
25impl ValueContent {
26    fn type_hash(&self) -> Option<TypeHash> {
27        match self {
28            Self::Null => None,
29            Self::Gc(data) => Some(data.type_hash()),
30            Self::Primitive { type_hash, .. } => Some(*type_hash),
31            Self::String(_) => Some(TypeHash::of::<String>()),
32            Self::Array(_) => Some(TypeHash::of::<Vec<Value>>()),
33            Self::Map(_) => Some(TypeHash::of::<BTreeMap<Value, Value>>()),
34        }
35    }
36
37    fn order(&self) -> u8 {
38        match self {
39            Self::Null => 0,
40            Self::Gc(_) => 1,
41            Self::Primitive { .. } => 2,
42            Self::String(_) => 3,
43            Self::Array(_) => 4,
44            Self::Map(_) => 5,
45        }
46    }
47}
48
49impl Clone for ValueContent {
50    fn clone(&self) -> Self {
51        match self {
52            Self::Null => Self::Null,
53            Self::Gc(value) => Self::Gc(value.reference()),
54            Self::Primitive { type_hash, data } => Self::Primitive {
55                type_hash: *type_hash,
56                data: *data,
57            },
58            Self::String(value) => Self::String(value.clone()),
59            Self::Array(value) => Self::Array(value.clone()),
60            Self::Map(value) => Self::Map(value.clone()),
61        }
62    }
63}
64
65impl PartialEq for ValueContent {
66    fn eq(&self, other: &Self) -> bool {
67        match (self, other) {
68            (Self::Null, Self::Null) => true,
69            (Self::Gc(me), Self::Gc(other)) => unsafe { me.memory() == other.memory() },
70            (
71                Self::Primitive {
72                    type_hash: my_type_hash,
73                    data: my_data,
74                },
75                Self::Primitive {
76                    type_hash: other_type_hash,
77                    data: other_data,
78                },
79            ) => my_type_hash == other_type_hash && my_data == other_data,
80            (Self::String(me), Self::String(other)) => me == other,
81            (Self::Array(me), Self::Array(other)) => me == other,
82            (Self::Map(me), Self::Map(other)) => me == other,
83            _ => false,
84        }
85    }
86}
87
88impl Eq for ValueContent {}
89
90impl PartialOrd for ValueContent {
91    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
92        Some(self.cmp(other))
93    }
94}
95
96impl Ord for ValueContent {
97    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
98        self.order()
99            .cmp(&other.order())
100            .then_with(|| match (self, other) {
101                (Self::Null, Self::Null) => Ordering::Equal,
102                (Self::Gc(me), Self::Gc(other)) => unsafe { me.memory().cmp(other.memory()) },
103                (
104                    Self::Primitive {
105                        type_hash: my_type_hash,
106                        data: my_data,
107                    },
108                    Self::Primitive {
109                        type_hash: other_type_hash,
110                        data: other_data,
111                    },
112                ) => my_type_hash
113                    .cmp(other_type_hash)
114                    .then_with(|| my_data.cmp(other_data)),
115                (Self::String(me), Self::String(other)) => me.cmp(other),
116                (Self::Array(me), Self::Array(other)) => me.cmp(other),
117                (Self::Map(me), Self::Map(other)) => me.cmp(other),
118                _ => unreachable!(),
119            })
120    }
121}
122
123#[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord)]
124pub struct Value {
125    inner: ValueContent,
126}
127
128impl Value {
129    pub fn type_hash(&self) -> Option<TypeHash> {
130        self.inner.type_hash()
131    }
132
133    pub fn null() -> Self {
134        Self {
135            inner: ValueContent::Null,
136        }
137    }
138
139    pub fn primitive_or_gc<T>(value: T) -> Self {
140        if is_copy::<T>() && std::mem::size_of::<T>() <= SIZE {
141            let mut data = [0; SIZE];
142            unsafe {
143                data.as_mut_ptr().cast::<T>().write(value);
144            }
145            Self {
146                inner: ValueContent::Primitive {
147                    type_hash: TypeHash::of::<T>(),
148                    data,
149                },
150            }
151        } else {
152            Self {
153                inner: ValueContent::Gc(DynamicManagedGc::new(value)),
154            }
155        }
156    }
157
158    pub fn gc_raw(value: DynamicManagedGc) -> Self {
159        Self {
160            inner: ValueContent::Gc(value),
161        }
162    }
163
164    pub fn string(value: impl ToString) -> Self {
165        Self {
166            inner: ValueContent::String(value.to_string()),
167        }
168    }
169
170    pub fn array(values: impl IntoIterator<Item = Value>) -> Self {
171        Self {
172            inner: ValueContent::Array(values.into_iter().collect()),
173        }
174    }
175
176    pub fn array_empty() -> Self {
177        Self {
178            inner: ValueContent::Array(Default::default()),
179        }
180    }
181
182    pub fn map(values: impl IntoIterator<Item = (Value, Value)>) -> Self {
183        Self {
184            inner: ValueContent::Map(values.into_iter().collect()),
185        }
186    }
187
188    pub fn map_empty() -> Self {
189        Self {
190            inner: ValueContent::Map(Default::default()),
191        }
192    }
193
194    pub fn is_null(&self) -> bool {
195        matches!(self.inner, ValueContent::Null)
196    }
197
198    pub fn is<T>(&self) -> bool {
199        self.type_hash()
200            .map(|type_hash| type_hash == TypeHash::of::<T>())
201            .unwrap_or_default()
202    }
203
204    pub fn as_gc<T>(&'_ self) -> Option<ValueReadAccess<'_, T>> {
205        if let ValueContent::Gc(value) = &self.inner {
206            value.try_read::<T>()
207        } else {
208            None
209        }
210    }
211
212    pub fn as_gc_mut<T>(&'_ mut self) -> Option<ValueWriteAccess<'_, T>> {
213        if let ValueContent::Gc(value) = &mut self.inner {
214            value.try_write::<T>()
215        } else {
216            None
217        }
218    }
219
220    pub fn as_primitive<T: Copy>(&self) -> Option<T> {
221        if let ValueContent::Primitive { type_hash, data } = &self.inner {
222            if *type_hash == TypeHash::of::<T>() {
223                unsafe { Some(data.as_ptr().cast::<T>().read()) }
224            } else {
225                None
226            }
227        } else {
228            None
229        }
230    }
231
232    pub fn as_string(&self) -> Option<&str> {
233        if let ValueContent::String(content) = &self.inner {
234            Some(content.as_str())
235        } else {
236            None
237        }
238    }
239
240    pub fn as_array(&self) -> Option<&Vec<Value>> {
241        if let ValueContent::Array(content) = &self.inner {
242            Some(content)
243        } else {
244            None
245        }
246    }
247
248    pub fn as_array_mut(&mut self) -> Option<&mut Vec<Value>> {
249        if let ValueContent::Array(content) = &mut self.inner {
250            Some(content)
251        } else {
252            None
253        }
254    }
255
256    pub fn as_map(&self) -> Option<&BTreeMap<Value, Value>> {
257        if let ValueContent::Map(content) = &self.inner {
258            Some(content)
259        } else {
260            None
261        }
262    }
263
264    pub fn as_map_mut(&mut self) -> Option<&mut BTreeMap<Value, Value>> {
265        if let ValueContent::Map(content) = &mut self.inner {
266            Some(content)
267        } else {
268            None
269        }
270    }
271}
272
273#[cfg(test)]
274mod tests {
275    use super::*;
276
277    #[test]
278    fn test_value() {
279        assert_eq!(
280            std::mem::size_of::<Value>(),
281            if cfg!(feature = "typehash_debug_name") {
282                112
283            } else {
284                80
285            }
286        );
287        assert_eq!(SIZE, 80);
288        let a = Value::primitive_or_gc(42u8);
289        let b = Value::primitive_or_gc(10u16);
290        let c = Value::primitive_or_gc(4.2f32);
291        let d = Value::array([a.clone(), b.clone(), c.clone()]);
292        let mut e = Value::primitive_or_gc([42u64; 10000]);
293        let k1 = Value::string("foo");
294        let k2 = Value::string("bar");
295        let f = Value::map([(k1.clone(), d.clone()), (k2.clone(), e.clone())]);
296        let g = Value::null();
297        assert_eq!(a.as_primitive::<u8>().unwrap(), 42);
298        assert_eq!(b.as_primitive::<u16>().unwrap(), 10);
299        assert_eq!(c.as_primitive::<f32>().unwrap(), 4.2);
300        e.as_gc_mut::<[u64; 10000]>().unwrap()[0] = 10;
301        assert_eq!(e.as_gc::<[u64; 10000]>().unwrap()[0], 10);
302        assert!(f.as_map().unwrap()[&k1] == d);
303        assert!(f.as_map().unwrap()[&k1].as_array().unwrap()[0] == a);
304        assert!(f.as_map().unwrap()[&k1].as_array().unwrap()[1] == b);
305        assert!(f.as_map().unwrap()[&k1].as_array().unwrap()[2] == c);
306        assert!(f.as_map().unwrap()[&k2] == e);
307        assert!(g.is_null());
308        drop(f);
309    }
310}