@@ -178,6 +178,22 @@ mod impl_details {
178178 pub fn assert_size ( x : usize ) -> SizeType {
179179 x
180180 }
181+
182+ #[ inline( always) ]
183+ pub fn pack_capacity_and_auto ( cap : SizeType , auto : bool ) -> SizeType {
184+ debug_assert ! ( !auto) ;
185+ cap
186+ }
187+
188+ #[ inline( always) ]
189+ pub fn unpack_capacity ( cap : SizeType ) -> usize {
190+ cap
191+ }
192+
193+ #[ inline( always) ]
194+ pub fn is_auto ( _: SizeType ) -> bool {
195+ false
196+ }
181197}
182198
183199#[ cfg( feature = "gecko-ffi" ) ]
@@ -193,7 +209,7 @@ mod impl_details {
193209 // struct {
194210 // uint32_t mLength;
195211 // uint32_t mCapacity: 31;
196- // uint32_t mIsAutoBuffer : 1;
212+ // uint32_t mIsAutoArray : 1;
197213 // }
198214 // ```
199215 //
@@ -213,15 +229,14 @@ mod impl_details {
213229
214230 pub const MAX_CAP : usize = i32:: max_value ( ) as usize ;
215231
232+ // See kAutoTArrayHeaderOffset
233+ pub const AUTO_ARRAY_HEADER_OFFSET : usize = 8 ;
234+
216235 // Little endian: the auto bit is the high bit, and the capacity is
217236 // verbatim. So we just need to mask off the high bit. Note that
218237 // this masking is unnecessary when packing, because assert_size
219238 // guards against the high bit being set.
220239 #[ cfg( target_endian = "little" ) ]
221- pub fn pack_capacity ( cap : SizeType ) -> SizeType {
222- cap
223- }
224- #[ cfg( target_endian = "little" ) ]
225240 pub fn unpack_capacity ( cap : SizeType ) -> usize {
226241 ( cap as usize ) & !( 1 << 31 )
227242 }
@@ -238,10 +253,6 @@ mod impl_details {
238253 // shifted up one bit. Masking out the auto bit is unnecessary,
239254 // as rust shifts always shift in 0's for unsigned integers.
240255 #[ cfg( target_endian = "big" ) ]
241- pub fn pack_capacity ( cap : SizeType ) -> SizeType {
242- cap << 1
243- }
244- #[ cfg( target_endian = "big" ) ]
245256 pub fn unpack_capacity ( cap : SizeType ) -> usize {
246257 ( cap >> 1 ) as usize
247258 }
@@ -323,41 +334,23 @@ impl Header {
323334 fn set_len ( & mut self , len : usize ) {
324335 self . _len = assert_size ( len) ;
325336 }
326- }
327337
328- #[ cfg( feature = "gecko-ffi" ) ]
329- impl Header {
330338 fn cap ( & self ) -> usize {
331339 unpack_capacity ( self . _cap )
332340 }
333341
334- fn set_cap ( & mut self , cap : usize ) {
342+ fn set_cap_and_auto ( & mut self , cap : usize , is_auto : bool ) {
335343 // debug check that our packing is working
336- debug_assert_eq ! ( unpack_capacity( pack_capacity( cap as SizeType ) ) , cap) ;
337- // FIXME: this assert is busted because it reads uninit memory
338- // debug_assert!(!self.uses_stack_allocated_buffer());
339-
340- // NOTE: this always stores a cleared auto bit, because set_cap
341- // is only invoked by Rust, and Rust doesn't create auto arrays.
342- self . _cap = pack_capacity ( assert_size ( cap) ) ;
343- }
344-
345- fn uses_stack_allocated_buffer ( & self ) -> bool {
346- is_auto ( self . _cap )
347- }
348- }
349-
350- #[ cfg( not( feature = "gecko-ffi" ) ) ]
351- impl Header {
352- #[ inline]
353- #[ allow( clippy:: unnecessary_cast) ]
354- fn cap ( & self ) -> usize {
355- self . _cap as usize
344+ debug_assert_eq ! (
345+ unpack_capacity( pack_capacity_and_auto( cap as SizeType , is_auto) ) ,
346+ cap
347+ ) ;
348+ self . _cap = pack_capacity_and_auto ( assert_size ( cap) , is_auto) ;
356349 }
357350
358351 #[ inline]
359- fn set_cap ( & mut self , cap : usize ) {
360- self . _cap = assert_size ( cap ) ;
352+ fn is_auto ( & self ) -> bool {
353+ is_auto ( self . _cap )
361354 }
362355}
363356
@@ -445,7 +438,7 @@ fn layout<T>(cap: usize) -> Layout {
445438/// # Panics
446439///
447440/// Panics if the required size overflows `isize::MAX`.
448- fn header_with_capacity < T > ( cap : usize ) -> NonNull < Header > {
441+ fn header_with_capacity < T > ( cap : usize , is_auto : bool ) -> NonNull < Header > {
449442 debug_assert ! ( cap > 0 ) ;
450443 unsafe {
451444 let layout = layout :: < T > ( cap) ;
@@ -463,7 +456,7 @@ fn header_with_capacity<T>(cap: usize) -> NonNull<Header> {
463456 // "Infinite" capacity for zero-sized types:
464457 MAX_CAP as SizeType
465458 } else {
466- assert_size ( cap)
459+ pack_capacity_and_auto ( assert_size ( cap) , is_auto )
467460 } ,
468461 } ,
469462 ) ;
@@ -600,7 +593,7 @@ impl<T> ThinVec<T> {
600593 }
601594 } else {
602595 ThinVec {
603- ptr : header_with_capacity :: < T > ( cap) ,
596+ ptr : header_with_capacity :: < T > ( cap, false ) ,
604597 boo : PhantomData ,
605598 }
606599 }
@@ -1208,13 +1201,32 @@ impl<T> ThinVec<T> {
12081201 pub fn shrink_to_fit ( & mut self ) {
12091202 let old_cap = self . capacity ( ) ;
12101203 let new_cap = self . len ( ) ;
1211- if new_cap < old_cap {
1212- if new_cap == 0 {
1213- * self = ThinVec :: new ( ) ;
1214- } else {
1215- unsafe {
1216- self . reallocate ( new_cap) ;
1204+ if new_cap >= old_cap {
1205+ return ;
1206+ }
1207+ #[ cfg( feature = "gecko-ffi" ) ]
1208+ unsafe {
1209+ let stack_buf = self . auto_array_header_mut ( ) ;
1210+ if !stack_buf. is_null ( ) && ( * stack_buf) . cap ( ) >= new_cap {
1211+ // Try to switch to our auto-buffer.
1212+ if stack_buf == self . ptr . as_ptr ( ) {
1213+ return ;
12171214 }
1215+ stack_buf
1216+ . add ( 1 )
1217+ . cast :: < T > ( )
1218+ . copy_from_nonoverlapping ( self . data_raw ( ) , new_cap) ;
1219+ dealloc ( self . ptr ( ) as * mut u8 , layout :: < T > ( old_cap) ) ;
1220+ self . ptr = NonNull :: new_unchecked ( stack_buf) ;
1221+ self . ptr . as_mut ( ) . set_len ( new_cap) ;
1222+ return ;
1223+ }
1224+ }
1225+ if new_cap == 0 {
1226+ * self = ThinVec :: new ( ) ;
1227+ } else {
1228+ unsafe {
1229+ self . reallocate ( new_cap) ;
12181230 }
12191231 }
12201232 }
@@ -1690,10 +1702,10 @@ impl<T> ThinVec<T> {
16901702 if ptr. is_null ( ) {
16911703 handle_alloc_error ( layout :: < T > ( new_cap) )
16921704 }
1693- ( * ptr) . set_cap ( new_cap) ;
1705+ ( * ptr) . set_cap_and_auto ( new_cap, ( * ptr ) . is_auto ( ) ) ;
16941706 self . ptr = NonNull :: new_unchecked ( ptr) ;
16951707 } else {
1696- let new_header = header_with_capacity :: < T > ( new_cap) ;
1708+ let new_header = header_with_capacity :: < T > ( new_cap, self . is_auto_array ( ) ) ;
16971709
16981710 // If we get here and have a non-zero len, then we must be handling
16991711 // a gecko auto array, and we have items in a stack buffer. We shouldn't
@@ -1720,33 +1732,46 @@ impl<T> ThinVec<T> {
17201732 }
17211733 }
17221734
1723- #[ cfg( feature = "gecko-ffi" ) ]
17241735 #[ inline]
17251736 #[ allow( unused_unsafe) ]
17261737 fn is_singleton ( & self ) -> bool {
1727- // NOTE: the tests will complain that this "unsafe" isn't needed, but it *IS*!
1728- // In production this refers to an *extern static* which *is* unsafe to reference.
1729- // In tests this refers to a local static because we don't have Firefox's codebase
1730- // providing the symbol!
17311738 unsafe { self . ptr . as_ptr ( ) as * const Header == & EMPTY_HEADER }
17321739 }
17331740
1734- #[ cfg( not ( feature = "gecko-ffi" ) ) ]
1741+ #[ cfg( feature = "gecko-ffi" ) ]
17351742 #[ inline]
1736- fn is_singleton ( & self ) -> bool {
1737- self . ptr . as_ptr ( ) as * const Header == & EMPTY_HEADER
1743+ fn auto_array_header_mut ( & mut self ) -> * mut Header {
1744+ if !self . is_auto_array ( ) {
1745+ return ptr:: null_mut ( ) ;
1746+ }
1747+ unsafe { ( self as * mut Self ) . byte_add ( AUTO_ARRAY_HEADER_OFFSET ) as * mut Header }
17381748 }
17391749
17401750 #[ cfg( feature = "gecko-ffi" ) ]
17411751 #[ inline]
1742- fn has_allocation ( & self ) -> bool {
1743- unsafe { !self . is_singleton ( ) && !self . ptr . as_ref ( ) . uses_stack_allocated_buffer ( ) }
1752+ fn auto_array_header ( & self ) -> * const Header {
1753+ if !self . is_auto_array ( ) {
1754+ return ptr:: null_mut ( ) ;
1755+ }
1756+ unsafe { ( self as * const Self ) . byte_add ( AUTO_ARRAY_HEADER_OFFSET ) as * const Header }
1757+ }
1758+
1759+ #[ inline]
1760+ fn is_auto_array ( & self ) -> bool {
1761+ unsafe { self . ptr . as_ref ( ) . is_auto ( ) }
1762+ }
1763+
1764+ #[ inline]
1765+ fn uses_stack_allocated_buffer ( & self ) -> bool {
1766+ #[ cfg( feature = "gecko-ffi" ) ]
1767+ return self . auto_array_header ( ) == self . ptr . as_ptr ( ) ;
1768+ #[ cfg( not( feature = "gecko-ffi" ) ) ]
1769+ return false ;
17441770 }
17451771
1746- #[ cfg( not( feature = "gecko-ffi" ) ) ]
17471772 #[ inline]
17481773 fn has_allocation ( & self ) -> bool {
1749- !self . is_singleton ( )
1774+ !self . is_singleton ( ) && ! self . uses_stack_allocated_buffer ( )
17501775 }
17511776}
17521777
@@ -1850,8 +1875,7 @@ impl<T> Drop for ThinVec<T> {
18501875 unsafe {
18511876 ptr:: drop_in_place ( & mut this[ ..] ) ;
18521877
1853- #[ cfg( feature = "gecko-ffi" ) ]
1854- if this. ptr . as_ref ( ) . uses_stack_allocated_buffer ( ) {
1878+ if this. uses_stack_allocated_buffer ( ) {
18551879 return ;
18561880 }
18571881
@@ -2764,7 +2788,7 @@ impl<I: Iterator> Drop for Splice<'_, I> {
27642788}
27652789
27662790#[ cfg( feature = "gecko-ffi" ) ]
2767- #[ repr( C ) ]
2791+ #[ repr( C , align ( 8 ) ) ]
27682792struct AutoBuffer < T , const N : usize > {
27692793 header : Header ,
27702794 buffer : mem:: MaybeUninit < [ T ; N ] > ,
@@ -2790,6 +2814,7 @@ impl<T, const N: usize> AutoThinVec<T, N> {
27902814 std:: mem:: align_of:: <T >( ) <= 8 ,
27912815 "Can't handle alignments greater than 8"
27922816 ) ;
2817+ assert_eq ! ( std:: mem:: offset_of!( Self , buffer) , AUTO_ARRAY_HEADER_OFFSET ) ;
27932818 Self {
27942819 inner : ThinVec :: new ( ) ,
27952820 buffer : AutoBuffer {
@@ -2807,6 +2832,7 @@ impl<T, const N: usize> AutoThinVec<T, N> {
28072832 /// need to make sure not to move the ThinVec manually via something like
28082833 /// `std::mem::take(&mut auto_vec)`.
28092834 pub fn as_mut_ptr ( self : std:: pin:: Pin < & mut Self > ) -> * mut ThinVec < T > {
2835+ debug_assert ! ( self . is_auto_array( ) ) ;
28102836 unsafe { & mut self . get_unchecked_mut ( ) . inner }
28112837 }
28122838
@@ -2817,31 +2843,14 @@ impl<T, const N: usize> AutoThinVec<T, N> {
28172843 this. buffer . header . set_len ( 0 ) ;
28182844 // TODO(emilio): Use NonNull::from_mut when msrv allows.
28192845 this. inner . ptr = NonNull :: new_unchecked ( & mut this. buffer . header ) ;
2846+ debug_assert ! ( this. inner. is_auto_array( ) ) ;
2847+ debug_assert ! ( this. inner. uses_stack_allocated_buffer( ) ) ;
28202848 }
28212849
28222850 pub fn shrink_to_fit ( self : std:: pin:: Pin < & mut Self > ) {
2823- if self . inner . is_singleton ( ) {
2824- return unsafe { self . shrink_to_fit_known_singleton ( ) } ;
2825- }
28262851 let this = unsafe { self . get_unchecked_mut ( ) } ;
2827- if !this. inner . has_allocation ( ) {
2828- return ;
2829- }
2830- let len = this. len ( ) ;
2831- if len > N {
2832- return this. inner . shrink_to_fit ( ) ;
2833- }
2834- let old_header = this. inner . ptr ( ) ;
2835- let old_cap = this. inner . capacity ( ) ;
2836- unsafe {
2837- ( this. buffer . buffer . as_mut_ptr ( ) as * mut T )
2838- . copy_from_nonoverlapping ( this. inner . data_raw ( ) , len) ;
2839- }
2840- this. buffer . header . set_len ( len) ;
2841- unsafe {
2842- this. inner . ptr = NonNull :: new_unchecked ( & mut this. buffer . header ) ;
2843- dealloc ( old_header as * mut u8 , layout :: < T > ( old_cap) ) ;
2844- }
2852+ this. inner . shrink_to_fit ( ) ;
2853+ debug_assert ! ( this. inner. is_auto_array( ) ) ;
28452854 }
28462855}
28472856
@@ -4563,11 +4572,13 @@ mod std_tests {
45634572 }
45644573 */
45654574
4566- #[ cfg( all ( feature = "gecko-ffi" ) ) ]
4575+ #[ cfg( feature = "gecko-ffi" ) ]
45674576 #[ test]
45684577 fn auto_t_array_basic ( ) {
45694578 crate :: auto_thin_vec!( let t: [ u8 ; 10 ] ) ;
45704579 assert_eq ! ( t. capacity( ) , 10 ) ;
4580+ assert ! ( t. is_auto_array( ) ) ;
4581+ assert ! ( t. uses_stack_allocated_buffer( ) ) ;
45714582 assert ! ( !t. has_allocation( ) ) ;
45724583 {
45734584 let inner = unsafe { & mut * t. as_mut ( ) . as_mut_ptr ( ) } ;
@@ -4576,6 +4587,8 @@ mod std_tests {
45764587 }
45774588 }
45784589
4590+ assert ! ( t. is_auto_array( ) ) ;
4591+ assert ! ( !t. uses_stack_allocated_buffer( ) ) ;
45794592 assert_eq ! ( t. len( ) , 30 ) ;
45804593 assert ! ( t. has_allocation( ) ) ;
45814594 assert_eq ! ( t[ 5 ] , 5 ) ;
@@ -4592,6 +4605,8 @@ mod std_tests {
45924605 assert ! ( t. has_allocation( ) ) ;
45934606 t. as_mut ( ) . shrink_to_fit ( ) ;
45944607 assert ! ( !t. has_allocation( ) ) ;
4608+ assert ! ( t. is_auto_array( ) ) ;
4609+ assert ! ( t. uses_stack_allocated_buffer( ) ) ;
45954610 assert_eq ! ( t. capacity( ) , 10 ) ;
45964611 }
45974612
0 commit comments