From 6754a923b3489fd239fa2b8edfcc54e9e0070803 Mon Sep 17 00:00:00 2001 From: Icxolu <10486322+Icxolu@users.noreply.github.com> Date: Wed, 6 Sep 2023 19:27:25 +0200 Subject: [PATCH] add `boxed` constructors to `ArrayVec` and `ArrayString` Usually one would need to construct them on the stack and then move them to the heap if neccessary. This limits the possible capacity to the stack size. This allows creation of them directly on the head, skipping the stack, which allows for larger capacities. --- src/array_string.rs | 35 +++++++++++++++++++++++++++++++ src/arrayvec.rs | 36 ++++++++++++++++++++++++++++++++ tests/tests.rs | 50 ++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 120 insertions(+), 1 deletion(-) diff --git a/src/array_string.rs b/src/array_string.rs index 90cfc09..0a8a397 100644 --- a/src/array_string.rs +++ b/src/array_string.rs @@ -80,6 +80,41 @@ impl ArrayString 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 { + assert_capacity_limit!(CAP); + let layout = std::alloc::Layout::new::(); + // 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 } diff --git a/src/arrayvec.rs b/src/arrayvec.rs index 37e151a..7fe881b 100644 --- a/src/arrayvec.rs +++ b/src/arrayvec.rs @@ -100,6 +100,42 @@ impl ArrayVec { 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 { + assert_capacity_limit!(CAP); + let layout = std::alloc::Layout::new::(); + // 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`. /// /// ``` diff --git a/tests/tests.rs b/tests/tests.rs index 2f8a5ef..4bc1e48 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -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); -} \ No newline at end of file +} + +#[test] +fn test_arrayvec_heap_constructible() { + let mut var: Box, 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::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::boxed(); + + assert!(var.is_empty()); + assert!(var.try_push(0).is_err()); +} + +#[test] +fn test_arraystring_heap_zero_capacity() { + let mut var: Box> = 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::boxed(); + + for _ in 0..5 { + var.push(Z); + } + assert_eq!(&var[..], &[Z; 5]); + assert_eq!(var.len(), 5); +}