Skip to content

Implement shrink_to method on collections #49400

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 1 commit into from
Mar 29, 2018
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
25 changes: 25 additions & 0 deletions src/liballoc/binary_heap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,31 @@ impl<T: Ord> BinaryHeap<T> {
self.data.shrink_to_fit();
}

/// Discards capacity with a lower bound.
///
/// The capacity will remain at least as large as both the length
/// and the supplied value.
///
/// Panics if the current capacity is smaller than the supplied
/// minimum capacity.
///
/// # Examples
///
/// ```
/// #![feature(shrink_to)]
/// use std::collections::BinaryHeap;
/// let mut heap: BinaryHeap<i32> = BinaryHeap::with_capacity(100);
///
/// assert!(heap.capacity() >= 100);
/// heap.shrink_to(10);
/// assert!(heap.capacity() >= 10);
/// ```
#[inline]
#[unstable(feature = "shrink_to", reason = "new API", issue="0")]
pub fn shrink_to(&mut self, min_capacity: usize) {
self.data.shrink_to(min_capacity)
}

/// Removes the greatest item from the binary heap and returns it, or `None` if it
/// is empty.
///
Expand Down
28 changes: 28 additions & 0 deletions src/liballoc/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1015,6 +1015,34 @@ impl String {
self.vec.shrink_to_fit()
}

/// Shrinks the capacity of this `String` with a lower bound.
///
/// The capacity will remain at least as large as both the length
/// and the supplied value.
///
/// Panics if the current capacity is smaller than the supplied
/// minimum capacity.
///
/// # Examples
///
/// ```
/// #![feature(shrink_to)]
/// let mut s = String::from("foo");
///
/// s.reserve(100);
/// assert!(s.capacity() >= 100);
///
/// s.shrink_to(10);
/// assert!(s.capacity() >= 10);
/// s.shrink_to(0);
/// assert!(s.capacity() >= 3);
/// ```
#[inline]
#[unstable(feature = "shrink_to", reason = "new API", issue="0")]
pub fn shrink_to(&mut self, min_capacity: usize) {
self.vec.shrink_to(min_capacity)
}

/// Appends the given [`char`] to the end of this `String`.
///
/// [`char`]: ../../std/primitive.char.html
Expand Down
27 changes: 26 additions & 1 deletion src/liballoc/vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@

#![stable(feature = "rust1", since = "1.0.0")]

use core::cmp::Ordering;
use core::cmp::{self, Ordering};
use core::fmt;
use core::hash::{self, Hash};
use core::intrinsics::{arith_offset, assume};
Expand Down Expand Up @@ -586,6 +586,31 @@ impl<T> Vec<T> {
self.buf.shrink_to_fit(self.len);
}

/// Shrinks the capacity of the vector with a lower bound.
///
/// The capacity will remain at least as large as both the length
/// and the supplied value.
///
/// Panics if the current capacity is smaller than the supplied
/// minimum capacity.
///
/// # Examples
///
/// ```
/// #![feature(shrink_to)]
/// let mut vec = Vec::with_capacity(10);
/// vec.extend([1, 2, 3].iter().cloned());
/// assert_eq!(vec.capacity(), 10);
/// vec.shrink_to(4);
/// assert!(vec.capacity() >= 4);
/// vec.shrink_to(0);
/// assert!(vec.capacity() >= 3);
/// ```
#[unstable(feature = "shrink_to", reason = "new API", issue="0")]
pub fn shrink_to(&mut self, min_capacity: usize) {
self.buf.shrink_to_fit(cmp::max(self.len, min_capacity));
}

/// Converts the vector into [`Box<[T]>`][owned slice].
///
/// Note that this will drop any excess capacity.
Expand Down
35 changes: 34 additions & 1 deletion src/liballoc/vec_deque.rs
Original file line number Diff line number Diff line change
Expand Up @@ -676,9 +676,42 @@ impl<T> VecDeque<T> {
/// ```
#[stable(feature = "deque_extras_15", since = "1.5.0")]
pub fn shrink_to_fit(&mut self) {
self.shrink_to(0);
}

/// Shrinks the capacity of the `VecDeque` with a lower bound.
///
/// The capacity will remain at least as large as both the length
/// and the supplied value.
///
/// Panics if the current capacity is smaller than the supplied
/// minimum capacity.
///
/// # Examples
///
/// ```
/// #![feature(shrink_to)]
/// use std::collections::VecDeque;
///
/// let mut buf = VecDeque::with_capacity(15);
/// buf.extend(0..4);
/// assert_eq!(buf.capacity(), 15);
/// buf.shrink_to(6);
/// assert!(buf.capacity() >= 6);
/// buf.shrink_to(0);
/// assert!(buf.capacity() >= 4);
/// ```
#[unstable(feature = "shrink_to", reason = "new API", issue="0")]
pub fn shrink_to(&mut self, min_capacity: usize) {
assert!(self.capacity() >= min_capacity, "Tried to shrink to a larger capacity");

// +1 since the ringbuffer always leaves one space empty
// len + 1 can't overflow for an existing, well-formed ringbuffer.
let target_cap = cmp::max(self.len() + 1, MINIMUM_CAPACITY + 1).next_power_of_two();
let target_cap = cmp::max(
cmp::max(min_capacity, self.len()) + 1,
MINIMUM_CAPACITY + 1
).next_power_of_two();

if target_cap < self.cap() {
// There are three cases of interest:
// All elements are out of desired bounds
Expand Down
40 changes: 40 additions & 0 deletions src/libstd/collections/hash/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -910,6 +910,46 @@ impl<K, V, S> HashMap<K, V, S>
}
}

/// Shrinks the capacity of the map with a lower limit. It will drop
/// down no lower than the supplied limit while maintaining the internal rules
/// and possibly leaving some space in accordance with the resize policy.
///
/// Panics if the current capacity is smaller than the supplied
/// minimum capacity.
///
/// # Examples
///
/// ```
/// #![feature(shrink_to)]
/// use std::collections::HashMap;
///
/// let mut map: HashMap<i32, i32> = HashMap::with_capacity(100);
/// map.insert(1, 2);
/// map.insert(3, 4);
/// assert!(map.capacity() >= 100);
/// map.shrink_to(10);
/// assert!(map.capacity() >= 10);
/// map.shrink_to(0);
/// assert!(map.capacity() >= 2);
/// ```
#[unstable(feature = "shrink_to", reason = "new API", issue="0")]
pub fn shrink_to(&mut self, min_capacity: usize) {
assert!(self.capacity() >= min_capacity, "Tried to shrink to a larger capacity");

let new_raw_cap = self.resize_policy.raw_capacity(max(self.len(), min_capacity));
if self.raw_capacity() != new_raw_cap {
let old_table = replace(&mut self.table, RawTable::new(new_raw_cap));
let old_size = old_table.size();

// Shrink the table. Naive algorithm for resizing:
for (h, k, v) in old_table.into_iter() {
self.insert_hashed_nocheck(h, k, v);
}

debug_assert_eq!(self.table.size(), old_size);
}
}

/// Insert a pre-hashed key-value pair, without first checking
/// that there's enough room in the buckets. Returns a reference to the
/// newly insert value.
Expand Down
28 changes: 28 additions & 0 deletions src/libstd/collections/hash/set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,34 @@ impl<T, S> HashSet<T, S>
self.map.shrink_to_fit()
}

/// Shrinks the capacity of the set with a lower limit. It will drop
/// down no lower than the supplied limit while maintaining the internal rules
/// and possibly leaving some space in accordance with the resize policy.
///
/// Panics if the current capacity is smaller than the supplied
/// minimum capacity.
///
/// # Examples
///
/// ```
/// #![feature(shrink_to)]
/// use std::collections::HashSet;
///
/// let mut set = HashSet::with_capacity(100);
/// set.insert(1);
/// set.insert(2);
/// assert!(set.capacity() >= 100);
/// set.shrink_to(10);
/// assert!(set.capacity() >= 10);
/// set.shrink_to(0);
/// assert!(set.capacity() >= 2);
/// ```
#[inline]
#[unstable(feature = "shrink_to", reason = "new API", issue="0")]
pub fn shrink_to(&mut self, min_capacity: usize) {
self.map.shrink_to(min_capacity)
}

/// An iterator visiting all elements in arbitrary order.
/// The iterator element type is `&'a T`.
///
Expand Down
30 changes: 30 additions & 0 deletions src/libstd/ffi/os_str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,36 @@ impl OsString {
self.inner.shrink_to_fit()
}

/// Shrinks the capacity of the `OsString` with a lower bound.
///
/// The capacity will remain at least as large as both the length
/// and the supplied value.
///
/// Panics if the current capacity is smaller than the supplied
/// minimum capacity.
///
/// # Examples
///
/// ```
/// #![feature(shrink_to)]
/// use std::ffi::OsString;
///
/// let mut s = OsString::from("foo");
///
/// s.reserve(100);
/// assert!(s.capacity() >= 100);
///
/// s.shrink_to(10);
/// assert!(s.capacity() >= 10);
/// s.shrink_to(0);
/// assert!(s.capacity() >= 3);
/// ```
#[inline]
#[unstable(feature = "shrink_to", reason = "new API", issue="0")]
pub fn shrink_to(&mut self, min_capacity: usize) {
self.inner.shrink_to(min_capacity)
}

/// Converts this `OsString` into a boxed [`OsStr`].
///
/// [`OsStr`]: struct.OsStr.html
Expand Down
1 change: 1 addition & 0 deletions src/libstd/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,7 @@
#![feature(raw)]
#![feature(rustc_attrs)]
#![feature(stdsimd)]
#![feature(shrink_to)]
#![feature(slice_bytes)]
#![feature(slice_concat_ext)]
#![feature(slice_internals)]
Expand Down
5 changes: 5 additions & 0 deletions src/libstd/sys/redox/os_str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,11 @@ impl Buf {
self.inner.shrink_to_fit()
}

#[inline]
pub fn shrink_to(&mut self, min_capacity: usize) {
self.inner.shrink_to(min_capacity)
}

pub fn as_slice(&self) -> &Slice {
unsafe { mem::transmute(&*self.inner) }
}
Expand Down
5 changes: 5 additions & 0 deletions src/libstd/sys/unix/os_str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,11 @@ impl Buf {
self.inner.shrink_to_fit()
}

#[inline]
pub fn shrink_to(&mut self, min_capacity: usize) {
self.inner.shrink_to(min_capacity)
}

pub fn as_slice(&self) -> &Slice {
unsafe { mem::transmute(&*self.inner) }
}
Expand Down
5 changes: 5 additions & 0 deletions src/libstd/sys/wasm/os_str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,11 @@ impl Buf {
self.inner.shrink_to_fit()
}

#[inline]
pub fn shrink_to(&mut self, min_capacity: usize) {
self.inner.shrink_to(min_capacity)
}

pub fn as_slice(&self) -> &Slice {
unsafe { mem::transmute(&*self.inner) }
}
Expand Down
5 changes: 5 additions & 0 deletions src/libstd/sys/windows/os_str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,11 @@ impl Buf {
self.inner.shrink_to_fit()
}

#[inline]
pub fn shrink_to(&mut self, min_capacity: usize) {
self.inner.shrink_to(min_capacity)
}

#[inline]
pub fn into_box(self) -> Box<Slice> {
unsafe { mem::transmute(self.inner.into_box()) }
Expand Down
5 changes: 5 additions & 0 deletions src/libstd/sys_common/wtf8.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,11 @@ impl Wtf8Buf {
self.bytes.shrink_to_fit()
}

#[inline]
pub fn shrink_to(&mut self, min_capacity: usize) {
self.bytes.shrink_to(min_capacity)
}

/// Returns the number of bytes that this string buffer can hold without reallocating.
#[inline]
pub fn capacity(&self) -> usize {
Expand Down