intuicio_framework_value/
lib.rs1use 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}