1use std::rc::Rc;
2use std::sync::atomic::Ordering;
3
4use super::VmValue;
5
6pub fn values_identical(a: &VmValue, b: &VmValue) -> bool {
11 match (a, b) {
12 (VmValue::List(x), VmValue::List(y)) => Rc::ptr_eq(x, y),
13 (VmValue::Dict(x), VmValue::Dict(y)) => Rc::ptr_eq(x, y),
14 (VmValue::Set(x), VmValue::Set(y)) => Rc::ptr_eq(x, y),
15 (VmValue::Closure(x), VmValue::Closure(y)) => Rc::ptr_eq(x, y),
16 (VmValue::String(x), VmValue::String(y)) => Rc::ptr_eq(x, y) || x == y,
17 (VmValue::Bytes(x), VmValue::Bytes(y)) => Rc::ptr_eq(x, y) || x == y,
18 (VmValue::BuiltinRef(x), VmValue::BuiltinRef(y)) => x == y,
19 (VmValue::BuiltinRefId { name: x, .. }, VmValue::BuiltinRefId { name: y, .. }) => x == y,
20 (VmValue::BuiltinRef(x), VmValue::BuiltinRefId { name: y, .. })
21 | (VmValue::BuiltinRefId { name: y, .. }, VmValue::BuiltinRef(x)) => x == y,
22 (VmValue::Pair(x), VmValue::Pair(y)) => Rc::ptr_eq(x, y),
23 _ => values_equal(a, b),
25 }
26}
27
28pub fn value_identity_key(v: &VmValue) -> String {
33 match v {
34 VmValue::List(x) => format!("list@{:p}", Rc::as_ptr(x)),
35 VmValue::Dict(x) => format!("dict@{:p}", Rc::as_ptr(x)),
36 VmValue::Set(x) => format!("set@{:p}", Rc::as_ptr(x)),
37 VmValue::Closure(x) => format!("closure@{:p}", Rc::as_ptr(x)),
38 VmValue::String(x) => format!("string@{:p}", x.as_ptr()),
39 VmValue::Bytes(x) => format!("bytes@{:p}", Rc::as_ptr(x)),
40 VmValue::BuiltinRef(name) => format!("builtin@{name}"),
41 VmValue::BuiltinRefId { name, .. } => format!("builtin@{name}"),
42 other => format!("{}@{}", other.type_name(), other.display()),
43 }
44}
45
46pub fn value_structural_hash_key(v: &VmValue) -> String {
52 let mut out = String::new();
53 write_structural_hash_key(v, &mut out);
54 out
55}
56
57fn write_structural_hash_key(v: &VmValue, out: &mut String) {
61 match v {
62 VmValue::Nil => out.push('N'),
63 VmValue::Bool(b) => {
64 out.push(if *b { 'T' } else { 'F' });
65 }
66 VmValue::Int(n) => {
67 out.push('i');
68 out.push_str(&n.to_string());
69 out.push(';');
70 }
71 VmValue::Float(n) => {
72 out.push('f');
73 out.push_str(&n.to_bits().to_string());
74 out.push(';');
75 }
76 VmValue::String(s) => {
77 out.push('s');
79 out.push_str(&s.len().to_string());
80 out.push(':');
81 out.push_str(s);
82 }
83 VmValue::Bytes(bytes) => {
84 out.push('b');
85 for byte in bytes.iter() {
86 out.push_str(&format!("{byte:02x}"));
87 }
88 out.push(';');
89 }
90 VmValue::Duration(ms) => {
91 out.push('d');
92 out.push_str(&ms.to_string());
93 out.push(';');
94 }
95 VmValue::List(items) => {
96 out.push('L');
97 for item in items.iter() {
98 write_structural_hash_key(item, out);
99 out.push(',');
100 }
101 out.push(']');
102 }
103 VmValue::Dict(map) => {
104 out.push('D');
105 for (k, v) in map.iter() {
106 out.push_str(&k.len().to_string());
108 out.push(':');
109 out.push_str(k);
110 out.push('=');
111 write_structural_hash_key(v, out);
112 out.push(',');
113 }
114 out.push('}');
115 }
116 VmValue::Set(items) => {
117 let mut keys: Vec<String> = items.iter().map(value_structural_hash_key).collect();
119 keys.sort();
120 out.push('S');
121 for k in &keys {
122 out.push_str(k);
123 out.push(',');
124 }
125 out.push('}');
126 }
127 other => {
128 let tn = other.type_name();
129 out.push('o');
130 out.push_str(&tn.len().to_string());
131 out.push(':');
132 out.push_str(tn);
133 let d = other.display();
134 out.push_str(&d.len().to_string());
135 out.push(':');
136 out.push_str(&d);
137 }
138 }
139}
140
141pub fn values_equal(a: &VmValue, b: &VmValue) -> bool {
142 match (a, b) {
143 (VmValue::Int(x), VmValue::Int(y)) => x == y,
144 (VmValue::Float(x), VmValue::Float(y)) => x == y,
145 (VmValue::String(x), VmValue::String(y)) => x == y,
146 (VmValue::Bytes(x), VmValue::Bytes(y)) => x == y,
147 (VmValue::BuiltinRef(x), VmValue::BuiltinRef(y)) => x == y,
148 (VmValue::BuiltinRefId { name: x, .. }, VmValue::BuiltinRefId { name: y, .. }) => x == y,
149 (VmValue::BuiltinRef(x), VmValue::BuiltinRefId { name: y, .. })
150 | (VmValue::BuiltinRefId { name: y, .. }, VmValue::BuiltinRef(x)) => x == y,
151 (VmValue::Bool(x), VmValue::Bool(y)) => x == y,
152 (VmValue::Nil, VmValue::Nil) => true,
153 (VmValue::Int(x), VmValue::Float(y)) => (*x as f64) == *y,
154 (VmValue::Float(x), VmValue::Int(y)) => *x == (*y as f64),
155 (VmValue::TaskHandle(a), VmValue::TaskHandle(b)) => a == b,
156 (VmValue::Channel(_), VmValue::Channel(_)) => false, (VmValue::Rng(_), VmValue::Rng(_)) => false,
158 (VmValue::SyncPermit(_), VmValue::SyncPermit(_)) => false,
159 (VmValue::Atomic(a), VmValue::Atomic(b)) => {
160 a.value.load(Ordering::SeqCst) == b.value.load(Ordering::SeqCst)
161 }
162 (VmValue::List(a), VmValue::List(b)) => {
163 a.len() == b.len() && a.iter().zip(b.iter()).all(|(x, y)| values_equal(x, y))
164 }
165 (VmValue::Dict(a), VmValue::Dict(b)) => {
166 a.len() == b.len()
167 && a.iter()
168 .zip(b.iter())
169 .all(|((k1, v1), (k2, v2))| k1 == k2 && values_equal(v1, v2))
170 }
171 (
172 VmValue::EnumVariant {
173 enum_name: a_e,
174 variant: a_v,
175 fields: a_f,
176 },
177 VmValue::EnumVariant {
178 enum_name: b_e,
179 variant: b_v,
180 fields: b_f,
181 },
182 ) => {
183 a_e == b_e
184 && a_v == b_v
185 && a_f.len() == b_f.len()
186 && a_f.iter().zip(b_f.iter()).all(|(x, y)| values_equal(x, y))
187 }
188 (
189 VmValue::StructInstance {
190 layout: a_layout,
191 fields: a_fields,
192 },
193 VmValue::StructInstance {
194 layout: b_layout,
195 fields: b_fields,
196 },
197 ) => {
198 if a_layout.struct_name() != b_layout.struct_name() {
199 return false;
200 }
201 let a_map = super::struct_fields_to_map(a_layout, a_fields);
202 let b_map = super::struct_fields_to_map(b_layout, b_fields);
203 a_map.len() == b_map.len()
204 && a_map
205 .iter()
206 .zip(b_map.iter())
207 .all(|((k1, v1), (k2, v2))| k1 == k2 && values_equal(v1, v2))
208 }
209 (VmValue::Set(a), VmValue::Set(b)) => {
210 a.len() == b.len() && a.iter().all(|x| b.iter().any(|y| values_equal(x, y)))
211 }
212 (VmValue::Generator(_), VmValue::Generator(_)) => false, (VmValue::Range(a), VmValue::Range(b)) => {
214 a.start == b.start && a.end == b.end && a.inclusive == b.inclusive
215 }
216 (VmValue::Iter(a), VmValue::Iter(b)) => Rc::ptr_eq(a, b),
217 (VmValue::Pair(a), VmValue::Pair(b)) => {
218 values_equal(&a.0, &b.0) && values_equal(&a.1, &b.1)
219 }
220 _ => false,
221 }
222}
223
224pub fn compare_values(a: &VmValue, b: &VmValue) -> i32 {
225 match (a, b) {
226 (VmValue::Int(x), VmValue::Int(y)) => x.cmp(y) as i32,
227 (VmValue::Float(x), VmValue::Float(y)) => {
228 if x < y {
229 -1
230 } else if x > y {
231 1
232 } else {
233 0
234 }
235 }
236 (VmValue::Int(x), VmValue::Float(y)) => {
237 let x = *x as f64;
238 if x < *y {
239 -1
240 } else if x > *y {
241 1
242 } else {
243 0
244 }
245 }
246 (VmValue::Float(x), VmValue::Int(y)) => {
247 let y = *y as f64;
248 if *x < y {
249 -1
250 } else if *x > y {
251 1
252 } else {
253 0
254 }
255 }
256 (VmValue::String(x), VmValue::String(y)) => x.cmp(y) as i32,
257 (VmValue::Pair(x), VmValue::Pair(y)) => {
258 let c = compare_values(&x.0, &y.0);
259 if c != 0 {
260 c
261 } else {
262 compare_values(&x.1, &y.1)
263 }
264 }
265 _ => 0,
266 }
267}