From 6ff944daa4deb06a4b10c371f92c12c27bd67294 Mon Sep 17 00:00:00 2001 From: Amanieu d'Antras Date: Tue, 17 Apr 2018 04:37:31 +0100 Subject: [PATCH 1/4] Use a union to reduce the size of SmallVec --- .travis.yml | 3 +- Cargo.toml | 3 + lib.rs | 416 +++++++++++++++++++++++++++++++--------------------- 3 files changed, 256 insertions(+), 166 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7123702..27d5398 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,10 +5,9 @@ rust: - stable script: | cargo build --verbose && - cargo build --all-features --verbose && cargo test --verbose && - cargo test --all-features --verbose && ([ $TRAVIS_RUST_VERSION != nightly ] || cargo test --verbose --no-default-features) && + ([ $TRAVIS_RUST_VERSION != nightly ] || cargo test --verbose --features union) && ([ $TRAVIS_RUST_VERSION != nightly ] || cargo bench --verbose bench) notifications: webhooks: http://build.servo.org:54856/travis diff --git a/Cargo.toml b/Cargo.toml index 2ce30c5..cbe4480 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ documentation = "http://doc.servo.org/smallvec/" [features] std = [] +union = [] default = ["std"] [lib] @@ -19,6 +20,8 @@ name = "smallvec" path = "lib.rs" [dependencies] +unreachable = "1.0.0" +debug_unreachable = "0.1" serde = { version = "1", optional = true } [dev_dependencies] diff --git a/lib.rs b/lib.rs index 2869548..47b5d14 100644 --- a/lib.rs +++ b/lib.rs @@ -17,6 +17,7 @@ #![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(not(feature = "std"), feature(alloc))] +#![cfg_attr(feature = "union", feature(untagged_unions))] #![deny(missing_docs)] @@ -30,6 +31,13 @@ use alloc::Vec; #[cfg(feature = "serde")] extern crate serde; +extern crate unreachable; +use unreachable::UncheckedOptionExt; + +#[cfg(not(feature = "union"))] +#[macro_use] +extern crate debug_unreachable; + #[cfg(not(feature = "std"))] mod std { pub use core::*; @@ -41,6 +49,8 @@ use std::fmt; use std::hash::{Hash, Hasher}; use std::iter::{IntoIterator, FromIterator, repeat}; use std::mem; +#[cfg(not(feature = "union"))] +use std::mem::ManuallyDrop; use std::ops; use std::ptr; use std::slice; @@ -53,8 +63,6 @@ use serde::de::{Deserialize, Deserializer, SeqAccess, Visitor}; #[cfg(feature = "serde")] use std::marker::PhantomData; -use SmallVecData::{Inline, Heap}; - /// Creates a [`SmallVec`] containing the arguments. /// /// `smallvec!` allows `SmallVec`s to be defined with the same syntax as array expressions. @@ -240,40 +248,90 @@ impl<'a, T: 'a> Drop for Drain<'a,T> { } } +#[cfg(feature = "union")] +#[allow(unions_with_drop_fields)] +union SmallVecData { + inline: A, + heap: (*mut A::Item, usize), +} + +#[cfg(feature = "union")] +impl SmallVecData { + #[inline] + unsafe fn inline(&self) -> &A { + &self.inline + } + #[inline] + unsafe fn inline_mut(&mut self) -> &mut A { + &mut self.inline + } + #[inline] + fn from_inline(inline: A) -> SmallVecData { + SmallVecData { inline } + } + #[inline] + unsafe fn heap(&self) -> (*mut A::Item, usize) { + self.heap + } + #[inline] + unsafe fn heap_mut(&mut self) -> &mut (*mut A::Item, usize) { + &mut self.heap + } + #[inline] + fn from_heap(ptr: *mut A::Item, len: usize) -> SmallVecData { + SmallVecData { heap: (ptr, len) } + } +} + +#[cfg(not(feature = "union"))] enum SmallVecData { - Inline { array: A }, - Heap { ptr: *mut A::Item, capacity: usize }, + Inline(ManuallyDrop), + Heap((*mut A::Item, usize)), } +#[cfg(not(feature = "union"))] impl SmallVecData { - fn ptr_mut(&mut self) -> *mut A::Item { + #[inline] + unsafe fn inline(&self) -> &A { + match *self { + SmallVecData::Inline(ref a) => a, + _ => debug_unreachable!(), + } + } + #[inline] + unsafe fn inline_mut(&mut self) -> &mut A { + match *self { + SmallVecData::Inline(ref mut a) => a, + _ => debug_unreachable!(), + } + } + #[inline] + fn from_inline(inline: A) -> SmallVecData { + SmallVecData::Inline(ManuallyDrop::new(inline)) + } + #[inline] + unsafe fn heap(&self) -> (*mut A::Item, usize) { + match *self { + SmallVecData::Heap(data) => data, + _ => debug_unreachable!(), + } + } + #[inline] + unsafe fn heap_mut(&mut self) -> &mut (*mut A::Item, usize) { match *self { - Inline { ref mut array } => array.ptr_mut(), - Heap { ptr, .. } => ptr, + SmallVecData::Heap(ref mut data) => data, + _ => debug_unreachable!(), } } + #[inline] + fn from_heap(ptr: *mut A::Item, len: usize) -> SmallVecData { + SmallVecData::Heap((ptr, len)) + } } unsafe impl Send for SmallVecData {} unsafe impl Sync for SmallVecData {} -impl Drop for SmallVecData { - fn drop(&mut self) { - unsafe { - match *self { - ref mut inline @ Inline { .. } => { - // Inhibit the array destructor. - ptr::write(inline, Heap { - ptr: ptr::null_mut(), - capacity: 0, - }); - } - Heap { ptr, capacity } => deallocate(ptr, capacity), - } - } - } -} - /// A `Vec`-like container that can store a small number of elements inline. /// /// `SmallVec` acts like a vector, but can store a limited amount of data inline within the @@ -301,7 +359,7 @@ impl Drop for SmallVecData { /// assert!(v.spilled()); /// ``` pub struct SmallVec { - len: usize, + capacity: usize, data: SmallVecData, } @@ -311,8 +369,8 @@ impl SmallVec { pub fn new() -> SmallVec { unsafe { SmallVec { - len: 0, - data: Inline { array: mem::uninitialized() }, + capacity: 0, + data: SmallVecData::from_inline(mem::uninitialized()), } } } @@ -350,14 +408,25 @@ impl SmallVec { /// ``` #[inline] pub fn from_vec(mut vec: Vec) -> SmallVec { - let (ptr, cap, len) = (vec.as_mut_ptr(), vec.capacity(), vec.len()); - mem::forget(vec); + if vec.capacity() <= A::size() { + unsafe { + let mut data = SmallVecData::::from_inline(mem::uninitialized()); + let len = vec.len(); + vec.set_len(0); + ptr::copy_nonoverlapping(vec.as_ptr(), data.inline_mut().ptr_mut(), len); - SmallVec { - len: len, - data: SmallVecData::Heap { - ptr: ptr, - capacity: cap + SmallVec { + capacity: len, + data, + } + } + } else { + let (ptr, cap, len) = (vec.as_mut_ptr(), vec.capacity(), vec.len()); + mem::forget(vec); + + SmallVec { + capacity: cap, + data: SmallVecData::from_heap(ptr, len), } } } @@ -376,8 +445,8 @@ impl SmallVec { #[inline] pub fn from_buf(buf: A) -> SmallVec { SmallVec { - len: A::size(), - data: SmallVecData::Inline { array: buf }, + capacity: A::size(), + data: SmallVecData::from_inline(buf), } } @@ -387,7 +456,8 @@ impl SmallVec { /// modifying its buffers, so it is up to the caller to ensure that the /// vector is actually the specified size. pub unsafe fn set_len(&mut self, new_len: usize) { - self.len = new_len + let (_, len_ptr, _) = self.triple_mut(); + *len_ptr = new_len; } /// The maximum number of elements this vector can hold inline @@ -399,41 +469,59 @@ impl SmallVec { /// The number of elements stored in the vector #[inline] pub fn len(&self) -> usize { - self.len + self.triple().1 } /// Returns `true` if the vector is empty #[inline] pub fn is_empty(&self) -> bool { - self.len == 0 + self.len() == 0 } /// The number of items the vector can hold without reallocating #[inline] pub fn capacity(&self) -> usize { - match self.data { - Inline { .. } => A::size(), - Heap { capacity, .. } => capacity, + self.triple().2 + } + + #[inline] + fn triple(&self) -> (*const A::Item, usize, usize) { + unsafe { + if self.spilled() { + let (ptr, len) = self.data.heap(); + (ptr, len, self.capacity) + } else { + (self.data.inline().ptr(), self.capacity, A::size()) + } + } + } + + #[inline] + fn triple_mut(&mut self) -> (*mut A::Item, &mut usize, usize) { + unsafe { + if self.spilled() { + let &mut (ptr, ref mut len_ptr) = self.data.heap_mut(); + (ptr, len_ptr, self.capacity) + } else { + (self.data.inline_mut().ptr_mut(), &mut self.capacity, A::size()) + } } } /// Returns `true` if the data has spilled into a separate heap-allocated buffer. #[inline] pub fn spilled(&self) -> bool { - match self.data { - Inline { .. } => false, - Heap { .. } => true, - } + self.capacity > A::size() } /// Empty the vector and return an iterator over its former contents. pub fn drain(&mut self) -> Drain { unsafe { + let ptr = self.as_mut_ptr(); + let current_len = self.len(); self.set_len(0); - let ptr = self.data.ptr_mut(); - let slice = slice::from_raw_parts_mut(ptr, current_len); Drain { @@ -445,55 +533,57 @@ impl SmallVec { /// Append an item to the vector. #[inline] pub fn push(&mut self, value: A::Item) { - let cap = self.capacity(); - if self.len == cap { - self.grow(cmp::max(cap * 2, 1)) - } unsafe { - let end = self.as_mut_ptr().offset(self.len as isize); - ptr::write(end, value); - let len = self.len; - self.set_len(len + 1) + let (_, &mut len, cap) = self.triple_mut(); + if len == cap { + self.grow(cmp::max(cap * 2, 1)) + } + let (ptr, len_ptr, _) = self.triple_mut(); + *len_ptr = len + 1; + ptr::write(ptr.offset(len as isize), value); } } /// Remove an item from the end of the vector and return it, or None if empty. #[inline] pub fn pop(&mut self) -> Option { - if self.len == 0 { - return None - } - let last_index = self.len - 1; - if (last_index as isize) < 0 { - panic!("overflow") - } unsafe { - let end_ptr = self.as_ptr().offset(last_index as isize); - let value = ptr::read(end_ptr); - self.set_len(last_index); - Some(value) + let (ptr, len_ptr, _) = self.triple_mut(); + if *len_ptr == 0 { + return None; + } + let last_index = *len_ptr - 1; + *len_ptr = last_index; + Some(ptr::read(ptr.offset(last_index as isize))) } } - /// Re-allocate to set the capacity to `new_cap`. + /// Re-allocate to set the capacity to `max(new_cap, inline_size())`. /// /// Panics if `new_cap` is less than the vector's length. pub fn grow(&mut self, new_cap: usize) { - assert!(new_cap >= self.len); - let mut vec: Vec = Vec::with_capacity(new_cap); - let new_alloc = vec.as_mut_ptr(); unsafe { - mem::forget(vec); - ptr::copy_nonoverlapping(self.as_ptr(), new_alloc, self.len); - - match self.data { - Inline { .. } => {} - Heap { ptr, capacity } => deallocate(ptr, capacity), + let (ptr, &mut len, cap) = self.triple_mut(); + let spilled = self.spilled(); + assert!(new_cap >= len); + if new_cap <= self.inline_size() { + if !spilled { + return; + } + self.data = SmallVecData::from_inline(mem::uninitialized()); + ptr::copy_nonoverlapping(ptr, self.data.inline_mut().ptr_mut(), len); + deallocate(ptr, cap); + } else if new_cap != cap { + let mut vec = Vec::with_capacity(new_cap); + let new_alloc = vec.as_mut_ptr(); + mem::forget(vec); + ptr::copy_nonoverlapping(ptr, new_alloc, len); + self.data = SmallVecData::from_heap(new_alloc, len); + self.capacity = new_cap; + if spilled { + deallocate(ptr, cap); + } } - ptr::write(&mut self.data, Heap { - ptr: new_alloc, - capacity: new_cap, - }); } } @@ -505,12 +595,15 @@ impl SmallVec { /// instead. (This means that inserting `additional` new elements is not guaranteed to be /// possible after calling this function.) pub fn reserve(&mut self, additional: usize) { - let len = self.len(); - if self.capacity() - len < additional { - match len.checked_add(additional).and_then(usize::checked_next_power_of_two) { - Some(cap) => self.grow(cap), - None => self.grow(usize::max_value()), - } + // prefer triple_mut() even if triple() would work + // so that the optimizer removes duplicated calls to it + // from callers like insert() + let (_, &mut len, cap) = self.triple_mut(); + if cap - len < additional { + let new_cap = len.checked_add(additional). + and_then(usize::checked_next_power_of_two). + unwrap_or(usize::max_value()); + self.grow(new_cap); } } @@ -518,8 +611,8 @@ impl SmallVec { /// /// Panics if the new capacity overflows `usize`. pub fn reserve_exact(&mut self, additional: usize) { - let len = self.len(); - if self.capacity() - len < additional { + let (_, &mut len, cap) = self.triple_mut(); + if cap - len < additional { match len.checked_add(additional) { Some(cap) => self.grow(cap), None => panic!("reserve_exact overflow"), @@ -532,16 +625,17 @@ impl SmallVec { /// When possible, this will move data from an external heap buffer to the vector's inline /// storage. pub fn shrink_to_fit(&mut self) { - let len = self.len; + if !self.spilled() { + return; + } + let len = self.len(); if self.inline_size() >= len { unsafe { - let (ptr, capacity) = match self.data { - Inline { .. } => return, - Heap { ptr, capacity } => (ptr, capacity), - }; - ptr::write(&mut self.data, Inline { array: mem::uninitialized() }); - ptr::copy_nonoverlapping(ptr, self.as_mut_ptr(), len); - deallocate(ptr, capacity); + let (ptr, len) = self.data.heap(); + self.data = SmallVecData::from_inline(mem::uninitialized()); + ptr::copy_nonoverlapping(ptr, self.data.inline_mut().ptr_mut(), len); + deallocate(ptr, self.capacity); + self.capacity = len; } } else if self.capacity() > len { self.grow(len); @@ -556,12 +650,12 @@ impl SmallVec { /// This does not re-allocate. If you want the vector's capacity to shrink, call /// `shrink_to_fit` after truncating. pub fn truncate(&mut self, len: usize) { - let end_ptr = self.as_ptr(); - while len < self.len { - unsafe { - let last_index = self.len - 1; - self.set_len(last_index); - ptr::read(end_ptr.offset(last_index as isize)); + unsafe { + let (ptr, len_ptr, _) = self.triple_mut(); + while len < *len_ptr { + let last_index = *len_ptr - 1; + *len_ptr = last_index; + ptr::read(ptr.offset(last_index as isize)); } } } @@ -587,9 +681,9 @@ impl SmallVec { /// Panics if `index` is out of bounds. #[inline] pub fn swap_remove(&mut self, index: usize) -> A::Item { - let len = self.len; + let len = self.len(); self.swap(len - 1, index); - self.pop().unwrap() + unsafe { self.pop().unchecked_unwrap() } } /// Remove all elements from the vector. @@ -603,15 +697,14 @@ impl SmallVec { /// /// Panics if `index` is out of bounds. pub fn remove(&mut self, index: usize) -> A::Item { - let len = self.len(); - - assert!(index < len); - unsafe { - let ptr = self.as_mut_ptr().offset(index as isize); + let (mut ptr, len_ptr, _) = self.triple_mut(); + let len = *len_ptr; + assert!(index < len); + *len_ptr = len - 1; + ptr = ptr.offset(index as isize); let item = ptr::read(ptr); ptr::copy(ptr.offset(1), ptr, len - index - 1); - self.set_len(len - 1); item } } @@ -622,14 +715,14 @@ impl SmallVec { pub fn insert(&mut self, index: usize, element: A::Item) { self.reserve(1); - let len = self.len; - assert!(index <= len); - unsafe { - let ptr = self.as_mut_ptr().offset(index as isize); + let (mut ptr, len_ptr, _) = self.triple_mut(); + let len = *len_ptr; + assert!(index <= len); + *len_ptr = len + 1; + ptr = ptr.offset(index as isize); ptr::copy(ptr, ptr.offset(1), len - index); ptr::write(ptr, element); - self.set_len(len + 1); } } @@ -647,21 +740,22 @@ impl SmallVec { self.reserve(lower_size_bound); unsafe { - let old_len = self.len; + let old_len = self.len(); assert!(index <= old_len); let ptr = self.as_mut_ptr().offset(index as isize); ptr::copy(ptr, ptr.offset(lower_size_bound as isize), old_len - index); for (off, element) in iter.enumerate() { if off < lower_size_bound { ptr::write(ptr.offset(off as isize), element); - self.len = self.len + 1; + let len = self.len() + 1; + self.set_len(len); } else { // Iterator provided more elements than the hint. assert!(index + off >= index); // Protect against overflow. self.insert(index + off, element); } } - let num_added = self.len - old_len; + let num_added = self.len() - old_len; if num_added < lower_size_bound { // Iterator provided fewer elements than the hint ptr::copy(ptr.offset(lower_size_bound as isize), ptr.offset(num_added as isize), old_len - index); @@ -672,13 +766,15 @@ impl SmallVec { /// Convert a SmallVec to a Vec, without reallocating if the SmallVec has already spilled onto /// the heap. pub fn into_vec(self) -> Vec { - match self.data { - Inline { .. } => self.into_iter().collect(), - Heap { ptr, capacity } => unsafe { - let v = Vec::from_raw_parts(ptr, self.len, capacity); + if self.spilled() { + unsafe { + let (ptr, len) = self.data.heap(); + let v = Vec::from_raw_parts(ptr, len, self.capacity); mem::forget(self); v } + } else { + self.into_iter().collect() } } @@ -689,7 +785,7 @@ impl SmallVec { /// elements. pub fn retain bool>(&mut self, mut f: F) { let mut del = 0; - let len = self.len; + let len = self.len(); for i in 0..len { if !f(&mut self[i]) { del += 1; @@ -706,12 +802,12 @@ impl SmallVec { } /// Removes consecutive duplicate elements using the given equality relation. - pub fn dedup_by(&mut self, mut same_bucket: F) + pub fn dedup_by(&mut self, mut same_bucket: F) where F: FnMut(&mut A::Item, &mut A::Item) -> bool { // See the implementation of Vec::dedup_by in the // standard library for an explanation of this algorithm. - let len = self.len; + let len = self.len(); if len <= 1 { return; } @@ -737,9 +833,9 @@ impl SmallVec { } /// Removes consecutive elements that map to the same key. - pub fn dedup_by_key(&mut self, mut key: F) - where F: FnMut(&mut A::Item) -> K, - K: PartialEq + pub fn dedup_by_key(&mut self, mut key: F) + where F: FnMut(&mut A::Item) -> K, + K: PartialEq { self.dedup_by(|a, b| key(a) == key(b)); } @@ -762,7 +858,7 @@ impl SmallVec where A::Item: Copy { pub fn insert_from_slice(&mut self, index: usize, slice: &[A::Item]) { self.reserve(slice.len()); - let len = self.len; + let len = self.len(); assert!(index <= len); unsafe { @@ -804,7 +900,7 @@ impl SmallVec where A::Item: Clone { /// Creates a `SmallVec` with `n` copies of `elem`. /// ``` /// use smallvec::SmallVec; - /// + /// /// let v = SmallVec::<[char; 128]>::from_elem('d', 2); /// assert_eq!(v, SmallVec::from_buf(['d', 'd'])); /// ``` @@ -821,8 +917,8 @@ impl SmallVec where A::Item: Clone { } SmallVec { - data: Inline { array: arr }, - len: n, + capacity: n, + data: SmallVecData::from_inline(arr), } } } @@ -833,12 +929,9 @@ impl ops::Deref for SmallVec { type Target = [A::Item]; #[inline] fn deref(&self) -> &[A::Item] { - let ptr: *const _ = match self.data { - Inline { ref array } => array.ptr(), - Heap { ptr, .. } => ptr, - }; unsafe { - slice::from_raw_parts(ptr, self.len) + let (ptr, len, _) = self.triple(); + slice::from_raw_parts(ptr, len) } } } @@ -846,9 +939,9 @@ impl ops::Deref for SmallVec { impl ops::DerefMut for SmallVec { #[inline] fn deref_mut(&mut self) -> &mut [A::Item] { - let ptr = self.data.ptr_mut(); unsafe { - slice::from_raw_parts_mut(ptr, self.len) + let (ptr, &mut len, _) = self.triple_mut(); + slice::from_raw_parts_mut(ptr, len) } } } @@ -1021,27 +1114,20 @@ impl Extend for SmallVec { fn extend>(&mut self, iterable: I) { let mut iter = iterable.into_iter(); let (lower_size_bound, _) = iter.size_hint(); - - let target_len = self.len + lower_size_bound; - - if target_len > self.capacity() { - self.grow(target_len); - } + self.reserve(lower_size_bound); unsafe { - let ptr = self.as_mut_ptr().offset(self.len() as isize); - let len = self.len(); + let ptr = self.as_mut_ptr().offset(len as isize); let mut count = 0; - for p in 0..lower_size_bound as isize { + while count < lower_size_bound { if let Some(out) = iter.next() { - ::std::ptr::write(ptr.offset(p), out); + ptr::write(ptr.offset(count as isize), out); count += 1; } else { break; } } - self.set_len(len + count); } @@ -1066,12 +1152,15 @@ impl Default for SmallVec { impl Drop for SmallVec { fn drop(&mut self) { - // Note on panic safety: dropping an element may panic, - // but the inner SmallVecData destructor will still run unsafe { - let ptr = self.as_ptr(); - for i in 0 .. self.len { - ptr::read(ptr.offset(i as isize)); + if self.spilled() { + let (ptr, len) = self.data.heap(); + Vec::from_raw_parts(ptr, len, self.capacity); + } else { + let ptr = self.as_ptr(); + for i in 0..self.len() { + ptr::read(ptr.offset(i as isize)); + } } } } @@ -1125,7 +1214,7 @@ unsafe impl Send for SmallVec where A::Item: Send {} /// /// [1]: struct.SmallVec.html#method.into_iter pub struct IntoIter { - data: SmallVecData, + data: SmallVec, current: usize, end: usize, } @@ -1148,7 +1237,7 @@ impl Iterator for IntoIter { unsafe { let current = self.current as isize; self.current += 1; - Some(ptr::read(self.data.ptr_mut().offset(current))) + Some(ptr::read(self.data.as_ptr().offset(current))) } } } @@ -1169,7 +1258,7 @@ impl DoubleEndedIterator for IntoIter { else { unsafe { self.end -= 1; - Some(ptr::read(self.data.ptr_mut().offset(self.end as isize))) + Some(ptr::read(self.data.as_ptr().offset(self.end as isize))) } } } @@ -1181,13 +1270,12 @@ impl IntoIterator for SmallVec { type IntoIter = IntoIter; type Item = A::Item; fn into_iter(mut self) -> Self::IntoIter { - let len = self.len(); unsafe { - // Only grab the `data` field, the `IntoIter` type handles dropping of the elements - let data = ptr::read(&mut self.data); - mem::forget(self); + // Set SmallVec len to zero as `IntoIter` drop handles dropping of the elements + let len = self.len(); + self.set_len(0); IntoIter { - data: data, + data: self, current: 0, end: len, } From 4f968c9b9fe15cc0e188356d16b12a7772357685 Mon Sep 17 00:00:00 2001 From: arthurprs Date: Tue, 22 May 2018 19:33:16 +0200 Subject: [PATCH 2/4] Add return type to remove_noinline --- benches/bench.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/benches/bench.rs b/benches/bench.rs index 7380473..c439bec 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -158,15 +158,15 @@ fn gen_insert>(n: u64, b: &mut Bencher) { fn gen_remove>(n: usize, b: &mut Bencher) { #[inline(never)] - fn remove_noinline>(vec: &mut V, p: usize) { - vec.remove(p); + fn remove_noinline>(vec: &mut V, p: usize) -> u64 { + vec.remove(p) } b.iter(|| { let mut vec = V::from_elem(0, n as _); for x in (0..n - 1).rev() { - remove_noinline(&mut vec, x) + remove_noinline(&mut vec, x); } }); } From 35b43c0e64a68f8231f7ae8518c9d3ac9f70f4f5 Mon Sep 17 00:00:00 2001 From: arthurprs Date: Tue, 22 May 2018 20:38:55 +0200 Subject: [PATCH 3/4] Add union feature to documentation --- lib.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib.rs b/lib.rs index 47b5d14..b399b68 100644 --- a/lib.rs +++ b/lib.rs @@ -14,6 +14,17 @@ //! //! To depend on `smallvec` without `libstd`, use `default-features = false` in the `smallvec` //! section of Cargo.toml to disable its `"std"` feature. +//! +//! ## `union` feature +//! +//! When the `union` feature is enabled `smallvec` will track its state (inline or spilled) +//! without the use of an enum tag, reducing the size of the `smallvec` by one machine word. +//! This means that there is potentially no space overhead compared to `Vec`. +//! Note that `smallvec` can still be larger than `Vec` if the inline buffer is larger than two +//! machine words. +//! +//! To use this feature add `features = ["union"]` in the `smallvec` section of Cargo.toml. +//! Note that this feature requires a nightly compiler (for now). #![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(not(feature = "std"), feature(alloc))] From cfa1f0cc23d00ccb38ea933126551837e5f16fb9 Mon Sep 17 00:00:00 2001 From: arthurprs Date: Sat, 2 Jun 2018 10:03:40 +0200 Subject: [PATCH 4/4] +docs -debug_unreachable +drop_in_place --- Cargo.toml | 1 - lib.rs | 34 +++++++++++++++++++++++++--------- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index cbe4480..bbf13bf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,6 @@ path = "lib.rs" [dependencies] unreachable = "1.0.0" -debug_unreachable = "0.1" serde = { version = "1", optional = true } [dev_dependencies] diff --git a/lib.rs b/lib.rs index b399b68..d5fdc9b 100644 --- a/lib.rs +++ b/lib.rs @@ -45,10 +45,6 @@ extern crate serde; extern crate unreachable; use unreachable::UncheckedOptionExt; -#[cfg(not(feature = "union"))] -#[macro_use] -extern crate debug_unreachable; - #[cfg(not(feature = "std"))] mod std { pub use core::*; @@ -123,6 +119,19 @@ macro_rules! smallvec { }); } +/// `panic!()` in debug builds, optimization hint in release. +#[cfg(not(feature = "union"))] +macro_rules! debug_unreachable { + () => { debug_unreachable!("entered unreachable code") }; + ($e:expr) => { + if cfg!(not(debug_assertions)) { + unreachable::unreachable(); + } else { + panic!($e); + } + } +} + /// Common operations implemented by both `Vec` and `SmallVec`. /// /// This can be used to write generic code that works with both `Vec` and `SmallVec`. @@ -370,6 +379,9 @@ unsafe impl Sync for SmallVecData {} /// assert!(v.spilled()); /// ``` pub struct SmallVec { + // The capacity field is used to determine which of the storage variants is active: + // If capacity <= A::size() then the inline variant is used and capacity holds the current length of the vector (number of elements actually in use). + // If capacity > A::size() then the heap variant is used and capacity holds the size of the memory allocation. capacity: usize, data: SmallVecData, } @@ -406,8 +418,9 @@ impl SmallVec { v } - /// Construct a new `SmallVec` from a `Vec` without copying - /// elements. + /// Construct a new `SmallVec` from a `Vec`. + /// + /// Elements will be copied to the inline buffer if vec.capacity() <= A::size(). /// /// ```rust /// use smallvec::SmallVec; @@ -495,6 +508,8 @@ impl SmallVec { self.triple().2 } + /// Returns a tuple with (data ptr, len, capacity) + /// Useful to get all SmallVec properties with a single check of the current storage variant. #[inline] fn triple(&self) -> (*const A::Item, usize, usize) { unsafe { @@ -507,6 +522,7 @@ impl SmallVec { } } + /// Returns a tuple with (data ptr, len ptr, capacity) #[inline] fn triple_mut(&mut self) -> (*mut A::Item, &mut usize, usize) { unsafe { @@ -666,7 +682,7 @@ impl SmallVec { while len < *len_ptr { let last_index = *len_ptr - 1; *len_ptr = last_index; - ptr::read(ptr.offset(last_index as isize)); + ptr::drop_in_place(ptr.offset(last_index as isize)); } } } @@ -1168,9 +1184,9 @@ impl Drop for SmallVec { let (ptr, len) = self.data.heap(); Vec::from_raw_parts(ptr, len, self.capacity); } else { - let ptr = self.as_ptr(); + let ptr = self.as_mut_ptr(); for i in 0..self.len() { - ptr::read(ptr.offset(i as isize)); + ptr::drop_in_place(ptr.offset(i as isize)); } } }