thin-vec-0.2.13/.cargo_vcs_info.json0000644000000001360000000000100126340ustar { "git": { "sha1": "60db5977dbe96a5271939b02c9376ea3310b97a1" }, "path_in_vcs": "" }thin-vec-0.2.13/.github/workflows/rust.yml000064400000000000000000000046260072674642500166010ustar 00000000000000name: Rust on: push: branches: [ main ] pull_request: branches: [ main ] env: CARGO_TERM_COLOR: always jobs: miri: name: "Miri" runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Install Miri run: | rustup toolchain install nightly --component miri rustup override set nightly cargo miri setup - name: Test (default) with Miri run: MIRIFLAGS=-Zmiri-strict-provenance cargo miri test - name: Test (gecko-ffi) with Miri run: MIRIFLAGS=-Zmiri-strict-provenance cargo miri test --features=gecko-ffi build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Build run: cargo build --verbose - name: Run tests run: cargo test --verbose - name: Run tests run: cargo test --verbose - name: Run tests (serde) run: cargo test --features=serde --verbose - name: Run tests (gecko-ffi) run: cargo test --tests --features=gecko-ffi --verbose - name: Run tests (no_std) run: cargo test --tests --no-default-features --verbose fmt: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: toolchain: stable profile: minimal components: rustfmt override: true - uses: actions-rs/cargo@v1 with: command: fmt args: --all -- --check clippy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: toolchain: stable profile: minimal components: clippy override: true - uses: actions-rs/clippy-check@v1 env: PWD: ${{ env.GITHUB_WORKSPACE }} with: token: ${{ secrets.GITHUB_TOKEN }} args: --workspace --tests --examples docs: runs-on: ubuntu-latest env: RUSTDOCFLAGS: -Dwarnings steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: toolchain: stable profile: minimal components: rust-docs override: true - uses: swatinem/rust-cache@v1 - uses: actions-rs/cargo@v1 with: command: doc args: --workspace --no-deps thin-vec-0.2.13/.gitignore000064400000000000000000000000220072674642500134360ustar 00000000000000target Cargo.lock thin-vec-0.2.13/Cargo.toml0000644000000017030000000000100106330ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "thin-vec" version = "0.2.13" authors = ["Aria Beingessner "] description = "A vec that takes up less space on the stack" homepage = "https://github.com/gankra/thin-vec" readme = "README.md" license = "MIT/Apache-2.0" repository = "https://github.com/gankra/thin-vec" [dependencies.serde] version = "1.0" optional = true [dev-dependencies.serde_test] version = "1.0" [features] default = ["std"] gecko-ffi = [] std = [] unstable = [] thin-vec-0.2.13/Cargo.toml.orig000064400000000000000000000013400072674642500143410ustar 00000000000000[package] edition = "2018" name = "thin-vec" version = "0.2.13" authors = ["Aria Beingessner "] description = "A vec that takes up less space on the stack" license = "MIT/Apache-2.0" repository = "https://github.com/gankra/thin-vec" homepage = "https://github.com/gankra/thin-vec" readme = "README.md" [features] unstable = [] default = ["std"] std = [] # Gecko specific features. These features cause thin-vec to have the same layout # and behaviour as nsTArray, allowing it to be used in C++ FFI. Requires # the empty header to be statically linked in with the symbol name "sEmptyTArrayHeader" gecko-ffi = [] [dependencies] serde = {version = "1.0", optional = true} [dev-dependencies] serde_test = "1.0" thin-vec-0.2.13/LICENSE-APACHE000064400000000000000000000232530072674642500134050ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS thin-vec-0.2.13/LICENSE-MIT000064400000000000000000000020260072674642500131100ustar 00000000000000Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. thin-vec-0.2.13/README.md000064400000000000000000000007470072674642500127430ustar 00000000000000![Rust CI](https://github.com/Gankra/thin-vec/workflows/Rust/badge.svg?branch=master) [![crates.io](https://img.shields.io/crates/v/thin-vec.svg)](https://crates.io/crates/thin-vec) [![](https://docs.rs/thin-vec/badge.svg)](https://docs.rs/thin-vec) # thin-vec ThinVec is a Vec that stores its length and capacity inline, making it take up less space. Currently this crate mostly exists to facilitate Gecko (Firefox) FFI, but it works perfectly fine as a native rust library as well. thin-vec-0.2.13/RELEASES.md000064400000000000000000000004210072674642500131760ustar 00000000000000# Version 0.2.13 (2023-12-02) * add default-on "std" feature for no_std support * added has_capacity method for checking if something is the empty singleton * marked more things as `#[inline]` * added license files * appeased clippy # Previous Versions *shrug*thin-vec-0.2.13/src/lib.rs000064400000000000000000004165620072674642500133750ustar 00000000000000#![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 #![cfg_attr(not(feature = "std"), no_std)] #![allow(clippy::comparison_chain, clippy::missing_safety_doc)] extern crate alloc; use alloc::alloc::*; use alloc::{boxed::Box, vec::Vec}; use core::borrow::*; use core::cmp::*; use core::convert::TryFrom; use core::convert::TryInto; use core::hash::*; use core::iter::FromIterator; use core::marker::PhantomData; use core::ops::Bound; use core::ops::{Deref, DerefMut, RangeBounds}; use core::ptr::NonNull; use core::slice::IterMut; use core::{fmt, 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 { #[inline] #[allow(clippy::unnecessary_cast)] fn cap(&self) -> usize { self._cap as usize } #[inline] 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() } /// Returns `true` if the vector has the capacity to hold any element. pub fn has_capacity(&self) -> bool { !self.is_singleton() } /// 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. debug_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 - core::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); 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 { core::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 core::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 core::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 core::iter::TrustedLen for Drain<'_, T> {} impl core::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`. #[cfg(feature = "std")] impl std::io::Write for ThinVec { #[inline] fn write(&mut self, buf: &[u8]) -> std::io::Result { self.extend_from_slice(buf); Ok(buf.len()) } #[inline] fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> { self.extend_from_slice(buf); Ok(()) } #[inline] fn flush(&mut self) -> std::io::Result<()> { Ok(()) } } // TODO: a million Index impls #[cfg(test)] mod tests { use super::{ThinVec, MAX_CAP}; use crate::alloc::{string::ToString, vec}; #[test] fn test_size_of() { use core::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(); #[allow(clippy::never_loop)] for _ in v.into_iter() { unreachable!(); } } { let mut v = ThinVec::::new(); assert_eq!(v.drain(..).len(), 0); #[allow(clippy::never_loop)] 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); #[allow(clippy::never_loop)] 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 crate::alloc::{ format, string::{String, ToString}, }; use core::mem::size_of; use core::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]; ::core::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 % core::mem::align_of::<$typename>(), 0, "expected Header::data<{}> to be aligned", stringify!($typename) ); }}; } const HEADER_SIZE: usize = core::mem::size_of::
(); assert_eq!(2 * core::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); } }