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::Atomic(a), VmValue::Atomic(b)) => {
245 a.value.load(Ordering::SeqCst) == b.value.load(Ordering::SeqCst)
246 }
247 (VmValue::List(a), VmValue::List(b)) => {
248 a.len() == b.len()
249 && guard_recursion(|| a.iter().zip(b.iter()).all(|(x, y)| values_equal(x, y)))
250 }
251 (VmValue::Dict(a), VmValue::Dict(b)) => {
252 a.len() == b.len()
253 && guard_recursion(|| {
254 a.iter()
255 .zip(b.iter())
256 .all(|((k1, v1), (k2, v2))| k1 == k2 && values_equal(v1, v2))
257 })
258 }
259 (VmValue::EnumVariant(a), VmValue::EnumVariant(b)) => {
260 a.enum_name == b.enum_name
261 && a.variant == b.variant
262 && a.fields.len() == b.fields.len()
263 && guard_recursion(|| {
264 a.fields
265 .iter()
266 .zip(b.fields.iter())
267 .all(|(x, y)| values_equal(x, y))
268 })
269 }
270 (VmValue::StructInstance(a), VmValue::StructInstance(b)) => {
271 let (a_layout, a_fields) = (&a.layout, &a.fields);
272 let (b_layout, b_fields) = (&b.layout, &b.fields);
273 if a_layout.struct_name() != b_layout.struct_name() {
274 return false;
275 }
276 let a_map = super::struct_fields_to_map(a_layout, a_fields);
277 let b_map = super::struct_fields_to_map(b_layout, b_fields);
278 a_map.len() == b_map.len()
279 && guard_recursion(|| {
280 a_map
281 .iter()
282 .zip(b_map.iter())
283 .all(|((k1, v1), (k2, v2))| k1 == k2 && values_equal(v1, v2))
284 })
285 }
286 (VmValue::Set(a), VmValue::Set(b)) => {
287 a.len() == b.len() && a.iter().all(|x| b.contains(x))
290 }
291 (VmValue::Generator(_), VmValue::Generator(_)) => false, (VmValue::Stream(_), VmValue::Stream(_)) => false, (VmValue::Range(a), VmValue::Range(b)) => {
294 a.start == b.start && a.end == b.end && a.inclusive == b.inclusive
295 }
296 (VmValue::Iter(a), VmValue::Iter(b)) => Arc::ptr_eq(a, b),
297 (VmValue::Pair(a), VmValue::Pair(b)) => {
298 guard_recursion(|| values_equal(&a.0, &b.0) && values_equal(&a.1, &b.1))
299 }
300 (VmValue::Harness(_), VmValue::Harness(_)) => false,
305 _ => false,
306 }
307}
308
309pub fn dedup_values<'a, I>(items: I) -> Vec<VmValue>
319where
320 I: IntoIterator<Item = &'a VmValue>,
321{
322 use std::collections::HashMap;
323 let mut buckets: HashMap<String, Vec<VmValue>> = HashMap::new();
324 let mut result = Vec::new();
325 for item in items {
326 let bucket = buckets.entry(value_structural_hash_key(item)).or_default();
327 if !bucket.iter().any(|kept| values_equal(kept, item)) {
328 bucket.push(item.clone());
329 result.push(item.clone());
330 }
331 }
332 result
333}
334
335pub fn compare_values(a: &VmValue, b: &VmValue) -> i32 {
343 try_compare_values(a, b).unwrap_or(0)
344}
345
346pub fn try_compare_values(a: &VmValue, b: &VmValue) -> Option<i32> {
351 match (a, b) {
352 (VmValue::Int(x), VmValue::Int(y)) => Some(x.cmp(y) as i32),
353 (VmValue::Float(x), VmValue::Float(y)) => float_ordering(*x, *y),
354 (VmValue::Int(x), VmValue::Float(y)) => float_ordering(*x as f64, *y),
355 (VmValue::Float(x), VmValue::Int(y)) => float_ordering(*x, *y as f64),
356 (VmValue::Decimal(x), VmValue::Decimal(y)) => Some(x.cmp(y) as i32),
362 (VmValue::Decimal(_), VmValue::Int(_) | VmValue::Float(_))
363 | (VmValue::Int(_) | VmValue::Float(_), VmValue::Decimal(_)) => None,
364 (VmValue::String(x), VmValue::String(y)) => Some(x.cmp(y) as i32),
365 (VmValue::Pair(x), VmValue::Pair(y)) => guard_recursion(|| {
366 let c = try_compare_values(&x.0, &y.0)?;
367 if c != 0 {
368 Some(c)
369 } else {
370 try_compare_values(&x.1, &y.1)
371 }
372 }),
373 _ => Some(0),
374 }
375}
376
377fn float_ordering(x: f64, y: f64) -> Option<i32> {
378 x.partial_cmp(&y).map(|ord| ord as i32)
379}