Skip to main content

cjc_runtime/
det_map.rs

1use crate::value::Value;
2
3// ---------------------------------------------------------------------------
4// 5. Deterministic Map (open addressing, MurmurHash3, insertion-order iteration)
5// ---------------------------------------------------------------------------
6
7/// Deterministic MurmurHash3 finalizer (64-bit, fixed seed).
8pub fn murmurhash3_finalize(mut h: u64) -> u64 {
9    h ^= h >> 33;
10    h = h.wrapping_mul(0xff51afd7ed558ccd);
11    h ^= h >> 33;
12    h = h.wrapping_mul(0xc4ceb9fe1a85ec53);
13    h ^= h >> 33;
14    h
15}
16
17/// Hash a byte slice using MurmurHash3 with a fixed seed.
18pub fn murmurhash3(data: &[u8]) -> u64 {
19    const SEED: u64 = 0x5f3759df;
20    let mut h = SEED;
21    // Process 8-byte chunks
22    let chunks = data.len() / 8;
23    for i in 0..chunks {
24        let mut k = [0u8; 8];
25        k.copy_from_slice(&data[i * 8..(i + 1) * 8]);
26        let k = u64::from_le_bytes(k);
27        let k = k.wrapping_mul(0x87c37b91114253d5);
28        let k = k.rotate_left(31);
29        let k = k.wrapping_mul(0x4cf5ad432745937f);
30        h ^= k;
31        h = h.rotate_left(27);
32        h = h.wrapping_mul(5).wrapping_add(0x52dce729);
33    }
34    // Tail
35    let tail = &data[chunks * 8..];
36    let mut k: u64 = 0;
37    for (i, &b) in tail.iter().enumerate() {
38        k |= (b as u64) << (i * 8);
39    }
40    if !tail.is_empty() {
41        let k = k.wrapping_mul(0x87c37b91114253d5);
42        let k = k.rotate_left(31);
43        let k = k.wrapping_mul(0x4cf5ad432745937f);
44        h ^= k;
45    }
46    h ^= data.len() as u64;
47    murmurhash3_finalize(h)
48}
49
50/// Deterministic hash for a Value. Uses MurmurHash3 with fixed seed.
51pub fn value_hash(val: &Value) -> u64 {
52    match val {
53        Value::Int(n) => murmurhash3(&n.to_le_bytes()),
54        Value::Float(f) => murmurhash3(&f.to_bits().to_le_bytes()),
55        Value::Bool(b) => murmurhash3(&[*b as u8]),
56        Value::String(s) => murmurhash3(s.as_bytes()),
57        Value::Bytes(b) => murmurhash3(&b.borrow()),
58        Value::ByteSlice(b) => murmurhash3(b),
59        Value::StrView(b) => murmurhash3(b),
60        Value::U8(v) => murmurhash3(&[*v]),
61        Value::Bf16(v) => murmurhash3(&v.0.to_le_bytes()),
62        Value::Enum {
63            enum_name,
64            variant,
65            fields,
66        } => {
67            // Hash enum_name + variant + each field
68            let mut h = murmurhash3(enum_name.as_bytes());
69            h ^= murmurhash3(variant.as_bytes());
70            for f in fields {
71                h ^= value_hash(f);
72            }
73            h
74        }
75        Value::Void => murmurhash3(&[0xff]),
76        _ => murmurhash3(&[0xfe]),
77    }
78}
79
80/// A deterministic hash map with insertion-order iteration.
81/// Uses open addressing with Fibonacci hashing.
82#[derive(Debug, Clone)]
83pub struct DetMap {
84    /// Entries: (hash, key, value). None for empty slots.
85    entries: Vec<Option<(u64, Value, Value)>>,
86    /// Insertion-order indices into `entries`.
87    order: Vec<usize>,
88    len: usize,
89    capacity: usize,
90}
91
92const FIBONACCI_CONSTANT: u64 = 11400714819323198485; // 2^64 / phi
93
94impl DetMap {
95    pub fn new() -> Self {
96        let capacity = 8;
97        DetMap {
98            entries: vec![None; capacity],
99            order: Vec::new(),
100            len: 0,
101            capacity,
102        }
103    }
104
105    fn slot_index(&self, hash: u64) -> usize {
106        let shift = 64 - (self.capacity.trailing_zeros() as u64);
107        (hash.wrapping_mul(FIBONACCI_CONSTANT) >> shift) as usize
108    }
109
110    pub fn insert(&mut self, key: Value, value: Value) {
111        if self.len * 4 >= self.capacity * 3 {
112            self.grow();
113        }
114        let hash = value_hash(&key);
115        let mut slot = self.slot_index(hash);
116        loop {
117            match &self.entries[slot] {
118                None => {
119                    self.entries[slot] = Some((hash, key, value));
120                    self.order.push(slot);
121                    self.len += 1;
122                    return;
123                }
124                Some((h, k, _)) => {
125                    if *h == hash && values_equal_static(k, &key) {
126                        // Overwrite value, preserve insertion order
127                        self.entries[slot] = Some((hash, key, value));
128                        return;
129                    }
130                    slot = (slot + 1) % self.capacity;
131                }
132            }
133        }
134    }
135
136    pub fn get(&self, key: &Value) -> Option<&Value> {
137        let hash = value_hash(key);
138        let mut slot = self.slot_index(hash);
139        let start = slot;
140        loop {
141            match &self.entries[slot] {
142                None => return None,
143                Some((h, k, v)) => {
144                    if *h == hash && values_equal_static(k, key) {
145                        return Some(v);
146                    }
147                    slot = (slot + 1) % self.capacity;
148                    if slot == start {
149                        return None;
150                    }
151                }
152            }
153        }
154    }
155
156    pub fn contains_key(&self, key: &Value) -> bool {
157        self.get(key).is_some()
158    }
159
160    pub fn remove(&mut self, key: &Value) -> Option<Value> {
161        let hash = value_hash(key);
162        let mut slot = self.slot_index(hash);
163        let start = slot;
164        loop {
165            match &self.entries[slot] {
166                None => return None,
167                Some((h, k, _)) => {
168                    if *h == hash && values_equal_static(k, key) {
169                        let (_, _, v) = self.entries[slot].take().unwrap();
170                        self.len -= 1;
171                        self.order.retain(|&s| s != slot);
172                        // Re-insert displaced entries (Robin Hood-style cleanup)
173                        // Preserve insertion-order by updating slot refs in-place
174                        let mut next = (slot + 1) % self.capacity;
175                        while self.entries[next].is_some() {
176                            let entry = self.entries[next].take().unwrap();
177                            let old_slot = next;
178                            let rehash = entry.0;
179                            let rkey = entry.1;
180                            let rval = entry.2;
181                            // Find new slot
182                            let mut new_slot = self.slot_index(rehash);
183                            while self.entries[new_slot].is_some() {
184                                new_slot = (new_slot + 1) % self.capacity;
185                            }
186                            self.entries[new_slot] = Some((rehash, rkey, rval));
187                            // Update order in-place: replace old_slot with new_slot
188                            // This preserves the original insertion position
189                            for s in &mut self.order {
190                                if *s == old_slot {
191                                    *s = new_slot;
192                                    break;
193                                }
194                            }
195                            next = (next + 1) % self.capacity;
196                        }
197                        return Some(v);
198                    }
199                    slot = (slot + 1) % self.capacity;
200                    if slot == start {
201                        return None;
202                    }
203                }
204            }
205        }
206    }
207
208    /// Iterate in insertion order.
209    pub fn iter(&self) -> impl Iterator<Item = (&Value, &Value)> {
210        self.order.iter().filter_map(move |&slot| {
211            self.entries[slot]
212                .as_ref()
213                .map(|(_, k, v)| (k, v))
214        })
215    }
216
217    pub fn keys(&self) -> Vec<Value> {
218        self.iter().map(|(k, _)| k.clone()).collect()
219    }
220
221    pub fn values_vec(&self) -> Vec<Value> {
222        self.iter().map(|(_, v)| v.clone()).collect()
223    }
224
225    pub fn len(&self) -> usize {
226        self.len
227    }
228
229    pub fn is_empty(&self) -> bool {
230        self.len == 0
231    }
232
233    fn grow(&mut self) {
234        let new_capacity = self.capacity * 2;
235        let old_order = self.order.clone();
236        let old_entries: Vec<_> = old_order
237            .iter()
238            .filter_map(|&slot| self.entries[slot].clone())
239            .collect();
240
241        self.entries = vec![None; new_capacity];
242        self.order = Vec::new();
243        self.len = 0;
244        self.capacity = new_capacity;
245
246        for (hash, key, value) in old_entries {
247            let _ = hash; // Re-hash with new capacity
248            self.insert(key, value);
249        }
250    }
251}
252
253impl Default for DetMap {
254    fn default() -> Self {
255        Self::new()
256    }
257}
258
259/// Static value equality (no interpreter context needed).
260pub fn values_equal_static(a: &Value, b: &Value) -> bool {
261    match (a, b) {
262        (Value::Int(a), Value::Int(b)) => a == b,
263        (Value::Float(a), Value::Float(b)) => a.to_bits() == b.to_bits(),
264        (Value::Bool(a), Value::Bool(b)) => a == b,
265        (Value::String(a), Value::String(b)) => a == b,
266        (Value::Bytes(a), Value::Bytes(b)) => *a.borrow() == *b.borrow(),
267        (Value::ByteSlice(a), Value::ByteSlice(b)) => **a == **b,
268        (Value::StrView(a), Value::StrView(b)) => **a == **b,
269        (Value::U8(a), Value::U8(b)) => a == b,
270        (Value::Bf16(a), Value::Bf16(b)) => a.0 == b.0,
271        (
272            Value::Enum {
273                enum_name: en1,
274                variant: v1,
275                fields: f1,
276            },
277            Value::Enum {
278                enum_name: en2,
279                variant: v2,
280                fields: f2,
281            },
282        ) => {
283            en1 == en2
284                && v1 == v2
285                && f1.len() == f2.len()
286                && f1
287                    .iter()
288                    .zip(f2.iter())
289                    .all(|(a, b)| values_equal_static(a, b))
290        }
291        (Value::Void, Value::Void) => true,
292        _ => false,
293    }
294}
295