From 0a1a4f8e855e9955d7affd427f54008bd108f8f1 Mon Sep 17 00:00:00 2001 From: "Brandon H. Gomes" Date: Mon, 2 Aug 2021 04:43:30 -0400 Subject: [PATCH] add Vec/Box<_> conversions to ArrayVec/ArrayString --- src/array_string.rs | 138 +++++++++++++++++++++++++++++++++ src/arrayvec.rs | 181 ++++++++++++++++++++++++++++++++++++++++++-- tests/tests.rs | 12 +++ 3 files changed, 323 insertions(+), 8 deletions(-) diff --git a/src/array_string.rs b/src/array_string.rs index a044cb5..7f022b0 100644 --- a/src/array_string.rs +++ b/src/array_string.rs @@ -365,6 +365,102 @@ impl ArrayString self.len = length as LenUint; } + /// Converts `self` into an allocated byte-vector with the given capacity. + /// + /// # Safety + /// + /// The caller is responsible for ensuring that `capacity >= self.len()`. + pub unsafe fn into_bytes_with_capacity(self, capacity: usize) -> Vec { + debug_assert!(capacity >= self.len()); + + let mut vec = Vec::with_capacity(capacity); + let me = core::mem::ManuallyDrop::new(self); + let len = me.len(); + + // SAFETY: The caller ensures that we own a region of memory at least as large as `len` + // at the location pointed to by `vec`. + me.as_ptr().copy_to_nonoverlapping(vec.as_mut_ptr(), len); + vec.set_len(len); + + vec + } + + /// Converts `self` into an allocated vector of bytes. + /// + /// # Allocation + /// + /// This method allocates a vector with capacity equal to the capacity of the original + /// `ArrayString`. + /// + /// To only allocate exactly enough space for the string stored in `self`, use the + /// [`into_boxed_str`](Self::into_boxed_str) method. + /// + /// To allocate a specific amount of space, see the + /// [`into_bytes_with_capacity`](Self::into_bytes_with_capacity) method. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use arrayvec::ArrayString; + /// + /// let s = ArrayString::<5>::from("hello").unwrap(); + /// let bytes = s.into_bytes(); + /// + /// assert_eq!(&[104, 101, 108, 108, 111][..], &bytes[..]); + /// ``` + #[inline] + pub fn into_bytes(self) -> Vec { + // SAFETY: The capacity of `self` is at least as large as the length of `self`. + unsafe { + self.into_bytes_with_capacity(CAP) + } + } + + /// Converts this `ArrayString` into a [`Box`]`<`[`str`]`>`. + /// + /// This will drop any excess capacity. + /// + /// [`str`]: prim@str + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use arrayvec::ArrayString; + /// + /// let s = ArrayString::<5>::from("hello").unwrap(); + /// + /// let b = s.into_boxed_str(); + /// ``` + #[inline] + pub fn into_boxed_str(self) -> Box { + let len = self.len(); + // SAFETY: We only require the memory capacity equal to the length of the initialized data region in `self`. + unsafe { + str::from_boxed_utf8_unchecked(self.into_bytes_with_capacity(len).into_boxed_slice()) + } + } + + /// Create a new `ArrayString` by consuming a boxed string, moving the heap-allocated `str` to a + /// stack-allocated array. + /// + /// # Safety + /// + /// The caller is responsible for ensuring that the string has a length no larger than the + /// capacity of `ArrayString`. + pub unsafe fn from_boxed_str_unchecked(boxed_string: Box) -> Self { + debug_assert!(CAP >= boxed_string.len()); + assert_capacity_limit!(CAP); + let len = boxed_string.len() as LenUint; + Self { + xs: *Box::from_raw(Box::into_raw(boxed_string) as *mut [MaybeUninit; CAP]) , + len, + } + } + /// Return a string slice of the whole `ArrayString`. pub fn as_str(&self) -> &str { self @@ -581,6 +677,48 @@ impl<'de, const CAP: usize> Deserialize<'de> for ArrayString } } +impl From> for Box { + #[inline] + fn from(array: ArrayString) -> Self { + array.into_boxed_str() + } +} + +impl TryFrom> for ArrayString { + type Error = CapacityError; + + fn try_from(string: Box) -> Result { + if CAP < string.len() { + Err(CapacityError::new(())) + } else { + Ok(unsafe { Self::from_boxed_str_unchecked(string) }) + } + } +} + +impl From> for Vec { + #[inline] + fn from(array: ArrayString) -> Self { + array.into_bytes() + } +} + +impl From> for String { + #[inline] + fn from(array: ArrayString) -> Self { + array.to_string() + } +} + +impl TryFrom for ArrayString { + type Error = CapacityError; + + #[inline] + fn try_from(string: String) -> Result { + Self::try_from(string.into_boxed_str()) + } +} + impl<'a, const CAP: usize> TryFrom<&'a str> for ArrayString { type Error = CapacityError<&'a str>; diff --git a/src/arrayvec.rs b/src/arrayvec.rs index 6b4faf8..460521e 100644 --- a/src/arrayvec.rs +++ b/src/arrayvec.rs @@ -1,5 +1,5 @@ - use std::cmp; +use std::convert::TryFrom; use std::iter; use std::mem; use std::ops::{Bound, Deref, DerefMut, RangeBounds}; @@ -61,9 +61,6 @@ macro_rules! panic_oob { } impl ArrayVec { - /// Capacity - const CAPACITY: usize = CAP; - /// Create a new empty `ArrayVec`. /// /// The maximum capacity is given by the generic parameter `CAP`. @@ -642,7 +639,8 @@ impl ArrayVec { /// Return the inner fixed size array. /// - /// Safety: + /// # Safety + /// /// This operation is safe if and only if length equals capacity. pub unsafe fn into_inner_unchecked(self) -> [T; CAP] { debug_assert_eq!(self.len(), self.capacity()); @@ -650,6 +648,74 @@ impl ArrayVec { let array = ptr::read(self_.as_ptr() as *const [T; CAP]); array } + + /// Converts `self` into an allocated vector with the given capacity. + /// + /// # Safety + /// + /// The caller is responsible for ensuring that `capacity >= self.len()`. + pub unsafe fn into_vec_with_capacity(self, capacity: usize) -> Vec { + debug_assert!(capacity >= self.len()); + + let mut vec = Vec::with_capacity(capacity); + let me = ManuallyDrop::new(self); + let len = me.len(); + + // SAFETY: The caller ensures that we own a region of memory at least as large as `len` + // at the location pointed to by `vec`. + me.as_ptr().copy_to_nonoverlapping(vec.as_mut_ptr(), len); + vec.set_len(len); + + vec + } + + /// Converts `self` into an allocated vector. + /// + /// # Allocation + /// + /// This method allocates a vector with capacity equal to the capacity of the original `ArrayVec`. + /// + /// To only allocate exactly enough space for the elements stored in `self`, use the + /// [`into_boxed_slice`](Self::into_boxed_slice) method. + /// + /// To allocate a specific amount of space, see the + /// [`into_vec_with_capacity`](Self::into_vec_with_capacity) method. + #[inline] + pub fn into_vec(self) -> Vec { + // SAFETY: The capacity of `self` is at least as large as the length of `self`. + unsafe { + self.into_vec_with_capacity(CAP) + } + } + + /// Converts the `ArrayVec` into `Box<[T]>`. + /// + /// Note that this will drop any excess capacity. + #[inline] + pub fn into_boxed_slice(self) -> Box<[T]> { + let len = self.len(); + // SAFETY: We only require the memory capacity equal to the length of the initialized data region in `self`. + unsafe { + self.into_vec_with_capacity(len).into_boxed_slice() + } + } + + /// Create a new `ArrayVec` by consuming a boxed slice, moving the heap-allocated slice to a + /// stack-allocated array. + /// + /// # Safety + /// + /// The caller is responsible for ensuring that the slice has a length no larger than the + /// capacity of `ArrayVec`. + pub unsafe fn from_boxed_slice_unchecked(boxed_slice: Box<[T]>) -> Self { + debug_assert!(CAP >= boxed_slice.len()); + assert_capacity_limit!(CAP); + let len = boxed_slice.len() as LenUint; + Self { + xs: *Box::from_raw(Box::into_raw(boxed_slice) as *mut [MaybeUninit; CAP]) , + len, + } + } /// Returns the ArrayVec, replacing the original with a new empty ArrayVec. /// @@ -743,7 +809,6 @@ impl From<[T; CAP]> for ArrayVec { } } - /// Try to create an `ArrayVec` from a slice. This will return an error if the slice was too big to /// fit. /// @@ -755,13 +820,13 @@ impl From<[T; CAP]> for ArrayVec { /// assert_eq!(array.len(), 3); /// assert_eq!(array.capacity(), 4); /// ``` -impl std::convert::TryFrom<&[T]> for ArrayVec +impl TryFrom<&[T]> for ArrayVec where T: Clone, { type Error = CapacityError; fn try_from(slice: &[T]) -> Result { - if Self::CAPACITY < slice.len() { + if CAP < slice.len() { Err(CapacityError::new(())) } else { let mut array = Self::new(); @@ -771,6 +836,106 @@ impl std::convert::TryFrom<&[T]> for ArrayVec } } +/// Create a `Box<[T]>` from an `ArrayVec`. +/// +/// ``` +/// use arrayvec::ArrayVec; +/// +/// let array = ArrayVec::from([1, 2, 3]); +/// let slice = Box::<[_]>::from(array.clone()); +/// assert_eq!(array.as_slice(), slice.as_ref()); +/// ``` +impl From> for Box<[T]> { + #[inline] + fn from(array: ArrayVec) -> Self { + array.into_boxed_slice() + } +} + +/// Try to create an `ArrayVec` from a `Box<[_]>`. This will return an error if the boxed slice was too big to +/// fit. +/// +/// ``` +/// use arrayvec::ArrayVec; +/// use std::convert::TryInto as _; +/// +/// let slice: Box<[_]> = Box::new([1, 2, 3]); +/// let array: ArrayVec<_, 4> = slice.clone().try_into().unwrap(); +/// assert_eq!(array.len(), 3); +/// assert_eq!(array.capacity(), 4); +/// ``` +impl TryFrom> for ArrayVec { + type Error = CapacityError; + + fn try_from(slice: Box<[T]>) -> Result { + if CAP < slice.len() { + Err(CapacityError::new(())) + } else { + Ok(unsafe { Self::from_boxed_slice_unchecked(slice) }) + } + } +} + +/// Try to create an `ArrayVec` from a `Box<[_; N]>`. This will return an error if the boxed array was too big to +/// fit. +/// +/// ``` +/// use arrayvec::ArrayVec; +/// use std::convert::TryInto as _; +/// +/// let slice: Box<[_; 3]> = Box::new([1, 2, 3]); +/// let array: ArrayVec<_, 4> = slice.clone().try_into().unwrap(); +/// assert_eq!(array.len(), 3); +/// assert_eq!(array.capacity(), 4); +/// ``` +impl TryFrom> for ArrayVec { + type Error = CapacityError; + + #[inline] + fn try_from(array: Box<[T; N]>) -> Result { + Self::try_from(array as Box<[_]>) + } +} + +/// Create a `Vec` from an `ArrayVec`. +/// +/// ``` +/// use arrayvec::ArrayVec; +/// use std::convert::TryInto as _; +/// +/// let array: ArrayVec<_, 4> = vec![1, 2, 3].try_into().unwrap(); +/// let vector = Vec::from(array.clone()); +/// assert_eq!(array.capacity(), vector.capacity()); +/// assert_eq!(array.as_slice(), vector.as_slice()); +/// ``` +impl From> for Vec { + #[inline] + fn from(array: ArrayVec) -> Self { + array.into_vec() + } +} + +/// Try to create an `ArrayVec` from a `Vec`. This will return an error if the vector was too big to +/// fit. +/// +/// ``` +/// use arrayvec::ArrayVec; +/// use std::convert::TryInto as _; +/// +/// let vec = vec![1, 2, 3]; +/// let array: ArrayVec<_, 4> = vec.clone().try_into().unwrap(); +/// assert_eq!(array.len(), 3); +/// assert_eq!(array.capacity(), 4); +/// assert_eq!(vec.as_slice(), array.as_slice()); +/// ``` +impl TryFrom> for ArrayVec { + type Error = CapacityError; + + #[inline] + fn try_from(vec: Vec) -> Result { + Self::try_from(vec.into_boxed_slice()) + } +} /// Iterate the `ArrayVec` with references to each element. /// diff --git a/tests/tests.rs b/tests/tests.rs index 26d09ae..608c5b6 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -74,6 +74,18 @@ fn test_try_from_slice_error() { assert_matches!(res, Err(_)); } +#[test] +fn test_try_from_vec() { + use arrayvec::ArrayVec; + use std::convert::TryInto as _; + + let ok: Result, _> = vec![1, 2, 3].try_into(); + assert_matches!(ok, Ok(_)); + + let err: Result, _> = vec![1, 2, 3].try_into(); + assert_matches!(err, Err(_)); +} + #[test] fn test_u16_index() { const N: usize = 4096;