@@ -19,13 +19,14 @@ const NB_BUCKETS: usize = 1 << 12; // 4096
19
19
const BUCKET_MASK : u32 = ( 1 << 12 ) - 1 ;
20
20
21
21
pub ( crate ) struct Set {
22
- buckets : Box < [ Mutex < Vec < Box < Entry > > > ] > ,
22
+ buckets : Box < [ Mutex < Option < Box < Entry > > > ] > ,
23
23
}
24
24
25
25
pub ( crate ) struct Entry {
26
26
pub ( crate ) string : Box < str > ,
27
27
pub ( crate ) hash : u32 ,
28
28
pub ( crate ) ref_count : AtomicIsize ,
29
+ next_in_bucket : Option < Box < Entry > > ,
29
30
}
30
31
31
32
// Addresses are a multiples of this,
@@ -40,53 +41,70 @@ fn entry_alignment_is_sufficient() {
40
41
pub ( crate ) static DYNAMIC_SET : Lazy < Set > = Lazy :: new ( || {
41
42
// NOTE: Using const initialization for buckets breaks the small-stack test.
42
43
// ```
43
- // // buckets: [Mutex<Vec <Box<Entry>>>; NB_BUCKETS],
44
- // const MUTEX_VEC : Mutex<Vec <Box<Entry>>> = Mutex::new(vec![] );
45
- // let buckets = Box::new([MUTEX_VEC ; NB_BUCKETS]);
44
+ // // buckets: [Mutex<Option <Box<Entry>>>; NB_BUCKETS],
45
+ // const MUTEX : Mutex<Option <Box<Entry>>> = Mutex::new(None );
46
+ // let buckets = Box::new([MUTEX ; NB_BUCKETS]);
46
47
// ```
47
- let buckets = ( 0 ..NB_BUCKETS ) . map ( |_| Mutex :: new ( vec ! [ ] ) ) . collect ( ) ;
48
+ let buckets = ( 0 ..NB_BUCKETS ) . map ( |_| Mutex :: new ( None ) ) . collect ( ) ;
48
49
Set { buckets }
49
50
} ) ;
50
51
51
52
impl Set {
52
53
pub ( crate ) fn insert ( & self , string : Cow < str > , hash : u32 ) -> NonNull < Entry > {
53
54
let bucket_index = ( hash & BUCKET_MASK ) as usize ;
54
- let mut vec = self . buckets [ bucket_index] . lock ( ) ;
55
- if let Some ( entry) = vec
56
- . iter_mut ( )
57
- . find ( |e| e. hash == hash && * e. string == * string)
55
+ let mut linked_list = self . buckets [ bucket_index] . lock ( ) ;
56
+
58
57
{
59
- if entry. ref_count . fetch_add ( 1 , SeqCst ) > 0 {
60
- return NonNull :: from ( & mut * * entry) ;
58
+ let mut ptr: Option < & mut Box < Entry > > = linked_list. as_mut ( ) ;
59
+
60
+ while let Some ( entry) = ptr. take ( ) {
61
+ if entry. hash == hash && * entry. string == * string {
62
+ if entry. ref_count . fetch_add ( 1 , SeqCst ) > 0 {
63
+ return NonNull :: from ( & mut * * entry) ;
64
+ }
65
+ // Uh-oh. The pointer's reference count was zero, which means someone may try
66
+ // to free it. (Naive attempts to defend against this, for example having the
67
+ // destructor check to see whether the reference count is indeed zero, don't
68
+ // work due to ABA.) Thus we need to temporarily add a duplicate string to the
69
+ // list.
70
+ entry. ref_count . fetch_sub ( 1 , SeqCst ) ;
71
+ break ;
72
+ }
73
+ ptr = entry. next_in_bucket . as_mut ( ) ;
61
74
}
62
- // Uh-oh. The pointer's reference count was zero, which means someone may try
63
- // to free it. (Naive attempts to defend against this, for example having the
64
- // destructor check to see whether the reference count is indeed zero, don't
65
- // work due to ABA.) Thus we need to temporarily add a duplicate string to the
66
- // list.
67
- entry. ref_count . fetch_sub ( 1 , SeqCst ) ;
68
75
}
69
76
debug_assert ! ( mem:: align_of:: <Entry >( ) >= ENTRY_ALIGNMENT ) ;
70
77
let string = string. into_owned ( ) ;
71
78
let mut entry = Box :: new ( Entry {
79
+ next_in_bucket : linked_list. take ( ) ,
72
80
hash,
73
81
ref_count : AtomicIsize :: new ( 1 ) ,
74
82
string : string. into_boxed_str ( ) ,
75
83
} ) ;
76
84
let ptr = NonNull :: from ( & mut * entry) ;
77
- vec . push ( entry) ;
85
+ * linked_list = Some ( entry) ;
78
86
ptr
79
87
}
80
88
81
- pub ( crate ) fn remove ( & self , ptr : * const Entry ) {
89
+ pub ( crate ) fn remove ( & self , ptr : * mut Entry ) {
82
90
let bucket_index = {
83
91
let value: & Entry = unsafe { & * ptr } ;
84
92
debug_assert ! ( value. ref_count. load( SeqCst ) == 0 ) ;
85
93
( value. hash & BUCKET_MASK ) as usize
86
94
} ;
87
95
88
- let mut vec = self . buckets [ bucket_index] . lock ( ) ;
96
+ let mut linked_list = self . buckets [ bucket_index] . lock ( ) ;
97
+ let mut current: & mut Option < Box < Entry > > = & mut linked_list;
89
98
90
- vec. retain ( |e| ( & * * e as * const Entry ) != ptr) ;
99
+ while let Some ( entry_ptr) = current. as_mut ( ) {
100
+ let entry_ptr: * mut Entry = & mut * * entry_ptr;
101
+ if entry_ptr == ptr {
102
+ mem:: drop ( mem:: replace ( current, unsafe {
103
+ ( * entry_ptr) . next_in_bucket . take ( )
104
+ } ) ) ;
105
+ break ;
106
+ }
107
+ current = unsafe { & mut ( * entry_ptr) . next_in_bucket } ;
108
+ }
91
109
}
92
110
}
0 commit comments