Skip to content

Commit 5513f47

Browse files
authored
Auto merge of #228 - servo:zero, r=jdm
Don’t heap-allocate for zero-size items Allocating zero bytes is Undefined Behavior. CC servo/servo#26304 (comment)
2 parents 5f42f5f + a376b02 commit 5513f47

File tree

2 files changed

+39
-13
lines changed

2 files changed

+39
-13
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "smallvec"
3-
version = "1.4.0"
3+
version = "1.4.1"
44
edition = "2018"
55
authors = ["Simon Sapin <simon.sapin@exyr.org>"]
66
license = "MIT/Apache-2.0"

lib.rs

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -462,8 +462,8 @@ unsafe impl<A: Array + Sync> Sync for SmallVecData<A> {}
462462
/// ```
463463
pub struct SmallVec<A: Array> {
464464
// The capacity field is used to determine which of the storage variants is active:
465-
// 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).
466-
// If capacity > A::size() then the heap variant is used and capacity holds the size of the memory allocation.
465+
// If capacity <= Self::inline_capacity() then the inline variant is used and capacity holds the current length of the vector (number of elements actually in use).
466+
// If capacity > Self::inline_capacity() then the heap variant is used and capacity holds the size of the memory allocation.
467467
capacity: usize,
468468
data: SmallVecData<A>,
469469
}
@@ -506,7 +506,7 @@ impl<A: Array> SmallVec<A> {
506506

507507
/// Construct a new `SmallVec` from a `Vec<A::Item>`.
508508
///
509-
/// Elements will be copied to the inline buffer if vec.capacity() <= A::size().
509+
/// Elements will be copied to the inline buffer if vec.capacity() <= Self::inline_capacity().
510510
///
511511
/// ```rust
512512
/// use smallvec::SmallVec;
@@ -518,7 +518,7 @@ impl<A: Array> SmallVec<A> {
518518
/// ```
519519
#[inline]
520520
pub fn from_vec(mut vec: Vec<A::Item>) -> SmallVec<A> {
521-
if vec.capacity() <= A::size() {
521+
if vec.capacity() <= Self::inline_capacity() {
522522
unsafe {
523523
let mut data = SmallVecData::<A>::from_inline(MaybeUninit::uninit());
524524
let len = vec.len();
@@ -611,10 +611,30 @@ impl<A: Array> SmallVec<A> {
611611
*len_ptr = new_len;
612612
}
613613

614+
/// The maximum number of elements this vector can hold inline
615+
#[inline]
616+
fn inline_capacity() -> usize {
617+
if mem::size_of::<A::Item>() > 0 {
618+
A::size()
619+
} else {
620+
// For zero-size items code like `ptr.add(offset)` always returns the same pointer.
621+
// Therefore all items are at the same address,
622+
// and any array size has capacity for infinitely many items.
623+
// The capacity is limited by the bit width of the length field.
624+
//
625+
// `Vec` also does this:
626+
// https://github.com/rust-lang/rust/blob/1.44.0/src/liballoc/raw_vec.rs#L186
627+
//
628+
// In our case, this also ensures that a smallvec of zero-size items never spills,
629+
// and we never try to allocate zero bytes which `std::alloc::alloc` disallows.
630+
core::usize::MAX
631+
}
632+
}
633+
614634
/// The maximum number of elements this vector can hold inline
615635
#[inline]
616636
pub fn inline_size(&self) -> usize {
617-
A::size()
637+
Self::inline_capacity()
618638
}
619639

620640
/// The number of elements stored in the vector
@@ -644,7 +664,7 @@ impl<A: Array> SmallVec<A> {
644664
let (ptr, len) = self.data.heap();
645665
(ptr, len, self.capacity)
646666
} else {
647-
(self.data.inline(), self.capacity, A::size())
667+
(self.data.inline(), self.capacity, Self::inline_capacity())
648668
}
649669
}
650670
}
@@ -657,15 +677,15 @@ impl<A: Array> SmallVec<A> {
657677
let &mut (ptr, ref mut len_ptr) = self.data.heap_mut();
658678
(ptr, len_ptr, self.capacity)
659679
} else {
660-
(self.data.inline_mut(), &mut self.capacity, A::size())
680+
(self.data.inline_mut(), &mut self.capacity, Self::inline_capacity())
661681
}
662682
}
663683
}
664684

665685
/// Returns `true` if the data has spilled into a separate heap-allocated buffer.
666686
#[inline]
667687
pub fn spilled(&self) -> bool {
668-
self.capacity > A::size()
688+
self.capacity > Self::inline_capacity()
669689
}
670690

671691
/// Creates a draining iterator that removes the specified range in the vector
@@ -770,6 +790,7 @@ impl<A: Array> SmallVec<A> {
770790
deallocate(ptr, cap);
771791
} else if new_cap != cap {
772792
let layout = layout_array::<A::Item>(new_cap)?;
793+
debug_assert!(layout.size() > 0);
773794
let new_alloc;
774795
if unspilled {
775796
new_alloc = NonNull::new(alloc::alloc::alloc(layout))
@@ -1029,7 +1050,7 @@ impl<A: Array> SmallVec<A> {
10291050
/// This method returns `Err(Self)` if the SmallVec is too short (and the `A` contains uninitialized elements),
10301051
/// or if the SmallVec is too long (and all the elements were spilled to the heap).
10311052
pub fn into_inner(self) -> Result<A, Self> {
1032-
if self.spilled() || self.len() != A::size() {
1053+
if self.spilled() || self.len() != A::size() { // Note: A::size, not Self::inline_capacity
10331054
Err(self)
10341055
} else {
10351056
unsafe {
@@ -1219,7 +1240,7 @@ impl<A: Array> SmallVec<A> {
12191240
/// }
12201241
#[inline]
12211242
pub unsafe fn from_raw_parts(ptr: *mut A::Item, length: usize, capacity: usize) -> SmallVec<A> {
1222-
assert!(capacity > A::size());
1243+
assert!(capacity > Self::inline_capacity());
12231244
SmallVec {
12241245
capacity,
12251246
data: SmallVecData::from_heap(ptr, length),
@@ -1236,7 +1257,7 @@ where
12361257
/// For slices of `Copy` types, this is more efficient than `SmallVec::from(slice)`.
12371258
pub fn from_slice(slice: &[A::Item]) -> Self {
12381259
let len = slice.len();
1239-
if len <= A::size() {
1260+
if len <= Self::inline_capacity() {
12401261
SmallVec {
12411262
capacity: len,
12421263
data: SmallVecData::from_inline(unsafe {
@@ -1317,7 +1338,7 @@ where
13171338
/// assert_eq!(v, SmallVec::from_buf(['d', 'd']));
13181339
/// ```
13191340
pub fn from_elem(elem: A::Item, n: usize) -> Self {
1320-
if n > A::size() {
1341+
if n > Self::inline_capacity() {
13211342
vec![elem; n].into()
13221343
} else {
13231344
let mut v = SmallVec::<A>::new();
@@ -2731,4 +2752,9 @@ mod tests {
27312752
fn empty_macro() {
27322753
let _v: SmallVec<[u8; 1]> = smallvec![];
27332754
}
2755+
2756+
#[test]
2757+
fn zero_size_items() {
2758+
SmallVec::<[(); 0]>::new().push(());
2759+
}
27342760
}

0 commit comments

Comments
 (0)