intuicio_framework_value/
lib.rs

1use intuicio_data::{
2    is_copy,
3    lifetime::{ValueReadAccess, ValueWriteAccess},
4    managed_box::DynamicManagedBox,
5    type_hash::TypeHash,
6};
7use std::{cmp::Ordering, collections::BTreeMap};
8
9const SIZE: usize = std::mem::size_of::<DynamicManagedBox>();
10
11#[derive(Default)]
12enum ValueContent {
13    #[default]
14    Null,
15    Object(DynamicManagedBox),
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::Object(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::Object(_) => 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::Object(value) => Self::Object(value.clone()),
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::Object(me), Self::Object(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::Object(me), Self::Object(other)) => unsafe {
103                    me.memory().cmp(other.memory())
104                },
105                (
106                    Self::Primitive {
107                        type_hash: my_type_hash,
108                        data: my_data,
109                    },
110                    Self::Primitive {
111                        type_hash: other_type_hash,
112                        data: other_data,
113                    },
114                ) => my_type_hash
115                    .cmp(other_type_hash)
116                    .then_with(|| my_data.cmp(other_data)),
117                (Self::String(me), Self::String(other)) => me.cmp(other),
118                (Self::Array(me), Self::Array(other)) => me.cmp(other),
119                (Self::Map(me), Self::Map(other)) => me.cmp(other),
120                _ => unreachable!(),
121            })
122    }
123}
124
125#[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord)]
126pub struct Value {
127    inner: ValueContent,
128}
129
130impl Value {
131    pub fn type_hash(&self) -> Option<TypeHash> {
132        self.inner.type_hash()
133    }
134
135    pub fn null() -> Self {
136        Self {
137            inner: ValueContent::Null,
138        }
139    }
140
141    pub fn primitive_or_object<T>(value: T) -> Self {
142        if is_copy::<T>() && std::mem::size_of::<T>() <= SIZE {
143            let mut data = [0; SIZE];
144            unsafe {
145                data.as_mut_ptr().cast::<T>().write(value);
146            }
147            Self {
148                inner: ValueContent::Primitive {
149                    type_hash: TypeHash::of::<T>(),
150                    data,
151                },
152            }
153        } else {
154            Self {
155                inner: ValueContent::Object(DynamicManagedBox::new(value).ok().unwrap()),
156            }
157        }
158    }
159
160    pub fn object_raw(value: DynamicManagedBox) -> Self {
161        Self {
162            inner: ValueContent::Object(value),
163        }
164    }
165
166    pub fn string(value: impl ToString) -> Self {
167        Self {
168            inner: ValueContent::String(value.to_string()),
169        }
170    }
171
172    pub fn array(values: impl IntoIterator<Item = Value>) -> Self {
173        Self {
174            inner: ValueContent::Array(values.into_iter().collect()),
175        }
176    }
177
178    pub fn array_empty() -> Self {
179        Self {
180            inner: ValueContent::Array(Default::default()),
181        }
182    }
183
184    pub fn map(values: impl IntoIterator<Item = (Value, Value)>) -> Self {
185        Self {
186            inner: ValueContent::Map(values.into_iter().collect()),
187        }
188    }
189
190    pub fn map_empty() -> Self {
191        Self {
192            inner: ValueContent::Map(Default::default()),
193        }
194    }
195
196    pub fn is_null(&self) -> bool {
197        matches!(self.inner, ValueContent::Null)
198    }
199
200    pub fn is<T>(&self) -> bool {
201        self.type_hash()
202            .map(|type_hash| type_hash == TypeHash::of::<T>())
203            .unwrap_or_default()
204    }
205
206    pub fn as_object<T>(&'_ self) -> Option<ValueReadAccess<'_, T>> {
207        if let ValueContent::Object(value) = &self.inner {
208            value.read::<T>()
209        } else {
210            None
211        }
212    }
213
214    pub fn as_object_mut<T>(&'_ mut self) -> Option<ValueWriteAccess<'_, T>> {
215        if let ValueContent::Object(value) = &mut self.inner {
216            value.write::<T>()
217        } else {
218            None
219        }
220    }
221
222    pub fn as_primitive<T: Copy>(&self) -> Option<T> {
223        if let ValueContent::Primitive { type_hash, data } = &self.inner {
224            if *type_hash == TypeHash::of::<T>() {
225                unsafe { Some(data.as_ptr().cast::<T>().read()) }
226            } else {
227                None
228            }
229        } else {
230            None
231        }
232    }
233
234    pub fn as_string(&self) -> Option<&str> {
235        if let ValueContent::String(content) = &self.inner {
236            Some(content.as_str())
237        } else {
238            None
239        }
240    }
241
242    pub fn as_array(&self) -> Option<&Vec<Value>> {
243        if let ValueContent::Array(content) = &self.inner {
244            Some(content)
245        } else {
246            None
247        }
248    }
249
250    pub fn as_array_mut(&mut self) -> Option<&mut Vec<Value>> {
251        if let ValueContent::Array(content) = &mut self.inner {
252            Some(content)
253        } else {
254            None
255        }
256    }
257
258    pub fn as_map(&self) -> Option<&BTreeMap<Value, Value>> {
259        if let ValueContent::Map(content) = &self.inner {
260            Some(content)
261        } else {
262            None
263        }
264    }
265
266    pub fn as_map_mut(&mut self) -> Option<&mut BTreeMap<Value, Value>> {
267        if let ValueContent::Map(content) = &mut self.inner {
268            Some(content)
269        } else {
270            None
271        }
272    }
273}
274
275#[cfg(test)]
276mod tests {
277    use super::*;
278
279    #[test]
280    fn test_value() {
281        assert_eq!(std::mem::size_of::<Value>(), 32);
282        assert_eq!(SIZE, 8);
283        let a = Value::primitive_or_object(42u8);
284        let b = Value::primitive_or_object(10u16);
285        let c = Value::primitive_or_object(4.2f32);
286        let d = Value::array([a.clone(), b.clone(), c.clone()]);
287        let mut e = Value::primitive_or_object([42u64; 10000]);
288        let k1 = Value::string("foo");
289        let k2 = Value::string("bar");
290        let f = Value::map([(k1.clone(), d.clone()), (k2.clone(), e.clone())]);
291        let g = Value::null();
292        assert_eq!(a.as_primitive::<u8>().unwrap(), 42);
293        assert_eq!(b.as_primitive::<u16>().unwrap(), 10);
294        assert_eq!(c.as_primitive::<f32>().unwrap(), 4.2);
295        e.as_object_mut::<[u64; 10000]>().unwrap()[0] = 10;
296        assert_eq!(e.as_object::<[u64; 10000]>().unwrap()[0], 10);
297        assert!(f.as_map().unwrap()[&k1] == d);
298        assert!(f.as_map().unwrap()[&k1].as_array().unwrap()[0] == a);
299        assert!(f.as_map().unwrap()[&k1].as_array().unwrap()[1] == b);
300        assert!(f.as_map().unwrap()[&k1].as_array().unwrap()[2] == c);
301        assert!(f.as_map().unwrap()[&k2] == e);
302        assert!(g.is_null());
303        drop(f);
304    }
305}