@@ -37,34 +37,26 @@ impl<T> fmt::Debug for Dict<T> {
3737 }
3838}
3939
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 ) ;
4643
4744impl IndexEntry {
48- const FREE : i64 = -1 ;
49- const DUMMY : i64 = -2 ;
50- }
45+ const FREE : Self = Self ( -1 ) ;
46+ const DUMMY : Self = Self ( -2 ) ;
5147
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 )
5953 }
60- }
6154
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
6860 }
6961 }
7062}
@@ -73,10 +65,8 @@ impl From<IndexEntry> for i64 {
7365struct DictInner < T > {
7466 used : usize ,
7567 filled : usize ,
76- indices : Vec < i64 > ,
68+ indices : Vec < IndexEntry > ,
7769 entries : Vec < Option < DictEntry < T > > > ,
78- // index to new inserted element should be in entries
79- next_new_entry_idx : usize ,
8070}
8171
8272impl < T : Clone > Clone for Dict < T > {
@@ -95,7 +85,6 @@ impl<T> Default for Dict<T> {
9585 filled : 0 ,
9686 indices : vec ! [ IndexEntry :: FREE ; 8 ] ,
9787 entries : Vec :: new ( ) ,
98- next_new_entry_idx : 0 ,
9988 } ) ,
10089 }
10190 }
@@ -108,6 +97,7 @@ struct DictEntry<T> {
10897 index : IndexIndex ,
10998 value : T ,
11099}
100+ static_assertions:: assert_eq_size!( DictEntry <PyObjectRef >, Option <DictEntry <PyObjectRef >>) ;
111101
112102impl < T : Clone > DictEntry < T > {
113103 pub ( crate ) fn as_tuple ( & self ) -> ( PyObjectRef , T ) {
@@ -175,19 +165,23 @@ impl<T> DictInner<T> {
175165 let mut idxs = GenIndexes :: new ( entry. hash , mask) ;
176166 loop {
177167 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+ }
183178 }
184179 }
185180 } else {
186181 //removed entry
187182 }
188183 }
189184 self . filled = self . used ;
190- self . next_new_entry_idx = self . entries . len ( ) ;
191185 }
192186
193187 fn unchecked_push (
@@ -204,16 +198,15 @@ impl<T> DictInner<T> {
204198 value,
205199 index,
206200 } ;
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+ } ;
214208 self . used += 1 ;
215- self . next_new_entry_idx += 1 ;
216- if let IndexEntry :: Free = index_entry {
209+ if let IndexEntry :: FREE = index_entry {
217210 self . filled += 1 ;
218211 if let Some ( new_size) = self . should_resize ( ) {
219212 self . resize ( new_size)
@@ -260,7 +253,7 @@ impl<T: Clone> Dict<T> {
260253 let _removed = loop {
261254 let ( entry_index, index_index) = self . lookup ( vm, key, hash, None ) ?;
262255 let mut inner = self . write ( ) ;
263- if let IndexEntry :: Index ( index) = entry_index {
256+ if let Some ( index) = entry_index. index ( ) {
264257 // Update existing key
265258 if let Some ( entry) = inner. entries . get_mut ( index) {
266259 let entry = entry
@@ -287,7 +280,7 @@ impl<T: Clone> Dict<T> {
287280
288281 pub fn contains < K : DictKey + ?Sized > ( & self , vm : & VirtualMachine , key : & K ) -> PyResult < bool > {
289282 let ( entry, _) = self . lookup ( vm, key, key. key_hash ( vm) ?, None ) ?;
290- Ok ( matches ! ( entry, IndexEntry :: Index ( _ ) ) )
283+ Ok ( entry. index ( ) . is_some ( ) )
291284 }
292285
293286 /// Retrieve a key
@@ -305,7 +298,7 @@ impl<T: Clone> Dict<T> {
305298 ) -> PyResult < Option < T > > {
306299 let ret = loop {
307300 let ( entry, index_index) = self . lookup ( vm, key, hash, None ) ?;
308- if let IndexEntry :: Index ( index) = entry {
301+ if let Some ( index) = entry. index ( ) {
309302 let inner = self . read ( ) ;
310303 if let Some ( entry) = inner. entries . get ( index) {
311304 let entry = extract_dict_entry ( entry) ;
@@ -346,7 +339,6 @@ impl<T: Clone> Dict<T> {
346339 inner. indices . resize ( 8 , IndexEntry :: FREE ) ;
347340 inner. used = 0 ;
348341 inner. filled = 0 ;
349- inner. next_new_entry_idx = 0 ;
350342 // defer dec rc
351343 std:: mem:: take ( & mut inner. entries )
352344 } ;
@@ -394,7 +386,7 @@ impl<T: Clone> Dict<T> {
394386 let _removed = loop {
395387 let lookup = self . lookup ( vm, key, hash, None ) ?;
396388 let ( entry, index_index) = lookup;
397- if let IndexEntry :: Index ( _ ) = entry {
389+ if entry . index ( ) . is_some ( ) {
398390 match self . pop_inner ( lookup) {
399391 ControlFlow :: Break ( Some ( entry) ) => break Some ( entry) ,
400392 _ => continue ,
@@ -416,8 +408,8 @@ impl<T: Clone> Dict<T> {
416408 let hash = key. key_hash ( vm) ?;
417409 let res = loop {
418410 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 ( ) {
421413 let inner = self . read ( ) ;
422414 if let Some ( entry) = inner. entries . get ( index) {
423415 let entry = extract_dict_entry ( entry) ;
@@ -433,7 +425,13 @@ impl<T: Clone> Dict<T> {
433425 } else {
434426 let value = default ( ) ;
435427 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+ ) ;
437435 break value;
438436 }
439437 } ;
@@ -454,8 +452,8 @@ impl<T: Clone> Dict<T> {
454452 let hash = key. key_hash ( vm) ?;
455453 let res = loop {
456454 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 ( ) {
459457 let inner = self . read ( ) ;
460458 if let Some ( entry) = inner. entries . get ( index) {
461459 let entry = extract_dict_entry ( entry) ;
@@ -473,7 +471,7 @@ impl<T: Clone> Dict<T> {
473471 let key = key. to_pyobject ( vm) ;
474472 let mut inner = self . write ( ) ;
475473 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 ) ;
477475 break ret;
478476 }
479477 } ;
@@ -541,7 +539,7 @@ impl<T: Clone> Dict<T> {
541539 mut lock : Option < PyRwLockReadGuard < DictInner < T > > > ,
542540 ) -> PyResult < LookupResult > {
543541 let mut idxs = None ;
544- let mut freeslot = None ;
542+ let mut free_slot = None ;
545543 let ret = ' outer: loop {
546544 let ( entry_key, ret) = {
547545 let inner = lock. take ( ) . unwrap_or_else ( || self . read ( ) ) ;
@@ -550,22 +548,31 @@ impl<T: Clone> Dict<T> {
550548 } ) ;
551549 loop {
552550 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) ;
557559 }
558560 }
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) ,
563565 } ;
564566 return Ok ( idxs) ;
565567 }
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) ;
569576 if key. key_is ( & entry. key ) {
570577 break ' outer ret;
571578 } else if entry. hash == hash_value {
@@ -602,7 +609,7 @@ impl<T: Clone> Dict<T> {
602609 pred : impl Fn ( & T ) -> Result < bool , E > ,
603610 ) -> Result < PopInnerResult < T > , E > {
604611 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 ( ) {
606613 entry_index
607614 } else {
608615 return Ok ( ControlFlow :: Break ( None ) ) ;
@@ -623,7 +630,10 @@ impl<T: Clone> Dict<T> {
623630 // The dict was changed since we did lookup. Let's try again.
624631 _ => return Ok ( ControlFlow :: Continue ( ( ) ) ) ,
625632 }
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 ;
627637 inner. used -= 1 ;
628638 let removed = slot. take ( ) ;
629639 Ok ( ControlFlow :: Break ( removed) )
@@ -644,14 +654,17 @@ impl<T: Clone> Dict<T> {
644654
645655 pub fn pop_back ( & self ) -> Option < ( PyObjectRef , T ) > {
646656 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+ } ;
652663 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 ;
655668 Some ( ( entry. key , entry. value ) )
656669 }
657670
0 commit comments