#![deny(missing_docs)] //! `ThinVec` is exactly the same as `Vec`, except that it stores its `len` and `capacity` in the buffer //! it allocates. //! //! This makes the memory footprint of ThinVecs lower; notably in cases where space is reserved for //! a non-existence `ThinVec`. So `Vec>` and `Option>::None` will waste less //! space. Being pointer-sized also means it can be passed/stored in registers. //! //! Of course, any actually constructed `ThinVec` will theoretically have a bigger allocation, but //! the fuzzy nature of allocators means that might not actually be the case. //! //! Properties of `Vec` that are preserved: //! * `ThinVec::new()` doesn't allocate (it points to a statically allocated singleton) //! * reallocation can be done in place //! * `size_of::>()` == `size_of::>>()` //! //! Properties of `Vec` that aren't preserved: //! * `ThinVec` can't ever be zero-cost roundtripped to a `Box<[T]>`, `String`, or `*mut T` //! * `from_raw_parts` doesn't exist //! * `ThinVec` currently doesn't bother to not-allocate for Zero Sized Types (e.g. `ThinVec<()>`), //! but it could be done if someone cared enough to implement it. //! //! //! //! # Gecko FFI //! //! If you enable the gecko-ffi feature, `ThinVec` will verbatim bridge with the nsTArray type in //! Gecko (Firefox). That is, `ThinVec` and nsTArray have identical layouts *but not ABIs*, //! so nsTArrays/ThinVecs an be natively manipulated by C++ and Rust, and ownership can be //! transferred across the FFI boundary (**IF YOU ARE CAREFUL, SEE BELOW!!**). //! //! While this feature is handy, it is also inherently dangerous to use because Rust and C++ do not //! know about each other. Specifically, this can be an issue with non-POD types (types which //! have destructors, move constructors, or are `!Copy`). //! //! ## Do Not Pass By Value //! //! The biggest thing to keep in mind is that **FFI functions cannot pass ThinVec/nsTArray //! by-value**. That is, these are busted APIs: //! //! ```rust,ignore //! // BAD WRONG //! extern fn process_data(data: ThinVec) { ... } //! // BAD WRONG //! extern fn get_data() -> ThinVec { ... } //! ``` //! //! You must instead pass by-reference: //! //! ```rust //! # use thin_vec::*; //! # use std::mem; //! //! // Read-only access, ok! //! extern fn process_data(data: &ThinVec) { //! for val in data { //! println!("{}", val); //! } //! } //! //! // Replace with empty instance to take ownership, ok! //! extern fn consume_data(data: &mut ThinVec) { //! let owned = mem::replace(data, ThinVec::new()); //! mem::drop(owned); //! } //! //! // Mutate input, ok! //! extern fn add_data(dataset: &mut ThinVec) { //! dataset.push(37); //! dataset.push(12); //! } //! //! // Return via out-param, usually ok! //! // //! // WARNING: output must be initialized! (Empty nsTArrays are free, so just do it!) //! extern fn get_data(output: &mut ThinVec) { //! *output = thin_vec![1, 2, 3, 4, 5]; //! } //! ``` //! //! Ignorable Explanation For Those Who Really Want To Know Why: //! //! > The fundamental issue is that Rust and C++ can't currently communicate about destructors, and //! > the semantics of C++ require destructors of function arguments to be run when the function //! > returns. Whether the callee or caller is responsible for this is also platform-specific, so //! > trying to hack around it manually would be messy. //! > //! > Also a type having a destructor changes its C++ ABI, because that type must actually exist //! > in memory (unlike a trivial struct, which is often passed in registers). We don't currently //! > have a way to communicate to Rust that this is happening, so even if we worked out the //! > destructor issue with say, MaybeUninit, it would still be a non-starter without some RFCs //! > to add explicit rustc support. //! > //! > Realistically, the best answer here is to have a "heavier" bindgen that can secretly //! > generate FFI glue so we can pass things "by value" and have it generate by-reference code //! > behind our back (like the cxx crate does). This would muddy up debugging/searchfox though. //! //! ## Types Should Be Trivially Relocatable //! //! Types in Rust are always trivially relocatable (unless suitably borrowed/[pinned][]/hidden). //! This means all Rust types are legal to relocate with a bitwise copy, you cannot provide //! copy or move constructors to execute when this happens, and the old location won't have its //! destructor run. This will cause problems for types which have a significant location //! (types that intrusively point into themselves or have their location registered with a service). //! //! While relocations are generally predictable if you're very careful, **you should avoid using //! types with significant locations with Rust FFI**. //! //! Specifically, `ThinVec` will trivially relocate its contents whenever it needs to reallocate its //! buffer to change its capacity. This is the default reallocation strategy for nsTArray, and is //! suitable for the vast majority of types. Just be aware of this limitation! //! //! ## Auto Arrays Are Dangerous //! //! `ThinVec` has *some* support for handling auto arrays which store their buffer on the stack, //! but this isn't well tested. //! //! Regardless of how much support we provide, Rust won't be aware of the buffer's limited lifetime, //! so standard auto array safety caveats apply about returning/storing them! `ThinVec` won't ever //! produce an auto array on its own, so this is only an issue for transferring an nsTArray into //! Rust. //! //! ## Other Issues //! //! Standard FFI caveats also apply: //! //! * Rust is more strict about POD types being initialized (use MaybeUninit if you must) //! * `ThinVec` has no idea if the C++ version of `T` has move/copy/assign/delete overloads //! * `nsTArray` has no idea if the Rust version of `T` has a Drop/Clone impl //! * C++ can do all sorts of unsound things that Rust can't catch //! * C++ and Rust don't agree on how zero-sized/empty types should be handled //! //! The gecko-ffi feature will not work if you aren't linking with code that has nsTArray //! defined. Specifically, we must share the symbol for nsTArray's empty singleton. You will get //! linking errors if that isn't defined. //! //! The gecko-ffi feature also limits `ThinVec` to the legacy behaviors of nsTArray. Most notably, //! nsTArray has a maximum capacity of i32::MAX (~2.1 billion items). Probably not an issue. //! Probably. //! //! [pinned]: https://doc.rust-lang.org/std/pin/index.html #![allow(clippy::comparison_chain, clippy::missing_safety_doc)] use std::alloc::*; use std::borrow::*; use std::cmp::*; use std::convert::TryFrom; use std::convert::TryInto; use std::hash::*; use std::iter::FromIterator; use std::marker::PhantomData; use std::ops::Bound; use std::ops::{Deref, DerefMut, RangeBounds}; use std::ptr::NonNull; use std::slice::IterMut; use std::{fmt, io, mem, ptr, slice}; use impl_details::*; // modules: a simple way to cfg a whole bunch of impl details at once #[cfg(not(feature = "gecko-ffi"))] mod impl_details { pub type SizeType = usize; pub const MAX_CAP: usize = !0; #[inline(always)] pub fn assert_size(x: usize) -> SizeType { x } } #[cfg(feature = "gecko-ffi")] mod impl_details { // Support for briding a gecko nsTArray verbatim into a ThinVec. // // `ThinVec` can't see copy/move/delete implementations // from C++ // // The actual layout of an nsTArray is: // // ```cpp // struct { // uint32_t mLength; // uint32_t mCapacity: 31; // uint32_t mIsAutoArray: 1; // } // ``` // // Rust doesn't natively support bit-fields, so we manually mask // and shift the bit. When the "auto" bit is set, the header and buffer // are actually on the stack, meaning the `ThinVec` pointer-to-header // is essentially an "owned borrow", and therefore dangerous to handle. // There are no safety guards for this situation. // // On little-endian platforms, the auto bit will be the high-bit of // our capacity u32. On big-endian platforms, it will be the low bit. // Hence we need some platform-specific CFGs for the necessary masking/shifting. // // `ThinVec` won't ever construct an auto array. They only happen when // bridging from C++. This means we don't need to ever set/preserve the bit. // We just need to be able to read and handle it if it happens to be there. // // Handling the auto bit mostly just means not freeing/reallocating the buffer. pub type SizeType = u32; pub const MAX_CAP: usize = i32::max_value() as usize; // Little endian: the auto bit is the high bit, and the capacity is // verbatim. So we just need to mask off the high bit. Note that // this masking is unnecessary when packing, because assert_size // guards against the high bit being set. #[cfg(target_endian = "little")] pub fn pack_capacity(cap: SizeType) -> SizeType { cap as SizeType } #[cfg(target_endian = "little")] pub fn unpack_capacity(cap: SizeType) -> usize { (cap as usize) & !(1 << 31) } #[cfg(target_endian = "little")] pub fn is_auto(cap: SizeType) -> bool { (cap & (1 << 31)) != 0 } // Big endian: the auto bit is the low bit, and the capacity is // shifted up one bit. Masking out the auto bit is unnecessary, // as rust shifts always shift in 0's for unsigned integers. #[cfg(target_endian = "big")] pub fn pack_capacity(cap: SizeType) -> SizeType { (cap as SizeType) << 1 } #[cfg(target_endian = "big")] pub fn unpack_capacity(cap: SizeType) -> usize { (cap >> 1) as usize } #[cfg(target_endian = "big")] pub fn is_auto(cap: SizeType) -> bool { (cap & 1) != 0 } #[inline] pub fn assert_size(x: usize) -> SizeType { if x > MAX_CAP as usize { panic!("nsTArray size may not exceed the capacity of a 32-bit sized int"); } x as SizeType } } // The header of a ThinVec. // // The _cap can be a bitfield, so use accessors to avoid trouble. // // In "real" gecko-ffi mode, the empty singleton will be aligned // to 8 by gecko. But in tests we have to provide the singleton // ourselves, and Rust makes it hard to "just" align a static. // To avoid messing around with a wrapper type around the // singleton *just* for tests, we just force all headers to be // aligned to 8 in this weird "zombie" gecko mode. // // This shouldn't affect runtime layout (padding), but it will // result in us asking the allocator to needlessly overalign // non-empty ThinVecs containing align < 8 types in // zombie-mode, but not in "real" geck-ffi mode. Minor. #[cfg_attr(all(feature = "gecko-ffi", any(test, miri)), repr(align(8)))] #[repr(C)] struct Header { _len: SizeType, _cap: SizeType, } impl Header { #[inline] #[allow(clippy::unnecessary_cast)] fn len(&self) -> usize { self._len as usize } #[inline] fn set_len(&mut self, len: usize) { self._len = assert_size(len); } } #[cfg(feature = "gecko-ffi")] impl Header { fn cap(&self) -> usize { unpack_capacity(self._cap) } fn set_cap(&mut self, cap: usize) { // debug check that our packing is working debug_assert_eq!(unpack_capacity(pack_capacity(cap as SizeType)), cap); // FIXME: this assert is busted because it reads uninit memory // debug_assert!(!self.uses_stack_allocated_buffer()); // NOTE: this always stores a cleared auto bit, because set_cap // is only invoked by Rust, and Rust doesn't create auto arrays. self._cap = pack_capacity(assert_size(cap)); } fn uses_stack_allocated_buffer(&self) -> bool { is_auto(self._cap) } } #[cfg(not(feature = "gecko-ffi"))] impl Header { #[allow(clippy::unnecessary_cast)] fn cap(&self) -> usize { self._cap as usize } fn set_cap(&mut self, cap: usize) { self._cap = assert_size(cap); } } /// Singleton that all empty collections share. /// Note: can't store non-zero ZSTs, we allocate in that case. We could /// optimize everything to not do that (basically, make ptr == len and branch /// on size == 0 in every method), but it's a bunch of work for something that /// doesn't matter much. #[cfg(any(not(feature = "gecko-ffi"), test, miri))] static EMPTY_HEADER: Header = Header { _len: 0, _cap: 0 }; #[cfg(all(feature = "gecko-ffi", not(test), not(miri)))] extern "C" { #[link_name = "sEmptyTArrayHeader"] static EMPTY_HEADER: Header; } // Utils for computing layouts of allocations /// Gets the size necessary to allocate a `ThinVec` with the give capacity. /// /// # Panics /// /// This will panic if isize::MAX is overflowed at any point. fn alloc_size(cap: usize) -> usize { // Compute "real" header size with pointer math // // We turn everything into isizes here so that we can catch isize::MAX overflow, // we never want to allow allocations larger than that! let header_size = mem::size_of::
() as isize; let padding = padding::() as isize; let data_size = if mem::size_of::() == 0 { // If we're allocating an array for ZSTs we need a header/padding but no actual // space for items, so we don't care about the capacity that was requested! 0 } else { let cap: isize = cap.try_into().expect("capacity overflow"); let elem_size = mem::size_of::() as isize; elem_size.checked_mul(cap).expect("capacity overflow") }; let final_size = data_size .checked_add(header_size + padding) .expect("capacity overflow"); // Ok now we can turn it back into a usize (don't need to worry about negatives) final_size as usize } /// Gets the padding necessary for the array of a `ThinVec` fn padding() -> usize { let alloc_align = alloc_align::(); let header_size = mem::size_of::
(); if alloc_align > header_size { if cfg!(feature = "gecko-ffi") { panic!( "nsTArray does not handle alignment above > {} correctly", header_size ); } alloc_align - header_size } else { 0 } } /// Gets the align necessary to allocate a `ThinVec` fn alloc_align() -> usize { max(mem::align_of::(), mem::align_of::
()) } /// Gets the layout necessary to allocate a `ThinVec` /// /// # Panics /// /// Panics if the required size overflows `isize::MAX`. fn layout(cap: usize) -> Layout { unsafe { Layout::from_size_align_unchecked(alloc_size::(cap), alloc_align::()) } } /// Allocates a header (and array) for a `ThinVec` with the given capacity. /// /// # Panics /// /// Panics if the required size overflows `isize::MAX`. fn header_with_capacity(cap: usize) -> NonNull
{ debug_assert!(cap > 0); unsafe { let layout = layout::(cap); let header = alloc(layout) as *mut Header; if header.is_null() { handle_alloc_error(layout) } // "Infinite" capacity for zero-sized types: (*header).set_cap(if mem::size_of::() == 0 { MAX_CAP } else { cap }); (*header).set_len(0); NonNull::new_unchecked(header) } } /// See the crate's top level documentation for a description of this type. #[repr(C)] pub struct ThinVec { ptr: NonNull
, boo: PhantomData, } unsafe impl Sync for ThinVec {} unsafe impl Send for ThinVec {} /// Creates a `ThinVec` containing the arguments. /// // A hack to avoid linking problems with `cargo test --features=gecko-ffi`. #[cfg_attr(not(feature = "gecko-ffi"), doc = "```")] #[cfg_attr(feature = "gecko-ffi", doc = "```ignore")] /// #[macro_use] extern crate thin_vec; /// /// fn main() { /// let v = thin_vec![1, 2, 3]; /// assert_eq!(v.len(), 3); /// assert_eq!(v[0], 1); /// assert_eq!(v[1], 2); /// assert_eq!(v[2], 3); /// /// let v = thin_vec![1; 3]; /// assert_eq!(v, [1, 1, 1]); /// } /// ``` #[macro_export] macro_rules! thin_vec { (@UNIT $($t:tt)*) => (()); ($elem:expr; $n:expr) => ({ let mut vec = $crate::ThinVec::new(); vec.resize($n, $elem); vec }); () => {$crate::ThinVec::new()}; ($($x:expr),*) => ({ let len = [$(thin_vec!(@UNIT $x)),*].len(); let mut vec = $crate::ThinVec::with_capacity(len); $(vec.push($x);)* vec }); ($($x:expr,)*) => (thin_vec![$($x),*]); } impl ThinVec { /// Creates a new empty ThinVec. /// /// This will not allocate. pub fn new() -> ThinVec { ThinVec::with_capacity(0) } /// Constructs a new, empty `ThinVec` with at least the specified capacity. /// /// The vector will be able to hold at least `capacity` elements without /// reallocating. This method is allowed to allocate for more elements than /// `capacity`. If `capacity` is 0, the vector will not allocate. /// /// It is important to note that although the returned vector has the /// minimum *capacity* specified, the vector will have a zero *length*. /// /// If it is important to know the exact allocated capacity of a `ThinVec`, /// always use the [`capacity`] method after construction. /// /// **NOTE**: unlike `Vec`, `ThinVec` **MUST** allocate once to keep track of non-zero /// lengths. As such, we cannot provide the same guarantees about ThinVecs /// of ZSTs not allocating. However the allocation never needs to be resized /// to add more ZSTs, since the underlying array is still length 0. /// /// [Capacity and reallocation]: #capacity-and-reallocation /// [`capacity`]: Vec::capacity /// /// # Panics /// /// Panics if the new capacity exceeds `isize::MAX` bytes. /// /// # Examples /// /// ``` /// use thin_vec::ThinVec; /// /// let mut vec = ThinVec::with_capacity(10); /// /// // The vector contains no items, even though it has capacity for more /// assert_eq!(vec.len(), 0); /// assert!(vec.capacity() >= 10); /// /// // These are all done without reallocating... /// for i in 0..10 { /// vec.push(i); /// } /// assert_eq!(vec.len(), 10); /// assert!(vec.capacity() >= 10); /// /// // ...but this may make the vector reallocate /// vec.push(11); /// assert_eq!(vec.len(), 11); /// assert!(vec.capacity() >= 11); /// /// // A vector of a zero-sized type will always over-allocate, since no /// // space is needed to store the actual elements. /// let vec_units = ThinVec::<()>::with_capacity(10); /// /// // Only true **without** the gecko-ffi feature! /// // assert_eq!(vec_units.capacity(), usize::MAX); /// ``` pub fn with_capacity(cap: usize) -> ThinVec { // `padding` contains ~static assertions against types that are // incompatible with the current feature flags. We also call it to // invoke these assertions when getting a pointer to the `ThinVec` // contents, but since we also get a pointer to the contents in the // `Drop` impl, trippng an assertion along that code path causes a // double panic. We duplicate the assertion here so that it is // testable, let _ = padding::(); if cap == 0 { unsafe { ThinVec { ptr: NonNull::new_unchecked(&EMPTY_HEADER as *const Header as *mut Header), boo: PhantomData, } } } else { ThinVec { ptr: header_with_capacity::(cap), boo: PhantomData, } } } // Accessor conveniences fn ptr(&self) -> *mut Header { self.ptr.as_ptr() } fn header(&self) -> &Header { unsafe { self.ptr.as_ref() } } fn data_raw(&self) -> *mut T { // `padding` contains ~static assertions against types that are // incompatible with the current feature flags. Even if we don't // care about its result, we should always call it before getting // a data pointer to guard against invalid types! let padding = padding::(); // Although we ensure the data array is aligned when we allocate, // we can't do that with the empty singleton. So when it might not // be properly aligned, we substitute in the NonNull::dangling // which *is* aligned. // // To minimize dynamic branches on `cap` for all accesses // to the data, we include this guard which should only involve // compile-time constants. Ideally this should result in the branch // only be included for types with excessive alignment. let empty_header_is_aligned = if cfg!(feature = "gecko-ffi") { // in gecko-ffi mode `padding` will ensure this under // the assumption that the header has size 8 and the // static empty singleton is aligned to 8. true } else { // In non-gecko-ffi mode, the empty singleton is just // naturally aligned to the Header. If the Header is at // least as aligned as T *and* the padding would have // been 0, then one-past-the-end of the empty singleton // *is* a valid data pointer and we can remove the // `dangling` special case. mem::align_of::
() >= mem::align_of::() && padding == 0 }; unsafe { if !empty_header_is_aligned && self.header().cap() == 0 { NonNull::dangling().as_ptr() } else { // This could technically result in overflow, but padding // would have to be absurdly large for this to occur. let header_size = mem::size_of::
(); let ptr = self.ptr.as_ptr() as *mut u8; ptr.add(header_size + padding) as *mut T } } } // This is unsafe when the header is EMPTY_HEADER. unsafe fn header_mut(&mut self) -> &mut Header { &mut *self.ptr() } /// Returns the number of elements in the vector, also referred to /// as its 'length'. /// /// # Examples /// /// ``` /// use thin_vec::thin_vec; /// /// let a = thin_vec![1, 2, 3]; /// assert_eq!(a.len(), 3); /// ``` pub fn len(&self) -> usize { self.header().len() } /// Returns `true` if the vector contains no elements. /// /// # Examples /// /// ``` /// use thin_vec::ThinVec; /// /// let mut v = ThinVec::new(); /// assert!(v.is_empty()); /// /// v.push(1); /// assert!(!v.is_empty()); /// ``` pub fn is_empty(&self) -> bool { self.len() == 0 } /// Returns the number of elements the vector can hold without /// reallocating. /// /// # Examples /// /// ``` /// use thin_vec::ThinVec; /// /// let vec: ThinVec = ThinVec::with_capacity(10); /// assert_eq!(vec.capacity(), 10); /// ``` pub fn capacity(&self) -> usize { self.header().cap() } /// Forces the length of the vector to `new_len`. /// /// This is a low-level operation that maintains none of the normal /// invariants of the type. Normally changing the length of a vector /// is done using one of the safe operations instead, such as /// [`truncate`], [`resize`], [`extend`], or [`clear`]. /// /// [`truncate`]: ThinVec::truncate /// [`resize`]: ThinVec::resize /// [`extend`]: ThinVec::extend /// [`clear`]: ThinVec::clear /// /// # Safety /// /// - `new_len` must be less than or equal to [`capacity()`]. /// - The elements at `old_len..new_len` must be initialized. /// /// [`capacity()`]: ThinVec::capacity /// /// # Examples /// /// This method can be useful for situations in which the vector /// is serving as a buffer for other code, particularly over FFI: /// /// ```no_run /// use thin_vec::ThinVec; /// /// # // This is just a minimal skeleton for the doc example; /// # // don't use this as a starting point for a real library. /// # pub struct StreamWrapper { strm: *mut std::ffi::c_void } /// # const Z_OK: i32 = 0; /// # extern "C" { /// # fn deflateGetDictionary( /// # strm: *mut std::ffi::c_void, /// # dictionary: *mut u8, /// # dictLength: *mut usize, /// # ) -> i32; /// # } /// # impl StreamWrapper { /// pub fn get_dictionary(&self) -> Option> { /// // Per the FFI method's docs, "32768 bytes is always enough". /// let mut dict = ThinVec::with_capacity(32_768); /// let mut dict_length = 0; /// // SAFETY: When `deflateGetDictionary` returns `Z_OK`, it holds that: /// // 1. `dict_length` elements were initialized. /// // 2. `dict_length` <= the capacity (32_768) /// // which makes `set_len` safe to call. /// unsafe { /// // Make the FFI call... /// let r = deflateGetDictionary(self.strm, dict.as_mut_ptr(), &mut dict_length); /// if r == Z_OK { /// // ...and update the length to what was initialized. /// dict.set_len(dict_length); /// Some(dict) /// } else { /// None /// } /// } /// } /// # } /// ``` /// /// While the following example is sound, there is a memory leak since /// the inner vectors were not freed prior to the `set_len` call: /// /// ```no_run /// use thin_vec::thin_vec; /// /// let mut vec = thin_vec![thin_vec![1, 0, 0], /// thin_vec![0, 1, 0], /// thin_vec![0, 0, 1]]; /// // SAFETY: /// // 1. `old_len..0` is empty so no elements need to be initialized. /// // 2. `0 <= capacity` always holds whatever `capacity` is. /// unsafe { /// vec.set_len(0); /// } /// ``` /// /// Normally, here, one would use [`clear`] instead to correctly drop /// the contents and thus not leak memory. pub unsafe fn set_len(&mut self, len: usize) { if self.is_singleton() { // A prerequisite of `Vec::set_len` is that `new_len` must be // less than or equal to capacity(). The same applies here. assert!(len == 0, "invalid set_len({}) on empty ThinVec", len); } else { self.header_mut().set_len(len) } } // For internal use only, when setting the length and it's known to be the non-singleton. unsafe fn set_len_non_singleton(&mut self, len: usize) { self.header_mut().set_len(len) } /// Appends an element to the back of a collection. /// /// # Panics /// /// Panics if the new capacity exceeds `isize::MAX` bytes. /// /// # Examples /// /// ``` /// use thin_vec::thin_vec; /// /// let mut vec = thin_vec![1, 2]; /// vec.push(3); /// assert_eq!(vec, [1, 2, 3]); /// ``` pub fn push(&mut self, val: T) { let old_len = self.len(); if old_len == self.capacity() { self.reserve(1); } unsafe { ptr::write(self.data_raw().add(old_len), val); self.set_len_non_singleton(old_len + 1); } } /// Removes the last element from a vector and returns it, or [`None`] if it /// is empty. /// /// # Examples /// /// ``` /// use thin_vec::thin_vec; /// /// let mut vec = thin_vec![1, 2, 3]; /// assert_eq!(vec.pop(), Some(3)); /// assert_eq!(vec, [1, 2]); /// ``` pub fn pop(&mut self) -> Option { let old_len = self.len(); if old_len == 0 { return None; } unsafe { self.set_len_non_singleton(old_len - 1); Some(ptr::read(self.data_raw().add(old_len - 1))) } } /// Inserts an element at position `index` within the vector, shifting all /// elements after it to the right. /// /// # Panics /// /// Panics if `index > len`. /// /// # Examples /// /// ``` /// use thin_vec::thin_vec; /// /// let mut vec = thin_vec![1, 2, 3]; /// vec.insert(1, 4); /// assert_eq!(vec, [1, 4, 2, 3]); /// vec.insert(4, 5); /// assert_eq!(vec, [1, 4, 2, 3, 5]); /// ``` pub fn insert(&mut self, idx: usize, elem: T) { let old_len = self.len(); assert!(idx <= old_len, "Index out of bounds"); if old_len == self.capacity() { self.reserve(1); } unsafe { let ptr = self.data_raw(); ptr::copy(ptr.add(idx), ptr.add(idx + 1), old_len - idx); ptr::write(ptr.add(idx), elem); self.set_len_non_singleton(old_len + 1); } } /// Removes and returns the element at position `index` within the vector, /// shifting all elements after it to the left. /// /// Note: Because this shifts over the remaining elements, it has a /// worst-case performance of *O*(*n*). If you don't need the order of elements /// to be preserved, use [`swap_remove`] instead. If you'd like to remove /// elements from the beginning of the `ThinVec`, consider using `std::collections::VecDeque`. /// /// [`swap_remove`]: ThinVec::swap_remove /// /// # Panics /// /// Panics if `index` is out of bounds. /// /// # Examples /// /// ``` /// use thin_vec::thin_vec; /// /// let mut v = thin_vec![1, 2, 3]; /// assert_eq!(v.remove(1), 2); /// assert_eq!(v, [1, 3]); /// ``` pub fn remove(&mut self, idx: usize) -> T { let old_len = self.len(); assert!(idx < old_len, "Index out of bounds"); unsafe { self.set_len_non_singleton(old_len - 1); let ptr = self.data_raw(); let val = ptr::read(self.data_raw().add(idx)); ptr::copy(ptr.add(idx + 1), ptr.add(idx), old_len - idx - 1); val } } /// Removes an element from the vector and returns it. /// /// The removed element is replaced by the last element of the vector. /// /// This does not preserve ordering, but is *O*(1). /// If you need to preserve the element order, use [`remove`] instead. /// /// [`remove`]: ThinVec::remove /// /// # Panics /// /// Panics if `index` is out of bounds. /// /// # Examples /// /// ``` /// use thin_vec::thin_vec; /// /// let mut v = thin_vec!["foo", "bar", "baz", "qux"]; /// /// assert_eq!(v.swap_remove(1), "bar"); /// assert_eq!(v, ["foo", "qux", "baz"]); /// /// assert_eq!(v.swap_remove(0), "foo"); /// assert_eq!(v, ["baz", "qux"]); /// ``` pub fn swap_remove(&mut self, idx: usize) -> T { let old_len = self.len(); assert!(idx < old_len, "Index out of bounds"); unsafe { let ptr = self.data_raw(); ptr::swap(ptr.add(idx), ptr.add(old_len - 1)); self.set_len_non_singleton(old_len - 1); ptr::read(ptr.add(old_len - 1)) } } /// Shortens the vector, keeping the first `len` elements and dropping /// the rest. /// /// If `len` is greater than the vector's current length, this has no /// effect. /// /// The [`drain`] method can emulate `truncate`, but causes the excess /// elements to be returned instead of dropped. /// /// Note that this method has no effect on the allocated capacity /// of the vector. /// /// # Examples /// /// Truncating a five element vector to two elements: /// /// ``` /// use thin_vec::thin_vec; /// /// let mut vec = thin_vec![1, 2, 3, 4, 5]; /// vec.truncate(2); /// assert_eq!(vec, [1, 2]); /// ``` /// /// No truncation occurs when `len` is greater than the vector's current /// length: /// /// ``` /// use thin_vec::thin_vec; /// /// let mut vec = thin_vec![1, 2, 3]; /// vec.truncate(8); /// assert_eq!(vec, [1, 2, 3]); /// ``` /// /// Truncating when `len == 0` is equivalent to calling the [`clear`] /// method. /// /// ``` /// use thin_vec::thin_vec; /// /// let mut vec = thin_vec![1, 2, 3]; /// vec.truncate(0); /// assert_eq!(vec, []); /// ``` /// /// [`clear`]: ThinVec::clear /// [`drain`]: ThinVec::drain pub fn truncate(&mut self, len: usize) { unsafe { // drop any extra elements while len < self.len() { // decrement len before the drop_in_place(), so a panic on Drop // doesn't re-drop the just-failed value. let new_len = self.len() - 1; self.set_len_non_singleton(new_len); ptr::drop_in_place(self.data_raw().add(new_len)); } } } /// Clears the vector, removing all values. /// /// Note that this method has no effect on the allocated capacity /// of the vector. /// /// # Examples /// /// ``` /// use thin_vec::thin_vec; /// /// let mut v = thin_vec![1, 2, 3]; /// v.clear(); /// assert!(v.is_empty()); /// ``` pub fn clear(&mut self) { unsafe { ptr::drop_in_place(&mut self[..]); self.set_len(0); // could be the singleton } } /// Extracts a slice containing the entire vector. /// /// Equivalent to `&s[..]`. /// /// # Examples /// /// ``` /// use thin_vec::thin_vec; /// use std::io::{self, Write}; /// let buffer = thin_vec![1, 2, 3, 5, 8]; /// io::sink().write(buffer.as_slice()).unwrap(); /// ``` pub fn as_slice(&self) -> &[T] { unsafe { slice::from_raw_parts(self.data_raw(), self.len()) } } /// Extracts a mutable slice of the entire vector. /// /// Equivalent to `&mut s[..]`. /// /// # Examples /// /// ``` /// use thin_vec::thin_vec; /// use std::io::{self, Read}; /// let mut buffer = vec![0; 3]; /// io::repeat(0b101).read_exact(buffer.as_mut_slice()).unwrap(); /// ``` pub fn as_mut_slice(&mut self) -> &mut [T] { unsafe { slice::from_raw_parts_mut(self.data_raw(), self.len()) } } /// Reserve capacity for at least `additional` more elements to be inserted. /// /// May reserve more space than requested, to avoid frequent reallocations. /// /// Panics if the new capacity overflows `usize`. /// /// Re-allocates only if `self.capacity() < self.len() + additional`. #[cfg(not(feature = "gecko-ffi"))] pub fn reserve(&mut self, additional: usize) { let len = self.len(); let old_cap = self.capacity(); let min_cap = len.checked_add(additional).expect("capacity overflow"); if min_cap <= old_cap { return; } // Ensure the new capacity is at least double, to guarantee exponential growth. let double_cap = if old_cap == 0 { // skip to 4 because tiny ThinVecs are dumb; but not if that would cause overflow if mem::size_of::() > (!0) / 8 { 1 } else { 4 } } else { old_cap.saturating_mul(2) }; let new_cap = max(min_cap, double_cap); unsafe { self.reallocate(new_cap); } } /// Reserve capacity for at least `additional` more elements to be inserted. /// /// This method mimics the growth algorithm used by the C++ implementation /// of nsTArray. #[cfg(feature = "gecko-ffi")] pub fn reserve(&mut self, additional: usize) { let elem_size = mem::size_of::(); let len = self.len(); let old_cap = self.capacity(); let min_cap = len.checked_add(additional).expect("capacity overflow"); if min_cap <= old_cap { return; } // The growth logic can't handle zero-sized types, so we have to exit // early here. if elem_size == 0 { unsafe { self.reallocate(min_cap); } return; } let min_cap_bytes = assert_size(min_cap) .checked_mul(assert_size(elem_size)) .and_then(|x| x.checked_add(assert_size(mem::size_of::
()))) .unwrap(); // Perform some checked arithmetic to ensure all of the numbers we // compute will end up in range. let will_fit = min_cap_bytes.checked_mul(2).is_some(); if !will_fit { panic!("Exceeded maximum nsTArray size"); } const SLOW_GROWTH_THRESHOLD: usize = 8 * 1024 * 1024; let bytes = if min_cap > SLOW_GROWTH_THRESHOLD { // Grow by a minimum of 1.125x let old_cap_bytes = old_cap * elem_size + mem::size_of::
(); let min_growth = old_cap_bytes + (old_cap_bytes >> 3); let growth = max(min_growth, min_cap_bytes as usize); // Round up to the next megabyte. const MB: usize = 1 << 20; MB * ((growth + MB - 1) / MB) } else { // Try to allocate backing buffers in powers of two. min_cap_bytes.next_power_of_two() as usize }; let cap = (bytes - std::mem::size_of::
()) / elem_size; unsafe { self.reallocate(cap); } } /// Reserves the minimum capacity for `additional` more elements to be inserted. /// /// Panics if the new capacity overflows `usize`. /// /// Re-allocates only if `self.capacity() < self.len() + additional`. pub fn reserve_exact(&mut self, additional: usize) { let new_cap = self .len() .checked_add(additional) .expect("capacity overflow"); let old_cap = self.capacity(); if new_cap > old_cap { unsafe { self.reallocate(new_cap); } } } /// Shrinks the capacity of the vector as much as possible. /// /// It will drop down as close as possible to the length but the allocator /// may still inform the vector that there is space for a few more elements. /// /// # Examples /// /// ``` /// use thin_vec::ThinVec; /// /// let mut vec = ThinVec::with_capacity(10); /// vec.extend([1, 2, 3]); /// assert_eq!(vec.capacity(), 10); /// vec.shrink_to_fit(); /// assert!(vec.capacity() >= 3); /// ``` pub fn shrink_to_fit(&mut self) { let old_cap = self.capacity(); let new_cap = self.len(); if new_cap < old_cap { if new_cap == 0 { *self = ThinVec::new(); } else { unsafe { self.reallocate(new_cap); } } } } /// Retains only the elements specified by the predicate. /// /// In other words, remove all elements `e` such that `f(&e)` returns `false`. /// This method operates in place and preserves the order of the retained /// elements. /// /// # Examples /// // A hack to avoid linking problems with `cargo test --features=gecko-ffi`. #[cfg_attr(not(feature = "gecko-ffi"), doc = "```")] #[cfg_attr(feature = "gecko-ffi", doc = "```ignore")] /// # #[macro_use] extern crate thin_vec; /// # fn main() { /// let mut vec = thin_vec![1, 2, 3, 4]; /// vec.retain(|&x| x%2 == 0); /// assert_eq!(vec, [2, 4]); /// # } /// ``` pub fn retain(&mut self, mut f: F) where F: FnMut(&T) -> bool, { self.retain_mut(|x| f(&*x)); } /// Retains only the elements specified by the predicate, passing a mutable reference to it. /// /// In other words, remove all elements `e` such that `f(&mut e)` returns `false`. /// This method operates in place and preserves the order of the retained /// elements. /// /// # Examples /// // A hack to avoid linking problems with `cargo test --features=gecko-ffi`. #[cfg_attr(not(feature = "gecko-ffi"), doc = "```")] #[cfg_attr(feature = "gecko-ffi", doc = "```ignore")] /// # #[macro_use] extern crate thin_vec; /// # fn main() { /// let mut vec = thin_vec![1, 2, 3, 4, 5]; /// vec.retain_mut(|x| { /// *x += 1; /// (*x)%2 == 0 /// }); /// assert_eq!(vec, [2, 4, 6]); /// # } /// ``` pub fn retain_mut(&mut self, mut f: F) where F: FnMut(&mut T) -> bool, { let len = self.len(); let mut del = 0; { let v = &mut self[..]; for i in 0..len { if !f(&mut v[i]) { del += 1; } else if del > 0 { v.swap(i - del, i); } } } if del > 0 { self.truncate(len - del); } } /// Removes consecutive elements in the vector that resolve to the same key. /// /// If the vector is sorted, this removes all duplicates. /// /// # Examples /// // A hack to avoid linking problems with `cargo test --features=gecko-ffi`. #[cfg_attr(not(feature = "gecko-ffi"), doc = "```")] #[cfg_attr(feature = "gecko-ffi", doc = "```ignore")] /// # #[macro_use] extern crate thin_vec; /// # fn main() { /// let mut vec = thin_vec![10, 20, 21, 30, 20]; /// /// vec.dedup_by_key(|i| *i / 10); /// /// assert_eq!(vec, [10, 20, 30, 20]); /// # } /// ``` pub fn dedup_by_key(&mut self, mut key: F) where F: FnMut(&mut T) -> K, K: PartialEq, { self.dedup_by(|a, b| key(a) == key(b)) } /// Removes consecutive elements in the vector according to a predicate. /// /// The `same_bucket` function is passed references to two elements from the vector, and /// returns `true` if the elements compare equal, or `false` if they do not. Only the first /// of adjacent equal items is kept. /// /// If the vector is sorted, this removes all duplicates. /// /// # Examples /// // A hack to avoid linking problems with `cargo test --features=gecko-ffi`. #[cfg_attr(not(feature = "gecko-ffi"), doc = "```")] #[cfg_attr(feature = "gecko-ffi", doc = "```ignore")] /// # #[macro_use] extern crate thin_vec; /// # fn main() { /// let mut vec = thin_vec!["foo", "bar", "Bar", "baz", "bar"]; /// /// vec.dedup_by(|a, b| a.eq_ignore_ascii_case(b)); /// /// assert_eq!(vec, ["foo", "bar", "baz", "bar"]); /// # } /// ``` #[allow(clippy::swap_ptr_to_ref)] pub fn dedup_by(&mut self, mut same_bucket: F) where F: FnMut(&mut T, &mut T) -> bool, { // See the comments in `Vec::dedup` for a detailed explanation of this code. unsafe { let ln = self.len(); if ln <= 1 { return; } // Avoid bounds checks by using raw pointers. let p = self.as_mut_ptr(); let mut r: usize = 1; let mut w: usize = 1; while r < ln { let p_r = p.add(r); let p_wm1 = p.add(w - 1); if !same_bucket(&mut *p_r, &mut *p_wm1) { if r != w { let p_w = p_wm1.add(1); mem::swap(&mut *p_r, &mut *p_w); } w += 1; } r += 1; } self.truncate(w); } } /// Splits the collection into two at the given index. /// /// Returns a newly allocated vector containing the elements in the range /// `[at, len)`. After the call, the original vector will be left containing /// the elements `[0, at)` with its previous capacity unchanged. /// /// # Panics /// /// Panics if `at > len`. /// /// # Examples /// /// ``` /// use thin_vec::thin_vec; /// /// let mut vec = thin_vec![1, 2, 3]; /// let vec2 = vec.split_off(1); /// assert_eq!(vec, [1]); /// assert_eq!(vec2, [2, 3]); /// ``` pub fn split_off(&mut self, at: usize) -> ThinVec { let old_len = self.len(); let new_vec_len = old_len - at; assert!(at <= old_len, "Index out of bounds"); unsafe { let mut new_vec = ThinVec::with_capacity(new_vec_len); ptr::copy_nonoverlapping(self.data_raw().add(at), new_vec.data_raw(), new_vec_len); new_vec.set_len(new_vec_len); // could be the singleton self.set_len(at); // could be the singleton new_vec } } /// Moves all the elements of `other` into `self`, leaving `other` empty. /// /// # Panics /// /// Panics if the new capacity exceeds `isize::MAX` bytes. /// /// # Examples /// /// ``` /// use thin_vec::thin_vec; /// /// let mut vec = thin_vec![1, 2, 3]; /// let mut vec2 = thin_vec![4, 5, 6]; /// vec.append(&mut vec2); /// assert_eq!(vec, [1, 2, 3, 4, 5, 6]); /// assert_eq!(vec2, []); /// ``` pub fn append(&mut self, other: &mut ThinVec) { self.extend(other.drain(..)) } /// Removes the specified range from the vector in bulk, returning all /// removed elements as an iterator. If the iterator is dropped before /// being fully consumed, it drops the remaining removed elements. /// /// The returned iterator keeps a mutable borrow on the vector to optimize /// its implementation. /// /// # Panics /// /// Panics if the starting point is greater than the end point or if /// the end point is greater than the length of the vector. /// /// # Leaking /// /// If the returned iterator goes out of scope without being dropped (due to /// [`mem::forget`], for example), the vector may have lost and leaked /// elements arbitrarily, including elements outside the range. /// /// # Examples /// /// ``` /// use thin_vec::{ThinVec, thin_vec}; /// /// let mut v = thin_vec![1, 2, 3]; /// let u: ThinVec<_> = v.drain(1..).collect(); /// assert_eq!(v, &[1]); /// assert_eq!(u, &[2, 3]); /// /// // A full range clears the vector, like `clear()` does /// v.drain(..); /// assert_eq!(v, &[]); /// ``` pub fn drain(&mut self, range: R) -> Drain<'_, T> where R: RangeBounds, { // See comments in the Drain struct itself for details on this let len = self.len(); let start = match range.start_bound() { Bound::Included(&n) => n, Bound::Excluded(&n) => n + 1, Bound::Unbounded => 0, }; let end = match range.end_bound() { Bound::Included(&n) => n + 1, Bound::Excluded(&n) => n, Bound::Unbounded => len, }; assert!(start <= end); assert!(end <= len); unsafe { // Set our length to the start bound self.set_len(start); // could be the singleton let iter = slice::from_raw_parts_mut(self.data_raw().add(start), end - start).iter_mut(); Drain { iter, vec: self, end, tail: len - end, } } } /// Creates a splicing iterator that replaces the specified range in the vector /// with the given `replace_with` iterator and yields the removed items. /// `replace_with` does not need to be the same length as `range`. /// /// `range` is removed even if the iterator is not consumed until the end. /// /// It is unspecified how many elements are removed from the vector /// if the `Splice` value is leaked. /// /// The input iterator `replace_with` is only consumed when the `Splice` value is dropped. /// /// This is optimal if: /// /// * The tail (elements in the vector after `range`) is empty, /// * or `replace_with` yields fewer or equal elements than `range`’s length /// * or the lower bound of its `size_hint()` is exact. /// /// Otherwise, a temporary vector is allocated and the tail is moved twice. /// /// # Panics /// /// Panics if the starting point is greater than the end point or if /// the end point is greater than the length of the vector. /// /// # Examples /// /// ``` /// use thin_vec::{ThinVec, thin_vec}; /// /// let mut v = thin_vec![1, 2, 3, 4]; /// let new = [7, 8, 9]; /// let u: ThinVec<_> = v.splice(1..3, new).collect(); /// assert_eq!(v, &[1, 7, 8, 9, 4]); /// assert_eq!(u, &[2, 3]); /// ``` #[inline] pub fn splice(&mut self, range: R, replace_with: I) -> Splice<'_, I::IntoIter> where R: RangeBounds, I: IntoIterator, { Splice { drain: self.drain(range), replace_with: replace_with.into_iter(), } } /// Resize the buffer and update its capacity, without changing the length. /// Unsafe because it can cause length to be greater than capacity. unsafe fn reallocate(&mut self, new_cap: usize) { debug_assert!(new_cap > 0); if self.has_allocation() { let old_cap = self.capacity(); let ptr = realloc( self.ptr() as *mut u8, layout::(old_cap), alloc_size::(new_cap), ) as *mut Header; if ptr.is_null() { handle_alloc_error(layout::(new_cap)) } (*ptr).set_cap(new_cap); self.ptr = NonNull::new_unchecked(ptr); } else { let new_header = header_with_capacity::(new_cap); // If we get here and have a non-zero len, then we must be handling // a gecko auto array, and we have items in a stack buffer. We shouldn't // free it, but we should memcopy the contents out of it and mark it as empty. // // T is assumed to be trivially relocatable, as this is ~required // for Rust compatibility anyway. Furthermore, we assume C++ won't try // to unconditionally destroy the contents of the stack allocated buffer // (i.e. it's obfuscated behind a union). // // In effect, we are partially reimplementing the auto array move constructor // by leaving behind a valid empty instance. let len = self.len(); if cfg!(feature = "gecko-ffi") && len > 0 { new_header .as_ptr() .add(1) .cast::() .copy_from_nonoverlapping(self.data_raw(), len); self.set_len_non_singleton(0); } self.ptr = new_header; } } #[cfg(feature = "gecko-ffi")] #[inline] #[allow(unused_unsafe)] fn is_singleton(&self) -> bool { // NOTE: the tests will complain that this "unsafe" isn't needed, but it *IS*! // In production this refers to an *extern static* which *is* unsafe to reference. // In tests this refers to a local static because we don't have Firefox's codebase // providing the symbol! unsafe { self.ptr.as_ptr() as *const Header == &EMPTY_HEADER } } #[cfg(not(feature = "gecko-ffi"))] #[inline] fn is_singleton(&self) -> bool { self.ptr.as_ptr() as *const Header == &EMPTY_HEADER } #[cfg(feature = "gecko-ffi")] #[inline] fn has_allocation(&self) -> bool { unsafe { !self.is_singleton() && !self.ptr.as_ref().uses_stack_allocated_buffer() } } #[cfg(not(feature = "gecko-ffi"))] #[inline] fn has_allocation(&self) -> bool { !self.is_singleton() } } impl ThinVec { /// Resizes the `Vec` in-place so that `len()` is equal to `new_len`. /// /// If `new_len` is greater than `len()`, the `Vec` is extended by the /// difference, with each additional slot filled with `value`. /// If `new_len` is less than `len()`, the `Vec` is simply truncated. /// /// # Examples /// // A hack to avoid linking problems with `cargo test --features=gecko-ffi`. #[cfg_attr(not(feature = "gecko-ffi"), doc = "```")] #[cfg_attr(feature = "gecko-ffi", doc = "```ignore")] /// # #[macro_use] extern crate thin_vec; /// # fn main() { /// let mut vec = thin_vec!["hello"]; /// vec.resize(3, "world"); /// assert_eq!(vec, ["hello", "world", "world"]); /// /// let mut vec = thin_vec![1, 2, 3, 4]; /// vec.resize(2, 0); /// assert_eq!(vec, [1, 2]); /// # } /// ``` pub fn resize(&mut self, new_len: usize, value: T) { let old_len = self.len(); if new_len > old_len { let additional = new_len - old_len; self.reserve(additional); for _ in 1..additional { self.push(value.clone()); } // We can write the last element directly without cloning needlessly if additional > 0 { self.push(value); } } else if new_len < old_len { self.truncate(new_len); } } /// Clones and appends all elements in a slice to the `ThinVec`. /// /// Iterates over the slice `other`, clones each element, and then appends /// it to this `ThinVec`. The `other` slice is traversed in-order. /// /// Note that this function is same as [`extend`] except that it is /// specialized to work with slices instead. If and when Rust gets /// specialization this function will likely be deprecated (but still /// available). /// /// # Examples /// /// ``` /// use thin_vec::thin_vec; /// /// let mut vec = thin_vec![1]; /// vec.extend_from_slice(&[2, 3, 4]); /// assert_eq!(vec, [1, 2, 3, 4]); /// ``` /// /// [`extend`]: ThinVec::extend pub fn extend_from_slice(&mut self, other: &[T]) { self.extend(other.iter().cloned()) } } impl ThinVec { /// Removes consecutive repeated elements in the vector. /// /// If the vector is sorted, this removes all duplicates. /// /// # Examples /// // A hack to avoid linking problems with `cargo test --features=gecko-ffi`. #[cfg_attr(not(feature = "gecko-ffi"), doc = "```")] #[cfg_attr(feature = "gecko-ffi", doc = "```ignore")] /// # #[macro_use] extern crate thin_vec; /// # fn main() { /// let mut vec = thin_vec![1, 2, 2, 3, 2]; /// /// vec.dedup(); /// /// assert_eq!(vec, [1, 2, 3, 2]); /// # } /// ``` pub fn dedup(&mut self) { self.dedup_by(|a, b| a == b) } } impl Drop for ThinVec { #[inline] fn drop(&mut self) { #[cold] #[inline(never)] fn drop_non_singleton(this: &mut ThinVec) { unsafe { ptr::drop_in_place(&mut this[..]); #[cfg(feature = "gecko-ffi")] if this.ptr.as_ref().uses_stack_allocated_buffer() { return; } dealloc(this.ptr() as *mut u8, layout::(this.capacity())) } } if !self.is_singleton() { drop_non_singleton(self); } } } impl Deref for ThinVec { type Target = [T]; fn deref(&self) -> &[T] { self.as_slice() } } impl DerefMut for ThinVec { fn deref_mut(&mut self) -> &mut [T] { self.as_mut_slice() } } impl Borrow<[T]> for ThinVec { fn borrow(&self) -> &[T] { self.as_slice() } } impl BorrowMut<[T]> for ThinVec { fn borrow_mut(&mut self) -> &mut [T] { self.as_mut_slice() } } impl AsRef<[T]> for ThinVec { fn as_ref(&self) -> &[T] { self.as_slice() } } impl Extend for ThinVec { #[inline] fn extend(&mut self, iter: I) where I: IntoIterator, { let iter = iter.into_iter(); let hint = iter.size_hint().0; if hint > 0 { self.reserve(hint); } for x in iter { self.push(x); } } } impl fmt::Debug for ThinVec { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&**self, f) } } impl Hash for ThinVec where T: Hash, { fn hash(&self, state: &mut H) where H: Hasher, { self[..].hash(state); } } impl PartialOrd for ThinVec where T: PartialOrd, { #[inline] fn partial_cmp(&self, other: &ThinVec) -> Option { self[..].partial_cmp(&other[..]) } } impl Ord for ThinVec where T: Ord, { #[inline] fn cmp(&self, other: &ThinVec) -> Ordering { self[..].cmp(&other[..]) } } impl PartialEq> for ThinVec where A: PartialEq, { #[inline] fn eq(&self, other: &ThinVec) -> bool { self[..] == other[..] } } impl PartialEq> for ThinVec where A: PartialEq, { #[inline] fn eq(&self, other: &Vec) -> bool { self[..] == other[..] } } impl PartialEq<[B]> for ThinVec where A: PartialEq, { #[inline] fn eq(&self, other: &[B]) -> bool { self[..] == other[..] } } impl<'a, A, B> PartialEq<&'a [B]> for ThinVec where A: PartialEq, { #[inline] fn eq(&self, other: &&'a [B]) -> bool { self[..] == other[..] } } // Serde impls based on // https://github.com/bluss/arrayvec/blob/67ec907a98c0f40c4b76066fed3c1af59d35cf6a/src/arrayvec.rs#L1222-L1267 #[cfg(feature = "serde")] impl serde::Serialize for ThinVec { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, { serializer.collect_seq(self.as_slice()) } } #[cfg(feature = "serde")] impl<'de, T: serde::Deserialize<'de>> serde::Deserialize<'de> for ThinVec { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { use serde::de::{SeqAccess, Visitor}; use serde::Deserialize; struct ThinVecVisitor(PhantomData); impl<'de, T: Deserialize<'de>> Visitor<'de> for ThinVecVisitor { type Value = ThinVec; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { write!(formatter, "a sequence") } fn visit_seq(self, mut seq: SA) -> Result where SA: SeqAccess<'de>, { // Same policy as // https://github.com/serde-rs/serde/blob/ce0844b9ecc32377b5e4545d759d385a8c46bc6a/serde/src/private/size_hint.rs#L13 let initial_capacity = seq.size_hint().unwrap_or_default().min(4096); let mut values = ThinVec::::with_capacity(initial_capacity); while let Some(value) = seq.next_element()? { values.push(value); } Ok(values) } } deserializer.deserialize_seq(ThinVecVisitor::(PhantomData)) } } macro_rules! array_impls { ($($N:expr)*) => {$( impl PartialEq<[B; $N]> for ThinVec where A: PartialEq { #[inline] fn eq(&self, other: &[B; $N]) -> bool { self[..] == other[..] } } impl<'a, A, B> PartialEq<&'a [B; $N]> for ThinVec where A: PartialEq { #[inline] fn eq(&self, other: &&'a [B; $N]) -> bool { self[..] == other[..] } } )*} } array_impls! { 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 } impl Eq for ThinVec where T: Eq {} impl IntoIterator for ThinVec { type Item = T; type IntoIter = IntoIter; fn into_iter(self) -> IntoIter { IntoIter { vec: self, start: 0, } } } impl<'a, T> IntoIterator for &'a ThinVec { type Item = &'a T; type IntoIter = slice::Iter<'a, T>; fn into_iter(self) -> slice::Iter<'a, T> { self.iter() } } impl<'a, T> IntoIterator for &'a mut ThinVec { type Item = &'a mut T; type IntoIter = slice::IterMut<'a, T>; fn into_iter(self) -> slice::IterMut<'a, T> { self.iter_mut() } } impl Clone for ThinVec where T: Clone, { #[inline] fn clone(&self) -> ThinVec { #[cold] #[inline(never)] fn clone_non_singleton(this: &ThinVec) -> ThinVec { let len = this.len(); let mut new_vec = ThinVec::::with_capacity(len); let mut data_raw = new_vec.data_raw(); for x in this.iter() { unsafe { ptr::write(data_raw, x.clone()); data_raw = data_raw.add(1); } } unsafe { // `this` is not the singleton, but `new_vec` will be if // `this` is empty. new_vec.set_len(len); // could be the singleton } new_vec } if self.is_singleton() { ThinVec::new() } else { clone_non_singleton(self) } } } impl Default for ThinVec { fn default() -> ThinVec { ThinVec::new() } } impl FromIterator for ThinVec { #[inline] fn from_iter>(iter: I) -> ThinVec { let mut vec = ThinVec::new(); vec.extend(iter.into_iter()); vec } } impl From<&[T]> for ThinVec { /// Allocate a `ThinVec` and fill it by cloning `s`'s items. /// /// # Examples /// /// ``` /// use thin_vec::{ThinVec, thin_vec}; /// /// assert_eq!(ThinVec::from(&[1, 2, 3][..]), thin_vec![1, 2, 3]); /// ``` fn from(s: &[T]) -> ThinVec { s.iter().cloned().collect() } } #[cfg(not(no_global_oom_handling))] impl From<&mut [T]> for ThinVec { /// Allocate a `ThinVec` and fill it by cloning `s`'s items. /// /// # Examples /// /// ``` /// use thin_vec::{ThinVec, thin_vec}; /// /// assert_eq!(ThinVec::from(&mut [1, 2, 3][..]), thin_vec![1, 2, 3]); /// ``` fn from(s: &mut [T]) -> ThinVec { s.iter().cloned().collect() } } impl From<[T; N]> for ThinVec { /// Allocate a `ThinVec` and move `s`'s items into it. /// /// # Examples /// /// ``` /// use thin_vec::{ThinVec, thin_vec}; /// /// assert_eq!(ThinVec::from([1, 2, 3]), thin_vec![1, 2, 3]); /// ``` fn from(s: [T; N]) -> ThinVec { std::iter::IntoIterator::into_iter(s).collect() } } impl From> for ThinVec { /// Convert a boxed slice into a vector by transferring ownership of /// the existing heap allocation. /// /// **NOTE:** unlike `std`, this must reallocate to change the layout! /// /// # Examples /// /// ``` /// use thin_vec::{ThinVec, thin_vec}; /// /// let b: Box<[i32]> = thin_vec![1, 2, 3].into_iter().collect(); /// assert_eq!(ThinVec::from(b), thin_vec![1, 2, 3]); /// ``` fn from(s: Box<[T]>) -> Self { // Can just lean on the fact that `Box<[T]>` -> `Vec` is Free. Vec::from(s).into_iter().collect() } } impl From> for ThinVec { /// Convert a `std::Vec` into a `ThinVec`. /// /// **NOTE:** this must reallocate to change the layout! /// /// # Examples /// /// ``` /// use thin_vec::{ThinVec, thin_vec}; /// /// let b: Vec = vec![1, 2, 3]; /// assert_eq!(ThinVec::from(b), thin_vec![1, 2, 3]); /// ``` fn from(s: Vec) -> Self { s.into_iter().collect() } } impl From> for Vec { /// Convert a `ThinVec` into a `std::Vec`. /// /// **NOTE:** this must reallocate to change the layout! /// /// # Examples /// /// ``` /// use thin_vec::{ThinVec, thin_vec}; /// /// let b: ThinVec = thin_vec![1, 2, 3]; /// assert_eq!(Vec::from(b), vec![1, 2, 3]); /// ``` fn from(s: ThinVec) -> Self { s.into_iter().collect() } } impl From> for Box<[T]> { /// Convert a vector into a boxed slice. /// /// If `v` has excess capacity, its items will be moved into a /// newly-allocated buffer with exactly the right capacity. /// /// **NOTE:** unlike `std`, this must reallocate to change the layout! /// /// # Examples /// /// ``` /// use thin_vec::{ThinVec, thin_vec}; /// assert_eq!(Box::from(thin_vec![1, 2, 3]), thin_vec![1, 2, 3].into_iter().collect()); /// ``` fn from(v: ThinVec) -> Self { v.into_iter().collect() } } impl From<&str> for ThinVec { /// Allocate a `ThinVec` and fill it with a UTF-8 string. /// /// # Examples /// /// ``` /// use thin_vec::{ThinVec, thin_vec}; /// /// assert_eq!(ThinVec::from("123"), thin_vec![b'1', b'2', b'3']); /// ``` fn from(s: &str) -> ThinVec { From::from(s.as_bytes()) } } impl TryFrom> for [T; N] { type Error = ThinVec; /// Gets the entire contents of the `ThinVec` as an array, /// if its size exactly matches that of the requested array. /// /// # Examples /// /// ``` /// use thin_vec::{ThinVec, thin_vec}; /// use std::convert::TryInto; /// /// assert_eq!(thin_vec![1, 2, 3].try_into(), Ok([1, 2, 3])); /// assert_eq!(>::new().try_into(), Ok([])); /// ``` /// /// If the length doesn't match, the input comes back in `Err`: /// ``` /// use thin_vec::{ThinVec, thin_vec}; /// use std::convert::TryInto; /// /// let r: Result<[i32; 4], _> = (0..10).collect::>().try_into(); /// assert_eq!(r, Err(thin_vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9])); /// ``` /// /// If you're fine with just getting a prefix of the `ThinVec`, /// you can call [`.truncate(N)`](ThinVec::truncate) first. /// ``` /// use thin_vec::{ThinVec, thin_vec}; /// use std::convert::TryInto; /// /// let mut v = ThinVec::from("hello world"); /// v.sort(); /// v.truncate(2); /// let [a, b]: [_; 2] = v.try_into().unwrap(); /// assert_eq!(a, b' '); /// assert_eq!(b, b'd'); /// ``` fn try_from(mut vec: ThinVec) -> Result<[T; N], ThinVec> { if vec.len() != N { return Err(vec); } // SAFETY: `.set_len(0)` is always sound. unsafe { vec.set_len(0) }; // SAFETY: A `ThinVec`'s pointer is always aligned properly, and // the alignment the array needs is the same as the items. // We checked earlier that we have sufficient items. // The items will not double-drop as the `set_len` // tells the `ThinVec` not to also drop them. let array = unsafe { ptr::read(vec.data_raw() as *const [T; N]) }; Ok(array) } } /// An iterator that moves out of a vector. /// /// This `struct` is created by the [`ThinVec::into_iter`][] /// (provided by the [`IntoIterator`] trait). /// /// # Example /// /// ``` /// use thin_vec::thin_vec; /// /// let v = thin_vec![0, 1, 2]; /// let iter: thin_vec::IntoIter<_> = v.into_iter(); /// ``` pub struct IntoIter { vec: ThinVec, start: usize, } impl IntoIter { /// Returns the remaining items of this iterator as a slice. /// /// # Examples /// /// ``` /// use thin_vec::thin_vec; /// /// let vec = thin_vec!['a', 'b', 'c']; /// let mut into_iter = vec.into_iter(); /// assert_eq!(into_iter.as_slice(), &['a', 'b', 'c']); /// let _ = into_iter.next().unwrap(); /// assert_eq!(into_iter.as_slice(), &['b', 'c']); /// ``` pub fn as_slice(&self) -> &[T] { unsafe { slice::from_raw_parts(self.vec.data_raw().add(self.start), self.len()) } } /// Returns the remaining items of this iterator as a mutable slice. /// /// # Examples /// /// ``` /// use thin_vec::thin_vec; /// /// let vec = thin_vec!['a', 'b', 'c']; /// let mut into_iter = vec.into_iter(); /// assert_eq!(into_iter.as_slice(), &['a', 'b', 'c']); /// into_iter.as_mut_slice()[2] = 'z'; /// assert_eq!(into_iter.next().unwrap(), 'a'); /// assert_eq!(into_iter.next().unwrap(), 'b'); /// assert_eq!(into_iter.next().unwrap(), 'z'); /// ``` pub fn as_mut_slice(&mut self) -> &mut [T] { unsafe { &mut *self.as_raw_mut_slice() } } fn as_raw_mut_slice(&mut self) -> *mut [T] { unsafe { ptr::slice_from_raw_parts_mut(self.vec.data_raw().add(self.start), self.len()) } } } impl Iterator for IntoIter { type Item = T; fn next(&mut self) -> Option { if self.start == self.vec.len() { None } else { unsafe { let old_start = self.start; self.start += 1; Some(ptr::read(self.vec.data_raw().add(old_start))) } } } fn size_hint(&self) -> (usize, Option) { let len = self.vec.len() - self.start; (len, Some(len)) } } impl DoubleEndedIterator for IntoIter { fn next_back(&mut self) -> Option { if self.start == self.vec.len() { None } else { self.vec.pop() } } } impl ExactSizeIterator for IntoIter {} impl std::iter::FusedIterator for IntoIter {} // SAFETY: the length calculation is trivial, we're an array! And if it's wrong we're So Screwed. #[cfg(feature = "unstable")] unsafe impl std::iter::TrustedLen for IntoIter {} impl Drop for IntoIter { #[inline] fn drop(&mut self) { #[cold] #[inline(never)] fn drop_non_singleton(this: &mut IntoIter) { unsafe { let mut vec = mem::replace(&mut this.vec, ThinVec::new()); ptr::drop_in_place(&mut vec[this.start..]); vec.set_len_non_singleton(0) } } if !self.vec.is_singleton() { drop_non_singleton(self); } } } impl fmt::Debug for IntoIter { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("IntoIter").field(&self.as_slice()).finish() } } impl AsRef<[T]> for IntoIter { fn as_ref(&self) -> &[T] { self.as_slice() } } impl Clone for IntoIter { #[allow(clippy::into_iter_on_ref)] fn clone(&self) -> Self { // Just create a new `ThinVec` from the remaining elements and IntoIter it self.as_slice() .into_iter() .cloned() .collect::>() .into_iter() } } /// A draining iterator for `ThinVec`. /// /// This `struct` is created by [`ThinVec::drain`]. /// See its documentation for more. /// /// # Example /// /// ``` /// use thin_vec::thin_vec; /// /// let mut v = thin_vec![0, 1, 2]; /// let iter: thin_vec::Drain<_> = v.drain(..); /// ``` pub struct Drain<'a, T> { // Ok so ThinVec::drain takes a range of the ThinVec and yields the contents by-value, // then backshifts the array. During iteration the array is in an unsound state // (big deinitialized hole in it), and this is very dangerous. // // Our first line of defense is the borrow checker: we have a mutable borrow, so nothing // can access the ThinVec while we exist. As long as we make sure the ThinVec is in a valid // state again before we release the borrow, everything should be A-OK! We do this cleanup // in our Drop impl. // // Unfortunately, that's unsound, because mem::forget exists and The Leakpocalypse Is Real. // So we can't actually guarantee our destructor runs before our borrow expires. Thankfully // this isn't fatal: we can just set the ThinVec's len to 0 at the start, so if anyone // leaks the Drain, we just leak everything the ThinVec contained out of spite! If they // *don't* leak us then we can properly repair the len in our Drop impl. This is known // as "leak amplification", and is the same approach std uses. // // But we can do slightly better than setting the len to 0! The drain breaks us up into // these parts: // // ```text // // [A, B, C, D, E, F, G, H, _, _] // ____ __________ ____ ____ // | | | | // prefix drain tail spare-cap // ``` // // As the drain iterator is consumed from both ends (DoubleEnded!), we'll start to look // like this: // // ```text // [A, B, _, _, E, _, G, H, _, _] // ____ __________ ____ ____ // | | | | // prefix drain tail spare-cap // ``` // // Note that the prefix is always valid and untouched, as such we can set the len // to the prefix when doing leak-amplification. As a bonus, we can use this value // to remember where the drain range starts. At the end we'll look like this // (we exhaust ourselves in our Drop impl): // // ```text // [A, B, _, _, _, _, G, H, _, _] // _____ __________ _____ ____ // | | | | // len drain tail spare-cap // ``` // // And need to become this: // // ```text // [A, B, G, H, _, _, _, _, _, _] // ___________ ________________ // | | // len spare-cap // ``` // // All this requires is moving the tail back to the prefix (stored in `len`) // and setting `len` to `len + tail_len` to undo the leak amplification. /// An iterator over the elements we're removing. /// /// As we go we'll be `read`ing out of the mutable refs yielded by this. /// It's ok to use IterMut here because it promises to only take mutable /// refs to the parts we haven't yielded yet. /// /// A downside of this (and the *mut below) is that it makes this type invariant, when /// technically it could be covariant? iter: IterMut<'a, T>, /// The actual ThinVec, which we need to hold onto to undo the leak amplification /// and backshift the tail into place. This should only be accessed when we're /// completely done with the IterMut in the `drop` impl of this type (or miri will get mad). /// /// Since we set the `len` of this to be before `IterMut`, we can use that `len` /// to retrieve the index of the start of the drain range later. vec: *mut ThinVec, /// The one-past-the-end index of the drain range, or equivalently the start of the tail. end: usize, /// The length of the tail. tail: usize, } impl<'a, T> Iterator for Drain<'a, T> { type Item = T; fn next(&mut self) -> Option { self.iter.next().map(|x| unsafe { ptr::read(x) }) } fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } } impl<'a, T> DoubleEndedIterator for Drain<'a, T> { fn next_back(&mut self) -> Option { self.iter.next_back().map(|x| unsafe { ptr::read(x) }) } } impl<'a, T> ExactSizeIterator for Drain<'a, T> {} // SAFETY: we need to keep track of this perfectly Or Else anyway! #[cfg(feature = "unstable")] unsafe impl std::iter::TrustedLen for Drain<'_, T> {} impl std::iter::FusedIterator for Drain<'_, T> {} impl<'a, T> Drop for Drain<'a, T> { fn drop(&mut self) { // Consume the rest of the iterator. for _ in self.by_ref() {} // Move the tail over the drained items, and update the length. unsafe { let vec = &mut *self.vec; // Don't mutate the empty singleton! if !vec.is_singleton() { let old_len = vec.len(); let start = vec.data_raw().add(old_len); let end = vec.data_raw().add(self.end); ptr::copy(end, start, self.tail); vec.set_len_non_singleton(old_len + self.tail); } } } } impl fmt::Debug for Drain<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("Drain").field(&self.iter.as_slice()).finish() } } impl<'a, T> Drain<'a, T> { /// Returns the remaining items of this iterator as a slice. /// /// # Examples /// /// ``` /// use thin_vec::thin_vec; /// /// let mut vec = thin_vec!['a', 'b', 'c']; /// let mut drain = vec.drain(..); /// assert_eq!(drain.as_slice(), &['a', 'b', 'c']); /// let _ = drain.next().unwrap(); /// assert_eq!(drain.as_slice(), &['b', 'c']); /// ``` #[must_use] pub fn as_slice(&self) -> &[T] { // SAFETY: this is A-OK because the elements that the underlying // iterator still points at are still logically initialized and contiguous. self.iter.as_slice() } } impl<'a, T> AsRef<[T]> for Drain<'a, T> { fn as_ref(&self) -> &[T] { self.as_slice() } } /// A splicing iterator for `ThinVec`. /// /// This struct is created by [`ThinVec::splice`][]. /// See its documentation for more. /// /// # Example /// /// ``` /// use thin_vec::thin_vec; /// /// let mut v = thin_vec![0, 1, 2]; /// let new = [7, 8]; /// let iter: thin_vec::Splice<_> = v.splice(1.., new); /// ``` #[derive(Debug)] pub struct Splice<'a, I: Iterator + 'a> { drain: Drain<'a, I::Item>, replace_with: I, } impl Iterator for Splice<'_, I> { type Item = I::Item; fn next(&mut self) -> Option { self.drain.next() } fn size_hint(&self) -> (usize, Option) { self.drain.size_hint() } } impl DoubleEndedIterator for Splice<'_, I> { fn next_back(&mut self) -> Option { self.drain.next_back() } } impl ExactSizeIterator for Splice<'_, I> {} impl Drop for Splice<'_, I> { fn drop(&mut self) { // Ensure we've fully drained out the range self.drain.by_ref().for_each(drop); unsafe { // If there's no tail elements, then the inner ThinVec is already // correct and we can just extend it like normal. if self.drain.tail == 0 { (*self.drain.vec).extend(self.replace_with.by_ref()); return; } // First fill the range left by drain(). if !self.drain.fill(&mut self.replace_with) { return; } // There may be more elements. Use the lower bound as an estimate. let (lower_bound, _upper_bound) = self.replace_with.size_hint(); if lower_bound > 0 { self.drain.move_tail(lower_bound); if !self.drain.fill(&mut self.replace_with) { return; } } // Collect any remaining elements. // This is a zero-length vector which does not allocate if `lower_bound` was exact. let mut collected = self .replace_with .by_ref() .collect::>() .into_iter(); // Now we have an exact count. if collected.len() > 0 { self.drain.move_tail(collected.len()); let filled = self.drain.fill(&mut collected); debug_assert!(filled); debug_assert_eq!(collected.len(), 0); } } // Let `Drain::drop` move the tail back if necessary and restore `vec.len`. } } /// Private helper methods for `Splice::drop` impl Drain<'_, T> { /// The range from `self.vec.len` to `self.tail_start` contains elements /// that have been moved out. /// Fill that range as much as possible with new elements from the `replace_with` iterator. /// Returns `true` if we filled the entire range. (`replace_with.next()` didn’t return `None`.) unsafe fn fill>(&mut self, replace_with: &mut I) -> bool { let vec = unsafe { &mut *self.vec }; let range_start = vec.len(); let range_end = self.end; let range_slice = unsafe { slice::from_raw_parts_mut(vec.data_raw().add(range_start), range_end - range_start) }; for place in range_slice { if let Some(new_item) = replace_with.next() { unsafe { ptr::write(place, new_item) }; vec.set_len(vec.len() + 1); } else { return false; } } true } /// Makes room for inserting more elements before the tail. unsafe fn move_tail(&mut self, additional: usize) { let vec = unsafe { &mut *self.vec }; let len = self.end + self.tail; vec.reserve(len.checked_add(additional).expect("capacity overflow")); let new_tail_start = self.end + additional; unsafe { let src = vec.data_raw().add(self.end); let dst = vec.data_raw().add(new_tail_start); ptr::copy(src, dst, self.tail); } self.end = new_tail_start; } } /// Write is implemented for `ThinVec` by appending to the vector. /// The vector will grow as needed. /// This implementation is identical to the one for `Vec`. impl io::Write for ThinVec { #[inline] fn write(&mut self, buf: &[u8]) -> io::Result { self.extend_from_slice(buf); Ok(buf.len()) } #[inline] fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { self.extend_from_slice(buf); Ok(()) } #[inline] fn flush(&mut self) -> io::Result<()> { Ok(()) } } // TODO: a million Index impls #[cfg(test)] mod tests { use super::{ThinVec, MAX_CAP}; #[test] fn test_size_of() { use std::mem::size_of; assert_eq!(size_of::>(), size_of::<&u8>()); assert_eq!(size_of::>>(), size_of::<&u8>()); } #[test] fn test_drop_empty() { ThinVec::::new(); } #[test] fn test_data_ptr_alignment() { let v = ThinVec::::new(); assert!(v.data_raw() as usize % 2 == 0); let v = ThinVec::::new(); assert!(v.data_raw() as usize % 4 == 0); let v = ThinVec::::new(); assert!(v.data_raw() as usize % 8 == 0); } #[test] #[cfg_attr(feature = "gecko-ffi", should_panic)] fn test_overaligned_type_is_rejected_for_gecko_ffi_mode() { #[repr(align(16))] struct Align16(u8); let v = ThinVec::::new(); assert!(v.data_raw() as usize % 16 == 0); } #[test] fn test_partial_eq() { assert_eq!(thin_vec![0], thin_vec![0]); assert_ne!(thin_vec![0], thin_vec![1]); assert_eq!(thin_vec![1, 2, 3], vec![1, 2, 3]); } #[test] fn test_alloc() { let mut v = ThinVec::new(); assert!(!v.has_allocation()); v.push(1); assert!(v.has_allocation()); v.pop(); assert!(v.has_allocation()); v.shrink_to_fit(); assert!(!v.has_allocation()); v.reserve(64); assert!(v.has_allocation()); v = ThinVec::with_capacity(64); assert!(v.has_allocation()); v = ThinVec::with_capacity(0); assert!(!v.has_allocation()); } #[test] fn test_drain_items() { let mut vec = thin_vec![1, 2, 3]; let mut vec2 = thin_vec![]; for i in vec.drain(..) { vec2.push(i); } assert_eq!(vec, []); assert_eq!(vec2, [1, 2, 3]); } #[test] fn test_drain_items_reverse() { let mut vec = thin_vec![1, 2, 3]; let mut vec2 = thin_vec![]; for i in vec.drain(..).rev() { vec2.push(i); } assert_eq!(vec, []); assert_eq!(vec2, [3, 2, 1]); } #[test] fn test_drain_items_zero_sized() { let mut vec = thin_vec![(), (), ()]; let mut vec2 = thin_vec![]; for i in vec.drain(..) { vec2.push(i); } assert_eq!(vec, []); assert_eq!(vec2, [(), (), ()]); } #[test] #[should_panic] fn test_drain_out_of_bounds() { let mut v = thin_vec![1, 2, 3, 4, 5]; v.drain(5..6); } #[test] fn test_drain_range() { let mut v = thin_vec![1, 2, 3, 4, 5]; for _ in v.drain(4..) {} assert_eq!(v, &[1, 2, 3, 4]); let mut v: ThinVec<_> = (1..6).map(|x| x.to_string()).collect(); for _ in v.drain(1..4) {} assert_eq!(v, &[1.to_string(), 5.to_string()]); let mut v: ThinVec<_> = (1..6).map(|x| x.to_string()).collect(); for _ in v.drain(1..4).rev() {} assert_eq!(v, &[1.to_string(), 5.to_string()]); let mut v: ThinVec<_> = thin_vec![(); 5]; for _ in v.drain(1..4).rev() {} assert_eq!(v, &[(), ()]); } #[test] fn test_drain_max_vec_size() { let mut v = ThinVec::<()>::with_capacity(MAX_CAP); unsafe { v.set_len(MAX_CAP); } for _ in v.drain(MAX_CAP - 1..) {} assert_eq!(v.len(), MAX_CAP - 1); } #[test] fn test_clear() { let mut v = ThinVec::::new(); assert_eq!(v.len(), 0); assert_eq!(v.capacity(), 0); assert_eq!(&v[..], &[]); v.clear(); assert_eq!(v.len(), 0); assert_eq!(v.capacity(), 0); assert_eq!(&v[..], &[]); v.push(1); v.push(2); assert_eq!(v.len(), 2); assert!(v.capacity() >= 2); assert_eq!(&v[..], &[1, 2]); v.clear(); assert_eq!(v.len(), 0); assert!(v.capacity() >= 2); assert_eq!(&v[..], &[]); v.push(3); v.push(4); assert_eq!(v.len(), 2); assert!(v.capacity() >= 2); assert_eq!(&v[..], &[3, 4]); v.clear(); assert_eq!(v.len(), 0); assert!(v.capacity() >= 2); assert_eq!(&v[..], &[]); v.clear(); assert_eq!(v.len(), 0); assert!(v.capacity() >= 2); assert_eq!(&v[..], &[]); } #[test] fn test_empty_singleton_torture() { { let mut v = ThinVec::::new(); assert_eq!(v.len(), 0); assert_eq!(v.capacity(), 0); assert!(v.is_empty()); assert_eq!(&v[..], &[]); assert_eq!(&mut v[..], &mut []); assert_eq!(v.pop(), None); assert_eq!(v.len(), 0); assert_eq!(v.capacity(), 0); assert_eq!(&v[..], &[]); } { let v = ThinVec::::new(); assert_eq!(v.into_iter().count(), 0); let v = ThinVec::::new(); for _ in v.into_iter() { unreachable!(); } } { let mut v = ThinVec::::new(); assert_eq!(v.drain(..).len(), 0); for _ in v.drain(..) { unreachable!() } assert_eq!(v.len(), 0); assert_eq!(v.capacity(), 0); assert_eq!(&v[..], &[]); } { let mut v = ThinVec::::new(); assert_eq!(v.splice(.., []).len(), 0); for _ in v.splice(.., []) { unreachable!() } assert_eq!(v.len(), 0); assert_eq!(v.capacity(), 0); assert_eq!(&v[..], &[]); } { let mut v = ThinVec::::new(); v.truncate(1); assert_eq!(v.len(), 0); assert_eq!(v.capacity(), 0); assert_eq!(&v[..], &[]); v.truncate(0); assert_eq!(v.len(), 0); assert_eq!(v.capacity(), 0); assert_eq!(&v[..], &[]); } { let mut v = ThinVec::::new(); v.shrink_to_fit(); assert_eq!(v.len(), 0); assert_eq!(v.capacity(), 0); assert_eq!(&v[..], &[]); } { let mut v = ThinVec::::new(); let new = v.split_off(0); assert_eq!(v.len(), 0); assert_eq!(v.capacity(), 0); assert_eq!(&v[..], &[]); assert_eq!(new.len(), 0); assert_eq!(new.capacity(), 0); assert_eq!(&new[..], &[]); } { let mut v = ThinVec::::new(); let mut other = ThinVec::::new(); v.append(&mut other); assert_eq!(v.len(), 0); assert_eq!(v.capacity(), 0); assert_eq!(&v[..], &[]); assert_eq!(other.len(), 0); assert_eq!(other.capacity(), 0); assert_eq!(&other[..], &[]); } { let mut v = ThinVec::::new(); v.reserve(0); assert_eq!(v.len(), 0); assert_eq!(v.capacity(), 0); assert_eq!(&v[..], &[]); } { let mut v = ThinVec::::new(); v.reserve_exact(0); assert_eq!(v.len(), 0); assert_eq!(v.capacity(), 0); assert_eq!(&v[..], &[]); } { let mut v = ThinVec::::new(); v.reserve(0); assert_eq!(v.len(), 0); assert_eq!(v.capacity(), 0); assert_eq!(&v[..], &[]); } { let v = ThinVec::::with_capacity(0); assert_eq!(v.len(), 0); assert_eq!(v.capacity(), 0); assert_eq!(&v[..], &[]); } { let v = ThinVec::::default(); assert_eq!(v.len(), 0); assert_eq!(v.capacity(), 0); assert_eq!(&v[..], &[]); } { let mut v = ThinVec::::new(); v.retain(|_| unreachable!()); assert_eq!(v.len(), 0); assert_eq!(v.capacity(), 0); assert_eq!(&v[..], &[]); } { let mut v = ThinVec::::new(); v.retain_mut(|_| unreachable!()); assert_eq!(v.len(), 0); assert_eq!(v.capacity(), 0); assert_eq!(&v[..], &[]); } { let mut v = ThinVec::::new(); v.dedup_by_key(|x| *x); assert_eq!(v.len(), 0); assert_eq!(v.capacity(), 0); assert_eq!(&v[..], &[]); } { let mut v = ThinVec::::new(); v.dedup_by(|_, _| unreachable!()); assert_eq!(v.len(), 0); assert_eq!(v.capacity(), 0); assert_eq!(&v[..], &[]); } { let v = ThinVec::::new(); let v = v.clone(); assert_eq!(v.len(), 0); assert_eq!(v.capacity(), 0); assert_eq!(&v[..], &[]); } } #[test] fn test_clone() { let mut v = ThinVec::::new(); assert!(v.is_singleton()); v.push(0); v.pop(); assert!(!v.is_singleton()); let v2 = v.clone(); assert!(v2.is_singleton()); } } #[cfg(test)] mod std_tests { #![allow(clippy::reversed_empty_ranges)] use super::*; use std::mem::size_of; use std::usize; struct DropCounter<'a> { count: &'a mut u32, } impl<'a> Drop for DropCounter<'a> { fn drop(&mut self) { *self.count += 1; } } #[test] fn test_small_vec_struct() { assert!(size_of::>() == size_of::()); } #[test] fn test_double_drop() { struct TwoVec { x: ThinVec, y: ThinVec, } let (mut count_x, mut count_y) = (0, 0); { let mut tv = TwoVec { x: ThinVec::new(), y: ThinVec::new(), }; tv.x.push(DropCounter { count: &mut count_x, }); tv.y.push(DropCounter { count: &mut count_y, }); // If ThinVec had a drop flag, here is where it would be zeroed. // Instead, it should rely on its internal state to prevent // doing anything significant when dropped multiple times. drop(tv.x); // Here tv goes out of scope, tv.y should be dropped, but not tv.x. } assert_eq!(count_x, 1); assert_eq!(count_y, 1); } #[test] fn test_reserve() { let mut v = ThinVec::new(); assert_eq!(v.capacity(), 0); v.reserve(2); assert!(v.capacity() >= 2); for i in 0..16 { v.push(i); } assert!(v.capacity() >= 16); v.reserve(16); assert!(v.capacity() >= 32); v.push(16); v.reserve(16); assert!(v.capacity() >= 33) } #[test] fn test_extend() { let mut v = ThinVec::::new(); let mut w = ThinVec::new(); v.extend(w.clone()); assert_eq!(v, &[]); v.extend(0..3); for i in 0..3 { w.push(i) } assert_eq!(v, w); v.extend(3..10); for i in 3..10 { w.push(i) } assert_eq!(v, w); v.extend(w.clone()); // specializes to `append` assert!(v.iter().eq(w.iter().chain(w.iter()))); // Zero sized types #[derive(PartialEq, Debug)] struct Foo; let mut a = ThinVec::new(); let b = thin_vec![Foo, Foo]; a.extend(b); assert_eq!(a, &[Foo, Foo]); // Double drop let mut count_x = 0; { let mut x = ThinVec::new(); let y = thin_vec![DropCounter { count: &mut count_x }]; x.extend(y); } assert_eq!(count_x, 1); } /* TODO: implement extend for Iter<&Copy> #[test] fn test_extend_ref() { let mut v = thin_vec![1, 2]; v.extend(&[3, 4, 5]); assert_eq!(v.len(), 5); assert_eq!(v, [1, 2, 3, 4, 5]); let w = thin_vec![6, 7]; v.extend(&w); assert_eq!(v.len(), 7); assert_eq!(v, [1, 2, 3, 4, 5, 6, 7]); } */ #[test] fn test_slice_from_mut() { let mut values = thin_vec![1, 2, 3, 4, 5]; { let slice = &mut values[2..]; assert!(slice == [3, 4, 5]); for p in slice { *p += 2; } } assert!(values == [1, 2, 5, 6, 7]); } #[test] fn test_slice_to_mut() { let mut values = thin_vec![1, 2, 3, 4, 5]; { let slice = &mut values[..2]; assert!(slice == [1, 2]); for p in slice { *p += 1; } } assert!(values == [2, 3, 3, 4, 5]); } #[test] fn test_split_at_mut() { let mut values = thin_vec![1, 2, 3, 4, 5]; { let (left, right) = values.split_at_mut(2); { let left: &[_] = left; assert!(left[..left.len()] == [1, 2]); } for p in left { *p += 1; } { let right: &[_] = right; assert!(right[..right.len()] == [3, 4, 5]); } for p in right { *p += 2; } } assert_eq!(values, [2, 3, 5, 6, 7]); } #[test] fn test_clone() { let v: ThinVec = thin_vec![]; let w = thin_vec![1, 2, 3]; assert_eq!(v, v.clone()); let z = w.clone(); assert_eq!(w, z); // they should be disjoint in memory. assert!(w.as_ptr() != z.as_ptr()) } #[test] fn test_clone_from() { let mut v = thin_vec![]; let three: ThinVec> = thin_vec![Box::new(1), Box::new(2), Box::new(3)]; let two: ThinVec> = thin_vec![Box::new(4), Box::new(5)]; // zero, long v.clone_from(&three); assert_eq!(v, three); // equal v.clone_from(&three); assert_eq!(v, three); // long, short v.clone_from(&two); assert_eq!(v, two); // short, long v.clone_from(&three); assert_eq!(v, three) } #[test] fn test_retain() { let mut vec = thin_vec![1, 2, 3, 4]; vec.retain(|&x| x % 2 == 0); assert_eq!(vec, [2, 4]); } #[test] fn test_retain_mut() { let mut vec = thin_vec![9, 9, 9, 9]; let mut i = 0; vec.retain_mut(|x| { i += 1; *x = i; i != 4 }); assert_eq!(vec, [1, 2, 3]); } #[test] fn test_dedup() { fn case(a: ThinVec, b: ThinVec) { let mut v = a; v.dedup(); assert_eq!(v, b); } case(thin_vec![], thin_vec![]); case(thin_vec![1], thin_vec![1]); case(thin_vec![1, 1], thin_vec![1]); case(thin_vec![1, 2, 3], thin_vec![1, 2, 3]); case(thin_vec![1, 1, 2, 3], thin_vec![1, 2, 3]); case(thin_vec![1, 2, 2, 3], thin_vec![1, 2, 3]); case(thin_vec![1, 2, 3, 3], thin_vec![1, 2, 3]); case(thin_vec![1, 1, 2, 2, 2, 3, 3], thin_vec![1, 2, 3]); } #[test] fn test_dedup_by_key() { fn case(a: ThinVec, b: ThinVec) { let mut v = a; v.dedup_by_key(|i| *i / 10); assert_eq!(v, b); } case(thin_vec![], thin_vec![]); case(thin_vec![10], thin_vec![10]); case(thin_vec![10, 11], thin_vec![10]); case(thin_vec![10, 20, 30], thin_vec![10, 20, 30]); case(thin_vec![10, 11, 20, 30], thin_vec![10, 20, 30]); case(thin_vec![10, 20, 21, 30], thin_vec![10, 20, 30]); case(thin_vec![10, 20, 30, 31], thin_vec![10, 20, 30]); case(thin_vec![10, 11, 20, 21, 22, 30, 31], thin_vec![10, 20, 30]); } #[test] fn test_dedup_by() { let mut vec = thin_vec!["foo", "bar", "Bar", "baz", "bar"]; vec.dedup_by(|a, b| a.eq_ignore_ascii_case(b)); assert_eq!(vec, ["foo", "bar", "baz", "bar"]); let mut vec = thin_vec![("foo", 1), ("foo", 2), ("bar", 3), ("bar", 4), ("bar", 5)]; vec.dedup_by(|a, b| { a.0 == b.0 && { b.1 += a.1; true } }); assert_eq!(vec, [("foo", 3), ("bar", 12)]); } #[test] fn test_dedup_unique() { let mut v0: ThinVec> = thin_vec![Box::new(1), Box::new(1), Box::new(2), Box::new(3)]; v0.dedup(); let mut v1: ThinVec> = thin_vec![Box::new(1), Box::new(2), Box::new(2), Box::new(3)]; v1.dedup(); let mut v2: ThinVec> = thin_vec![Box::new(1), Box::new(2), Box::new(3), Box::new(3)]; v2.dedup(); // If the boxed pointers were leaked or otherwise misused, valgrind // and/or rt should raise errors. } #[test] fn zero_sized_values() { let mut v = ThinVec::new(); assert_eq!(v.len(), 0); v.push(()); assert_eq!(v.len(), 1); v.push(()); assert_eq!(v.len(), 2); assert_eq!(v.pop(), Some(())); assert_eq!(v.pop(), Some(())); assert_eq!(v.pop(), None); assert_eq!(v.iter().count(), 0); v.push(()); assert_eq!(v.iter().count(), 1); v.push(()); assert_eq!(v.iter().count(), 2); for &() in &v {} assert_eq!(v.iter_mut().count(), 2); v.push(()); assert_eq!(v.iter_mut().count(), 3); v.push(()); assert_eq!(v.iter_mut().count(), 4); for &mut () in &mut v {} unsafe { v.set_len(0); } assert_eq!(v.iter_mut().count(), 0); } #[test] fn test_partition() { assert_eq!( thin_vec![].into_iter().partition(|x: &i32| *x < 3), (thin_vec![], thin_vec![]) ); assert_eq!( thin_vec![1, 2, 3].into_iter().partition(|x| *x < 4), (thin_vec![1, 2, 3], thin_vec![]) ); assert_eq!( thin_vec![1, 2, 3].into_iter().partition(|x| *x < 2), (thin_vec![1], thin_vec![2, 3]) ); assert_eq!( thin_vec![1, 2, 3].into_iter().partition(|x| *x < 0), (thin_vec![], thin_vec![1, 2, 3]) ); } #[test] fn test_zip_unzip() { let z1 = thin_vec![(1, 4), (2, 5), (3, 6)]; let (left, right): (ThinVec<_>, ThinVec<_>) = z1.iter().cloned().unzip(); assert_eq!((1, 4), (left[0], right[0])); assert_eq!((2, 5), (left[1], right[1])); assert_eq!((3, 6), (left[2], right[2])); } #[test] fn test_vec_truncate_drop() { static mut DROPS: u32 = 0; struct Elem(i32); impl Drop for Elem { fn drop(&mut self) { unsafe { DROPS += 1; } } } let mut v = thin_vec![Elem(1), Elem(2), Elem(3), Elem(4), Elem(5)]; assert_eq!(unsafe { DROPS }, 0); v.truncate(3); assert_eq!(unsafe { DROPS }, 2); v.truncate(0); assert_eq!(unsafe { DROPS }, 5); } #[test] #[should_panic] fn test_vec_truncate_fail() { struct BadElem(i32); impl Drop for BadElem { fn drop(&mut self) { let BadElem(ref mut x) = *self; if *x == 0xbadbeef { panic!("BadElem panic: 0xbadbeef") } } } let mut v = thin_vec![BadElem(1), BadElem(2), BadElem(0xbadbeef), BadElem(4)]; v.truncate(0); } #[test] fn test_index() { let vec = thin_vec![1, 2, 3]; assert!(vec[1] == 2); } #[test] #[should_panic] fn test_index_out_of_bounds() { let vec = thin_vec![1, 2, 3]; let _ = vec[3]; } #[test] #[should_panic] fn test_slice_out_of_bounds_1() { let x = thin_vec![1, 2, 3, 4, 5]; let _ = &x[!0..]; } #[test] #[should_panic] fn test_slice_out_of_bounds_2() { let x = thin_vec![1, 2, 3, 4, 5]; let _ = &x[..6]; } #[test] #[should_panic] fn test_slice_out_of_bounds_3() { let x = thin_vec![1, 2, 3, 4, 5]; let _ = &x[!0..4]; } #[test] #[should_panic] fn test_slice_out_of_bounds_4() { let x = thin_vec![1, 2, 3, 4, 5]; let _ = &x[1..6]; } #[test] #[should_panic] fn test_slice_out_of_bounds_5() { let x = thin_vec![1, 2, 3, 4, 5]; let _ = &x[3..2]; } #[test] #[should_panic] fn test_swap_remove_empty() { let mut vec = ThinVec::::new(); vec.swap_remove(0); } #[test] fn test_move_items() { let vec = thin_vec![1, 2, 3]; let mut vec2 = thin_vec![]; for i in vec { vec2.push(i); } assert_eq!(vec2, [1, 2, 3]); } #[test] fn test_move_items_reverse() { let vec = thin_vec![1, 2, 3]; let mut vec2 = thin_vec![]; for i in vec.into_iter().rev() { vec2.push(i); } assert_eq!(vec2, [3, 2, 1]); } #[test] fn test_move_items_zero_sized() { let vec = thin_vec![(), (), ()]; let mut vec2 = thin_vec![]; for i in vec { vec2.push(i); } assert_eq!(vec2, [(), (), ()]); } #[test] fn test_drain_items() { let mut vec = thin_vec![1, 2, 3]; let mut vec2 = thin_vec![]; for i in vec.drain(..) { vec2.push(i); } assert_eq!(vec, []); assert_eq!(vec2, [1, 2, 3]); } #[test] fn test_drain_items_reverse() { let mut vec = thin_vec![1, 2, 3]; let mut vec2 = thin_vec![]; for i in vec.drain(..).rev() { vec2.push(i); } assert_eq!(vec, []); assert_eq!(vec2, [3, 2, 1]); } #[test] fn test_drain_items_zero_sized() { let mut vec = thin_vec![(), (), ()]; let mut vec2 = thin_vec![]; for i in vec.drain(..) { vec2.push(i); } assert_eq!(vec, []); assert_eq!(vec2, [(), (), ()]); } #[test] #[should_panic] fn test_drain_out_of_bounds() { let mut v = thin_vec![1, 2, 3, 4, 5]; v.drain(5..6); } #[test] fn test_drain_range() { let mut v = thin_vec![1, 2, 3, 4, 5]; for _ in v.drain(4..) {} assert_eq!(v, &[1, 2, 3, 4]); let mut v: ThinVec<_> = (1..6).map(|x| x.to_string()).collect(); for _ in v.drain(1..4) {} assert_eq!(v, &[1.to_string(), 5.to_string()]); let mut v: ThinVec<_> = (1..6).map(|x| x.to_string()).collect(); for _ in v.drain(1..4).rev() {} assert_eq!(v, &[1.to_string(), 5.to_string()]); let mut v: ThinVec<_> = thin_vec![(); 5]; for _ in v.drain(1..4).rev() {} assert_eq!(v, &[(), ()]); } #[test] fn test_drain_inclusive_range() { let mut v = thin_vec!['a', 'b', 'c', 'd', 'e']; for _ in v.drain(1..=3) {} assert_eq!(v, &['a', 'e']); let mut v: ThinVec<_> = (0..=5).map(|x| x.to_string()).collect(); for _ in v.drain(1..=5) {} assert_eq!(v, &["0".to_string()]); let mut v: ThinVec = (0..=5).map(|x| x.to_string()).collect(); for _ in v.drain(0..=5) {} assert_eq!(v, ThinVec::::new()); let mut v: ThinVec<_> = (0..=5).map(|x| x.to_string()).collect(); for _ in v.drain(0..=3) {} assert_eq!(v, &["4".to_string(), "5".to_string()]); let mut v: ThinVec<_> = (0..=1).map(|x| x.to_string()).collect(); for _ in v.drain(..=0) {} assert_eq!(v, &["1".to_string()]); } #[test] #[cfg(not(feature = "gecko-ffi"))] fn test_drain_max_vec_size() { let mut v = ThinVec::<()>::with_capacity(usize::max_value()); unsafe { v.set_len(usize::max_value()); } for _ in v.drain(usize::max_value() - 1..) {} assert_eq!(v.len(), usize::max_value() - 1); let mut v = ThinVec::<()>::with_capacity(usize::max_value()); unsafe { v.set_len(usize::max_value()); } for _ in v.drain(usize::max_value() - 1..=usize::max_value() - 1) {} assert_eq!(v.len(), usize::max_value() - 1); } #[test] #[should_panic] fn test_drain_inclusive_out_of_bounds() { let mut v = thin_vec![1, 2, 3, 4, 5]; v.drain(5..=5); } #[test] fn test_splice() { let mut v = thin_vec![1, 2, 3, 4, 5]; let a = [10, 11, 12]; v.splice(2..4, a.iter().cloned()); assert_eq!(v, &[1, 2, 10, 11, 12, 5]); v.splice(1..3, Some(20)); assert_eq!(v, &[1, 20, 11, 12, 5]); } #[test] fn test_splice_inclusive_range() { let mut v = thin_vec![1, 2, 3, 4, 5]; let a = [10, 11, 12]; let t1: ThinVec<_> = v.splice(2..=3, a.iter().cloned()).collect(); assert_eq!(v, &[1, 2, 10, 11, 12, 5]); assert_eq!(t1, &[3, 4]); let t2: ThinVec<_> = v.splice(1..=2, Some(20)).collect(); assert_eq!(v, &[1, 20, 11, 12, 5]); assert_eq!(t2, &[2, 10]); } #[test] #[should_panic] fn test_splice_out_of_bounds() { let mut v = thin_vec![1, 2, 3, 4, 5]; let a = [10, 11, 12]; v.splice(5..6, a.iter().cloned()); } #[test] #[should_panic] fn test_splice_inclusive_out_of_bounds() { let mut v = thin_vec![1, 2, 3, 4, 5]; let a = [10, 11, 12]; v.splice(5..=5, a.iter().cloned()); } #[test] fn test_splice_items_zero_sized() { let mut vec = thin_vec![(), (), ()]; let vec2 = thin_vec![]; let t: ThinVec<_> = vec.splice(1..2, vec2.iter().cloned()).collect(); assert_eq!(vec, &[(), ()]); assert_eq!(t, &[()]); } #[test] fn test_splice_unbounded() { let mut vec = thin_vec![1, 2, 3, 4, 5]; let t: ThinVec<_> = vec.splice(.., None).collect(); assert_eq!(vec, &[]); assert_eq!(t, &[1, 2, 3, 4, 5]); } #[test] fn test_splice_forget() { let mut v = thin_vec![1, 2, 3, 4, 5]; let a = [10, 11, 12]; ::std::mem::forget(v.splice(2..4, a.iter().cloned())); assert_eq!(v, &[1, 2]); } #[test] fn test_splice_from_empty() { let mut v = thin_vec![]; let a = [10, 11, 12]; v.splice(.., a.iter().cloned()); assert_eq!(v, &[10, 11, 12]); } /* probs won't ever impl this #[test] fn test_into_boxed_slice() { let xs = thin_vec![1, 2, 3]; let ys = xs.into_boxed_slice(); assert_eq!(&*ys, [1, 2, 3]); } */ #[test] fn test_append() { let mut vec = thin_vec![1, 2, 3]; let mut vec2 = thin_vec![4, 5, 6]; vec.append(&mut vec2); assert_eq!(vec, [1, 2, 3, 4, 5, 6]); assert_eq!(vec2, []); } #[test] fn test_split_off() { let mut vec = thin_vec![1, 2, 3, 4, 5, 6]; let vec2 = vec.split_off(4); assert_eq!(vec, [1, 2, 3, 4]); assert_eq!(vec2, [5, 6]); } #[test] fn test_into_iter_as_slice() { let vec = thin_vec!['a', 'b', 'c']; let mut into_iter = vec.into_iter(); assert_eq!(into_iter.as_slice(), &['a', 'b', 'c']); let _ = into_iter.next().unwrap(); assert_eq!(into_iter.as_slice(), &['b', 'c']); let _ = into_iter.next().unwrap(); let _ = into_iter.next().unwrap(); assert_eq!(into_iter.as_slice(), &[]); } #[test] fn test_into_iter_as_mut_slice() { let vec = thin_vec!['a', 'b', 'c']; let mut into_iter = vec.into_iter(); assert_eq!(into_iter.as_slice(), &['a', 'b', 'c']); into_iter.as_mut_slice()[0] = 'x'; into_iter.as_mut_slice()[1] = 'y'; assert_eq!(into_iter.next().unwrap(), 'x'); assert_eq!(into_iter.as_slice(), &['y', 'c']); } #[test] fn test_into_iter_debug() { let vec = thin_vec!['a', 'b', 'c']; let into_iter = vec.into_iter(); let debug = format!("{:?}", into_iter); assert_eq!(debug, "IntoIter(['a', 'b', 'c'])"); } #[test] fn test_into_iter_count() { assert_eq!(thin_vec![1, 2, 3].into_iter().count(), 3); } #[test] fn test_into_iter_clone() { fn iter_equal>(it: I, slice: &[i32]) { let v: ThinVec = it.collect(); assert_eq!(&v[..], slice); } let mut it = thin_vec![1, 2, 3].into_iter(); iter_equal(it.clone(), &[1, 2, 3]); assert_eq!(it.next(), Some(1)); let mut it = it.rev(); iter_equal(it.clone(), &[3, 2]); assert_eq!(it.next(), Some(3)); iter_equal(it.clone(), &[2]); assert_eq!(it.next(), Some(2)); iter_equal(it.clone(), &[]); assert_eq!(it.next(), None); } /* TODO: make drain covariant #[allow(dead_code)] fn assert_covariance() { fn drain<'new>(d: Drain<'static, &'static str>) -> Drain<'new, &'new str> { d } fn into_iter<'new>(i: IntoIter<&'static str>) -> IntoIter<&'new str> { i } } */ /* TODO: specialize vec.into_iter().collect::>(); #[test] fn from_into_inner() { let vec = thin_vec![1, 2, 3]; let ptr = vec.as_ptr(); let vec = vec.into_iter().collect::>(); assert_eq!(vec, [1, 2, 3]); assert_eq!(vec.as_ptr(), ptr); let ptr = &vec[1] as *const _; let mut it = vec.into_iter(); it.next().unwrap(); let vec = it.collect::>(); assert_eq!(vec, [2, 3]); assert!(ptr != vec.as_ptr()); } */ #[test] #[cfg_attr(feature = "gecko-ffi", ignore)] fn overaligned_allocations() { #[repr(align(256))] struct Foo(usize); let mut v = thin_vec![Foo(273)]; for i in 0..0x1000 { v.reserve_exact(i); assert!(v[0].0 == 273); assert!(v.as_ptr() as usize & 0xff == 0); v.shrink_to_fit(); assert!(v[0].0 == 273); assert!(v.as_ptr() as usize & 0xff == 0); } } /* TODO: implement drain_filter? #[test] fn drain_filter_empty() { let mut vec: ThinVec = thin_vec![]; { let mut iter = vec.drain_filter(|_| true); assert_eq!(iter.size_hint(), (0, Some(0))); assert_eq!(iter.next(), None); assert_eq!(iter.size_hint(), (0, Some(0))); assert_eq!(iter.next(), None); assert_eq!(iter.size_hint(), (0, Some(0))); } assert_eq!(vec.len(), 0); assert_eq!(vec, thin_vec![]); } #[test] fn drain_filter_zst() { let mut vec = thin_vec![(), (), (), (), ()]; let initial_len = vec.len(); let mut count = 0; { let mut iter = vec.drain_filter(|_| true); assert_eq!(iter.size_hint(), (0, Some(initial_len))); while let Some(_) = iter.next() { count += 1; assert_eq!(iter.size_hint(), (0, Some(initial_len - count))); } assert_eq!(iter.size_hint(), (0, Some(0))); assert_eq!(iter.next(), None); assert_eq!(iter.size_hint(), (0, Some(0))); } assert_eq!(count, initial_len); assert_eq!(vec.len(), 0); assert_eq!(vec, thin_vec![]); } #[test] fn drain_filter_false() { let mut vec = thin_vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; let initial_len = vec.len(); let mut count = 0; { let mut iter = vec.drain_filter(|_| false); assert_eq!(iter.size_hint(), (0, Some(initial_len))); for _ in iter.by_ref() { count += 1; } assert_eq!(iter.size_hint(), (0, Some(0))); assert_eq!(iter.next(), None); assert_eq!(iter.size_hint(), (0, Some(0))); } assert_eq!(count, 0); assert_eq!(vec.len(), initial_len); assert_eq!(vec, thin_vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); } #[test] fn drain_filter_true() { let mut vec = thin_vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; let initial_len = vec.len(); let mut count = 0; { let mut iter = vec.drain_filter(|_| true); assert_eq!(iter.size_hint(), (0, Some(initial_len))); while let Some(_) = iter.next() { count += 1; assert_eq!(iter.size_hint(), (0, Some(initial_len - count))); } assert_eq!(iter.size_hint(), (0, Some(0))); assert_eq!(iter.next(), None); assert_eq!(iter.size_hint(), (0, Some(0))); } assert_eq!(count, initial_len); assert_eq!(vec.len(), 0); assert_eq!(vec, thin_vec![]); } #[test] fn drain_filter_complex() { { // [+xxx++++++xxxxx++++x+x++] let mut vec = thin_vec![1, 2, 4, 6, 7, 9, 11, 13, 15, 17, 18, 20, 22, 24, 26, 27, 29, 31, 33, 34, 35, 36, 37, 39]; let removed = vec.drain_filter(|x| *x % 2 == 0).collect::>(); assert_eq!(removed.len(), 10); assert_eq!(removed, thin_vec![2, 4, 6, 18, 20, 22, 24, 26, 34, 36]); assert_eq!(vec.len(), 14); assert_eq!(vec, thin_vec![1, 7, 9, 11, 13, 15, 17, 27, 29, 31, 33, 35, 37, 39]); } { // [xxx++++++xxxxx++++x+x++] let mut vec = thin_vec![2, 4, 6, 7, 9, 11, 13, 15, 17, 18, 20, 22, 24, 26, 27, 29, 31, 33, 34, 35, 36, 37, 39]; let removed = vec.drain_filter(|x| *x % 2 == 0).collect::>(); assert_eq!(removed.len(), 10); assert_eq!(removed, thin_vec![2, 4, 6, 18, 20, 22, 24, 26, 34, 36]); assert_eq!(vec.len(), 13); assert_eq!(vec, thin_vec![7, 9, 11, 13, 15, 17, 27, 29, 31, 33, 35, 37, 39]); } { // [xxx++++++xxxxx++++x+x] let mut vec = thin_vec![2, 4, 6, 7, 9, 11, 13, 15, 17, 18, 20, 22, 24, 26, 27, 29, 31, 33, 34, 35, 36]; let removed = vec.drain_filter(|x| *x % 2 == 0).collect::>(); assert_eq!(removed.len(), 10); assert_eq!(removed, thin_vec![2, 4, 6, 18, 20, 22, 24, 26, 34, 36]); assert_eq!(vec.len(), 11); assert_eq!(vec, thin_vec![7, 9, 11, 13, 15, 17, 27, 29, 31, 33, 35]); } { // [xxxxxxxxxx+++++++++++] let mut vec = thin_vec![2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19]; let removed = vec.drain_filter(|x| *x % 2 == 0).collect::>(); assert_eq!(removed.len(), 10); assert_eq!(removed, thin_vec![2, 4, 6, 8, 10, 12, 14, 16, 18, 20]); assert_eq!(vec.len(), 10); assert_eq!(vec, thin_vec![1, 3, 5, 7, 9, 11, 13, 15, 17, 19]); } { // [+++++++++++xxxxxxxxxx] let mut vec = thin_vec![1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20]; let removed = vec.drain_filter(|x| *x % 2 == 0).collect::>(); assert_eq!(removed.len(), 10); assert_eq!(removed, thin_vec![2, 4, 6, 8, 10, 12, 14, 16, 18, 20]); assert_eq!(vec.len(), 10); assert_eq!(vec, thin_vec![1, 3, 5, 7, 9, 11, 13, 15, 17, 19]); } } */ #[test] fn test_reserve_exact() { // This is all the same as test_reserve let mut v = ThinVec::new(); assert_eq!(v.capacity(), 0); v.reserve_exact(2); assert!(v.capacity() >= 2); for i in 0..16 { v.push(i); } assert!(v.capacity() >= 16); v.reserve_exact(16); assert!(v.capacity() >= 32); v.push(16); v.reserve_exact(16); assert!(v.capacity() >= 33) } /* TODO: implement try_reserve #[test] fn test_try_reserve() { // These are the interesting cases: // * exactly isize::MAX should never trigger a CapacityOverflow (can be OOM) // * > isize::MAX should always fail // * On 16/32-bit should CapacityOverflow // * On 64-bit should OOM // * overflow may trigger when adding `len` to `cap` (in number of elements) // * overflow may trigger when multiplying `new_cap` by size_of:: (to get bytes) const MAX_CAP: usize = isize::MAX as usize; const MAX_USIZE: usize = usize::MAX; // On 16/32-bit, we check that allocations don't exceed isize::MAX, // on 64-bit, we assume the OS will give an OOM for such a ridiculous size. // Any platform that succeeds for these requests is technically broken with // ptr::offset because LLVM is the worst. let guards_against_isize = size_of::() < 8; { // Note: basic stuff is checked by test_reserve let mut empty_bytes: ThinVec = ThinVec::new(); // Check isize::MAX doesn't count as an overflow if let Err(CapacityOverflow) = empty_bytes.try_reserve(MAX_CAP) { panic!("isize::MAX shouldn't trigger an overflow!"); } // Play it again, frank! (just to be sure) if let Err(CapacityOverflow) = empty_bytes.try_reserve(MAX_CAP) { panic!("isize::MAX shouldn't trigger an overflow!"); } if guards_against_isize { // Check isize::MAX + 1 does count as overflow if let Err(CapacityOverflow) = empty_bytes.try_reserve(MAX_CAP + 1) { } else { panic!("isize::MAX + 1 should trigger an overflow!") } // Check usize::MAX does count as overflow if let Err(CapacityOverflow) = empty_bytes.try_reserve(MAX_USIZE) { } else { panic!("usize::MAX should trigger an overflow!") } } else { // Check isize::MAX + 1 is an OOM if let Err(AllocErr) = empty_bytes.try_reserve(MAX_CAP + 1) { } else { panic!("isize::MAX + 1 should trigger an OOM!") } // Check usize::MAX is an OOM if let Err(AllocErr) = empty_bytes.try_reserve(MAX_USIZE) { } else { panic!("usize::MAX should trigger an OOM!") } } } { // Same basic idea, but with non-zero len let mut ten_bytes: ThinVec = thin_vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_CAP - 10) { panic!("isize::MAX shouldn't trigger an overflow!"); } if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_CAP - 10) { panic!("isize::MAX shouldn't trigger an overflow!"); } if guards_against_isize { if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_CAP - 9) { } else { panic!("isize::MAX + 1 should trigger an overflow!"); } } else { if let Err(AllocErr) = ten_bytes.try_reserve(MAX_CAP - 9) { } else { panic!("isize::MAX + 1 should trigger an OOM!") } } // Should always overflow in the add-to-len if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_USIZE) { } else { panic!("usize::MAX should trigger an overflow!") } } { // Same basic idea, but with interesting type size let mut ten_u32s: ThinVec = thin_vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; if let Err(CapacityOverflow) = ten_u32s.try_reserve(MAX_CAP/4 - 10) { panic!("isize::MAX shouldn't trigger an overflow!"); } if let Err(CapacityOverflow) = ten_u32s.try_reserve(MAX_CAP/4 - 10) { panic!("isize::MAX shouldn't trigger an overflow!"); } if guards_against_isize { if let Err(CapacityOverflow) = ten_u32s.try_reserve(MAX_CAP/4 - 9) { } else { panic!("isize::MAX + 1 should trigger an overflow!"); } } else { if let Err(AllocErr) = ten_u32s.try_reserve(MAX_CAP/4 - 9) { } else { panic!("isize::MAX + 1 should trigger an OOM!") } } // Should fail in the mul-by-size if let Err(CapacityOverflow) = ten_u32s.try_reserve(MAX_USIZE - 20) { } else { panic!("usize::MAX should trigger an overflow!"); } } } #[test] fn test_try_reserve_exact() { // This is exactly the same as test_try_reserve with the method changed. // See that test for comments. const MAX_CAP: usize = isize::MAX as usize; const MAX_USIZE: usize = usize::MAX; let guards_against_isize = size_of::() < 8; { let mut empty_bytes: ThinVec = ThinVec::new(); if let Err(CapacityOverflow) = empty_bytes.try_reserve_exact(MAX_CAP) { panic!("isize::MAX shouldn't trigger an overflow!"); } if let Err(CapacityOverflow) = empty_bytes.try_reserve_exact(MAX_CAP) { panic!("isize::MAX shouldn't trigger an overflow!"); } if guards_against_isize { if let Err(CapacityOverflow) = empty_bytes.try_reserve_exact(MAX_CAP + 1) { } else { panic!("isize::MAX + 1 should trigger an overflow!") } if let Err(CapacityOverflow) = empty_bytes.try_reserve_exact(MAX_USIZE) { } else { panic!("usize::MAX should trigger an overflow!") } } else { if let Err(AllocErr) = empty_bytes.try_reserve_exact(MAX_CAP + 1) { } else { panic!("isize::MAX + 1 should trigger an OOM!") } if let Err(AllocErr) = empty_bytes.try_reserve_exact(MAX_USIZE) { } else { panic!("usize::MAX should trigger an OOM!") } } } { let mut ten_bytes: ThinVec = thin_vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_CAP - 10) { panic!("isize::MAX shouldn't trigger an overflow!"); } if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_CAP - 10) { panic!("isize::MAX shouldn't trigger an overflow!"); } if guards_against_isize { if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_CAP - 9) { } else { panic!("isize::MAX + 1 should trigger an overflow!"); } } else { if let Err(AllocErr) = ten_bytes.try_reserve_exact(MAX_CAP - 9) { } else { panic!("isize::MAX + 1 should trigger an OOM!") } } if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_USIZE) { } else { panic!("usize::MAX should trigger an overflow!") } } { let mut ten_u32s: ThinVec = thin_vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; if let Err(CapacityOverflow) = ten_u32s.try_reserve_exact(MAX_CAP/4 - 10) { panic!("isize::MAX shouldn't trigger an overflow!"); } if let Err(CapacityOverflow) = ten_u32s.try_reserve_exact(MAX_CAP/4 - 10) { panic!("isize::MAX shouldn't trigger an overflow!"); } if guards_against_isize { if let Err(CapacityOverflow) = ten_u32s.try_reserve_exact(MAX_CAP/4 - 9) { } else { panic!("isize::MAX + 1 should trigger an overflow!"); } } else { if let Err(AllocErr) = ten_u32s.try_reserve_exact(MAX_CAP/4 - 9) { } else { panic!("isize::MAX + 1 should trigger an OOM!") } } if let Err(CapacityOverflow) = ten_u32s.try_reserve_exact(MAX_USIZE - 20) { } else { panic!("usize::MAX should trigger an overflow!") } } } */ #[test] #[cfg_attr(feature = "gecko-ffi", ignore)] fn test_header_data() { macro_rules! assert_aligned_head_ptr { ($typename:ty) => {{ let v: ThinVec<$typename> = ThinVec::with_capacity(1 /* ensure allocation */); let head_ptr: *mut $typename = v.data_raw(); assert_eq!( head_ptr as usize % std::mem::align_of::<$typename>(), 0, "expected Header::data<{}> to be aligned", stringify!($typename) ); }}; } const HEADER_SIZE: usize = std::mem::size_of::
(); assert_eq!(2 * std::mem::size_of::(), HEADER_SIZE); #[repr(C, align(128))] struct Funky(T); assert_eq!(padding::>(), 128 - HEADER_SIZE); assert_aligned_head_ptr!(Funky<()>); assert_eq!(padding::>(), 128 - HEADER_SIZE); assert_aligned_head_ptr!(Funky); assert_eq!(padding::>(), 128 - HEADER_SIZE); assert_aligned_head_ptr!(Funky<[(); 1024]>); assert_eq!(padding::>(), 128 - HEADER_SIZE); assert_aligned_head_ptr!(Funky<[*mut usize; 1024]>); } #[cfg(feature = "serde")] use serde_test::{assert_tokens, Token}; #[test] #[cfg(feature = "serde")] fn test_ser_de_empty() { let vec = ThinVec::::new(); assert_tokens(&vec, &[Token::Seq { len: Some(0) }, Token::SeqEnd]); } #[test] #[cfg(feature = "serde")] fn test_ser_de() { let mut vec = ThinVec::::new(); vec.push(20); vec.push(55); vec.push(123); assert_tokens( &vec, &[ Token::Seq { len: Some(3) }, Token::U32(20), Token::U32(55), Token::U32(123), Token::SeqEnd, ], ); } #[test] fn test_set_len() { let mut vec: ThinVec = thin_vec![]; unsafe { vec.set_len(0); // at one point this caused a crash } } #[test] #[should_panic(expected = "invalid set_len(1) on empty ThinVec")] fn test_set_len_invalid() { let mut vec: ThinVec = thin_vec![]; unsafe { vec.set_len(1); } } #[test] #[should_panic(expected = "capacity overflow")] fn test_capacity_overflow_header_too_big() { let vec: ThinVec = ThinVec::with_capacity(isize::MAX as usize - 2); assert!(vec.capacity() > 0); } #[test] #[should_panic(expected = "capacity overflow")] fn test_capacity_overflow_cap_too_big() { let vec: ThinVec = ThinVec::with_capacity(isize::MAX as usize + 1); assert!(vec.capacity() > 0); } #[test] #[should_panic(expected = "capacity overflow")] fn test_capacity_overflow_size_mul1() { let vec: ThinVec = ThinVec::with_capacity(isize::MAX as usize + 1); assert!(vec.capacity() > 0); } #[test] #[should_panic(expected = "capacity overflow")] fn test_capacity_overflow_size_mul2() { let vec: ThinVec = ThinVec::with_capacity(isize::MAX as usize / 2 + 1); assert!(vec.capacity() > 0); } #[test] #[should_panic(expected = "capacity overflow")] fn test_capacity_overflow_cap_really_isnt_isize() { let vec: ThinVec = ThinVec::with_capacity(isize::MAX as usize); assert!(vec.capacity() > 0); } }