@@ -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.
@@ -40,19 +49,24 @@ impl TaggedArcPtr {
40
49
const BOOL_BITS : usize = true as usize ;
41
50
42
51
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
- }
52
+ assert ! (
53
+ mem:: align_of:: <& ' static & ' static str >( ) . trailing_zeros( ) as usize > Self :: BOOL_BITS
54
+ ) ;
55
+ // SAFETY: The pointer is non-null as it is derived from a reference
56
+ // Ideally we would call out to `pack_arc` but for a `false` tag, unfortunately the
57
+ // packing stuff requires reading out the pointer to an integer which is not supported
58
+ // in const contexts, so here we make use of the fact that for the non-arc version the
59
+ // tag is false (0) and thus does not need touching the actual pointer value.ext)
60
+
61
+ let packed =
62
+ unsafe { NonNull :: new_unchecked ( ( r as * const & str ) . cast :: < * const str > ( ) . cast_mut ( ) ) } ;
63
+ Self { packed }
53
64
}
54
65
55
66
fn arc ( arc : Arc < Box < str > > ) -> Self {
67
+ assert ! (
68
+ mem:: align_of:: <& ' static & ' static str >( ) . trailing_zeros( ) as usize > Self :: BOOL_BITS
69
+ ) ;
56
70
Self {
57
71
packed : Self :: pack_arc (
58
72
// Safety: `Arc::into_raw` always returns a non null pointer
@@ -63,12 +77,14 @@ impl TaggedArcPtr {
63
77
64
78
/// Retrieves the tag.
65
79
#[ inline]
66
- pub ( crate ) fn try_as_arc_owned ( self ) -> Option < Arc < Box < str > > > {
80
+ pub ( crate ) fn try_as_arc_owned ( self ) -> Option < ManuallyDrop < Arc < Box < str > > > > {
67
81
// Unpack the tag from the alignment niche
68
82
let tag = Strict :: addr ( self . packed . as_ptr ( ) ) & Self :: BOOL_BITS ;
69
83
if tag != 0 {
70
84
// 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 > > ( ) ) } )
85
+ Some ( ManuallyDrop :: new ( unsafe {
86
+ Arc :: from_raw ( self . pointer ( ) . as_ptr ( ) . cast :: < Box < str > > ( ) )
87
+ } ) )
72
88
} else {
73
89
None
74
90
}
@@ -122,10 +138,11 @@ impl TaggedArcPtr {
122
138
}
123
139
}
124
140
125
- #[ derive( PartialEq , Eq , Hash , Clone , Debug ) ]
141
+ #[ derive( PartialEq , Eq , Hash , Debug ) ]
126
142
pub struct Symbol {
127
143
repr : TaggedArcPtr ,
128
144
}
145
+
129
146
const _: ( ) = assert ! ( std:: mem:: size_of:: <Symbol >( ) == std:: mem:: size_of:: <NonNull <( ) >>( ) ) ;
130
147
const _: ( ) = assert ! ( std:: mem:: align_of:: <Symbol >( ) == std:: mem:: align_of:: <NonNull <( ) >>( ) ) ;
131
148
@@ -185,19 +202,27 @@ impl Symbol {
185
202
fn drop_slow ( arc : & Arc < Box < str > > ) {
186
203
let ( mut shard, hash) = Self :: select_shard ( arc) ;
187
204
188
- if Arc :: count ( arc) != 2 {
189
- // Another thread has interned another copy
190
- return ;
205
+ match Arc :: count ( arc) {
206
+ 0 => unreachable ! ( ) ,
207
+ 1 => unreachable ! ( ) ,
208
+ 2 => ( ) ,
209
+ _ => {
210
+ // Another thread has interned another copy
211
+ return ;
212
+ }
191
213
}
192
214
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 ( ) ;
215
+ ManuallyDrop :: into_inner (
216
+ match shard. raw_entry_mut ( ) . from_key_hashed_nocheck :: < str > ( hash, arc. as_ref ( ) ) {
217
+ RawEntryMut :: Occupied ( occ) => occ. remove_entry ( ) ,
218
+ RawEntryMut :: Vacant ( _) => unreachable ! ( ) ,
219
+ }
220
+ . 0
221
+ . 0
222
+ . try_as_arc_owned ( )
223
+ . unwrap ( ) ,
224
+ ) ;
225
+ debug_assert_eq ! ( Arc :: count( & arc) , 1 ) ;
201
226
202
227
// Shrink the backing storage if the shard is less than 50% occupied.
203
228
if shard. len ( ) * 2 < shard. capacity ( ) {
@@ -219,7 +244,13 @@ impl Drop for Symbol {
219
244
Self :: drop_slow ( & arc) ;
220
245
}
221
246
// decrement the ref count
222
- drop ( arc) ;
247
+ ManuallyDrop :: into_inner ( arc) ;
248
+ }
249
+ }
250
+
251
+ impl Clone for Symbol {
252
+ fn clone ( & self ) -> Self {
253
+ Self { repr : increase_arc_refcount ( self . repr ) }
223
254
}
224
255
}
225
256
@@ -228,8 +259,7 @@ fn increase_arc_refcount(repr: TaggedArcPtr) -> TaggedArcPtr {
228
259
return repr;
229
260
} ;
230
261
// increase the ref count
231
- mem:: forget ( arc. clone ( ) ) ;
232
- mem:: forget ( arc) ;
262
+ mem:: forget ( Arc :: clone ( & arc) ) ;
233
263
repr
234
264
}
235
265
@@ -265,6 +295,7 @@ mod tests {
265
295
let base_len = MAP . get ( ) . unwrap ( ) . len ( ) ;
266
296
let hello = Symbol :: intern ( "hello" ) ;
267
297
let world = Symbol :: intern ( "world" ) ;
298
+ let more_worlds = world. clone ( ) ;
268
299
let bang = Symbol :: intern ( "!" ) ;
269
300
let q = Symbol :: intern ( "?" ) ;
270
301
assert_eq ! ( MAP . get( ) . unwrap( ) . len( ) , base_len + 4 ) ;
@@ -275,6 +306,7 @@ mod tests {
275
306
drop ( q) ;
276
307
assert_eq ! ( MAP . get( ) . unwrap( ) . len( ) , base_len + 3 ) ;
277
308
let default = Symbol :: intern ( "default" ) ;
309
+ let many_worlds = world. clone ( ) ;
278
310
assert_eq ! ( MAP . get( ) . unwrap( ) . len( ) , base_len + 3 ) ;
279
311
assert_eq ! (
280
312
"hello default world!" ,
@@ -285,6 +317,8 @@ mod tests {
285
317
"hello world!" ,
286
318
format!( "{} {}{}" , hello. as_str( ) , world. as_str( ) , bang. as_str( ) )
287
319
) ;
320
+ drop ( many_worlds) ;
321
+ drop ( more_worlds) ;
288
322
drop ( hello) ;
289
323
drop ( world) ;
290
324
drop ( bang) ;
0 commit comments