Skip to main content

aver_memory/
compare.rs

1use super::*;
2use alloc::string::ToString;
3
4impl NanValue {
5    pub fn eq_in<T: ArenaTypes>(self, other: Self, arena: &Arena<T>) -> bool {
6        // Float comparison goes first and never short-circuits on
7        // bit-pattern equality. IEEE 754 says `NaN == NaN` is false
8        // and `+0.0 == -0.0` is true; both rules disagree with raw
9        // bit comparison, so we always defer to `f64::PartialEq` for
10        // floats. This keeps the VM's `==` aligned with what Rust
11        // codegen and WASM emit (both use IEEE 754 directly).
12        if self.is_float() || other.is_float() {
13            if !(self.is_float() && other.is_float()) {
14                return false;
15            }
16            return self.as_float() == other.as_float();
17        }
18        if self.0 == other.0 {
19            return true;
20        }
21        match (self.wrapper_parts(arena), other.wrapper_parts(arena)) {
22            (Some((self_kind, self_inner)), Some((other_kind, other_inner))) => {
23                return self_kind == other_kind && self_inner.eq_in(other_inner, arena);
24            }
25            (Some(_), None) | (None, Some(_)) => return false,
26            (None, None) => {}
27        }
28        // Inline variant fast path (no arena indirection)
29        if self.tag() == TAG_INLINE_VARIANT || other.tag() == TAG_INLINE_VARIANT {
30            let s_ctor = self.variant_ctor_id(arena);
31            let o_ctor = other.variant_ctor_id(arena);
32            return match (s_ctor, o_ctor) {
33                (Some(sc), Some(oc)) if sc == oc => {
34                    // Same constructor — compare field(s)
35                    if self.tag() == TAG_INLINE_VARIANT && other.tag() == TAG_INLINE_VARIANT {
36                        self.inline_variant_inner()
37                            .eq_in(other.inline_variant_inner(), arena)
38                    } else {
39                        // Cross-representation: extract the single field from each side
40                        let sf = self.variant_single_field(arena);
41                        let of = other.variant_single_field(arena);
42                        sf.eq_in(of, arena)
43                    }
44                }
45                (Some(_), Some(_)) => false,
46                _ => false,
47            };
48        }
49        match (self.variant_parts(arena), other.variant_parts(arena)) {
50            (Some((st, sv, sf)), Some((ot, ov, of))) => {
51                return st == ot
52                    && sv == ov
53                    && sf.len() == of.len()
54                    && sf.iter().zip(of).all(|(a, b)| a.eq_in(*b, arena));
55            }
56            (Some(_), None) | (None, Some(_)) => return false,
57            (None, None) => {}
58        }
59        if self.is_string() || other.is_string() {
60            return self.is_string()
61                && other.is_string()
62                && arena.get_string_value(self) == arena.get_string_value(other);
63        }
64        if self.tag() != other.tag() {
65            return false;
66        }
67        match self.tag() {
68            TAG_INT => self.as_int(arena) == other.as_int(arena),
69            TAG_IMMEDIATE | TAG_NONE => false,
70            TAG_STRING => arena.get_string_value(self) == arena.get_string_value(other),
71            TAG_LIST => {
72                let a_len = arena.list_len_value(self);
73                let b_len = arena.list_len_value(other);
74                a_len == b_len
75                    && (0..a_len).all(|i| {
76                        arena
77                            .list_get_value(self, i)
78                            .zip(arena.list_get_value(other, i))
79                            .is_some_and(|(x, y)| x.eq_in(y, arena))
80                    })
81            }
82            TAG_TUPLE => {
83                let a = arena.get_tuple(self.arena_index());
84                let b = arena.get_tuple(other.arena_index());
85                a.len() == b.len() && a.iter().zip(b).all(|(x, y)| x.eq_in(*y, arena))
86            }
87            TAG_VECTOR => {
88                let a = arena.vector_ref_value(self);
89                let b = arena.vector_ref_value(other);
90                a.len() == b.len() && a.iter().zip(b).all(|(x, y)| x.eq_in(*y, arena))
91            }
92            TAG_MAP => {
93                let a = arena.map_ref_value(self);
94                let b = arena.map_ref_value(other);
95                a.len() == b.len()
96                    && a.iter()
97                        .all(|(k, (_, v1))| b.get(k).is_some_and(|(_, v2)| v1.eq_in(*v2, arena)))
98            }
99            TAG_RECORD => {
100                let (ta, fa) = arena.get_record(self.arena_index());
101                let (tb, fb) = arena.get_record(other.arena_index());
102                ta == tb
103                    && fa.len() == fb.len()
104                    && fa.iter().zip(fb).all(|(a, b)| a.eq_in(*b, arena))
105            }
106            TAG_VARIANT | TAG_INLINE_VARIANT => {
107                unreachable!("variant comparison handled above")
108            }
109            TAG_SYMBOL => self.bits() == other.bits(),
110            _ => false,
111        }
112    }
113
114    pub fn hash_in<T: ArenaTypes, H: core::hash::Hasher>(self, state: &mut H, arena: &Arena<T>) {
115        use core::hash::Hash;
116        if self.is_float() {
117            1u8.hash(state);
118            let f = self.as_float();
119            let bits = if f == 0.0 {
120                0.0f64.to_bits()
121            } else {
122                f.to_bits()
123            };
124            bits.hash(state);
125            return;
126        }
127        if let Some((kind, inner)) = self.wrapper_parts(arena) {
128            match kind {
129                WRAP_SOME => (TAG_SOME as u8).hash(state),
130                WRAP_OK => (TAG_OK as u8).hash(state),
131                WRAP_ERR => (TAG_ERR as u8).hash(state),
132                _ => (0xFFu8).hash(state),
133            }
134            inner.hash_in(state, arena);
135            return;
136        }
137        if let Some((tid, vid, inner)) = self.inline_variant_info(arena) {
138            (TAG_VARIANT as u8).hash(state);
139            tid.hash(state);
140            vid.hash(state);
141            inner.hash_in(state, arena);
142            return;
143        }
144        if let Some((tid, vid, fields)) = self.variant_parts(arena) {
145            (TAG_VARIANT as u8).hash(state);
146            tid.hash(state);
147            vid.hash(state);
148            for field in fields {
149                field.hash_in(state, arena);
150            }
151            return;
152        }
153        if self.is_string() {
154            (TAG_STRING as u8).hash(state);
155            arena.get_string_value(self).hash(state);
156            return;
157        }
158        let tag = self.tag();
159        (tag as u8).hash(state);
160        match tag {
161            TAG_INT => self.as_int(arena).hash(state),
162            TAG_IMMEDIATE => self.payload().hash(state),
163            TAG_NONE => 0u8.hash(state),
164            TAG_STRING => arena.get_string_value(self).hash(state),
165            TAG_LIST => {
166                arena.list_len_value(self).hash(state);
167                for item in arena.list_to_vec_value(self) {
168                    item.hash_in(state, arena);
169                }
170            }
171            TAG_TUPLE => {
172                let items = arena.get_tuple(self.arena_index());
173                items.len().hash(state);
174                for item in items {
175                    item.hash_in(state, arena);
176                }
177            }
178            TAG_VECTOR => {
179                let items = arena.vector_ref_value(self);
180                items.len().hash(state);
181                for item in items {
182                    item.hash_in(state, arena);
183                }
184            }
185            TAG_RECORD => {
186                let (tid, fields) = arena.get_record(self.arena_index());
187                tid.hash(state);
188                for f in fields {
189                    f.hash_in(state, arena);
190                }
191            }
192            TAG_VARIANT | TAG_INLINE_VARIANT => {
193                unreachable!("variant hashing handled above")
194            }
195            TAG_SYMBOL => self.bits().hash(state),
196            _ => self.0.hash(state),
197        }
198    }
199
200    pub fn repr<T: ArenaTypes>(self, arena: &Arena<T>) -> String {
201        if self.is_float() {
202            return format!("{}", self.as_float());
203        }
204        if let Some((kind, inner)) = self.wrapper_parts(arena) {
205            let ir = inner.repr_inner(arena);
206            return match kind {
207                WRAP_SOME => format!("Option.Some({})", ir),
208                WRAP_OK => format!("Result.Ok({})", ir),
209                WRAP_ERR => format!("Result.Err({})", ir),
210                _ => String::from("??"),
211            };
212        }
213        if let Some((tid, vid, inner)) = self.inline_variant_info(arena) {
214            let vname = arena.get_variant_name(tid, vid);
215            return format!("{}({})", vname, inner.repr_inner(arena));
216        }
217        if let Some((tid, vid, fields)) = self.variant_parts(arena) {
218            let vname = arena.get_variant_name(tid, vid);
219            return if fields.is_empty() {
220                String::from(vname)
221            } else {
222                let parts: Vec<_> = fields.iter().map(|v| v.repr_inner(arena)).collect();
223                format!("{}({})", vname, parts.join(", "))
224            };
225        }
226        if self.is_string() {
227            return arena.get_string_value(self).to_string();
228        }
229        match self.tag() {
230            TAG_INT => format!("{}", self.as_int(arena)),
231            TAG_IMMEDIATE => match self.payload() {
232                IMM_FALSE => String::from("false"),
233                IMM_TRUE => String::from("true"),
234                IMM_UNIT => String::from("Unit"),
235                _ => String::from("??"),
236            },
237            TAG_NONE => String::from("Option.None"),
238            TAG_STRING => arena.get_string_value(self).to_string(),
239            TAG_LIST => {
240                let parts: Vec<_> = arena
241                    .list_to_vec_value(self)
242                    .into_iter()
243                    .map(|v| v.repr_inner(arena))
244                    .collect();
245                format!("[{}]", parts.join(", "))
246            }
247            TAG_TUPLE => {
248                let items = arena.get_tuple(self.arena_index());
249                let parts: Vec<_> = items.iter().map(|v| v.repr_inner(arena)).collect();
250                format!("({})", parts.join(", "))
251            }
252            TAG_VECTOR => {
253                let items = arena.vector_ref_value(self);
254                let parts: Vec<_> = items.iter().map(|v| v.repr_inner(arena)).collect();
255                format!("Vector[{}]", parts.join(", "))
256            }
257            TAG_MAP => {
258                let map = arena.map_ref_value(self);
259                let mut pairs: Vec<_> = map
260                    .values()
261                    .map(|(k, v)| (k.repr_inner(arena), v.repr_inner(arena)))
262                    .collect();
263                pairs.sort_by(|(a, _), (b, _)| a.cmp(b));
264                let parts: Vec<_> = pairs
265                    .into_iter()
266                    .map(|(k, v)| format!("{}: {}", k, v))
267                    .collect();
268                format!("{{{}}}", parts.join(", "))
269            }
270            TAG_RECORD => {
271                let (tid, fields) = arena.get_record(self.arena_index());
272                let name = arena.get_type_name(tid);
273                let fnames = arena.get_field_names(tid);
274                let parts: Vec<_> = fnames
275                    .iter()
276                    .zip(fields)
277                    .map(|(n, v)| format!("{}: {}", n, v.repr_inner(arena)))
278                    .collect();
279                format!("{}({})", name, parts.join(", "))
280            }
281            TAG_VARIANT | TAG_INLINE_VARIANT => unreachable!("variant repr handled above"),
282            TAG_SYMBOL => match self.symbol_kind() {
283                SYMBOL_FN => format!("<fn {}>", arena.get_fn(self.symbol_index()).name()),
284                SYMBOL_BUILTIN => format!("<builtin {}>", arena.get_builtin(self.symbol_index())),
285                SYMBOL_NAMESPACE => {
286                    let (name, _) = arena.get_namespace(self.symbol_index());
287                    format!("<type {}>", name)
288                }
289                SYMBOL_NULLARY_VARIANT => unreachable!("variant repr handled above"),
290                _ => String::from("??"),
291            },
292            _ => String::from("??"),
293        }
294    }
295
296    fn repr_inner<T: ArenaTypes>(self, arena: &Arena<T>) -> String {
297        if self.is_string() {
298            return format!("\"{}\"", arena.get_string_value(self));
299        }
300        self.repr(arena)
301    }
302
303    pub fn display<T: ArenaTypes>(self, arena: &Arena<T>) -> Option<String> {
304        if self.is_unit() {
305            None
306        } else {
307            Some(self.repr(arena))
308        }
309    }
310}