"try_*" functions and "to_vec" for arrayvec (#82)

* change behavior on extend_from_slice, append

* preallocation in extend()

* append v2

* obviously

* fix resize_with

* try_functions and to_vec on arrayvec

* shrink_to_fit

* bit of cleaning

* cosmetic changes

* use asserts instead of if-panics

* I promise, this is the last commit

* examples, tests

* matches macro exists since rust 1.42

* Update tinyvec.rs

* new extend and insert

* mem::take is from rust 1.40
This commit is contained in:
Soveu
2020-07-18 00:07:10 +02:00
committed by GitHub
parent 66077cfa99
commit af665296b5
3 changed files with 368 additions and 74 deletions
+134 -18
View File
@@ -146,11 +146,47 @@ impl<A: Array> ArrayVec<A> {
/// ```
#[inline]
pub fn append(&mut self, other: &mut Self) {
let new_len = self.len() + other.len();
assert!(new_len <= A::CAPACITY,
"ArrayVec::append> total length {} exceeds capacity {}!",
new_len, A::CAPACITY
);
for item in other.drain(..) {
self.push(item)
self.push(item);
}
}
/// Move all values from `other` into this vec.
/// If appending would overflow the capacity, Some(other) is returned.
/// ## Example
/// ```rust
/// # use tinyvec::*;
/// let mut av = array_vec!([i32; 7] => 1, 2, 3);
/// let mut av2 = array_vec!([i32; 7] => 4, 5, 6);
/// av.append(&mut av2);
/// assert_eq!(av, &[1, 2, 3, 4, 5, 6][..]);
/// assert_eq!(av2, &[][..]);
///
/// let mut av3 = array_vec!([i32; 7] => 7, 8, 9);
/// assert!(av.try_append(&mut av3).is_some());
/// assert_eq!(av, &[1, 2, 3, 4, 5, 6][..]);
/// assert_eq!(av3, &[7, 8, 9][..]);
/// ```
#[inline]
pub fn try_append<'other>(&mut self, other: &'other mut Self) -> Option<&'other mut Self> {
let new_len = self.len() + other.len();
if new_len > A::CAPACITY {
return Some(other);
}
for item in other.drain(..) {
self.push(item);
}
return None;
}
/// A `*mut` pointer to the backing array.
///
/// ## Safety
@@ -274,13 +310,12 @@ impl<A: Array> ArrayVec<A> {
}
let new_len = self.len + sli.len();
if new_len > A::CAPACITY {
panic!(
"ArrayVec::extend_from_slice> total length {} exceeds capacity {}!",
new_len,
A::CAPACITY
)
}
assert!(
new_len <= A::CAPACITY,
"ArrayVec::extend_from_slice> total length {} exceeds capacity {}!",
new_len,
A::CAPACITY
);
let target = &mut self.data.as_slice_mut()[self.len..new_len];
target.clone_from_slice(sli);
@@ -364,14 +399,41 @@ impl<A: Array> ArrayVec<A> {
/// ```
#[inline]
pub fn insert(&mut self, index: usize, item: A::Item) {
if index > self.len {
panic!("ArrayVec::insert> index {} is out of bounds {}", index, self.len);
let x = self.try_insert(index, item);
assert!(x.is_none(), "ArrayVec::insert> capacity overflow!");
}
/// Tries to insert an item at the position given, moving all following elements +1
/// index.
/// Returns back the element if the capacity is exhausted,
/// otherwise returns None.
///
/// ## Panics
/// * If `index` > `len`
///
/// ## Example
/// ```rust
/// use tinyvec::*;
/// let mut av = array_vec!([&'static str; 4] => "one", "two", "three");
/// av.insert(1, "four");
/// assert_eq!(av.as_slice(), &["one", "four", "two", "three"]);
/// assert_eq!(av.try_insert(4, "five"), Some("five"));
/// ```
#[inline]
pub fn try_insert(&mut self, index: usize, item: A::Item) -> Option<A::Item> {
assert!(
index <= self.len,
"ArrayVec::try_insert> index {} is out of bounds {}",
index,
self.len
);
if let Some(x) = self.try_push(item) {
return Some(x);
}
// Try to push the element.
self.push(item);
// And move it into its place.
self.as_mut_slice()[index..].rotate_right(1);
return None;
}
/// Checks if the length is 0.
@@ -440,12 +502,32 @@ impl<A: Array> ArrayVec<A> {
/// ```
#[inline(always)]
pub fn push(&mut self, val: A::Item) {
if self.len < A::CAPACITY {
self.data.as_slice_mut()[self.len] = val;
self.len += 1;
} else {
panic!("ArrayVec::push> capacity overflow!")
let x = self.try_push(val);
assert!(x.is_none(), "ArrayVec::push> capacity overflow!");
}
/// Tries to place an element onto the end of the vec.\
/// Returns back the element if the capacity is exhausted,
/// otherwise returns None.
/// ```rust
/// # use tinyvec::*;
/// let mut av = array_vec!([i32; 2]);
/// assert_eq!(av.as_slice(), []);
/// assert_eq!(av.try_push(1), None);
/// assert_eq!(&av[..], [1]);
/// assert_eq!(av.try_push(2), None);
/// assert_eq!(&av[..], [1, 2]);
/// assert_eq!(av.try_push(3), Some(3));
/// ```
#[inline(always)]
pub fn try_push(&mut self, val: A::Item) -> Option<A::Item> {
if self.len == A::CAPACITY {
return Some(val);
}
self.data.as_slice_mut()[self.len] = val;
self.len += 1;
return None;
}
/// Removes the item at `index`, shifting all others down by one index.
@@ -1220,3 +1302,37 @@ where
write!(f, "]")
}
}
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
#[cfg(feature = "alloc")]
impl<A: Array> ArrayVec<A> {
/// Drains all elements to a Vec, but reserves additional space
/// ```
/// # use tinyvec::*;
/// let mut av = array_vec!([i32; 7] => 1, 2, 3);
/// let v = av.drain_to_vec_and_reserve(10);
/// assert_eq!(v, &[1, 2, 3]);
/// assert_eq!(v.capacity(), 13);
/// ```
pub fn drain_to_vec_and_reserve(&mut self, n: usize) -> Vec<A::Item> {
let cap = n + self.len();
let mut v = Vec::with_capacity(cap);
v.extend(self.drain(..));
return v;
}
/// Drains all elements to a Vec
/// ```
/// # use tinyvec::*;
/// let mut av = array_vec!([i32; 7] => 1, 2, 3);
/// let v = av.drain_to_vec();
/// assert_eq!(v, &[1, 2, 3]);
/// assert_eq!(v.capacity(), 3);
/// ```
pub fn drain_to_vec(&mut self) -> Vec<A::Item> {
self.drain_to_vec_and_reserve(0)
}
}
+182 -56
View File
@@ -137,40 +137,139 @@ impl<A: Array, I: SliceIndex<[A::Item]>> IndexMut<I> for TinyVec<A> {
}
impl<A: Array> TinyVec<A> {
/// Moves the content of the TinyVec to the heap, if it's inline.
#[allow(clippy::missing_inline_in_public_items)]
pub fn move_to_the_heap(&mut self) {
let len = match self {
TinyVec::Heap(_) => return,
TinyVec::Inline(ref arr) => arr.len(),
/// Returns whether elements are on heap
pub fn is_heap(&self) -> bool {
match self {
TinyVec::Heap(_) => true,
TinyVec::Inline(_) => false,
}
}
/// Returns whether elements are on stack
pub fn is_inline(&self) -> bool {
!self.is_heap()
}
/// Shrinks the capacity of the vector as much as possible.\
/// It is inlined if length is less than `A::CAPACITY`.
/// ```rust
/// use tinyvec::*;
/// let mut tv = tiny_vec!([i32; 2] => 1, 2, 3);
/// assert!(tv.is_heap());
/// let _ = tv.pop();
/// assert!(tv.is_heap());
/// tv.shrink_to_fit();
/// assert!(tv.is_inline());
/// ```
pub fn shrink_to_fit(&mut self)
where A: Default,
{
let vec = match self {
TinyVec::Inline(_) => return,
TinyVec::Heap(h) => h,
};
self.move_to_the_heap_and_reserve(len);
if vec.len() > A::CAPACITY {
return vec.shrink_to_fit();
}
let movedvec = core::mem::replace(vec, Vec::new());
let mut av = ArrayVec::default();
let mut rest = av.fill(movedvec);
debug_assert!(rest.next().is_none());
*self = TinyVec::Inline(av);
}
/// Moves the content of the TinyVec to the heap, if it's inline.
/// ```rust
/// use tinyvec::*;
/// let mut tv = tiny_vec!([i32; 4] => 1, 2, 3);
/// assert!(tv.is_inline());
/// tv.move_to_the_heap();
/// assert!(tv.is_heap());
/// ```
#[allow(clippy::missing_inline_in_public_items)]
pub fn move_to_the_heap(&mut self) {
let arr = match self {
TinyVec::Heap(_) => return,
TinyVec::Inline(a) => a,
};
let v = arr.drain_to_vec();
*self = TinyVec::Heap(v);
}
/// If TinyVec is inline, moves the content of it to the heap.
/// Also reserves additional space.
/// ```rust
/// use tinyvec::*;
/// let mut tv = tiny_vec!([i32; 4] => 1, 2, 3);
/// assert!(tv.is_inline());
/// tv.move_to_the_heap_and_reserve(32);
/// assert!(tv.is_heap());
/// assert!(tv.capacity() >= 35);
/// ```
pub fn move_to_the_heap_and_reserve(&mut self, n: usize) {
match self {
TinyVec::Heap(h) => h.reserve(n),
TinyVec::Inline(ref mut arr) => {
let mut v = Vec::with_capacity(arr.len() + n);
v.extend(arr.drain(..));
*self = TinyVec::Heap(v);
}
}
let arr = match self {
TinyVec::Heap(h) => return h.reserve(n),
TinyVec::Inline(a) => a,
};
let v = arr.drain_to_vec_and_reserve(n);
*self = TinyVec::Heap(v);
}
/// Reserves additional space.
/// Moves to the heap if array can't hold `n` more items
/// ```rust
/// use tinyvec::*;
/// let mut tv = tiny_vec!([i32; 4] => 1, 2, 3, 4);
/// assert!(tv.is_inline());
/// tv.reserve(1);
/// assert!(tv.is_heap());
/// assert!(tv.capacity() >= 5);
/// ```
pub fn reserve(&mut self, n: usize) {
let arr = match self {
TinyVec::Heap(h) => return h.reserve(n),
TinyVec::Inline(ref mut a) => a,
TinyVec::Heap(h) => return h.reserve(n),
TinyVec::Inline(a) => a,
};
if n > arr.capacity() - arr.len() {
return self.move_to_the_heap_and_reserve(n);
let v = arr.drain_to_vec_and_reserve(n);
*self = TinyVec::Heap(v);
}
/* In this place array has enough place, so no work is needed more */
return;
}
/// Reserves additional space.
/// Moves to the heap if array can't hold `n` more items
///
/// From [Vec::reserve_exact](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.reserve_exact)
/// ```text
/// Note that the allocator may give the collection more space than it requests.
/// Therefore, capacity can not be relied upon to be precisely minimal.
/// Prefer reserve if future insertions are expected.
/// ```
/// ```rust
/// use tinyvec::*;
/// let mut tv = tiny_vec!([i32; 4] => 1, 2, 3, 4);
/// assert!(tv.is_inline());
/// tv.reserve_exact(1);
/// assert!(tv.is_heap());
/// assert!(tv.capacity() >= 5);
/// ```
pub fn reserve_exact(&mut self, n: usize) {
let arr = match self {
TinyVec::Heap(h) => return h.reserve_exact(n),
TinyVec::Inline(a) => a,
};
if n > arr.capacity() - arr.len() {
let v = arr.drain_to_vec_and_reserve(n);
*self = TinyVec::Heap(v);
}
/* In this place array has enough place, so no work is needed more */
@@ -185,13 +284,9 @@ impl<A: Array> TinyVec<A> {
self.reserve(other.len());
let iter = other.drain(..);
let arr = match self {
TinyVec::Heap(h) => return h.extend(iter),
TinyVec::Inline(ref mut a) => a,
};
for item in iter {
arr.push(item)
match self {
TinyVec::Heap(h) => h.extend(iter),
TinyVec::Inline(a) => a.extend(iter),
}
}
@@ -346,6 +441,12 @@ impl<A: Array> TinyVec<A> {
}
/// Clone each element of the slice into this vec.
/// ```rust
/// use tinyvec::*;
/// let mut tv = tiny_vec!([i32; 4] => 1, 2);
/// tv.extend_from_slice(&[3, 4]);
/// assert_eq!(tv.as_slice(), [1, 2, 3, 4]);
/// ```
#[inline]
pub fn extend_from_slice(&mut self, sli: &[A::Item])
where
@@ -353,8 +454,8 @@ impl<A: Array> TinyVec<A> {
{
self.reserve(sli.len());
match self {
TinyVec::Inline(ref mut a) => a.extend_from_slice(sli),
TinyVec::Heap(ref mut h) => h.extend_from_slice(sli),
TinyVec::Inline(a) => a.extend_from_slice(sli),
TinyVec::Heap(h) => h.extend_from_slice(sli),
}
}
@@ -404,16 +505,23 @@ impl<A: Array> TinyVec<A> {
/// ```
#[inline]
pub fn insert(&mut self, index: usize, item: A::Item) {
match self {
TinyVec::Inline(a) => {
if a.len() == A::CAPACITY {
self.move_to_the_heap();
self.insert(index, item)
} else {
a.insert(index, item);
}
}
TinyVec::Heap(v) => v.insert(index, item),
assert!(index <= self.len(),
"insertion index (is {}) should be <= len (is {})",
index, self.len()
);
let arr = match self {
TinyVec::Heap(v) => return v.insert(index, item),
TinyVec::Inline(a) => a,
};
if let Some(x) = arr.try_insert(index, item) {
let mut v = Vec::with_capacity(arr.len() * 2);
let mut it = arr.iter_mut().map(|r| core::mem::replace(r, Default::default()));
v.extend(it.by_ref().take(index));
v.push(x);
v.extend(it);
*self = TinyVec::Heap(v);
}
}
@@ -459,18 +567,24 @@ impl<A: Array> TinyVec<A> {
/// Place an element onto the end of the vec.
/// ## Panics
/// * If the length of the vec would overflow the capacity.
/// ```rust
/// use tinyvec::*;
/// let mut tv = tiny_vec!([i32; 10] => 1, 2, 3);
/// tv.push(4);
/// assert_eq!(tv.as_slice(), &[1, 2, 3, 4]);
/// ```
#[inline(always)]
pub fn push(&mut self, val: A::Item) {
match self {
TinyVec::Inline(a) => {
if a.len() == A::CAPACITY {
self.move_to_the_heap();
self.push(val)
} else {
a.push(val);
}
}
TinyVec::Heap(v) => v.push(val),
let arr = match self {
TinyVec::Heap(v) => return v.push(val),
TinyVec::Inline(a) => a,
};
if let Some(x) = arr.try_push(val) {
/* Make the Vec twice the size to amortize the cost of draining */
let mut v = arr.drain_to_vec_and_reserve(arr.len());
v.push(x);
*self = TinyVec::Heap(v);
}
}
@@ -534,11 +648,11 @@ impl<A: Array> TinyVec<A> {
/// ```rust
/// use tinyvec::*;
///
/// let mut tv = tiny_vec!([i32; 10] => 1, 2, 3);
/// let mut tv = tiny_vec!([i32; 3] => 1, 2, 3);
/// tv.resize_with(5, Default::default);
/// assert_eq!(tv.as_slice(), &[1, 2, 3, 0, 0][..]);
///
/// let mut tv = tiny_vec!([i32; 10]);
/// let mut tv = tiny_vec!([i32; 2]);
/// let mut p = 1;
/// tv.resize_with(4, || {
/// p *= 2;
@@ -555,7 +669,7 @@ impl<A: Array> TinyVec<A> {
match self {
TinyVec::Inline(a) => a.resize_with(new_len, f),
TinyVec::Heap(v) => v.resize_with(new_len, f),
TinyVec::Heap(v) => v.resize_with(new_len, f),
}
}
@@ -725,9 +839,23 @@ impl<A: Array> Extend<A::Item> for TinyVec<A> {
let (lower_bound, _) = iter.size_hint();
self.reserve(lower_bound);
for t in iter {
self.push(t)
}
let a = match self {
TinyVec::Heap(h) => return h.extend(iter),
TinyVec::Inline(a) => a,
};
let mut iter = a.fill(iter);
let maybe = iter.next();
let surely = match maybe {
Some(x) => x,
None => return,
};
let mut v = a.drain_to_vec_and_reserve(a.len());
v.push(surely);
v.extend(iter);
*self = TinyVec::Heap(v);
}
}
@@ -781,9 +909,7 @@ impl<A: Array + Default> FromIterator<A::Item> for TinyVec<A> {
#[must_use]
fn from_iter<T: IntoIterator<Item = A::Item>>(iter: T) -> Self {
let mut av = Self::default();
for i in iter {
av.push(i)
}
av.extend(iter);
av
}
}