@@ -5,7 +5,7 @@ use std::{
5
5
borrow:: Borrow ,
6
6
fmt,
7
7
hash:: { BuildHasherDefault , Hash , Hasher } ,
8
- mem,
8
+ mem:: { self , ManuallyDrop } ,
9
9
ptr:: NonNull ,
10
10
sync:: OnceLock ,
11
11
} ;
@@ -25,6 +25,15 @@ const _: () = assert!(std::mem::align_of::<Box<str>>() == std::mem::align_of::<&
25
25
const _: ( ) = assert ! ( std:: mem:: size_of:: <Arc <Box <str >>>( ) == std:: mem:: size_of:: <&&str >( ) ) ;
26
26
const _: ( ) = assert ! ( std:: mem:: align_of:: <Arc <Box <str >>>( ) == std:: mem:: align_of:: <&&str >( ) ) ;
27
27
28
+ const _: ( ) =
29
+ assert ! ( std:: mem:: size_of:: <* const * const str >( ) == std:: mem:: size_of:: <TaggedArcPtr >( ) ) ;
30
+ const _: ( ) =
31
+ assert ! ( std:: mem:: align_of:: <* const * const str >( ) == std:: mem:: align_of:: <TaggedArcPtr >( ) ) ;
32
+
33
+ const _: ( ) = assert ! ( std:: mem:: size_of:: <Arc <Box <str >>>( ) == std:: mem:: size_of:: <TaggedArcPtr >( ) ) ;
34
+ const _: ( ) =
35
+ assert ! ( std:: mem:: align_of:: <Arc <Box <str >>>( ) == std:: mem:: align_of:: <TaggedArcPtr >( ) ) ;
36
+
28
37
/// A pointer that points to a pointer to a `str`, it may be backed as a `&'static &'static str` or
29
38
/// `Arc<Box<str>>` but its size is that of a thin pointer. The active variant is encoded as a tag
30
39
/// in the LSB of the alignment niche.
@@ -38,21 +47,27 @@ unsafe impl Sync for TaggedArcPtr {}
38
47
39
48
impl TaggedArcPtr {
40
49
const BOOL_BITS : usize = true as usize ;
50
+ const ALIGNMENT_NICHE : usize = mem:: align_of :: < TaggedArcPtr > ( ) . trailing_zeros ( ) as usize ;
41
51
42
52
const fn non_arc ( r : & ' static & ' static str ) -> Self {
43
- Self {
44
- // SAFETY: The pointer is non-null as it is derived from a reference
45
- // Ideally we would call out to `pack_arc` but for a `false` tag, unfortunately the
46
- // packing stuff requires reading out the pointer to an integer which is not supported
47
- // in const contexts, so here we make use of the fact that for the non-arc version the
48
- // tag is false (0) and thus does not need touching the actual pointer value.ext)
49
- packed : unsafe {
50
- NonNull :: new_unchecked ( ( r as * const & str ) . cast :: < * const str > ( ) . cast_mut ( ) )
51
- } ,
52
- }
53
+ assert ! (
54
+ mem:: align_of:: <& ' static & ' static str >( ) . trailing_zeros( ) as usize > Self :: BOOL_BITS
55
+ ) ;
56
+ // SAFETY: The pointer is non-null as it is derived from a reference
57
+ // Ideally we would call out to `pack_arc` but for a `false` tag, unfortunately the
58
+ // packing stuff requires reading out the pointer to an integer which is not supported
59
+ // in const contexts, so here we make use of the fact that for the non-arc version the
60
+ // tag is false (0) and thus does not need touching the actual pointer value.ext)
61
+
62
+ let packed =
63
+ unsafe { NonNull :: new_unchecked ( ( r as * const & str ) . cast :: < * const str > ( ) . cast_mut ( ) ) } ;
64
+ Self { packed }
53
65
}
54
66
55
67
fn arc ( arc : Arc < Box < str > > ) -> Self {
68
+ assert ! (
69
+ mem:: align_of:: <& ' static & ' static str >( ) . trailing_zeros( ) as usize > Self :: BOOL_BITS
70
+ ) ;
56
71
Self {
57
72
packed : Self :: pack_arc (
58
73
// Safety: `Arc::into_raw` always returns a non null pointer
@@ -63,12 +78,14 @@ impl TaggedArcPtr {
63
78
64
79
/// Retrieves the tag.
65
80
#[ inline]
66
- pub ( crate ) fn try_as_arc_owned ( self ) -> Option < Arc < Box < str > > > {
81
+ pub ( crate ) fn try_as_arc_owned ( self ) -> Option < ManuallyDrop < Arc < Box < str > > > > {
67
82
// Unpack the tag from the alignment niche
68
83
let tag = Strict :: addr ( self . packed . as_ptr ( ) ) & Self :: BOOL_BITS ;
69
84
if tag != 0 {
70
85
// Safety: We checked that the tag is non-zero -> true, so we are pointing to the data offset of an `Arc`
71
- Some ( unsafe { Arc :: from_raw ( self . pointer ( ) . as_ptr ( ) . cast :: < Box < str > > ( ) ) } )
86
+ Some ( ManuallyDrop :: new ( unsafe {
87
+ Arc :: from_raw ( self . pointer ( ) . as_ptr ( ) . cast :: < Box < str > > ( ) )
88
+ } ) )
72
89
} else {
73
90
None
74
91
}
@@ -122,10 +139,11 @@ impl TaggedArcPtr {
122
139
}
123
140
}
124
141
125
- #[ derive( PartialEq , Eq , Hash , Clone , Debug ) ]
142
+ #[ derive( PartialEq , Eq , Hash , Debug ) ]
126
143
pub struct Symbol {
127
144
repr : TaggedArcPtr ,
128
145
}
146
+
129
147
const _: ( ) = assert ! ( std:: mem:: size_of:: <Symbol >( ) == std:: mem:: size_of:: <NonNull <( ) >>( ) ) ;
130
148
const _: ( ) = assert ! ( std:: mem:: align_of:: <Symbol >( ) == std:: mem:: align_of:: <NonNull <( ) >>( ) ) ;
131
149
@@ -185,19 +203,27 @@ impl Symbol {
185
203
fn drop_slow ( arc : & Arc < Box < str > > ) {
186
204
let ( mut shard, hash) = Self :: select_shard ( arc) ;
187
205
188
- if Arc :: count ( arc) != 2 {
189
- // Another thread has interned another copy
190
- return ;
206
+ match Arc :: count ( arc) {
207
+ 0 => unreachable ! ( ) ,
208
+ 1 => unreachable ! ( ) ,
209
+ 2 => ( ) ,
210
+ _ => {
211
+ // Another thread has interned another copy
212
+ return ;
213
+ }
191
214
}
192
215
193
- match shard. raw_entry_mut ( ) . from_key_hashed_nocheck :: < str > ( hash, arc. as_ref ( ) ) {
194
- RawEntryMut :: Occupied ( occ) => occ. remove_entry ( ) ,
195
- RawEntryMut :: Vacant ( _) => unreachable ! ( ) ,
196
- }
197
- . 0
198
- . 0
199
- . try_as_arc_owned ( )
200
- . unwrap ( ) ;
216
+ ManuallyDrop :: into_inner (
217
+ match shard. raw_entry_mut ( ) . from_key_hashed_nocheck :: < str > ( hash, arc. as_ref ( ) ) {
218
+ RawEntryMut :: Occupied ( occ) => occ. remove_entry ( ) ,
219
+ RawEntryMut :: Vacant ( _) => unreachable ! ( ) ,
220
+ }
221
+ . 0
222
+ . 0
223
+ . try_as_arc_owned ( )
224
+ . unwrap ( ) ,
225
+ ) ;
226
+ debug_assert_eq ! ( Arc :: count( & arc) , 1 ) ;
201
227
202
228
// Shrink the backing storage if the shard is less than 50% occupied.
203
229
if shard. len ( ) * 2 < shard. capacity ( ) {
@@ -219,7 +245,13 @@ impl Drop for Symbol {
219
245
Self :: drop_slow ( & arc) ;
220
246
}
221
247
// decrement the ref count
222
- drop ( arc) ;
248
+ ManuallyDrop :: into_inner ( arc) ;
249
+ }
250
+ }
251
+
252
+ impl Clone for Symbol {
253
+ fn clone ( & self ) -> Self {
254
+ Self { repr : increase_arc_refcount ( self . repr ) }
223
255
}
224
256
}
225
257
@@ -228,8 +260,7 @@ fn increase_arc_refcount(repr: TaggedArcPtr) -> TaggedArcPtr {
228
260
return repr;
229
261
} ;
230
262
// increase the ref count
231
- mem:: forget ( arc. clone ( ) ) ;
232
- mem:: forget ( arc) ;
263
+ mem:: forget ( Arc :: clone ( & arc) ) ;
233
264
repr
234
265
}
235
266
0 commit comments