Skip to content

Commit

Permalink
Added feature const_new which enables SmallVec::new_const() (#265)
Browse files Browse the repository at this point in the history
  • Loading branch information
TimLuq committed Sep 28, 2021
1 parent 2691f34 commit 1e4b151
Show file tree
Hide file tree
Showing 3 changed files with 143 additions and 3 deletions.
5 changes: 5 additions & 0 deletions Cargo.toml
Expand Up @@ -13,6 +13,7 @@ documentation = "https://docs.rs/smallvec/"

[features]
const_generics = []
const_new = ["const_generics"]
write = []
union = []
specialization = []
Expand All @@ -23,3 +24,7 @@ serde = { version = "1", optional = true, default-features = false }

[dev_dependencies]
bincode = "1.0.1"

[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
112 changes: 109 additions & 3 deletions src/lib.rs
Expand Up @@ -48,6 +48,14 @@
//! When this feature is enabled, `SmallVec` works with any arrays of any size, not just a fixed
//! list of sizes.
//!
//! ### `const_new`
//!
//! **This feature requires Rust 1.51.**
//!
//! This feature exposes the functions [`SmallVec::new_const`], [`SmallVec::from_const`], and [`smallvec_inline`] which enables the `SmallVec` to be initialized from a const context.
//! For details, see the
//! [Rust Reference](https://doc.rust-lang.org/reference/const_eval.html#const-functions).
//!
//! ### `specialization`
//!
//! **This feature is unstable and requires a nightly build of the Rust toolchain.**
Expand All @@ -69,6 +77,7 @@
//! Tracking issue: [rust-lang/rust#34761](https://github.com/rust-lang/rust/issues/34761)

#![no_std]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(feature = "specialization", allow(incomplete_features))]
#![cfg_attr(feature = "specialization", feature(specialization))]
#![cfg_attr(feature = "may_dangle", feature(dropck_eyepatch))]
Expand Down Expand Up @@ -170,6 +179,52 @@ macro_rules! smallvec {
});
}

/// Creates an inline [`SmallVec`] containing the arguments. This macro is enabled by the feature `const_new`.
///
/// `smallvec_inline!` allows `SmallVec`s to be defined with the same syntax as array expressions in `const` contexts.
/// The inline storage `A` will always be an array of the size specified by the arguments.
/// There are two forms of this macro:
///
/// - Create a [`SmallVec`] containing a given list of elements:
///
/// ```
/// # #[macro_use] extern crate smallvec;
/// # use smallvec::SmallVec;
/// # fn main() {
/// const V: SmallVec<[i32; 3]> = smallvec_inline![1, 2, 3];
/// assert_eq!(V[0], 1);
/// assert_eq!(V[1], 2);
/// assert_eq!(V[2], 3);
/// # }
/// ```
///
/// - Create a [`SmallVec`] from a given element and size:
///
/// ```
/// # #[macro_use] extern crate smallvec;
/// # use smallvec::SmallVec;
/// # fn main() {
/// const V: SmallVec<[i32; 3]> = smallvec_inline![1; 3];
/// assert_eq!(V, SmallVec::from_buf([1, 1, 1]));
/// # }
/// ```
///
/// Note that the behavior mimics that of array expressions, in contrast to [`smallvec`].
#[cfg(feature = "const_new")]
#[cfg_attr(docsrs, doc(cfg(feature = "const_new")))]
#[macro_export]
macro_rules! smallvec_inline {
// count helper: transform any expression into 1
(@one $x:expr) => (1usize);
($elem:expr; $n:expr) => ({
$crate::SmallVec::<[_; $n]>::from_const([$elem; $n])
});
($($x:expr),+ $(,)?) => ({
const N: usize = 0usize $(+ $crate::smallvec_inline!(@one $x))*;
$crate::SmallVec::<[_; N]>::from_const([$($x,)*])
});
}

/// `panic!()` in debug builds, optimization hint in release.
#[cfg(not(feature = "union"))]
macro_rules! debug_unreachable {
Expand Down Expand Up @@ -353,6 +408,17 @@ union SmallVecData<A: Array> {
heap: (*mut A::Item, usize),
}

#[cfg(all(feature = "union", feature = "const_new"))]
impl<T, const N: usize> SmallVecData<[T; N]> {
#[cfg_attr(docsrs, doc(cfg(feature = "const_new")))]
#[inline]
const fn from_const(inline: MaybeUninit<[T; N]>) -> Self {
SmallVecData {
inline: core::mem::ManuallyDrop::new(inline),
}
}
}

#[cfg(feature = "union")]
impl<A: Array> SmallVecData<A> {
#[inline]
Expand Down Expand Up @@ -393,6 +459,15 @@ enum SmallVecData<A: Array> {
Heap((*mut A::Item, usize)),
}

#[cfg(all(not(feature = "union"), feature = "const_new"))]
impl<T, const N: usize> SmallVecData<[T; N]> {
#[cfg_attr(docsrs, doc(cfg(feature = "const_new")))]
#[inline]
const fn from_const(inline: MaybeUninit<[T; N]>) -> Self {
SmallVecData::Inline(inline)
}
}

#[cfg(not(feature = "union"))]
impl<A: Array> SmallVecData<A> {
#[inline]
Expand Down Expand Up @@ -1487,6 +1562,7 @@ impl<A: Array> BorrowMut<[A::Item]> for SmallVec<A> {
}

#[cfg(feature = "write")]
#[cfg_attr(docsrs, doc(cfg(feature = "write")))]
impl<A: Array<Item = u8>> io::Write for SmallVec<A> {
#[inline]
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
Expand All @@ -1507,6 +1583,7 @@ impl<A: Array<Item = u8>> io::Write for SmallVec<A> {
}

#[cfg(feature = "serde")]
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
impl<A: Array> Serialize for SmallVec<A>
where
A::Item: Serialize,
Expand All @@ -1521,6 +1598,7 @@ where
}

#[cfg(feature = "serde")]
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
impl<'de, A: Array> Deserialize<'de> for SmallVec<A>
where
A::Item: Deserialize<'de>,
Expand Down Expand Up @@ -1950,15 +2028,43 @@ impl<'a> Drop for SetLenOnDrop<'a> {
}
}

#[cfg(feature = "const_generics")]
#[cfg(feature = "const_new")]
impl<T, const N: usize> SmallVec<[T; N]> {
/// Construct an empty vector.
///
/// This is a `const` version of [`SmallVec::new`] that is enabled by the feature `const_new`, with the limitation that it only works for arrays.
#[cfg_attr(docsrs, doc(cfg(feature = "const_new")))]
#[inline]
pub const fn new_const() -> Self {
SmallVec {
capacity: 0,
data: SmallVecData::from_const(MaybeUninit::uninit()),
}
}

/// The array passed as an argument is moved to be an inline version of `SmallVec`.
///
/// This is a `const` version of [`SmallVec::from_buf`] that is enabled by the feature `const_new`, with the limitation that it only works for arrays.
#[cfg_attr(docsrs, doc(cfg(feature = "const_new")))]
#[inline]
pub const fn from_const(items: [T; N]) -> Self {
SmallVec {
capacity: N,
data: SmallVecData::from_const(MaybeUninit::new(items)),
}
}
}

#[cfg(all(feature = "const_generics", not(doc)))]
#[cfg_attr(docsrs, doc(cfg(feature = "const_generics")))]
unsafe impl<T, const N: usize> Array for [T; N] {
type Item = T;
fn size() -> usize {
N
}
}

#[cfg(not(feature = "const_generics"))]
#[cfg(any(not(feature = "const_generics"), doc))]
macro_rules! impl_array(
($($size:expr),+) => {
$(
Expand All @@ -1970,7 +2076,7 @@ macro_rules! impl_array(
}
);

#[cfg(not(feature = "const_generics"))]
#[cfg(any(not(feature = "const_generics"), doc))]
impl_array!(
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
26, 27, 28, 29, 30, 31, 32, 36, 0x40, 0x60, 0x80, 0x100, 0x200, 0x400, 0x600, 0x800, 0x1000,
Expand Down
29 changes: 29 additions & 0 deletions src/tests.rs
Expand Up @@ -903,6 +903,35 @@ fn const_generics() {
let _v = SmallVec::<[i32; 987]>::default();
}

#[cfg(feature = "const_new")]
#[test]
fn const_new() {
let v = const_new_inner();
assert_eq!(v.capacity(), 4);
assert_eq!(v.len(), 0);
let v = const_new_inline_sized();
assert_eq!(v.capacity(), 4);
assert_eq!(v.len(), 4);
assert_eq!(v[0], 1);
let v = const_new_inline_args();
assert_eq!(v.capacity(), 2);
assert_eq!(v.len(), 2);
assert_eq!(v[0], 1);
assert_eq!(v[1], 4);
}
#[cfg(feature = "const_new")]
const fn const_new_inner() -> SmallVec<[i32; 4]> {
SmallVec::<[i32; 4]>::new_const()
}
#[cfg(feature = "const_new")]
const fn const_new_inline_sized() -> SmallVec<[i32; 4]> {
crate::smallvec_inline![1; 4]
}
#[cfg(feature = "const_new")]
const fn const_new_inline_args() -> SmallVec<[i32; 2]> {
crate::smallvec_inline![1, 4]
}

#[test]
fn empty_macro() {
let _v: SmallVec<[u8; 1]> = smallvec![];
Expand Down

0 comments on commit 1e4b151

Please sign in to comment.