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

Add boxed constructors to ArrayVec and ArrayString #244

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
35 changes: 35 additions & 0 deletions src/array_string.rs
Expand Up @@ -80,6 +80,41 @@ impl<const CAP: usize> ArrayString<CAP>
ArrayString { xs: MakeMaybeUninit::ARRAY, len: 0 }
}

/// Create a new empty `ArrayString` directly on the heap.
///
/// Capacity is inferred from the type parameter.
///
/// ```
/// use arrayvec::ArrayString;
///
/// let mut string = ArrayString::<16>::boxed();
/// string.push_str("foo");
/// assert_eq!(&string[..], "foo");
/// assert_eq!(string.capacity(), 16);
/// ```
#[track_caller]
pub fn boxed() -> Box<Self> {
assert_capacity_limit!(CAP);
let layout = std::alloc::Layout::new::<Self>();
// SAFETY: `Self` is not a ZST, it contains at least `len`, even if
// `CAP` is 0
let ptr = unsafe { std::alloc::alloc(layout) as *mut Self };
if ptr.is_null() {
std::alloc::handle_alloc_error(layout);
}
// SAFETY:
// - `len` needs to be initialized to 0
// - `xs` does _not_ need to be initialized if `len` is 0.
// - `ptr` is valid and well aligned for type `Self`
unsafe { std::ptr::addr_of_mut!((*ptr).len).write(0) };

// SAFETY: "It is valid to convert both ways between a Box and a raw
// pointer allocated with the Global allocator, given that the Layout
// used with the allocator is correct for the type."
// https://doc.rust-lang.org/stable/std/boxed/index.html#memory-layout
unsafe { Box::from_raw(ptr) }
}

/// Return the length of the string.
#[inline]
pub const fn len(&self) -> usize { self.len as usize }
Expand Down
36 changes: 36 additions & 0 deletions src/arrayvec.rs
Expand Up @@ -100,6 +100,42 @@ impl<T, const CAP: usize> ArrayVec<T, CAP> {
ArrayVec { xs: MakeMaybeUninit::ARRAY, len: 0 }
}

/// Create a new empty `ArrayVec` directly on the heap.
///
/// The maximum capacity is given by the generic parameter `CAP`.
///
/// ```
/// use arrayvec::ArrayVec;
///
/// let mut array = ArrayVec::<_, 16>::boxed();
/// array.push(1);
/// array.push(2);
/// assert_eq!(&array[..], &[1, 2]);
/// assert_eq!(array.capacity(), 16);
/// ```
#[track_caller]
pub fn boxed() -> Box<Self> {
assert_capacity_limit!(CAP);
let layout = std::alloc::Layout::new::<Self>();
// SAFETY: `Self` is not a ZST, it contains at least `len`, even if `T`
// is a ZST or `CAP` is 0
let ptr = unsafe { std::alloc::alloc(layout) as *mut Self };
if ptr.is_null() {
std::alloc::handle_alloc_error(layout);
}
// SAFETY:
// - `len` needs to be initialized to 0
// - `xs` does _not_ need to be initialized if `len` is 0.
// - `ptr` is valid and well aligned for type `Self`
unsafe { std::ptr::addr_of_mut!((*ptr).len).write(0) };

// SAFETY: "It is valid to convert both ways between a Box and a raw
// pointer allocated with the Global allocator, given that the Layout
// used with the allocator is correct for the type."
// https://doc.rust-lang.org/stable/std/boxed/index.html#memory-layout
unsafe { Box::from_raw(ptr) }
}

/// Return the number of elements in the `ArrayVec`.
///
/// ```
Expand Down
50 changes: 49 additions & 1 deletion tests/tests.rs
Expand Up @@ -790,4 +790,52 @@ fn test_arraystring_zero_filled_has_some_sanity_checks() {
let string = ArrayString::<4>::zero_filled();
assert_eq!(string.as_str(), "\0\0\0\0");
assert_eq!(string.len(), 4);
}
}

#[test]
fn test_arrayvec_heap_constructible() {
let mut var: Box<ArrayVec<Vec<u8>, 10>> = ArrayVec::boxed();

assert!(var.is_empty());
var.push(vec![3, 5, 8]);
assert_eq!(var[..], [vec![3, 5, 8]]);
}

#[test]
fn test_arraystring_heap_constructible() {
let mut var: Box<ArrayString<10>> = ArrayString::boxed();

assert!(var.is_empty());
var.push_str("hello");
assert_eq!(*var, *"hello");
}

#[test]
fn test_arrayvec_heap_zero_capacity() {
let mut var: Box<ArrayVec<u8, 0>> = ArrayVec::boxed();

assert!(var.is_empty());
assert!(var.try_push(0).is_err());
}

#[test]
fn test_arraystring_heap_zero_capacity() {
let mut var: Box<ArrayString<0>> = ArrayString::boxed();

assert!(var.is_empty());
assert!(var.try_push_str("hello").is_err());
}

#[test]
fn test_arrayvec_heap_zst() {
#[derive(Copy, Clone, PartialEq, Debug)]
struct Z; // Zero sized type

let mut var: Box<ArrayVec<Z, 10>> = ArrayVec::boxed();

for _ in 0..5 {
var.push(Z);
}
assert_eq!(&var[..], &[Z; 5]);
assert_eq!(var.len(), 5);
}