@@ -182,46 +182,37 @@ impl DefaultResizePolicy {
182
182
// ----------------------
183
183
// To protect against degenerate performance scenarios (including DOS attacks),
184
184
// the implementation includes an adaptive behavior that can resize the map
185
- // early (before its capacity is exceeded) when suspiciously long probe or
186
- // forward shifts sequences are encountered.
185
+ // early (before its capacity is exceeded) when suspiciously long probe sequences
186
+ // are encountered.
187
187
//
188
188
// With this algorithm in place it would be possible to turn a CPU attack into
189
189
// a memory attack due to the aggressive resizing. To prevent that the
190
- // adaptive behavior only triggers when the map occupancy is half the maximum occupancy .
190
+ // adaptive behavior only triggers when the map is at least half full .
191
191
// This reduces the effectiveness of the algorithm but also makes it completely safe.
192
192
//
193
193
// The previous safety measure also prevents degenerate interactions with
194
194
// really bad quality hash algorithms that can make normal inputs look like a
195
195
// DOS attack.
196
196
//
197
197
const DISPLACEMENT_THRESHOLD : usize = 128 ;
198
- const FORWARD_SHIFT_THRESHOLD : usize = 512 ;
199
198
//
200
- // The thresholds of 128 and 512 are chosen to minimize the chance of exceeding them .
199
+ // The threshold of 128 is chosen to minimize the chance of exceeding it .
201
200
// In particular, we want that chance to be less than 10^-8 with a load of 90%.
202
201
// For displacement, the smallest constant that fits our needs is 90,
203
- // so we round that up to 128. For the number of forward-shifted buckets,
204
- // we choose k=512. Keep in mind that the run length is a sum of the displacement and
205
- // the number of forward-shifted buckets, so its threshold is 128+512=640.
206
- // Even though the probability of having a run length of more than 640 buckets may be
207
- // higher than the probability we want, it should be low enough.
202
+ // so we round that up to 128.
208
203
//
209
204
// At a load factor of α, the odds of finding the target bucket after exactly n
210
205
// unsuccesful probes[1] are
211
206
//
212
207
// Pr_α{displacement = n} =
213
208
// (1 - α) / α * ∑_{k≥1} e^(-kα) * (kα)^(k+n) / (k + n)! * (1 - kα / (k + n + 1))
214
209
//
215
- // We use this formula to find the probability of loading half of triggering the adaptive behavior
210
+ // We use this formula to find the probability of triggering the adaptive behavior
216
211
//
217
212
// Pr_0.909{displacement > 128} = 1.601 * 10^-11
218
213
//
219
- // FIXME: Extend with math for shift threshold in [2]
220
- //
221
214
// 1. Alfredo Viola (2005). Distributional analysis of Robin Hood linear probing
222
215
// hashing with buckets.
223
- // 2. http://www.cs.tau.ac.il/~zwick/Adv-Alg-2015/Linear-Probing.pdf
224
-
225
216
226
217
/// A hash map implementation which uses linear probing with Robin Hood bucket
227
218
/// stealing.
@@ -494,7 +485,7 @@ fn robin_hood<'a, K: 'a, V: 'a>(bucket: FullBucketMut<'a, K, V>,
494
485
mut hash : SafeHash ,
495
486
mut key : K ,
496
487
mut val : V )
497
- -> ( usize , & ' a mut V ) {
488
+ -> & ' a mut V {
498
489
let start_index = bucket. index ( ) ;
499
490
let size = bucket. table ( ) . size ( ) ;
500
491
// Save the *starting point*.
@@ -519,15 +510,14 @@ fn robin_hood<'a, K: 'a, V: 'a>(bucket: FullBucketMut<'a, K, V>,
519
510
Empty ( bucket) => {
520
511
// Found a hole!
521
512
let bucket = bucket. put ( hash, key, val) ;
522
- let end_index = bucket. index ( ) ;
523
513
// Now that it's stolen, just read the value's pointer
524
514
// right out of the table! Go back to the *starting point*.
525
515
//
526
516
// This use of `into_table` is misleading. It turns the
527
517
// bucket, which is a FullBucket on top of a
528
518
// FullBucketMut, into just one FullBucketMut. The "table"
529
519
// refers to the inner FullBucketMut in this context.
530
- return ( end_index - start_index , bucket. into_table ( ) . into_mut_refs ( ) . 1 ) ;
520
+ return bucket. into_table ( ) . into_mut_refs ( ) . 1 ;
531
521
}
532
522
Full ( bucket) => bucket,
533
523
} ;
@@ -2128,18 +2118,16 @@ impl<'a, K: 'a, V: 'a> VacantEntry<'a, K, V> {
2128
2118
pub fn insert ( self , value : V ) -> & ' a mut V {
2129
2119
match self . elem {
2130
2120
NeqElem ( bucket, disp) => {
2131
- let ( shift, v_ref) = robin_hood ( bucket, disp, self . hash , self . key , value) ;
2132
- if disp >= DISPLACEMENT_THRESHOLD || shift >= FORWARD_SHIFT_THRESHOLD {
2121
+ if disp >= DISPLACEMENT_THRESHOLD {
2133
2122
* self . long_probes = true ;
2134
2123
}
2135
- v_ref
2124
+ robin_hood ( bucket , disp , self . hash , self . key , value )
2136
2125
} ,
2137
2126
NoElem ( bucket, disp) => {
2138
2127
if disp >= DISPLACEMENT_THRESHOLD {
2139
2128
* self . long_probes = true ;
2140
2129
}
2141
- let bucket = bucket. put ( self . hash , self . key , value) ;
2142
- bucket. into_mut_refs ( ) . 1
2130
+ bucket. put ( self . hash , self . key , value) . into_mut_refs ( ) . 1
2143
2131
} ,
2144
2132
}
2145
2133
}
0 commit comments