1
1
use std:: collections:: { BTreeMap , HashMap } ;
2
2
use std:: hash:: { Hash , Hasher } ;
3
+ use std:: mem;
3
4
use std:: ops:: { Index , IndexMut } ;
4
5
5
6
use crate :: as_bytes:: AsBytes ;
6
7
use crate :: entry:: { Entry , OccupiedEntry , VacantEntry } ;
7
8
use crate :: iter:: { DrainIter , Iter , Keys , PrefixIter , PrefixKeys , PrefixValues , Values } ;
8
9
use crate :: node:: { clear_bit, popcount, set_bit, test_bit, TrieNode } ;
10
+ use crate :: slice_pool:: SlicePool ;
9
11
10
12
/// A `TrieMap` is a key-value data structure that uses a trie (prefix tree) for storage
11
13
/// and retrieval of data.
@@ -55,6 +57,7 @@ pub struct TrieMap<T> {
55
57
pub ( crate ) free_indices : Vec < usize > ,
56
58
pub ( crate ) root : TrieNode ,
57
59
pub ( crate ) size : usize ,
60
+ pub ( crate ) pool : SlicePool ,
58
61
}
59
62
60
63
impl < T , K : AsBytes , V : Into < T > , const N : usize > From < [ ( K , V ) ; N ] > for TrieMap < T > {
@@ -103,6 +106,7 @@ impl<T: Clone> Clone for TrieMap<T> {
103
106
free_indices : self . free_indices . clone ( ) ,
104
107
root : self . root . clone ( ) ,
105
108
size : self . size ,
109
+ pool : SlicePool :: new ( ) ,
106
110
}
107
111
}
108
112
}
@@ -241,6 +245,7 @@ impl<T> TrieMap<T> {
241
245
free_indices : Vec :: new ( ) ,
242
246
root : TrieNode :: new ( ) ,
243
247
size : 0 ,
248
+ pool : SlicePool :: new ( ) ,
244
249
}
245
250
}
246
251
@@ -262,6 +267,7 @@ impl<T> TrieMap<T> {
262
267
free_indices : Vec :: new ( ) ,
263
268
root : TrieNode :: new ( ) ,
264
269
size : 0 ,
270
+ pool : SlicePool :: new ( ) ,
265
271
}
266
272
}
267
273
@@ -341,19 +347,22 @@ impl<T> TrieMap<T> {
341
347
342
348
if !test_bit ( & current. is_present , byte) {
343
349
let current_size = current. children . len ( ) ;
344
- let mut new_children = Vec :: with_capacity ( current_size + 1 ) ;
350
+ let mut new_children = self . pool . get ( current_size + 1 ) ;
345
351
346
352
for i in 0 ..idx {
347
- new_children. push ( std:: mem:: replace ( & mut current. children [ i] , TrieNode :: new ( ) ) ) ;
353
+ mem:: swap ( & mut new_children[ i] , & mut current. children [ i] ) ;
354
+ //new_children.push(std::mem::replace(&mut current.children[i], TrieNode::new()));
348
355
}
349
356
350
- new_children. push ( TrieNode :: new ( ) ) ;
357
+ new_children[ idx ] = TrieNode :: new ( ) ;
351
358
352
359
for i in idx..current_size {
353
- new_children . push ( std :: mem:: replace ( & mut current. children [ i] , TrieNode :: new ( ) ) ) ;
360
+ mem:: swap ( & mut new_children [ i + 1 ] , & mut current. children [ i] ) ;
354
361
}
355
362
356
- current. children = new_children. into_boxed_slice ( ) ;
363
+ let old_children = mem:: replace ( & mut current. children , new_children) ;
364
+ self . pool . put ( old_children) ;
365
+
357
366
set_bit ( & mut current. is_present , byte) ;
358
367
}
359
368
@@ -669,48 +678,32 @@ impl<T> TrieMap<T> {
669
678
None
670
679
}
671
680
}
672
-
673
- /// Prunes unused nodes from the trie to reclaim memory.
674
- ///
675
- /// This method removes all nodes that don't contain values and don't lead to nodes with values.
676
- /// It's useful to call periodically if you've removed many items from the trie.
677
- ///
678
- /// # Examples
679
- ///
680
- /// ```
681
- /// # use triemap::TrieMap;
682
- /// let mut map = TrieMap::new();
683
- /// map.insert("apple", 1);
684
- /// map.insert("application", 2);
685
- ///
686
- /// map.remove("apple");
687
- /// map.remove("application");
688
- ///
689
- /// // The trie structure still contains nodes for "apple" and "application"
690
- /// // even though the values have been removed
691
- ///
692
- /// map.prune();
693
- /// // Now the unused nodes have been removed
694
- /// ```
695
681
pub fn prune ( & mut self ) -> usize {
696
- Self :: prune_node ( & mut self . root )
682
+ // We need to avoid having two mutable references to self
683
+ // Let's extract the nodes we need separately
684
+ let mut root = std:: mem:: take ( & mut self . root ) ;
685
+ let slice_pool = & mut self . pool ;
686
+
687
+ let pruned = Self :: prune_node_helper ( & mut root, slice_pool) ;
688
+
689
+ // Put the root back
690
+ self . root = root;
691
+
692
+ pruned
697
693
}
698
694
699
- // Helper method to recursively prune nodes
700
- fn prune_node ( node : & mut TrieNode ) -> usize {
695
+ fn prune_node_helper ( node : & mut TrieNode , slice_pool : & mut SlicePool ) -> usize {
701
696
let mut pruned_nodes = 0 ;
702
697
let mut bytes_to_clear = Vec :: new ( ) ;
703
698
704
- // Check each byte in the is_present array
705
699
for byte in 0 ..=255u8 {
706
700
if test_bit ( & node. is_present , byte) {
707
701
let idx = popcount ( & node. is_present , byte) as usize ;
708
702
if idx < node. children . len ( ) {
709
703
// Recursively prune the child node
710
- let child_pruned = Self :: prune_node ( & mut node. children [ idx] ) ;
704
+ let child_pruned = Self :: prune_node_helper ( & mut node. children [ idx] , slice_pool ) ;
711
705
pruned_nodes += child_pruned;
712
706
713
- // Check if the child node is now empty and can be removed
714
707
if node. children [ idx] . data_idx . is_none ( )
715
708
&& node. children [ idx] . children . is_empty ( )
716
709
{
@@ -720,32 +713,62 @@ impl<T> TrieMap<T> {
720
713
}
721
714
}
722
715
723
- // Remove empty children that were marked for removal
724
- for & byte in & bytes_to_clear {
725
- let idx = popcount ( & node. is_present , byte) as usize ;
726
-
727
- // Create a new children array without the empty node
728
- let mut new_children = Vec :: with_capacity ( node. children . len ( ) - 1 ) ;
716
+ if !bytes_to_clear. is_empty ( ) {
717
+ let current_size = node. children . len ( ) ;
718
+ let new_size = current_size - bytes_to_clear. len ( ) ;
729
719
730
- // Copy all children except the one being removed
731
- for i in 0 ..node. children . len ( ) {
732
- if i != idx {
733
- new_children. push ( std:: mem:: replace ( & mut node. children [ i] , TrieNode :: new ( ) ) ) ;
720
+ if new_size == 0 {
721
+ let old_children = std:: mem:: replace ( & mut node. children , Box :: new ( [ ] ) ) ;
722
+ slice_pool. put ( old_children) ;
723
+ } else {
724
+ let mut new_children = slice_pool. get ( new_size) ;
725
+ let mut new_idx = 0 ;
726
+
727
+ for byte in 0 ..=255u8 {
728
+ if test_bit ( & node. is_present , byte) && !bytes_to_clear. contains ( & byte) {
729
+ let idx = popcount ( & node. is_present , byte) as usize ;
730
+ if idx < node. children . len ( ) {
731
+ std:: mem:: swap ( & mut new_children[ new_idx] , & mut node. children [ idx] ) ;
732
+ new_idx += 1 ;
733
+ }
734
+ }
734
735
}
735
- }
736
-
737
- // Update the node's children
738
- node. children = new_children. into_boxed_slice ( ) ;
739
736
740
- // Update the is_present bits - need to clear the bit for the removed node
741
- clear_bit ( & mut node. is_present , byte) ;
737
+ let old_children = std:: mem:: replace ( & mut node. children , new_children) ;
738
+ slice_pool. put ( old_children) ;
739
+ }
742
740
743
- // Update counts
744
- pruned_nodes += 1 ;
741
+ pruned_nodes += bytes_to_clear. len ( ) ;
742
+ for byte in bytes_to_clear {
743
+ clear_bit ( & mut node. is_present , byte) ;
744
+ }
745
745
}
746
746
747
747
pruned_nodes
748
748
}
749
+ /// Prunes unused nodes from the trie to reclaim memory.
750
+ ///
751
+ /// This method removes all nodes that don't contain values and don't lead to nodes with values.
752
+ /// It's useful to call periodically if you've removed many items from the trie.
753
+ ///
754
+ /// # Examples
755
+ ///
756
+ /// ```
757
+ /// # use triemap::TrieMap;
758
+ /// let mut map = TrieMap::new();
759
+ /// map.insert("apple", 1);
760
+ /// map.insert("application", 2);
761
+ ///
762
+ /// map.remove("apple");
763
+ /// map.remove("application");
764
+ ///
765
+ /// // The trie structure still contains nodes for "apple" and "application"
766
+ /// // even though the values have been removed
767
+ ///
768
+ /// map.prune();
769
+ /// // Now the unused nodes have been removed
770
+ /// ```
771
+
749
772
/// Returns an iterator over the key-value pairs of the map.
750
773
///
751
774
/// # Examples
0 commit comments