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