Skip to content

Implement SmallVec::split_off #340

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@
//! When this feature is enabled, `SmallVec<u8, _>` implements the `std::io::Write` trait.
//! This feature is not compatible with `#![no_std]` programs.
//!
//! ### `drain_filter`
//!
//! **This feature is unstable.** It may change to match the unstable `drain_filter` method in libstd.
//!
//! Enables the `drain_filter` method, which produces an iterator that calls a user-provided
//! closure to determine which elements of the vector to remove and yield from the iterator.
//!
//! ### `specialization`
//!
//! **This feature is unstable and requires a nightly build of the Rust toolchain.**
Expand Down Expand Up @@ -873,6 +880,48 @@ impl<T, const N: usize> SmallVec<T, N> {
self.len.on_heap(Self::is_zst())
}

/// 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.
///
/// - If you want to take ownership of the entire contents and capacity of
/// the vector, see [`mem::take`] or [`mem::replace`].
/// - If you don't need the returned vector at all, see [`SmallVec::truncate`].
/// - If you want to take ownership of an arbitrary subslice, or you don't
/// necessarily want to store the removed items in a vector, see [`SmallVec::drain`].
///
/// # Panics
///
/// Panics if `at > len`.
///
/// # Examples
///
/// ```
/// let mut vec = vec![1, 2, 3];
/// let vec2 = vec.split_off(1);
/// assert_eq!(vec, [1]);
/// assert_eq!(vec2, [2, 3]);
/// ```
#[inline]
pub fn split_off(&mut self, at: usize) -> Self {
let len = self.len();
assert!(at <= len);

let other_len = len - at;
let mut other = Self::with_capacity(other_len);

// Unsafely `set_len` and copy items to `other`.
unsafe {
self.set_len(at);
other.set_len(other_len);

core::ptr::copy_nonoverlapping(self.as_ptr().add(at), other.as_mut_ptr(), other_len);
}
other
}

pub fn drain<R>(&mut self, range: R) -> Drain<'_, T, N>
where
R: core::ops::RangeBounds<usize>,
Expand Down
34 changes: 34 additions & 0 deletions src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,40 @@ fn test_truncate_references() {
);
}

#[test]
fn test_split_off() {
let mut vec: SmallVec<u32, 4> = smallvec![1, 2, 3, 4, 5, 6];
let orig_ptr = vec.as_ptr();
let orig_capacity = vec.capacity();

let split_off = vec.split_off(4);
assert_eq!(&vec[..], &[1, 2, 3, 4]);
assert_eq!(&split_off[..], &[5, 6]);
assert_eq!(vec.capacity(), orig_capacity);
assert_eq!(vec.as_ptr(), orig_ptr);
}

#[test]
fn test_split_off_take_all() {
// Allocate enough capacity that we can tell whether the split-off vector's
// capacity is based on its size, or (incorrectly) on the original capacity.
let mut vec = SmallVec::<u32, 4>::with_capacity(1000);
vec.extend([1, 2, 3, 4, 5, 6]);
let orig_ptr = vec.as_ptr();
let orig_capacity: usize = vec.capacity();

let split_off = vec.split_off(0);
assert_eq!(&vec[..], &[]);
assert_eq!(&split_off[..], &[1, 2, 3, 4, 5, 6]);
assert_eq!(vec.capacity(), orig_capacity);
assert_eq!(vec.as_ptr(), orig_ptr);

// The split-off vector should be newly-allocated, and should not have
// stolen the original vector's allocation.
assert!(split_off.capacity() < orig_capacity);
assert_ne!(split_off.as_ptr(), orig_ptr);
}

#[test]
fn test_insert_many() {
let mut v: SmallVec<u8, 8> = SmallVec::new();
Expand Down