From af665296b597f3b99deb7d39029558a5d498e3ce Mon Sep 17 00:00:00 2001 From: Soveu Date: Sat, 18 Jul 2020 00:07:10 +0200 Subject: [PATCH] "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 --- src/arrayvec.rs | 152 ++++++++++++++++++++++++++---- src/tinyvec.rs | 238 ++++++++++++++++++++++++++++++++++++----------- tests/tinyvec.rs | 52 +++++++++++ 3 files changed, 368 insertions(+), 74 deletions(-) diff --git a/src/arrayvec.rs b/src/arrayvec.rs index 2d8dadb..3527669 100644 --- a/src/arrayvec.rs +++ b/src/arrayvec.rs @@ -146,11 +146,47 @@ impl ArrayVec { /// ``` #[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 ArrayVec { } 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 ArrayVec { /// ``` #[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 { + 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 ArrayVec { /// ``` #[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 { + 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 ArrayVec { + /// 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 { + 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 { + self.drain_to_vec_and_reserve(0) + } +} + diff --git a/src/tinyvec.rs b/src/tinyvec.rs index ea2c7b5..956a39f 100644 --- a/src/tinyvec.rs +++ b/src/tinyvec.rs @@ -137,40 +137,139 @@ impl> IndexMut for TinyVec { } impl TinyVec { - /// 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 TinyVec { 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 TinyVec { } /// 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 TinyVec { { 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 TinyVec { /// ``` #[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 TinyVec { /// 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 TinyVec { /// ```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 TinyVec { 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 Extend for TinyVec { 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 FromIterator for TinyVec { #[must_use] fn from_iter>(iter: T) -> Self { let mut av = Self::default(); - for i in iter { - av.push(i) - } + av.extend(iter); av } } diff --git a/tests/tinyvec.rs b/tests/tinyvec.rs index bc00a1f..8f48798 100644 --- a/tests/tinyvec.rs +++ b/tests/tinyvec.rs @@ -100,3 +100,55 @@ fn TinyVec_macro_non_copy() { let _: TinyVec<[String; 10]> = tiny_vec!([String; 10] => s); } +#[test] +fn TinyVec_reserve() { + let mut tv: TinyVec<[i32; 4]> = Default::default(); + assert_eq!(tv.capacity(), 4); + tv.extend_from_slice(&[1, 2]); + assert_eq!(tv.capacity(), 4); + tv.reserve(2); + assert_eq!(tv.capacity(), 4); + tv.reserve(4); + assert!(tv.capacity() >= 6); + tv.extend_from_slice(&[3, 4, 5, 6]); + tv.reserve(4); + assert!(tv.capacity() >= 10); +} + +#[test] +fn TinyVec_reserve_exact() { + let mut tv: TinyVec<[i32; 4]> = Default::default(); + assert_eq!(tv.capacity(), 4); + + tv.extend_from_slice(&[1, 2]); + assert_eq!(tv.capacity(), 4); + tv.reserve_exact(2); + assert_eq!(tv.capacity(), 4); + tv.reserve_exact(4); + assert!(tv.capacity() >= 6); + tv.extend_from_slice(&[3, 4, 5, 6]); + tv.reserve_exact(4); + assert!(tv.capacity() >= 10); +} + +#[test] +fn TinyVec_move_to_heap_and_shrink() { + let mut tv: TinyVec<[i32; 4]> = Default::default(); + assert!(tv.is_inline()); + tv.move_to_the_heap(); + assert!(tv.is_heap()); + assert_eq!(tv.capacity(), 0); + + tv.push(1); + tv.shrink_to_fit(); + assert!(tv.is_inline()); + assert_eq!(tv.capacity(), 4); + + tv.move_to_the_heap_and_reserve(3); + assert!(tv.is_heap()); + assert_eq!(tv.capacity(), 4); + tv.extend(2..=4); + assert_eq!(tv.capacity(), 4); + assert_eq!(tv.as_slice(), [1,2,3,4]); +} +