Skip to main content

qanglang_core/memory/
hashmap_arena.rs

1use crate::Value;
2use crate::arena::{Arena, Index};
3
4const CHUNK_SIZE: usize = 8; // Buckets per chunk
5const DEFAULT_CAPACITY: usize = 16;
6const LOAD_FACTOR_THRESHOLD: f64 = 0.75;
7
8pub type HashMapHandle = Index;
9
10#[derive(Debug, Clone)]
11pub struct HashMapObject {
12    pub first_chunk: Option<BucketChunkHandle>,
13    pub len: usize,
14    pub capacity: usize,
15    pub is_marked: bool,
16}
17
18impl HashMapObject {
19    pub fn new(capacity: usize) -> Self {
20        Self {
21            first_chunk: None,
22            len: 0,
23            capacity,
24            is_marked: false,
25        }
26    }
27}
28
29#[derive(Debug, Clone)]
30pub struct BucketChunk {
31    pub buckets: [Bucket; CHUNK_SIZE],
32    pub next_chunk: Option<BucketChunkHandle>,
33}
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Default)]
36pub struct BucketChunkHandle(Index);
37
38#[derive(Debug, Clone, Copy)]
39pub struct Bucket {
40    pub kind: BucketKind,
41}
42
43#[derive(Debug, Clone, Copy)]
44pub enum BucketKind {
45    Empty,
46    Occupied { key: Value, value: Value },
47    Tombstone,
48}
49
50impl Default for Bucket {
51    fn default() -> Self {
52        Bucket {
53            kind: BucketKind::Empty,
54        }
55    }
56}
57
58impl Default for BucketChunk {
59    fn default() -> Self {
60        BucketChunk {
61            buckets: [Bucket::default(); CHUNK_SIZE],
62            next_chunk: None,
63        }
64    }
65}
66
67#[derive(Debug, Default, Clone)]
68pub struct HashMapArena {
69    hashmaps: Arena<HashMapObject>,
70    chunks: Arena<BucketChunk>,
71}
72
73impl HashMapArena {
74    pub fn new() -> Self {
75        Self::with_capacity(64)
76    }
77
78    pub fn with_capacity(initial_capacity: usize) -> Self {
79        Self {
80            hashmaps: Arena::with_capacity(initial_capacity),
81            chunks: Arena::with_capacity(initial_capacity * 4),
82        }
83    }
84
85    pub fn new_hashmap(&mut self) -> HashMapHandle {
86        self.new_hashmap_with_capacity(DEFAULT_CAPACITY)
87    }
88
89    pub fn new_hashmap_with_capacity(&mut self, capacity: usize) -> HashMapHandle {
90        let hashmap = HashMapObject::new(capacity);
91        self.hashmaps.insert(hashmap)
92    }
93
94    pub fn insert(&mut self, handle: HashMapHandle, key: Value, value: Value) -> Option<Value> {
95        let hash = key.hash();
96
97        // Check if we need to resize before inserting
98        if let Some(hashmap) = self.hashmaps.get(handle) {
99            let load_factor = hashmap.len as f64 / hashmap.capacity as f64;
100            if load_factor >= LOAD_FACTOR_THRESHOLD {
101                self.resize_hashmap(handle);
102            }
103        }
104
105        self.insert_with_hash(handle, key, value, hash)
106    }
107
108    fn insert_with_hash(
109        &mut self,
110        handle: HashMapHandle,
111        key: Value,
112        value: Value,
113        hash: u64,
114    ) -> Option<Value> {
115        let capacity = self.hashmaps.get(handle)?.capacity;
116        let bucket_index = (hash as usize) % capacity;
117
118        // Find or create the bucket position
119        let (chunk_handle, bucket_idx_in_chunk) = self.ensure_bucket_exists(handle, bucket_index);
120
121        // Linear probing to find insertion point
122        let mut current_chunk = chunk_handle;
123        let mut current_bucket_idx = bucket_idx_in_chunk;
124        let mut probed = 0;
125
126        loop {
127            if let Some(chunk) = self.chunks.get_mut(current_chunk.0) {
128                let bucket = &mut chunk.buckets[current_bucket_idx];
129
130                match bucket.kind {
131                    BucketKind::Empty | BucketKind::Tombstone => {
132                        // Found insertion point
133                        bucket.kind = BucketKind::Occupied { key, value };
134                        if let Some(hashmap) = self.hashmaps.get_mut(handle) {
135                            hashmap.len += 1;
136                        }
137                        return None;
138                    }
139                    BucketKind::Occupied {
140                        key: existing_key,
141                        value: existing_value,
142                    } => {
143                        if existing_key == key {
144                            // Key already exists, replace value
145                            bucket.kind = BucketKind::Occupied { key, value };
146                            return Some(existing_value);
147                        }
148                        // Continue probing
149                    }
150                }
151            }
152
153            // Move to next bucket
154            current_bucket_idx += 1;
155            if current_bucket_idx >= CHUNK_SIZE {
156                current_bucket_idx = 0;
157                if let Some(chunk) = self.chunks.get(current_chunk.0) {
158                    if let Some(next_chunk) = chunk.next_chunk {
159                        current_chunk = next_chunk;
160                    } else {
161                        // Need to allocate new chunk
162                        let new_chunk = BucketChunk::default();
163                        let new_chunk_handle = BucketChunkHandle(self.chunks.insert(new_chunk));
164
165                        // Link the new chunk
166                        if let Some(chunk) = self.chunks.get_mut(current_chunk.0) {
167                            chunk.next_chunk = Some(new_chunk_handle);
168                        }
169                        current_chunk = new_chunk_handle;
170                    }
171                }
172            }
173
174            probed += 1;
175            if probed >= capacity {
176                // This shouldn't happen with proper load factor management
177                break;
178            }
179        }
180
181        None
182    }
183
184    pub fn get(&self, handle: HashMapHandle, key: &Value) -> Option<Value> {
185        let hashmap = self.hashmaps.get(handle)?;
186        let hash = key.hash();
187        let bucket_index = (hash as usize) % hashmap.capacity;
188
189        let (chunk_handle, bucket_idx_in_chunk) = self.get_bucket_position(handle, bucket_index)?;
190
191        // Linear probing to find key
192        let mut current_chunk = chunk_handle;
193        let mut current_bucket_idx = bucket_idx_in_chunk;
194        let mut probed = 0;
195
196        loop {
197            if let Some(chunk) = self.chunks.get(current_chunk.0) {
198                let bucket = &chunk.buckets[current_bucket_idx];
199
200                match bucket.kind {
201                    BucketKind::Empty => {
202                        // Key not found
203                        return None;
204                    }
205                    BucketKind::Occupied {
206                        key: existing_key,
207                        value,
208                    } => {
209                        if existing_key == *key {
210                            return Some(value);
211                        }
212                    }
213                    BucketKind::Tombstone => {
214                        // Continue searching
215                    }
216                }
217            }
218
219            // Move to next bucket
220            current_bucket_idx += 1;
221            if current_bucket_idx >= CHUNK_SIZE {
222                current_bucket_idx = 0;
223                if let Some(chunk) = self.chunks.get(current_chunk.0) {
224                    if let Some(next_chunk) = chunk.next_chunk {
225                        current_chunk = next_chunk;
226                    } else {
227                        break;
228                    }
229                }
230            }
231
232            probed += 1;
233            if probed >= hashmap.capacity {
234                break;
235            }
236        }
237
238        None
239    }
240
241    pub fn remove(&mut self, handle: HashMapHandle, key: &Value) -> Option<Value> {
242        let hashmap = self.hashmaps.get(handle)?;
243        let hash = key.hash();
244        let bucket_index = (hash as usize) % hashmap.capacity;
245
246        let (chunk_handle, bucket_idx_in_chunk) = self.get_bucket_position(handle, bucket_index)?;
247
248        // Linear probing to find key
249        let mut current_chunk = chunk_handle;
250        let mut current_bucket_idx = bucket_idx_in_chunk;
251        let mut probed = 0;
252
253        loop {
254            if let Some(chunk) = self.chunks.get_mut(current_chunk.0) {
255                let bucket = &mut chunk.buckets[current_bucket_idx];
256
257                match bucket.kind {
258                    BucketKind::Empty => {
259                        // Key not found
260                        return None;
261                    }
262                    BucketKind::Occupied {
263                        key: existing_key,
264                        value,
265                    } => {
266                        if existing_key == *key {
267                            bucket.kind = BucketKind::Tombstone;
268                            if let Some(hashmap) = self.hashmaps.get_mut(handle) {
269                                hashmap.len -= 1;
270                            }
271                            return Some(value);
272                        }
273                    }
274                    BucketKind::Tombstone => {
275                        // Continue searching
276                    }
277                }
278            }
279
280            // Move to next bucket
281            current_bucket_idx += 1;
282            if current_bucket_idx >= CHUNK_SIZE {
283                current_bucket_idx = 0;
284                if let Some(chunk) = self.chunks.get(current_chunk.0) {
285                    if let Some(next_chunk) = chunk.next_chunk {
286                        current_chunk = next_chunk;
287                    } else {
288                        break;
289                    }
290                }
291            }
292
293            probed += 1;
294            if probed >= hashmap.capacity {
295                break;
296            }
297        }
298
299        None
300    }
301
302    pub fn len(&self, handle: HashMapHandle) -> usize {
303        self.hashmaps.get(handle).map(|hm| hm.len).unwrap_or(0)
304    }
305
306    pub fn is_empty(&self, handle: HashMapHandle) -> bool {
307        self.len(handle) == 0
308    }
309
310    fn ensure_bucket_exists(
311        &mut self,
312        handle: HashMapHandle,
313        bucket_index: usize,
314    ) -> (BucketChunkHandle, usize) {
315        let hashmap = self.hashmaps.get_mut(handle).unwrap();
316
317        // Create first chunk if needed
318        if hashmap.first_chunk.is_none() {
319            let chunk = BucketChunk::default();
320            let chunk_handle = BucketChunkHandle(self.chunks.insert(chunk));
321            hashmap.first_chunk = Some(chunk_handle);
322        }
323
324        let chunk_handle = hashmap.first_chunk.unwrap();
325        let bucket_idx_in_chunk = bucket_index % CHUNK_SIZE;
326
327        (chunk_handle, bucket_idx_in_chunk)
328    }
329
330    fn get_bucket_position(
331        &self,
332        handle: HashMapHandle,
333        bucket_index: usize,
334    ) -> Option<(BucketChunkHandle, usize)> {
335        let hashmap = self.hashmaps.get(handle)?;
336        let chunk_handle = hashmap.first_chunk?;
337        let bucket_idx_in_chunk = bucket_index % CHUNK_SIZE;
338
339        Some((chunk_handle, bucket_idx_in_chunk))
340    }
341
342    fn resize_hashmap(&mut self, handle: HashMapHandle) {
343        // Double the capacity
344        let old_capacity = self.hashmaps.get(handle).unwrap().capacity;
345        let new_capacity = old_capacity * 2;
346
347        // Collect all existing key-value pairs
348        // Pre-allocate with current length since we know exactly how many pairs we have
349        let current_len = self.hashmaps.get(handle).map(|hm| hm.len).unwrap_or(0);
350        let mut pairs = Vec::with_capacity(current_len);
351        if let Some(hashmap) = self.hashmaps.get(handle)
352            && let Some(first_chunk) = hashmap.first_chunk
353        {
354            self.collect_all_pairs(first_chunk, &mut pairs);
355        }
356
357        // Clear the hashmap and update capacity
358        if let Some(hashmap) = self.hashmaps.get_mut(handle) {
359            hashmap.first_chunk = None;
360            hashmap.len = 0;
361            hashmap.capacity = new_capacity;
362        }
363
364        // Reinsert all pairs
365        for (key, value) in pairs {
366            let hash = key.hash();
367            self.insert_with_hash(handle, key, value, hash);
368        }
369    }
370
371    fn collect_all_pairs(&self, chunk_handle: BucketChunkHandle, pairs: &mut Vec<(Value, Value)>) {
372        if let Some(chunk) = self.chunks.get(chunk_handle.0) {
373            for bucket in &chunk.buckets {
374                if let BucketKind::Occupied { key, value } = bucket.kind {
375                    pairs.push((key, value));
376                }
377            }
378
379            if let Some(next_chunk) = chunk.next_chunk {
380                self.collect_all_pairs(next_chunk, pairs);
381            }
382        }
383    }
384
385    pub fn iter(&self, handle: HashMapHandle) -> HashMapIterator<'_> {
386        HashMapIterator::new(self, handle)
387    }
388
389    pub fn mark_hashmap(&mut self, handle: HashMapHandle) {
390        if let Some(hashmap) = self.hashmaps.get_mut(handle) {
391            hashmap.is_marked = true;
392        }
393    }
394
395    pub fn copy_into(&mut self, source_handle: HashMapHandle, destination_handle: HashMapHandle) {
396        if source_handle == destination_handle {
397            return;
398        }
399
400        let source_first_chunk = match self.hashmaps.get(source_handle) {
401            Some(hashmap) => hashmap.first_chunk,
402            None => return,
403        };
404
405        if self.hashmaps.get(destination_handle).is_none() {
406            return;
407        }
408
409        if let Some(first_chunk) = source_first_chunk {
410            self.copy_all_pairs_from_chunk(first_chunk, destination_handle);
411        }
412    }
413
414    fn copy_all_pairs_from_chunk(
415        &mut self,
416        chunk_handle: BucketChunkHandle,
417        destination_handle: HashMapHandle,
418    ) {
419        let chunk_data = if let Some(chunk) = self.chunks.get(chunk_handle.0) {
420            // Use fixed-size array to avoid heap allocation entirely
421            let mut pairs: [(Value, Value); CHUNK_SIZE] =
422                [(Value::nil(), Value::nil()); CHUNK_SIZE];
423            let mut count = 0;
424
425            for bucket in &chunk.buckets {
426                if let BucketKind::Occupied { key, value } = bucket.kind {
427                    pairs[count] = (key, value);
428                    count += 1;
429                }
430            }
431            let next_chunk = chunk.next_chunk;
432            (pairs, count, next_chunk)
433        } else {
434            return;
435        };
436
437        // Only iterate over the occupied slots
438        for i in 0..chunk_data.1 {
439            let (key, value) = chunk_data.0[i];
440            self.insert(destination_handle, key, value);
441        }
442
443        if let Some(next_chunk) = chunk_data.2 {
444            self.copy_all_pairs_from_chunk(next_chunk, destination_handle);
445        }
446    }
447
448    pub fn collect_garbage(&mut self) {
449        let estimated_deletions = (self.hashmaps.len() / 4).max(8);
450        let mut deleted_hashmaps = Vec::with_capacity(estimated_deletions);
451
452        // Find unmarked hashmaps
453        for (handle, hashmap) in self.hashmaps.iter_mut() {
454            if hashmap.is_marked {
455                hashmap.is_marked = false; // Reset mark for next GC cycle
456            } else {
457                deleted_hashmaps.push(handle);
458            }
459        }
460
461        for handle in deleted_hashmaps {
462            self.delete_hashmap(handle);
463        }
464    }
465
466    pub fn delete_hashmap(&mut self, handle: HashMapHandle) -> bool {
467        if let Some(hashmap) = self.hashmaps.get(handle) {
468            if let Some(first_chunk) = hashmap.first_chunk {
469                self.mark_chunks_for_cleanup(first_chunk);
470            }
471
472            self.hashmaps.remove(handle);
473            true
474        } else {
475            false
476        }
477    }
478
479    fn mark_chunks_for_cleanup(&mut self, chunk_handle: BucketChunkHandle) {
480        if let Some(chunk) = self.chunks.get(chunk_handle.0) {
481            let next_chunk = chunk.next_chunk;
482            self.chunks.remove(chunk_handle.0);
483
484            if let Some(next) = next_chunk {
485                self.mark_chunks_for_cleanup(next);
486            }
487        }
488    }
489
490    pub fn get_allocated_bytes(&self) -> usize {
491        let hashmap_bytes = self.hashmaps.len() * std::mem::size_of::<HashMapObject>();
492        let chunk_bytes = self.chunks.len() * std::mem::size_of::<BucketChunk>();
493        hashmap_bytes + chunk_bytes
494    }
495}
496
497pub struct HashMapIterator<'a> {
498    arena: &'a HashMapArena,
499    current_chunk: Option<BucketChunkHandle>,
500    current_bucket_idx: usize,
501}
502
503impl<'a> HashMapIterator<'a> {
504    fn new(arena: &'a HashMapArena, handle: HashMapHandle) -> Self {
505        let current_chunk = arena.hashmaps.get(handle).and_then(|hm| hm.first_chunk);
506
507        Self {
508            arena,
509            current_chunk,
510            current_bucket_idx: 0,
511        }
512    }
513}
514
515impl<'a> Iterator for HashMapIterator<'a> {
516    type Item = (Value, Value);
517
518    fn next(&mut self) -> Option<Self::Item> {
519        while let Some(chunk_handle) = self.current_chunk {
520            if let Some(chunk) = self.arena.chunks.get(chunk_handle.0) {
521                // Search current chunk for next occupied bucket
522                while self.current_bucket_idx < CHUNK_SIZE {
523                    let bucket = &chunk.buckets[self.current_bucket_idx];
524                    self.current_bucket_idx += 1;
525
526                    if let BucketKind::Occupied { key, value } = bucket.kind {
527                        return Some((key, value));
528                    }
529                }
530
531                // Move to next chunk
532                self.current_chunk = chunk.next_chunk;
533                self.current_bucket_idx = 0;
534            } else {
535                break;
536            }
537        }
538
539        None
540    }
541}
542
543#[cfg(test)]
544mod tests {
545    use super::*;
546
547    fn create_test_value(n: i32) -> Value {
548        Value::number(n as f64)
549    }
550
551    #[test]
552    fn test_new_hashmap() {
553        let mut arena = HashMapArena::new();
554        let handle = arena.new_hashmap();
555
556        assert_eq!(arena.len(handle), 0);
557        assert!(arena.is_empty(handle));
558    }
559
560    #[test]
561    fn test_insert_and_get() {
562        let mut arena = HashMapArena::new();
563        let handle = arena.new_hashmap();
564
565        let key1 = create_test_value(1);
566        let value1 = create_test_value(100);
567
568        // Insert first key-value pair
569        let old_value = arena.insert(handle, key1, value1);
570        assert!(old_value.is_none());
571        assert_eq!(arena.len(handle), 1);
572
573        // Get the value back
574        let retrieved = arena.get(handle, &key1);
575        assert_eq!(retrieved, Some(value1));
576    }
577
578    #[test]
579    fn test_insert_duplicate_key() {
580        let mut arena = HashMapArena::new();
581        let handle = arena.new_hashmap();
582
583        let key = create_test_value(1);
584        let value1 = create_test_value(100);
585        let value2 = create_test_value(200);
586
587        // Insert first value
588        arena.insert(handle, key, value1);
589        assert_eq!(arena.len(handle), 1);
590
591        // Insert same key with different value
592        let old_value = arena.insert(handle, key, value2);
593        assert_eq!(old_value, Some(value1));
594        assert_eq!(arena.len(handle), 1);
595
596        // Verify new value is stored
597        assert_eq!(arena.get(handle, &key), Some(value2));
598    }
599
600    #[test]
601    fn test_multiple_insertions() {
602        let mut arena = HashMapArena::new();
603        let handle = arena.new_hashmap();
604
605        for i in 0..10 {
606            let key = create_test_value(i);
607            let value = create_test_value(i * 10);
608            arena.insert(handle, key, value);
609        }
610
611        assert_eq!(arena.len(handle), 10);
612
613        // Verify all values can be retrieved
614        for i in 0..10 {
615            let key = create_test_value(i);
616            let expected_value = create_test_value(i * 10);
617            assert_eq!(arena.get(handle, &key), Some(expected_value));
618        }
619    }
620
621    #[test]
622    fn test_remove() {
623        let mut arena = HashMapArena::new();
624        let handle = arena.new_hashmap();
625
626        let key = create_test_value(1);
627        let value = create_test_value(100);
628
629        // Insert and then remove
630        arena.insert(handle, key, value);
631        assert_eq!(arena.len(handle), 1);
632
633        let removed_value = arena.remove(handle, &key);
634        assert_eq!(removed_value, Some(value));
635        assert_eq!(arena.len(handle), 0);
636        assert!(arena.is_empty(handle));
637
638        // Try to get removed key
639        assert_eq!(arena.get(handle, &key), None);
640    }
641
642    #[test]
643    fn test_remove_nonexistent_key() {
644        let mut arena = HashMapArena::new();
645        let handle = arena.new_hashmap();
646
647        let key = create_test_value(1);
648        let removed = arena.remove(handle, &key);
649        assert!(removed.is_none());
650        assert_eq!(arena.len(handle), 0);
651    }
652
653    #[test]
654    fn test_get_nonexistent_key() {
655        let mut arena = HashMapArena::new();
656        let handle = arena.new_hashmap();
657
658        let key = create_test_value(1);
659        let result = arena.get(handle, &key);
660        assert!(result.is_none());
661    }
662
663    #[test]
664    fn test_linear_probing_collision() {
665        let mut arena = HashMapArena::new();
666        let handle = arena.new_hashmap_with_capacity(4); // Small capacity to force collisions
667
668        // Insert several different values - some will collide in a small hashmap
669        let key1 = create_test_value(1);
670        let key2 = create_test_value(2);
671        let key3 = create_test_value(3);
672        let value1 = create_test_value(100);
673        let value2 = create_test_value(200);
674        let value3 = create_test_value(300);
675
676        arena.insert(handle, key1, value1);
677        arena.insert(handle, key2, value2);
678        arena.insert(handle, key3, value3);
679
680        assert_eq!(arena.len(handle), 3);
681        assert_eq!(arena.get(handle, &key1), Some(value1));
682        assert_eq!(arena.get(handle, &key2), Some(value2));
683        assert_eq!(arena.get(handle, &key3), Some(value3));
684    }
685
686    #[test]
687    fn test_resize_on_load_factor() {
688        let mut arena = HashMapArena::new();
689        let handle = arena.new_hashmap_with_capacity(4);
690
691        // Insert enough items to trigger resize (load factor > 0.75)
692        for i in 0..6 {
693            let key = create_test_value(i);
694            let value = create_test_value(i * 10);
695            arena.insert(handle, key, value);
696        }
697
698        assert_eq!(arena.len(handle), 6);
699
700        // Verify capacity has increased and all values are still accessible
701        for i in 0..6 {
702            let key = create_test_value(i);
703            let expected_value = create_test_value(i * 10);
704            assert_eq!(arena.get(handle, &key), Some(expected_value));
705        }
706
707        // Verify capacity has increased
708        if let Some(hashmap) = arena.hashmaps.get(handle) {
709            assert!(hashmap.capacity > 4);
710        }
711    }
712
713    #[test]
714    fn test_iterator() {
715        let mut arena = HashMapArena::new();
716        let handle = arena.new_hashmap();
717
718        // Insert test data
719        let test_pairs = vec![
720            (create_test_value(1), create_test_value(10)),
721            (create_test_value(2), create_test_value(20)),
722            (create_test_value(3), create_test_value(30)),
723        ];
724
725        for (key, value) in &test_pairs {
726            arena.insert(handle, *key, *value);
727        }
728
729        // Collect iterator results
730        let mut collected: Vec<_> = arena.iter(handle).collect();
731        collected.sort_by_key(|(k, _)| match k.as_number() {
732            Some(n) => n as i32,
733            _ => 0,
734        });
735
736        assert_eq!(collected.len(), 3);
737        assert_eq!(collected[0], (create_test_value(1), create_test_value(10)));
738        assert_eq!(collected[1], (create_test_value(2), create_test_value(20)));
739        assert_eq!(collected[2], (create_test_value(3), create_test_value(30)));
740    }
741
742    #[test]
743    fn test_iterator_empty_hashmap() {
744        let arena = HashMapArena::new();
745        let handle = HashMapHandle::default();
746
747        let collected: Vec<_> = arena.iter(handle).collect();
748        assert!(collected.is_empty());
749    }
750
751    #[test]
752    fn test_iterator_after_removals() {
753        let mut arena = HashMapArena::new();
754        let handle = arena.new_hashmap();
755
756        // Insert several items
757        for i in 0..5 {
758            let key = create_test_value(i);
759            let value = create_test_value(i * 10);
760            arena.insert(handle, key, value);
761        }
762
763        // Remove some items
764        arena.remove(handle, &create_test_value(1));
765        arena.remove(handle, &create_test_value(3));
766
767        // Collect remaining items
768        let mut collected: Vec<_> = arena.iter(handle).collect();
769        collected.sort_by_key(|(k, _)| match k.as_number() {
770            Some(n) => n as i32,
771            _ => 0,
772        });
773
774        assert_eq!(collected.len(), 3);
775        assert_eq!(collected[0], (create_test_value(0), create_test_value(0)));
776        assert_eq!(collected[1], (create_test_value(2), create_test_value(20)));
777        assert_eq!(collected[2], (create_test_value(4), create_test_value(40)));
778    }
779
780    #[test]
781    fn test_stress_insertions_and_lookups() {
782        let mut arena = HashMapArena::new();
783        let handle = arena.new_hashmap();
784
785        const NUM_ITEMS: i32 = 100;
786
787        // Insert many items
788        for i in 0..NUM_ITEMS {
789            let key = create_test_value(i);
790            let value = create_test_value(i * 7); // Use different multiplier
791            arena.insert(handle, key, value);
792        }
793
794        assert_eq!(arena.len(handle), NUM_ITEMS as usize);
795
796        // Verify all can be retrieved
797        for i in 0..NUM_ITEMS {
798            let key = create_test_value(i);
799            let expected_value = create_test_value(i * 7);
800            assert_eq!(arena.get(handle, &key), Some(expected_value));
801        }
802
803        // Remove half
804        for i in 0..NUM_ITEMS / 2 {
805            let key = create_test_value(i * 2); // Remove even numbers
806            arena.remove(handle, &key);
807        }
808
809        assert_eq!(arena.len(handle), (NUM_ITEMS - NUM_ITEMS / 2) as usize);
810
811        // Verify remaining items
812        for i in 0..NUM_ITEMS {
813            let key = create_test_value(i);
814            let expected_value = create_test_value(i * 7);
815
816            if i % 2 == 0 && i < NUM_ITEMS {
817                // Even numbers should be removed
818                assert_eq!(arena.get(handle, &key), None);
819            } else {
820                // Odd numbers should still exist
821                assert_eq!(arena.get(handle, &key), Some(expected_value));
822            }
823        }
824    }
825
826    #[test]
827    fn test_different_value_types() {
828        let mut arena = HashMapArena::new();
829        let handle = arena.new_hashmap();
830
831        // Test different value types
832        arena.insert(handle, Value::nil(), Value::boolean(true));
833        arena.insert(handle, Value::boolean(false), Value::number(42.0));
834        arena.insert(handle, Value::number(123.0), Value::nil());
835
836        assert_eq!(arena.get(handle, &Value::nil()), Some(Value::boolean(true)));
837        assert_eq!(
838            arena.get(handle, &Value::boolean(false)),
839            Some(Value::number(42.0))
840        );
841        assert_eq!(arena.get(handle, &Value::number(123.0)), Some(Value::nil()));
842
843        assert_eq!(arena.len(handle), 3);
844    }
845
846    #[test]
847    fn test_delete_hashmap() {
848        let mut arena = HashMapArena::new();
849        let handle = arena.new_hashmap();
850
851        // Insert some data
852        for i in 0..5 {
853            let key = create_test_value(i);
854            let value = create_test_value(i * 10);
855            arena.insert(handle, key, value);
856        }
857
858        assert_eq!(arena.len(handle), 5);
859        assert!(!arena.is_empty(handle));
860
861        // Delete the hashmap
862        let deleted = arena.delete_hashmap(handle);
863        assert!(deleted);
864
865        // Verify hashmap is gone - operations should return default/empty values
866        assert_eq!(arena.len(handle), 0);
867        assert!(arena.is_empty(handle));
868        assert_eq!(arena.get(handle, &create_test_value(0)), None);
869    }
870
871    #[test]
872    fn test_delete_nonexistent_hashmap() {
873        let mut arena = HashMapArena::new();
874        let invalid_handle = HashMapHandle::default();
875
876        // Try to delete a hashmap that doesn't exist
877        let deleted = arena.delete_hashmap(invalid_handle);
878        assert!(!deleted);
879    }
880
881    #[test]
882    fn test_delete_hashmap_with_multiple_chunks() {
883        let mut arena = HashMapArena::new();
884        let handle = arena.new_hashmap_with_capacity(4); // Small capacity to force multiple chunks
885
886        // Insert enough data to create multiple chunks
887        for i in 0..20 {
888            let key = create_test_value(i);
889            let value = create_test_value(i * 10);
890            arena.insert(handle, key, value);
891        }
892
893        let initial_chunk_count = arena.chunks.len();
894        assert!(initial_chunk_count > 1); // Should have multiple chunks
895
896        // Delete the hashmap
897        let deleted = arena.delete_hashmap(handle);
898        assert!(deleted);
899
900        // Verify all chunks associated with this hashmap are cleaned up
901        // Note: The exact chunk count depends on internal implementation,
902        // but it should be significantly reduced
903        assert_eq!(arena.len(handle), 0);
904        assert!(arena.is_empty(handle));
905    }
906
907    #[test]
908    fn test_delete_hashmap_preserves_other_hashmaps() {
909        let mut arena = HashMapArena::new();
910        let handle1 = arena.new_hashmap();
911        let handle2 = arena.new_hashmap();
912
913        // Insert data in both hashmaps
914        for i in 0..3 {
915            let key = create_test_value(i);
916            let value1 = create_test_value(i * 10);
917            let value2 = create_test_value(i * 20);
918
919            arena.insert(handle1, key, value1);
920            arena.insert(handle2, key, value2);
921        }
922
923        assert_eq!(arena.len(handle1), 3);
924        assert_eq!(arena.len(handle2), 3);
925
926        // Delete first hashmap
927        let deleted = arena.delete_hashmap(handle1);
928        assert!(deleted);
929
930        // Verify first hashmap is gone
931        assert_eq!(arena.len(handle1), 0);
932        assert!(arena.is_empty(handle1));
933        assert_eq!(arena.get(handle1, &create_test_value(0)), None);
934
935        // Verify second hashmap is unaffected
936        assert_eq!(arena.len(handle2), 3);
937        assert!(!arena.is_empty(handle2));
938        for i in 0..3 {
939            let key = create_test_value(i);
940            let expected_value = create_test_value(i * 20);
941            assert_eq!(arena.get(handle2, &key), Some(expected_value));
942        }
943    }
944
945    #[test]
946    fn test_delete_hashmap_memory_efficiency() {
947        let mut arena = HashMapArena::new();
948        let handle = arena.new_hashmap();
949
950        // Insert substantial data
951        for i in 0..50 {
952            let key = create_test_value(i);
953            let value = create_test_value(i * 10);
954            arena.insert(handle, key, value);
955        }
956
957        let bytes_before = arena.get_allocated_bytes();
958        assert!(bytes_before > 0);
959
960        // Delete the hashmap
961        arena.delete_hashmap(handle);
962
963        let bytes_after = arena.get_allocated_bytes();
964
965        // Memory usage should be significantly reduced
966        // (May not be zero due to arena implementation details)
967        assert!(bytes_after < bytes_before);
968    }
969
970    #[test]
971    fn test_garbage_collection() {
972        let mut arena = HashMapArena::new();
973
974        // Create several hashmaps
975        let handle1 = arena.new_hashmap();
976        let handle2 = arena.new_hashmap();
977        let handle3 = arena.new_hashmap();
978
979        // Insert data in all hashmaps
980        for i in 0..5 {
981            let key = create_test_value(i);
982            let value = create_test_value(i * 10);
983            arena.insert(handle1, key, value);
984            arena.insert(handle2, key, value);
985            arena.insert(handle3, key, value);
986        }
987
988        assert_eq!(arena.len(handle1), 5);
989        assert_eq!(arena.len(handle2), 5);
990        assert_eq!(arena.len(handle3), 5);
991
992        // Mark only handle1 and handle2 as reachable
993        arena.mark_hashmap(handle1);
994        arena.mark_hashmap(handle2);
995        // handle3 remains unmarked
996
997        // Run garbage collection
998        arena.collect_garbage();
999
1000        // Verify handle1 and handle2 still exist and work
1001        assert_eq!(arena.len(handle1), 5);
1002        assert_eq!(arena.len(handle2), 5);
1003        assert_eq!(
1004            arena.get(handle1, &create_test_value(0)),
1005            Some(create_test_value(0))
1006        );
1007        assert_eq!(
1008            arena.get(handle2, &create_test_value(0)),
1009            Some(create_test_value(0))
1010        );
1011
1012        // Verify handle3 was garbage collected
1013        assert_eq!(arena.len(handle3), 0);
1014        assert!(arena.is_empty(handle3));
1015        assert_eq!(arena.get(handle3, &create_test_value(0)), None);
1016    }
1017
1018    #[test]
1019    fn test_garbage_collection_resets_marks() {
1020        let mut arena = HashMapArena::new();
1021        let handle = arena.new_hashmap();
1022
1023        // Insert some data
1024        arena.insert(handle, create_test_value(1), create_test_value(10));
1025
1026        // Mark the hashmap
1027        arena.mark_hashmap(handle);
1028
1029        // Verify it's marked (internal state check)
1030        if let Some(hashmap) = arena.hashmaps.get(handle) {
1031            assert!(hashmap.is_marked);
1032        }
1033
1034        // Run garbage collection
1035        arena.collect_garbage();
1036
1037        // Verify the hashmap still exists but mark is reset
1038        assert_eq!(arena.len(handle), 1);
1039        if let Some(hashmap) = arena.hashmaps.get(handle) {
1040            assert!(!hashmap.is_marked); // Mark should be reset
1041        }
1042    }
1043
1044    #[test]
1045    fn test_copy_into() {
1046        let mut arena = HashMapArena::new();
1047        let source_handle = arena.new_hashmap();
1048        let dest_handle = arena.new_hashmap();
1049
1050        // Insert data into source hashmap
1051        for i in 0..5 {
1052            let key = create_test_value(i);
1053            let value = create_test_value(i * 10);
1054            arena.insert(source_handle, key, value);
1055        }
1056
1057        // Insert some existing data into destination (should be overwritten)
1058        arena.insert(dest_handle, create_test_value(0), create_test_value(999));
1059        arena.insert(dest_handle, create_test_value(10), create_test_value(100));
1060
1061        assert_eq!(arena.len(source_handle), 5);
1062        assert_eq!(arena.len(dest_handle), 2);
1063
1064        // Copy source into destination
1065        arena.copy_into(source_handle, dest_handle);
1066
1067        // Verify destination contains all source data
1068        assert_eq!(arena.len(dest_handle), 6); // 5 from source + 1 unique from dest
1069
1070        // Check source data was copied
1071        for i in 0..5 {
1072            let key = create_test_value(i);
1073            let expected_value = create_test_value(i * 10);
1074            assert_eq!(arena.get(dest_handle, &key), Some(expected_value));
1075        }
1076
1077        // Check existing key was overwritten
1078        assert_eq!(
1079            arena.get(dest_handle, &create_test_value(0)),
1080            Some(create_test_value(0))
1081        );
1082
1083        // Check unique destination data is preserved
1084        assert_eq!(
1085            arena.get(dest_handle, &create_test_value(10)),
1086            Some(create_test_value(100))
1087        );
1088
1089        // Verify source hashmap is unchanged
1090        assert_eq!(arena.len(source_handle), 5);
1091        for i in 0..5 {
1092            let key = create_test_value(i);
1093            let expected_value = create_test_value(i * 10);
1094            assert_eq!(arena.get(source_handle, &key), Some(expected_value));
1095        }
1096    }
1097
1098    #[test]
1099    fn test_copy_into_empty_source() {
1100        let mut arena = HashMapArena::new();
1101        let source_handle = arena.new_hashmap();
1102        let dest_handle = arena.new_hashmap();
1103
1104        // Insert data only into destination
1105        arena.insert(dest_handle, create_test_value(1), create_test_value(10));
1106
1107        assert_eq!(arena.len(source_handle), 0);
1108        assert_eq!(arena.len(dest_handle), 1);
1109
1110        // Copy empty source into destination
1111        arena.copy_into(source_handle, dest_handle);
1112
1113        // Destination should be unchanged
1114        assert_eq!(arena.len(dest_handle), 1);
1115        assert_eq!(
1116            arena.get(dest_handle, &create_test_value(1)),
1117            Some(create_test_value(10))
1118        );
1119    }
1120
1121    #[test]
1122    fn test_copy_into_empty_destination() {
1123        let mut arena = HashMapArena::new();
1124        let source_handle = arena.new_hashmap();
1125        let dest_handle = arena.new_hashmap();
1126
1127        // Insert data only into source
1128        for i in 0..3 {
1129            let key = create_test_value(i);
1130            let value = create_test_value(i * 10);
1131            arena.insert(source_handle, key, value);
1132        }
1133
1134        assert_eq!(arena.len(source_handle), 3);
1135        assert_eq!(arena.len(dest_handle), 0);
1136
1137        // Copy source into empty destination
1138        arena.copy_into(source_handle, dest_handle);
1139
1140        // Destination should now contain all source data
1141        assert_eq!(arena.len(dest_handle), 3);
1142        for i in 0..3 {
1143            let key = create_test_value(i);
1144            let expected_value = create_test_value(i * 10);
1145            assert_eq!(arena.get(dest_handle, &key), Some(expected_value));
1146        }
1147    }
1148
1149    #[test]
1150    fn test_copy_into_same_handle() {
1151        let mut arena = HashMapArena::new();
1152        let handle = arena.new_hashmap();
1153
1154        // Insert some data
1155        for i in 0..3 {
1156            let key = create_test_value(i);
1157            let value = create_test_value(i * 10);
1158            arena.insert(handle, key, value);
1159        }
1160
1161        let original_len = arena.len(handle);
1162
1163        // Copy into itself (should be no-op)
1164        arena.copy_into(handle, handle);
1165
1166        // Should be unchanged
1167        assert_eq!(arena.len(handle), original_len);
1168        for i in 0..3 {
1169            let key = create_test_value(i);
1170            let expected_value = create_test_value(i * 10);
1171            assert_eq!(arena.get(handle, &key), Some(expected_value));
1172        }
1173    }
1174
1175    #[test]
1176    fn test_copy_into_invalid_handles() {
1177        let mut arena = HashMapArena::new();
1178        let valid_handle = arena.new_hashmap();
1179        let invalid_handle = HashMapHandle::default();
1180
1181        // Insert data into valid handle
1182        arena.insert(valid_handle, create_test_value(1), create_test_value(10));
1183
1184        // Copy from invalid source - should do nothing
1185        arena.copy_into(invalid_handle, valid_handle);
1186        assert_eq!(arena.len(valid_handle), 1);
1187
1188        // Copy to invalid destination - should do nothing
1189        arena.copy_into(valid_handle, invalid_handle);
1190        assert_eq!(arena.len(valid_handle), 1);
1191        assert_eq!(arena.len(invalid_handle), 0);
1192    }
1193}