31
31
#![ feature( hashmap_hasher) ]
32
32
#![ feature( box_raw) ]
33
33
#![ feature( iter_order) ]
34
+ #![ cfg_attr( test, feature( test) ) ]
34
35
35
36
use std:: borrow:: Borrow ;
36
37
use std:: cmp:: Ordering ;
@@ -57,6 +58,7 @@ struct LinkedHashMapEntry<K, V> {
57
58
pub struct LinkedHashMap < K , V , S = hash_map:: RandomState > {
58
59
map : HashMap < KeyRef < K > , Box < LinkedHashMapEntry < K , V > > , S > ,
59
60
head : * mut LinkedHashMapEntry < K , V > ,
61
+ free : * mut LinkedHashMapEntry < K , V > ,
60
62
}
61
63
62
64
impl < K : Hash > Hash for KeyRef < K > {
@@ -100,6 +102,13 @@ impl<K, V> LinkedHashMapEntry<K, V> {
100
102
}
101
103
}
102
104
105
+ unsafe fn drop_empty_entry_box < K , V > ( the_box : * mut LinkedHashMapEntry < K , V > ) {
106
+ // Prevent compiler from trying to drop the un-initialized key and values in the node.
107
+ let LinkedHashMapEntry { key, value, .. } = * Box :: from_raw ( the_box) ;
108
+ mem:: forget ( key) ;
109
+ mem:: forget ( value) ;
110
+ }
111
+
103
112
impl < K : Hash + Eq , V > LinkedHashMap < K , V > {
104
113
/// Creates a linked hash map.
105
114
pub fn new ( ) -> LinkedHashMap < K , V > { LinkedHashMap :: with_map ( HashMap :: new ( ) ) }
@@ -110,17 +119,27 @@ impl<K: Hash + Eq, V> LinkedHashMap<K, V> {
110
119
}
111
120
}
112
121
122
+ impl < K , V , S > LinkedHashMap < K , V , S > {
123
+ fn clear_free_list ( & mut self ) {
124
+ unsafe {
125
+ let mut free = self . free ;
126
+ while ! free. is_null ( ) {
127
+ let next_free = ( * free) . next ;
128
+ drop_empty_entry_box ( free) ;
129
+ free = next_free;
130
+ }
131
+ self . free = ptr:: null_mut ( ) ;
132
+ }
133
+ }
134
+ }
135
+
113
136
impl < K : Hash + Eq , V , S : HashState > LinkedHashMap < K , V , S > {
114
137
fn with_map ( map : HashMap < KeyRef < K > , Box < LinkedHashMapEntry < K , V > > , S > ) -> LinkedHashMap < K , V , S > {
115
- let map = LinkedHashMap {
138
+ LinkedHashMap {
116
139
map : map,
117
- head : unsafe { Box :: into_raw ( Box :: new ( mem:: uninitialized ( ) ) ) } ,
118
- } ;
119
- unsafe {
120
- ( * map. head ) . next = map. head ;
121
- ( * map. head ) . prev = map. head ;
140
+ head : ptr:: null_mut ( ) ,
141
+ free : ptr:: null_mut ( ) ,
122
142
}
123
- return map;
124
143
}
125
144
126
145
/// Creates an empty linked hash map with the given initial hash state.
@@ -144,7 +163,10 @@ impl<K: Hash + Eq, V, S: HashState> LinkedHashMap<K, V, S> {
144
163
/// Shrinks the capacity of the map as much as possible. It will drop down as much as possible
145
164
/// while maintaining the internal rules and possibly leaving some space in accordance with the
146
165
/// resize policy.
147
- pub fn shrink_to_fit ( & mut self ) { self . map . shrink_to_fit ( ) ; }
166
+ pub fn shrink_to_fit ( & mut self ) {
167
+ self . map . shrink_to_fit ( ) ;
168
+ self . clear_free_list ( ) ;
169
+ }
148
170
149
171
/// Inserts a key-value pair into the map. If the key already existed, the old value is
150
172
/// returned.
@@ -161,14 +183,32 @@ impl<K: Hash + Eq, V, S: HashState> LinkedHashMap<K, V, S> {
161
183
/// assert_eq!(map[&2], "b");
162
184
/// ```
163
185
pub fn insert ( & mut self , k : K , v : V ) -> Option < V > {
186
+ if self . head . is_null ( ) {
187
+ // allocate the guard node if not present
188
+ unsafe {
189
+ self . head = Box :: into_raw ( Box :: new ( mem:: uninitialized ( ) ) ) ;
190
+ ( * self . head ) . next = self . head ;
191
+ ( * self . head ) . prev = self . head ;
192
+ }
193
+ }
164
194
let ( node_ptr, node_opt, old_val) = match self . map . get_mut ( & KeyRef { k : & k} ) {
165
195
Some ( node) => {
166
196
let old_val = mem:: replace ( & mut node. value , v) ;
167
197
let node_ptr: * mut LinkedHashMapEntry < K , V > = & mut * * node;
168
198
( node_ptr, None , Some ( old_val) )
169
199
}
170
200
None => {
171
- let mut node = Box :: new ( LinkedHashMapEntry :: new ( k, v) ) ;
201
+ let mut node = if self . free . is_null ( ) {
202
+ Box :: new ( LinkedHashMapEntry :: new ( k, v) )
203
+ } else {
204
+ // use a recycled box
205
+ unsafe {
206
+ let free = self . free ;
207
+ self . free = ( * free) . next ;
208
+ ptr:: write ( free, LinkedHashMapEntry :: new ( k, v) ) ;
209
+ Box :: from_raw ( free)
210
+ }
211
+ } ;
172
212
let node_ptr: * mut LinkedHashMapEntry < K , V > = & mut * node;
173
213
( node_ptr, Some ( node) , None )
174
214
}
@@ -285,7 +325,15 @@ impl<K: Hash + Eq, V, S: HashState> LinkedHashMap<K, V, S> {
285
325
removed. map ( |mut node| {
286
326
let node_ptr: * mut LinkedHashMapEntry < K , V > = & mut * node;
287
327
self . detach ( node_ptr) ;
288
- node. value
328
+ unsafe {
329
+ // add to free list
330
+ ( * node_ptr) . next = self . free ;
331
+ self . free = node_ptr;
332
+ // forget the box but drop the key and return the value
333
+ mem:: forget ( node) ;
334
+ drop ( ptr:: read ( & ( * node_ptr) . key ) ) ;
335
+ ptr:: read ( & ( * node_ptr) . value )
336
+ }
289
337
} )
290
338
}
291
339
@@ -405,9 +453,12 @@ impl<K: Hash + Eq, V, S: HashState> LinkedHashMap<K, V, S> {
405
453
/// Clear the map of all key-value pairs.
406
454
pub fn clear ( & mut self ) {
407
455
self . map . clear ( ) ;
408
- unsafe {
409
- ( * self . head ) . prev = self . head ;
410
- ( * self . head ) . next = self . head ;
456
+ // update the guard node if present
457
+ if ! self . head . is_null ( ) {
458
+ unsafe {
459
+ ( * self . head ) . prev = self . head ;
460
+ ( * self . head ) . next = self . head ;
461
+ }
411
462
}
412
463
}
413
464
@@ -430,8 +481,13 @@ impl<K: Hash + Eq, V, S: HashState> LinkedHashMap<K, V, S> {
430
481
/// assert_eq!(None, iter.next());
431
482
/// ```
432
483
pub fn iter ( & self ) -> Iter < K , V > {
484
+ let head = if ! self . head . is_null ( ) {
485
+ unsafe { ( * self . head ) . prev }
486
+ } else {
487
+ ptr:: null_mut ( )
488
+ } ;
433
489
Iter {
434
- head : unsafe { ( * self . head ) . prev } ,
490
+ head : head,
435
491
tail : self . head ,
436
492
remaining : self . len ( ) ,
437
493
marker : marker:: PhantomData ,
@@ -459,8 +515,13 @@ impl<K: Hash + Eq, V, S: HashState> LinkedHashMap<K, V, S> {
459
515
/// assert_eq!(&17, map.get(&"a").unwrap());
460
516
/// ```
461
517
pub fn iter_mut ( & mut self ) -> IterMut < K , V > {
518
+ let head = if ! self . head . is_null ( ) {
519
+ unsafe { ( * self . head ) . prev }
520
+ } else {
521
+ ptr:: null_mut ( )
522
+ } ;
462
523
IterMut {
463
- head : unsafe { ( * self . head ) . prev } ,
524
+ head : head,
464
525
tail : self . head ,
465
526
remaining : self . len ( ) ,
466
527
marker : marker:: PhantomData ,
@@ -649,10 +710,10 @@ unsafe impl<K: Sync, V: Sync, S: Sync> Sync for LinkedHashMap<K, V, S> {}
649
710
impl < K , V , S > Drop for LinkedHashMap < K , V , S > {
650
711
fn drop ( & mut self ) {
651
712
unsafe {
652
- // Prevent compiler from trying to drop the un-initialized field in the sigil node.
653
- let LinkedHashMapEntry { next : _ , prev : _ , key : k , value : v } = * Box :: from_raw ( self . head ) ;
654
- mem :: forget ( k ) ;
655
- mem :: forget ( v ) ;
713
+ if ! self . head . is_null ( ) {
714
+ drop_empty_entry_box ( self . head ) ;
715
+ }
716
+ self . clear_free_list ( ) ;
656
717
}
657
718
}
658
719
}
@@ -1037,4 +1098,40 @@ mod tests {
1037
1098
assert_eq ! ( map. remove( & Foo ( Bar ( 1 ) ) ) , None ) ;
1038
1099
assert_eq ! ( map. remove( & Foo ( Bar ( 2 ) ) ) , None ) ;
1039
1100
}
1101
+
1102
+ extern crate test;
1103
+
1104
+ #[ bench]
1105
+ fn not_recycled_cycling ( b : & mut test:: Bencher ) {
1106
+ let mut hash_map = LinkedHashMap :: with_capacity ( 1000 ) ;
1107
+ for i in ( 0usize ..1000 ) {
1108
+ hash_map. insert ( i, i) ;
1109
+ }
1110
+ b. iter ( || {
1111
+ for i in ( 0usize ..1000 ) {
1112
+ hash_map. remove ( & i) ;
1113
+ }
1114
+ hash_map. clear_free_list ( ) ;
1115
+ for i in ( 0usize ..1000 ) {
1116
+ hash_map. insert ( i, i) ;
1117
+ }
1118
+ } )
1119
+ }
1120
+
1121
+ #[ bench]
1122
+ fn recycled_cycling ( b : & mut test:: Bencher ) {
1123
+ let mut hash_map = LinkedHashMap :: with_capacity ( 1000 ) ;
1124
+ for i in ( 0usize ..1000 ) {
1125
+ hash_map. insert ( i, i) ;
1126
+ }
1127
+ b. iter ( || {
1128
+ for i in ( 0usize ..1000 ) {
1129
+ hash_map. remove ( & i) ;
1130
+ }
1131
+ for i in ( 0usize ..1000 ) {
1132
+ hash_map. insert ( i, i) ;
1133
+ }
1134
+ } )
1135
+ }
1136
+
1040
1137
}
0 commit comments