@@ -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