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

Added feature const_new which enables SmallVec::new_const() #265

Merged
merged 4 commits into from
Sep 28, 2021
Merged
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
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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