Skip to content
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

WIP: rewrite based on MaybeUninit and #[repr(usize)] enum #157

Closed
wants to merge 1 commit into from
Closed
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
15 changes: 6 additions & 9 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
language: rust
rust:
- 1.20.0
- 1.36.0
- nightly
- beta
- stable
script: |
cargo build --verbose &&
cargo test --verbose &&
cargo test --verbose --features serde &&
([ $TRAVIS_RUST_VERSION != nightly ] || cargo check --verbose --no-default-features) &&
([ $TRAVIS_RUST_VERSION != nightly ] || cargo test --verbose --features union) &&
([ $TRAVIS_RUST_VERSION != nightly ] || cargo test --verbose --all-features) &&
([ $TRAVIS_RUST_VERSION != nightly ] || cargo bench --verbose bench)
script:
- cargo test
- cargo test --features serde
- "[ $TRAVIS_RUST_VERSION != nightly ] || cargo test --all-features"
- "[ $TRAVIS_RUST_VERSION != nightly ] || cargo bench"
9 changes: 2 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "smallvec"
version = "0.6.10"
authors = ["Simon Sapin <[email protected]>"]
version = "1.0.0"
authors = ["The Servo Project Developers"]
license = "MIT/Apache-2.0"
repository = "https://github.com/servo/rust-smallvec"
description = "'Small vector' optimization: store up to a small number of items on the stack"
Expand All @@ -11,15 +11,10 @@ readme = "README.md"
documentation = "https://doc.servo.org/smallvec/"

[features]
std = []
union = []
default = ["std"]
specialization = []
may_dangle = []

[lib]
name = "smallvec"
path = "lib.rs"

[dependencies]
serde = { version = "1", optional = true }
Expand Down
30 changes: 30 additions & 0 deletions src/array.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

/// Types that can be used as the backing store for a SmallVec
pub unsafe trait Array {
/// The type of the array's elements.
type Item;
/// Returns the number of items the array can hold.
const LEN: usize;
}

macro_rules! impl_array(
($($len:expr),+) => {
$(
unsafe impl<T> Array for [T; $len] {
type Item = T;
const LEN: usize = $len;
}
)+
}
);

impl_array!(
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 20, 24, 32, 36, 0x40, 0x80, 0x100,
0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000, 0x8000, 0x10000, 0x20000, 0x40000, 0x80000,
0x100000
);
59 changes: 59 additions & 0 deletions src/heap.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use self::TryReserveError::CapacityOverflow as CO;
use alloc::alloc::{self, Layout};
use core::mem;
use core::ptr::NonNull;

fn array_layout<T>(capacity: usize) -> Result<Layout, TryReserveError> {
let size = mem::size_of::<T>().checked_mul(capacity).ok_or(CO)?;
if size > crate::repr::MAX_LEN {
return Err(CO);
}
Layout::from_size_align(size, mem::align_of::<T>()).map_err(|_| CO)
}

pub(crate) unsafe fn alloc_array<T>(capacity: usize) -> Result<NonNull<T>, TryReserveError> {
let layout = array_layout::<T>(capacity)?;
let ptr = alloc::alloc(layout);
NonNull::new(ptr)
.map(NonNull::cast)
.ok_or(TryReserveError::AllocError { layout })
}

pub(crate) unsafe fn realloc_array<T>(
ptr: NonNull<T>,
old: usize,
new: usize,
) -> Result<NonNull<T>, TryReserveError> {
let old = array_layout::<T>(old)?;
let new = array_layout::<T>(new)?;
let ptr = alloc::realloc(ptr.cast().as_ptr(), old, new.size());
NonNull::new(ptr)
.map(NonNull::cast)
.ok_or(TryReserveError::AllocError { layout: new })
}

pub(crate) unsafe fn dealloc_array<T>(ptr: NonNull<T>, capacity: usize) {
let layout = array_layout::<T>(capacity).unwrap();
alloc::dealloc(ptr.cast().as_ptr(), layout)
}

#[derive(Debug)]
pub enum TryReserveError {
CapacityOverflow,
AllocError { layout: Layout },
}

impl TryReserveError {
pub(crate) fn bail(&self) -> ! {
match *self {
TryReserveError::CapacityOverflow => panic!("SmallVec capacity overflow"),
TryReserveError::AllocError { layout } => alloc::handle_alloc_error(layout),
}
}
}
232 changes: 232 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![no_std]
#![cfg_attr(feature = "may_dangle", feature(dropck_eyepatch))]

extern crate alloc;

mod array;
mod heap;
mod repr;
mod tagged_union;

pub use array::Array;
pub use heap::TryReserveError;
pub use repr::SmallVec;

use alloc::vec::Vec;

impl<A: Array> SmallVec<A> {
const INLINE_CAPACITY: usize = A::LEN;

pub fn is_empty(&self) -> bool {
self.len() == 0
}

pub fn capacity(&self) -> usize {
match self.as_inline() {
Ok(_) => Self::INLINE_CAPACITY,
Err(heap) => heap.capacity,
}
}

pub fn as_ptr(&self) -> *const A::Item {
match self.as_inline() {
Ok(inline) => inline.as_ptr(),
Err(heap) => heap.ptr.as_ptr(),
}
}

pub fn as_mut_ptr(&mut self) -> *mut A::Item {
match self.as_inline_mut() {
Ok(inline) => inline.as_mut_ptr(),
Err(heap) => heap.ptr.as_ptr(),
}
}

pub fn as_slice(&self) -> &[A::Item] {
unsafe { core::slice::from_raw_parts(self.as_ptr(), self.len()) }
}

pub fn as_mut_slice(&mut self) -> &mut [A::Item] {
unsafe { core::slice::from_raw_parts_mut(self.as_mut_ptr(), self.len()) }
}

pub fn shrink_to_fit(&mut self) {
self.shrink_to(0)
}

pub fn shrink_to(&mut self, new_capacity: usize) {
let len = self.len();
let new_capacity = new_capacity.max(len);
if let Err(heap) = self.as_inline_mut() {
if new_capacity <= Self::INLINE_CAPACITY {
let ptr = heap.ptr;
let capacity = heap.capacity;
// Do accesses to `heap` above here, so we can borrow `self` again below:
unsafe {
let inline = self.set_inline_tag(len);
core::ptr::copy_nonoverlapping(ptr.as_ptr(), inline.as_mut_ptr(), len);
heap::dealloc_array(ptr, capacity)
}
} else if new_capacity < heap.capacity {
let new_ptr = unsafe {
heap::realloc_array(heap.ptr, heap.capacity, new_capacity)
.unwrap_or_else(|err| err.bail())
};
heap.ptr = new_ptr;
heap.capacity = new_capacity;
}
}
}

fn try_reserve_internal(&mut self, extra: usize, exact: bool) -> Result<(), TryReserveError> {
let len = self.len();
let capacity = self.capacity();
let requested_capacity = len
.checked_add(extra)
.ok_or(TryReserveError::CapacityOverflow)?;
if requested_capacity <= capacity {
return Ok(());
}
let new_capacity = if exact {
requested_capacity
} else {
requested_capacity.max(capacity * 2)
};
unsafe {
match self.as_inline_mut() {
Ok(inline) => {
let new_ptr = heap::alloc_array(new_capacity)?;
core::ptr::copy_nonoverlapping(inline.as_ptr(), new_ptr.as_ptr(), len);
let heap = self.set_heap_tag(len);
heap.ptr = new_ptr;
heap.capacity = new_capacity;
}
Err(heap) => {
let new_ptr = heap::realloc_array(heap.ptr, capacity, new_capacity)?;
heap.ptr = new_ptr;
heap.capacity = new_capacity;
}
}
}
Ok(())
}

pub fn reserve(&mut self, extra: usize) {
self.try_reserve_internal(extra, false)
.unwrap_or_else(|err| err.bail())
}

pub fn reserve_exact(&mut self, extra: usize) {
self.try_reserve_internal(extra, true)
.unwrap_or_else(|err| err.bail())
}

pub fn try_reserve(&mut self, extra: usize) -> Result<(), TryReserveError> {
self.try_reserve_internal(extra, false)
}

pub fn try_reserve_exact(&mut self, extra: usize) -> Result<(), TryReserveError> {
self.try_reserve_internal(extra, true)
}

pub fn push(&mut self, value: A::Item) {
self.reserve(1);
let len = self.len();
unsafe {
self.as_mut_ptr().add(len).write(value);
self.set_len(len + 1)
}
}

pub fn clear(&mut self) {
self.truncate(0)
}

pub fn truncate(&mut self, new_len: usize) {
if let Some(to_drop) = self.get_mut(new_len..) {
let to_drop: *mut [A::Item] = to_drop;
unsafe {
self.set_len(new_len);
to_drop.drop_in_place()
}
}
}
}

#[cfg(feature = "may_dangle")]
unsafe impl<#[may_dangle] A: Array> Drop for SmallVec<A> {
fn drop(&mut self) {
drop_impl(self)
}
}

#[cfg(not(feature = "may_dangle"))]
impl<A: Array> Drop for SmallVec<A> {
fn drop(&mut self) {
drop_impl(self)
}
}

fn drop_impl<A: Array>(s: &mut SmallVec<A>) {
unsafe {
core::ptr::drop_in_place(s.as_mut_slice());
match s.as_inline() {
Ok(_) => {}
Err(heap) => heap::dealloc_array(heap.ptr, heap.capacity),
}
}
}

impl<A: Array> core::ops::Deref for SmallVec<A> {
type Target = [A::Item];
fn deref(&self) -> &[A::Item] {
self.as_slice()
}
}

impl<A: Array> core::ops::DerefMut for SmallVec<A> {
fn deref_mut(&mut self) -> &mut [A::Item] {
self.as_mut_slice()
}
}

impl<A: Array, I> core::ops::Index<I> for SmallVec<A>
where
I: core::slice::SliceIndex<[A::Item]>,
{
type Output = I::Output;
fn index(&self, index: I) -> &I::Output {
&self.as_slice()[index]
}
}

impl<A: Array, I> core::ops::IndexMut<I> for SmallVec<A>
where
I: core::slice::SliceIndex<[A::Item]>,
{
fn index_mut(&mut self, index: I) -> &mut I::Output {
&mut self.as_mut_slice()[index]
}
}

impl<A: Array> From<Vec<A::Item>> for SmallVec<A> {
fn from(mut vec: Vec<A::Item>) -> Self {
let ptr = vec.as_mut_ptr();
let len = vec.len();
let cap = vec.capacity();
core::mem::forget(vec);
let mut s = Self::new();
unsafe {
let heap = s.set_heap_tag(len);
heap.ptr = core::ptr::NonNull::new_unchecked(ptr);
heap.capacity = cap;
}
s
}
}
Loading