@@ -37,34 +37,26 @@ impl<T> fmt::Debug for Dict<T> {
37
37
}
38
38
}
39
39
40
- #[ derive( Debug , Copy , Clone ) ]
41
- enum IndexEntry {
42
- Dummy ,
43
- Free ,
44
- Index ( usize ) ,
45
- }
40
+ #[ derive( Debug , Copy , Clone , PartialEq , Eq ) ]
41
+ #[ repr( transparent) ]
42
+ struct IndexEntry ( i64 ) ;
46
43
47
44
impl IndexEntry {
48
- const FREE : i64 = -1 ;
49
- const DUMMY : i64 = -2 ;
50
- }
45
+ const FREE : Self = Self ( -1 ) ;
46
+ const DUMMY : Self = Self ( -2 ) ;
51
47
52
- impl From < i64 > for IndexEntry {
53
- fn from ( idx : i64 ) -> Self {
54
- match idx {
55
- IndexEntry :: FREE => IndexEntry :: Free ,
56
- IndexEntry :: DUMMY => IndexEntry :: Dummy ,
57
- x => IndexEntry :: Index ( x as usize ) ,
58
- }
48
+ /// # Safety
49
+ /// idx must not be one of FREE or DUMMY
50
+ unsafe fn from_index_unchecked ( idx : usize ) -> Self {
51
+ debug_assert ! ( ( idx as isize ) >= 0 ) ;
52
+ Self ( idx as i64 )
59
53
}
60
- }
61
54
62
- impl From < IndexEntry > for i64 {
63
- fn from ( idx : IndexEntry ) -> Self {
64
- match idx {
65
- IndexEntry :: Free => IndexEntry :: FREE ,
66
- IndexEntry :: Dummy => IndexEntry :: DUMMY ,
67
- IndexEntry :: Index ( i) => i as i64 ,
55
+ fn index ( self ) -> Option < usize > {
56
+ if self . 0 >= 0 {
57
+ Some ( self . 0 as usize )
58
+ } else {
59
+ None
68
60
}
69
61
}
70
62
}
@@ -73,10 +65,8 @@ impl From<IndexEntry> for i64 {
73
65
struct DictInner < T > {
74
66
used : usize ,
75
67
filled : usize ,
76
- indices : Vec < i64 > ,
68
+ indices : Vec < IndexEntry > ,
77
69
entries : Vec < Option < DictEntry < T > > > ,
78
- // index to new inserted element should be in entries
79
- next_new_entry_idx : usize ,
80
70
}
81
71
82
72
impl < T : Clone > Clone for Dict < T > {
@@ -95,7 +85,6 @@ impl<T> Default for Dict<T> {
95
85
filled : 0 ,
96
86
indices : vec ! [ IndexEntry :: FREE ; 8 ] ,
97
87
entries : Vec :: new ( ) ,
98
- next_new_entry_idx : 0 ,
99
88
} ) ,
100
89
}
101
90
}
@@ -108,6 +97,7 @@ struct DictEntry<T> {
108
97
index : IndexIndex ,
109
98
value : T ,
110
99
}
100
+ static_assertions:: assert_eq_size!( DictEntry <PyObjectRef >, Option <DictEntry <PyObjectRef >>) ;
111
101
112
102
impl < T : Clone > DictEntry < T > {
113
103
pub ( crate ) fn as_tuple ( & self ) -> ( PyObjectRef , T ) {
@@ -175,19 +165,23 @@ impl<T> DictInner<T> {
175
165
let mut idxs = GenIndexes :: new ( entry. hash , mask) ;
176
166
loop {
177
167
let index_index = idxs. next ( ) ;
178
- let idx = & mut self . indices [ index_index] ;
179
- if * idx == IndexEntry :: FREE {
180
- * idx = entry_idx as i64 ;
181
- entry. index = index_index;
182
- break ;
168
+ unsafe {
169
+ // Safety: index is always valid here
170
+ // index_index is generated by idxs
171
+ // entry_idx is saved one
172
+ let idx = self . indices . get_unchecked_mut ( index_index) ;
173
+ if * idx == IndexEntry :: FREE {
174
+ * idx = IndexEntry :: from_index_unchecked ( entry_idx) ;
175
+ entry. index = index_index;
176
+ break ;
177
+ }
183
178
}
184
179
}
185
180
} else {
186
181
//removed entry
187
182
}
188
183
}
189
184
self . filled = self . used ;
190
- self . next_new_entry_idx = self . entries . len ( ) ;
191
185
}
192
186
193
187
fn unchecked_push (
@@ -204,16 +198,15 @@ impl<T> DictInner<T> {
204
198
value,
205
199
index,
206
200
} ;
207
- let entry_index = self . next_new_entry_idx ;
208
- if self . entries . len ( ) == entry_index {
209
- self . entries . push ( Some ( entry ) ) ;
210
- } else {
211
- self . entries [ entry_index ] = Some ( entry ) ;
212
- }
213
- self . indices [ index ] = entry_index as i64 ;
201
+ let entry_index = self . entries . len ( ) ;
202
+ self . entries . push ( Some ( entry ) ) ;
203
+ self . indices [ index ] = unsafe {
204
+ // SAFETY: entry_index is self.entries.len(). it never can
205
+ // grow to `usize-2` because hash tables cannot full its index
206
+ IndexEntry :: from_index_unchecked ( entry_index )
207
+ } ;
214
208
self . used += 1 ;
215
- self . next_new_entry_idx += 1 ;
216
- if let IndexEntry :: Free = index_entry {
209
+ if let IndexEntry :: FREE = index_entry {
217
210
self . filled += 1 ;
218
211
if let Some ( new_size) = self . should_resize ( ) {
219
212
self . resize ( new_size)
@@ -260,7 +253,7 @@ impl<T: Clone> Dict<T> {
260
253
let _removed = loop {
261
254
let ( entry_index, index_index) = self . lookup ( vm, key, hash, None ) ?;
262
255
let mut inner = self . write ( ) ;
263
- if let IndexEntry :: Index ( index) = entry_index {
256
+ if let Some ( index) = entry_index. index ( ) {
264
257
// Update existing key
265
258
if let Some ( entry) = inner. entries . get_mut ( index) {
266
259
let entry = entry
@@ -287,7 +280,7 @@ impl<T: Clone> Dict<T> {
287
280
288
281
pub fn contains < K : DictKey + ?Sized > ( & self , vm : & VirtualMachine , key : & K ) -> PyResult < bool > {
289
282
let ( entry, _) = self . lookup ( vm, key, key. key_hash ( vm) ?, None ) ?;
290
- Ok ( matches ! ( entry, IndexEntry :: Index ( _ ) ) )
283
+ Ok ( entry. index ( ) . is_some ( ) )
291
284
}
292
285
293
286
/// Retrieve a key
@@ -305,7 +298,7 @@ impl<T: Clone> Dict<T> {
305
298
) -> PyResult < Option < T > > {
306
299
let ret = loop {
307
300
let ( entry, index_index) = self . lookup ( vm, key, hash, None ) ?;
308
- if let IndexEntry :: Index ( index) = entry {
301
+ if let Some ( index) = entry. index ( ) {
309
302
let inner = self . read ( ) ;
310
303
if let Some ( entry) = inner. entries . get ( index) {
311
304
let entry = extract_dict_entry ( entry) ;
@@ -346,7 +339,6 @@ impl<T: Clone> Dict<T> {
346
339
inner. indices . resize ( 8 , IndexEntry :: FREE ) ;
347
340
inner. used = 0 ;
348
341
inner. filled = 0 ;
349
- inner. next_new_entry_idx = 0 ;
350
342
// defer dec rc
351
343
std:: mem:: take ( & mut inner. entries )
352
344
} ;
@@ -394,7 +386,7 @@ impl<T: Clone> Dict<T> {
394
386
let _removed = loop {
395
387
let lookup = self . lookup ( vm, key, hash, None ) ?;
396
388
let ( entry, index_index) = lookup;
397
- if let IndexEntry :: Index ( _ ) = entry {
389
+ if entry . index ( ) . is_some ( ) {
398
390
match self . pop_inner ( lookup) {
399
391
ControlFlow :: Break ( Some ( entry) ) => break Some ( entry) ,
400
392
_ => continue ,
@@ -416,8 +408,8 @@ impl<T: Clone> Dict<T> {
416
408
let hash = key. key_hash ( vm) ?;
417
409
let res = loop {
418
410
let lookup = self . lookup ( vm, key, hash, None ) ?;
419
- let ( entry , index_index) = lookup;
420
- if let IndexEntry :: Index ( index) = entry {
411
+ let ( index_entry , index_index) = lookup;
412
+ if let Some ( index) = index_entry . index ( ) {
421
413
let inner = self . read ( ) ;
422
414
if let Some ( entry) = inner. entries . get ( index) {
423
415
let entry = extract_dict_entry ( entry) ;
@@ -433,7 +425,13 @@ impl<T: Clone> Dict<T> {
433
425
} else {
434
426
let value = default ( ) ;
435
427
let mut inner = self . write ( ) ;
436
- inner. unchecked_push ( index_index, hash, key. to_pyobject ( vm) , value. clone ( ) , entry) ;
428
+ inner. unchecked_push (
429
+ index_index,
430
+ hash,
431
+ key. to_pyobject ( vm) ,
432
+ value. clone ( ) ,
433
+ index_entry,
434
+ ) ;
437
435
break value;
438
436
}
439
437
} ;
@@ -454,8 +452,8 @@ impl<T: Clone> Dict<T> {
454
452
let hash = key. key_hash ( vm) ?;
455
453
let res = loop {
456
454
let lookup = self . lookup ( vm, key, hash, None ) ?;
457
- let ( entry , index_index) = lookup;
458
- if let IndexEntry :: Index ( index) = entry {
455
+ let ( index_entry , index_index) = lookup;
456
+ if let Some ( index) = index_entry . index ( ) {
459
457
let inner = self . read ( ) ;
460
458
if let Some ( entry) = inner. entries . get ( index) {
461
459
let entry = extract_dict_entry ( entry) ;
@@ -473,7 +471,7 @@ impl<T: Clone> Dict<T> {
473
471
let key = key. to_pyobject ( vm) ;
474
472
let mut inner = self . write ( ) ;
475
473
let ret = ( key. clone ( ) , value. clone ( ) ) ;
476
- inner. unchecked_push ( index_index, hash, key, value, entry ) ;
474
+ inner. unchecked_push ( index_index, hash, key, value, index_entry ) ;
477
475
break ret;
478
476
}
479
477
} ;
@@ -541,7 +539,7 @@ impl<T: Clone> Dict<T> {
541
539
mut lock : Option < PyRwLockReadGuard < DictInner < T > > > ,
542
540
) -> PyResult < LookupResult > {
543
541
let mut idxs = None ;
544
- let mut freeslot = None ;
542
+ let mut free_slot = None ;
545
543
let ret = ' outer: loop {
546
544
let ( entry_key, ret) = {
547
545
let inner = lock. take ( ) . unwrap_or_else ( || self . read ( ) ) ;
@@ -550,22 +548,31 @@ impl<T: Clone> Dict<T> {
550
548
} ) ;
551
549
loop {
552
550
let index_index = idxs. next ( ) ;
553
- match IndexEntry :: from ( inner. indices [ index_index] ) {
554
- IndexEntry :: Dummy => {
555
- if freeslot. is_none ( ) {
556
- freeslot = Some ( index_index) ;
551
+ let index_entry = * unsafe {
552
+ // Safety: index_index is generated
553
+ inner. indices . get_unchecked ( index_index)
554
+ } ;
555
+ match index_entry {
556
+ IndexEntry :: DUMMY => {
557
+ if free_slot. is_none ( ) {
558
+ free_slot = Some ( index_index) ;
557
559
}
558
560
}
559
- IndexEntry :: Free => {
560
- let idxs = match freeslot {
561
- Some ( free) => ( IndexEntry :: Dummy , free) ,
562
- None => ( IndexEntry :: Free , index_index) ,
561
+ IndexEntry :: FREE => {
562
+ let idxs = match free_slot {
563
+ Some ( free) => ( IndexEntry :: DUMMY , free) ,
564
+ None => ( IndexEntry :: FREE , index_index) ,
563
565
} ;
564
566
return Ok ( idxs) ;
565
567
}
566
- IndexEntry :: Index ( i) => {
567
- let entry = & inner. entries [ i] . as_ref ( ) . unwrap ( ) ;
568
- let ret = ( IndexEntry :: Index ( i) , index_index) ;
568
+ idx => {
569
+ let entry = unsafe {
570
+ // Safety: DUMMY and FREE are already handled above.
571
+ // i is always valid and entry always exists.
572
+ let i = idx. index ( ) . unwrap_unchecked ( ) ;
573
+ inner. entries . get_unchecked ( i) . as_ref ( ) . unwrap_unchecked ( )
574
+ } ;
575
+ let ret = ( idx, index_index) ;
569
576
if key. key_is ( & entry. key ) {
570
577
break ' outer ret;
571
578
} else if entry. hash == hash_value {
@@ -602,7 +609,7 @@ impl<T: Clone> Dict<T> {
602
609
pred : impl Fn ( & T ) -> Result < bool , E > ,
603
610
) -> Result < PopInnerResult < T > , E > {
604
611
let ( entry_index, index_index) = lookup;
605
- let entry_index = if let IndexEntry :: Index ( entry_index) = entry_index {
612
+ let entry_index = if let Some ( entry_index) = entry_index. index ( ) {
606
613
entry_index
607
614
} else {
608
615
return Ok ( ControlFlow :: Break ( None ) ) ;
@@ -623,7 +630,10 @@ impl<T: Clone> Dict<T> {
623
630
// The dict was changed since we did lookup. Let's try again.
624
631
_ => return Ok ( ControlFlow :: Continue ( ( ) ) ) ,
625
632
}
626
- inner. indices [ index_index] = IndexEntry :: DUMMY ;
633
+ * unsafe {
634
+ // index_index is result of lookup
635
+ inner. indices . get_unchecked_mut ( index_index)
636
+ } = IndexEntry :: DUMMY ;
627
637
inner. used -= 1 ;
628
638
let removed = slot. take ( ) ;
629
639
Ok ( ControlFlow :: Break ( removed) )
@@ -644,14 +654,17 @@ impl<T: Clone> Dict<T> {
644
654
645
655
pub fn pop_back ( & self ) -> Option < ( PyObjectRef , T ) > {
646
656
let mut inner = & mut * self . write ( ) ;
647
- let ( entry_idx, entry) = inner. entries [ ..inner. next_new_entry_idx ]
648
- . iter_mut ( )
649
- . enumerate ( )
650
- . rev ( )
651
- . find_map ( |( i, entry) | entry. take ( ) . map ( |e| ( i, e) ) ) ?;
657
+ let entry = loop {
658
+ let entry = inner. entries . pop ( ) ?;
659
+ if let Some ( entry) = entry {
660
+ break entry;
661
+ }
662
+ } ;
652
663
inner. used -= 1 ;
653
- inner. indices [ entry. index ] = IndexEntry :: DUMMY ;
654
- inner. next_new_entry_idx = entry_idx;
664
+ * unsafe {
665
+ // entry.index always refers valid index
666
+ inner. indices . get_unchecked_mut ( entry. index )
667
+ } = IndexEntry :: DUMMY ;
655
668
Some ( ( entry. key , entry. value ) )
656
669
}
657
670
0 commit comments