1use std::sync::atomic::Ordering;
2use std::sync::Arc;
3
4use super::recursion::guard_recursion;
5use super::VmValue;
6
7pub fn values_identical(a: &VmValue, b: &VmValue) -> bool {
12 match (a, b) {
13 (VmValue::List(x), VmValue::List(y)) => Arc::ptr_eq(x, y),
14 (VmValue::Dict(x), VmValue::Dict(y)) => Arc::ptr_eq(x, y),
15 (VmValue::Set(x), VmValue::Set(y)) => Arc::ptr_eq(x, y),
16 (VmValue::Closure(x), VmValue::Closure(y)) => Arc::ptr_eq(x, y),
17 (VmValue::String(x), VmValue::String(y)) => arcstr::ArcStr::ptr_eq(x, y) || x == y,
18 (VmValue::Bytes(x), VmValue::Bytes(y)) => Arc::ptr_eq(x, y) || x == y,
19 (VmValue::BuiltinRef(x), VmValue::BuiltinRef(y)) => x == y,
20 (VmValue::BuiltinRefId(x), VmValue::BuiltinRefId(y)) => x.name == y.name,
21 (VmValue::BuiltinRef(x), VmValue::BuiltinRefId(y))
22 | (VmValue::BuiltinRefId(y), VmValue::BuiltinRef(x)) => x.as_str() == y.name.as_str(),
23 (VmValue::Pair(x), VmValue::Pair(y)) => Arc::ptr_eq(x, y),
24 _ => values_equal(a, b),
26 }
27}
28
29pub fn value_identity_key(v: &VmValue) -> String {
34 match v {
35 VmValue::List(x) => format!("list@{:p}", Arc::as_ptr(x)),
36 VmValue::Dict(x) => format!("dict@{:p}", Arc::as_ptr(x)),
37 VmValue::Set(x) => format!("set@{:p}", Arc::as_ptr(x)),
38 VmValue::Closure(x) => format!("closure@{:p}", Arc::as_ptr(x)),
39 VmValue::String(x) => format!("string@{:p}", x.as_ptr()),
40 VmValue::Bytes(x) => format!("bytes@{:p}", Arc::as_ptr(x)),
41 VmValue::BuiltinRef(name) => format!("builtin@{name}"),
42 VmValue::BuiltinRefId(r) => format!("builtin@{}", r.name),
43 other => format!("{}@{}", other.type_name(), other.display()),
44 }
45}
46
47pub fn value_structural_hash_key(v: &VmValue) -> String {
53 let mut out = String::new();
54 write_structural_hash_key(v, &mut out);
55 out
56}
57
58fn write_structural_hash_key(v: &VmValue, out: &mut String) {
62 match v {
63 VmValue::Nil => out.push('N'),
64 VmValue::Bool(b) => {
65 out.push(if *b { 'T' } else { 'F' });
66 }
67 VmValue::Int(n) => write_int_hash_key(*n, out),
68 VmValue::Float(n) => {
69 match float_as_equivalent_int(*n) {
76 Some(i) => write_int_hash_key(i, out),
77 None => {
78 out.push('f');
79 out.push_str(&n.to_bits().to_string());
80 out.push(';');
81 }
82 }
83 }
84 VmValue::String(s) => {
85 out.push('s');
87 write_len_prefixed(s, out);
88 }
89 VmValue::Bytes(bytes) => {
90 out.push('b');
91 for byte in bytes.iter() {
92 out.push_str(&format!("{byte:02x}"));
93 }
94 out.push(';');
95 }
96 VmValue::Duration(ms) => {
97 out.push('d');
98 out.push_str(&ms.to_string());
99 out.push(';');
100 }
101 VmValue::Decimal(dec) => {
106 out.push('M');
107 out.push_str(&dec.normalize().to_string());
108 out.push(';');
109 }
110 VmValue::List(items) => {
111 out.push('L');
112 guard_recursion(|| {
113 for item in items.iter() {
114 write_structural_hash_key(item, out);
115 out.push(',');
116 }
117 });
118 out.push(']');
119 }
120 VmValue::Dict(map) => {
121 out.push('D');
122 guard_recursion(|| {
123 for (k, v) in map.iter() {
124 write_len_prefixed(k, out);
125 out.push('=');
126 write_structural_hash_key(v, out);
127 out.push(',');
128 }
129 });
130 out.push('}');
131 }
132 VmValue::Set(set) => {
133 out.push('S');
136 for k in set.sorted_keys() {
137 out.push_str(k);
138 out.push(',');
139 }
140 out.push('}');
141 }
142 VmValue::Pair(pair) => {
147 out.push('P');
148 guard_recursion(|| {
149 write_structural_hash_key(&pair.0, out);
150 out.push(',');
151 write_structural_hash_key(&pair.1, out);
152 });
153 out.push(';');
154 }
155 VmValue::EnumVariant(ev) => {
156 out.push('E');
157 write_len_prefixed(&ev.enum_name, out);
158 write_len_prefixed(&ev.variant, out);
159 guard_recursion(|| {
160 for field in ev.fields.iter() {
161 write_structural_hash_key(field, out);
162 out.push(',');
163 }
164 });
165 out.push(';');
166 }
167 VmValue::StructInstance(data) => {
168 let (layout, fields) = (&data.layout, &data.fields);
169 out.push('I');
172 write_len_prefixed(layout.struct_name(), out);
173 guard_recursion(|| {
174 for (k, v) in super::struct_fields_to_map(layout, fields) {
175 write_len_prefixed(&k, out);
176 out.push('=');
177 write_structural_hash_key(&v, out);
178 out.push(',');
179 }
180 });
181 out.push('}');
182 }
183 other => {
184 out.push('o');
188 write_len_prefixed(other.type_name(), out);
189 write_len_prefixed(&other.display(), out);
190 }
191 }
192}
193
194fn write_len_prefixed(s: &str, out: &mut String) {
197 out.push_str(&s.len().to_string());
198 out.push(':');
199 out.push_str(s);
200}
201
202fn write_int_hash_key(n: i64, out: &mut String) {
205 out.push('i');
206 out.push_str(&n.to_string());
207 out.push(';');
208}
209
210fn float_as_equivalent_int(n: f64) -> Option<i64> {
215 let candidate = n as i64; ((candidate as f64) == n).then_some(candidate)
217}
218
219pub fn values_equal(a: &VmValue, b: &VmValue) -> bool {
220 match (a, b) {
221 (VmValue::Int(x), VmValue::Int(y)) => x == y,
222 (VmValue::Float(x), VmValue::Float(y)) => x == y,
223 (VmValue::Decimal(x), VmValue::Decimal(y)) => x == y,
230 (VmValue::String(x), VmValue::String(y)) => x == y,
231 (VmValue::Bytes(x), VmValue::Bytes(y)) => x == y,
232 (VmValue::BuiltinRef(x), VmValue::BuiltinRef(y)) => x == y,
233 (VmValue::BuiltinRefId(x), VmValue::BuiltinRefId(y)) => x.name == y.name,
234 (VmValue::BuiltinRef(x), VmValue::BuiltinRefId(y))
235 | (VmValue::BuiltinRefId(y), VmValue::BuiltinRef(x)) => x.as_str() == y.name.as_str(),
236 (VmValue::Bool(x), VmValue::Bool(y)) => x == y,
237 (VmValue::Nil, VmValue::Nil) => true,
238 (VmValue::Int(x), VmValue::Float(y)) => (*x as f64) == *y,
239 (VmValue::Float(x), VmValue::Int(y)) => *x == (*y as f64),
240 (VmValue::TaskHandle(a), VmValue::TaskHandle(b)) => a == b,
241 (VmValue::Channel(_), VmValue::Channel(_)) => false, (VmValue::Rng(_), VmValue::Rng(_)) => false,
243 (VmValue::SyncPermit(_), VmValue::SyncPermit(_)) => false,
244 (VmValue::Resource(a), VmValue::Resource(b)) => a.ptr_eq(b),
245 (VmValue::ResourceGuard(_), VmValue::ResourceGuard(_)) => false,
246 (VmValue::Atomic(a), VmValue::Atomic(b)) => {
247 a.value.load(Ordering::SeqCst) == b.value.load(Ordering::SeqCst)
248 }
249 (VmValue::List(a), VmValue::List(b)) => {
250 a.len() == b.len()
251 && guard_recursion(|| a.iter().zip(b.iter()).all(|(x, y)| values_equal(x, y)))
252 }
253 (VmValue::Dict(a), VmValue::Dict(b)) => {
254 a.len() == b.len()
255 && guard_recursion(|| {
256 a.iter()
257 .zip(b.iter())
258 .all(|((k1, v1), (k2, v2))| k1 == k2 && values_equal(v1, v2))
259 })
260 }
261 (VmValue::EnumVariant(a), VmValue::EnumVariant(b)) => {
262 a.enum_name == b.enum_name
263 && a.variant == b.variant
264 && a.fields.len() == b.fields.len()
265 && guard_recursion(|| {
266 a.fields
267 .iter()
268 .zip(b.fields.iter())
269 .all(|(x, y)| values_equal(x, y))
270 })
271 }
272 (VmValue::StructInstance(a), VmValue::StructInstance(b)) => {
273 let (a_layout, a_fields) = (&a.layout, &a.fields);
274 let (b_layout, b_fields) = (&b.layout, &b.fields);
275 if a_layout.struct_name() != b_layout.struct_name() {
276 return false;
277 }
278 let a_map = super::struct_fields_to_map(a_layout, a_fields);
279 let b_map = super::struct_fields_to_map(b_layout, b_fields);
280 a_map.len() == b_map.len()
281 && guard_recursion(|| {
282 a_map
283 .iter()
284 .zip(b_map.iter())
285 .all(|((k1, v1), (k2, v2))| k1 == k2 && values_equal(v1, v2))
286 })
287 }
288 (VmValue::Set(a), VmValue::Set(b)) => {
289 a.len() == b.len() && a.iter().all(|x| b.contains(x))
292 }
293 (VmValue::Generator(_), VmValue::Generator(_)) => false, (VmValue::Stream(_), VmValue::Stream(_)) => false, (VmValue::Range(a), VmValue::Range(b)) => {
296 a.start == b.start && a.end == b.end && a.inclusive == b.inclusive
297 }
298 (VmValue::Iter(a), VmValue::Iter(b)) => Arc::ptr_eq(a, b),
299 (VmValue::Pair(a), VmValue::Pair(b)) => {
300 guard_recursion(|| values_equal(&a.0, &b.0) && values_equal(&a.1, &b.1))
301 }
302 (VmValue::Harness(_), VmValue::Harness(_)) => false,
307 _ => false,
308 }
309}
310
311pub fn dedup_values<'a, I>(items: I) -> Vec<VmValue>
321where
322 I: IntoIterator<Item = &'a VmValue>,
323{
324 use std::collections::HashMap;
325 let mut buckets: HashMap<String, Vec<VmValue>> = HashMap::new();
326 let mut result = Vec::new();
327 for item in items {
328 let bucket = buckets.entry(value_structural_hash_key(item)).or_default();
329 if !bucket.iter().any(|kept| values_equal(kept, item)) {
330 bucket.push(item.clone());
331 result.push(item.clone());
332 }
333 }
334 result
335}
336
337pub fn compare_values(a: &VmValue, b: &VmValue) -> i32 {
345 try_compare_values(a, b).unwrap_or(0)
346}
347
348pub fn try_compare_values(a: &VmValue, b: &VmValue) -> Option<i32> {
353 match (a, b) {
354 (VmValue::Int(x), VmValue::Int(y)) => Some(x.cmp(y) as i32),
355 (VmValue::Float(x), VmValue::Float(y)) => float_ordering(*x, *y),
356 (VmValue::Int(x), VmValue::Float(y)) => float_ordering(*x as f64, *y),
357 (VmValue::Float(x), VmValue::Int(y)) => float_ordering(*x, *y as f64),
358 (VmValue::Decimal(x), VmValue::Decimal(y)) => Some(x.cmp(y) as i32),
364 (VmValue::Decimal(_), VmValue::Int(_) | VmValue::Float(_))
365 | (VmValue::Int(_) | VmValue::Float(_), VmValue::Decimal(_)) => None,
366 (VmValue::String(x), VmValue::String(y)) => Some(x.cmp(y) as i32),
367 (VmValue::Pair(x), VmValue::Pair(y)) => guard_recursion(|| {
368 let c = try_compare_values(&x.0, &y.0)?;
369 if c != 0 {
370 Some(c)
371 } else {
372 try_compare_values(&x.1, &y.1)
373 }
374 }),
375 (VmValue::List(x), VmValue::List(y)) => guard_recursion(|| {
380 for (item_a, item_b) in x.iter().zip(y.iter()) {
381 let c = try_compare_values(item_a, item_b)?;
382 if c != 0 {
383 return Some(c);
384 }
385 }
386 Some(x.len().cmp(&y.len()) as i32)
387 }),
388 _ => Some(0),
389 }
390}
391
392fn float_ordering(x: f64, y: f64) -> Option<i32> {
393 x.partial_cmp(&y).map(|ord| ord as i32)
394}