use super::{TryClone, TryNew, Vec, try_alloc};
use crate::error::OutOfMemory;
use core::{
alloc::Layout,
mem::{self, MaybeUninit},
};
use std_alloc::boxed::Box;
#[inline]
fn new_uninit_box<T>() -> Result<Box<MaybeUninit<T>>, OutOfMemory> {
let layout = Layout::new::<MaybeUninit<T>>();
if layout.size() == 0 {
return Ok(Box::new(MaybeUninit::uninit()));
}
let ptr = unsafe { try_alloc(layout)? };
let ptr = ptr.cast::<MaybeUninit<T>>();
Ok(unsafe { Box::from_raw(ptr.as_ptr()) })
}
impl<T> TryNew for Box<T> {
type Value = T;
#[inline]
fn try_new(value: T) -> Result<Self, OutOfMemory>
where
Self: Sized,
{
let boxed = new_uninit_box::<T>()?;
Ok(Box::write(boxed, value))
}
}
impl<T> TryClone for Box<T>
where
T: TryClone,
{
fn try_clone(&self) -> Result<Self, OutOfMemory> {
let b = new_uninit_box::<T>()?;
let v = (**self).try_clone()?;
Ok(Box::write(b, v))
}
}
impl<T> TryClone for Box<[T]>
where
T: TryClone,
{
fn try_clone(&self) -> Result<Self, OutOfMemory> {
let mut builder = BoxedSliceBuilder::new(self.len())?;
for v in &*self {
builder.push(v.try_clone()?).expect("reserved capacity");
}
debug_assert_eq!(builder.init_len(), builder.capacity());
Ok(builder.finish())
}
}
pub fn new_uninit_boxed_slice<T>(len: usize) -> Result<Box<[MaybeUninit<T>]>, OutOfMemory> {
let layout = Layout::array::<MaybeUninit<T>>(len)
.map_err(|_| OutOfMemory::new(mem::size_of::<T>().saturating_mul(len)))?;
if layout.size() == 0 {
return Ok(Box::new_uninit_slice(len));
}
let ptr = unsafe { try_alloc(layout)? };
let ptr = ptr.cast::<MaybeUninit<T>>().as_ptr();
let ptr = core::ptr::slice_from_raw_parts_mut(ptr, len);
Ok(unsafe { Box::from_raw(ptr) })
}
use boxed_slice_builder::BoxedSliceBuilder;
mod boxed_slice_builder {
use super::*;
pub struct BoxedSliceBuilder<T> {
vec: Vec<T>,
}
impl<T> BoxedSliceBuilder<T> {
pub fn new(len: usize) -> Result<Self, OutOfMemory> {
let mut vec = Vec::new();
vec.reserve_exact(len)?;
Ok(Self { vec })
}
pub fn from_boxed_slice(boxed: Box<[MaybeUninit<T>]>) -> Self {
let len = boxed.len();
let ptr = Box::into_raw(boxed);
let ptr = ptr.cast::<T>();
let vec = unsafe { Vec::from_raw_parts(ptr, 0, len) };
Self { vec }
}
pub fn init_len(&self) -> usize {
self.vec.len()
}
pub fn capacity(&self) -> usize {
self.vec.capacity()
}
pub fn push(&mut self, value: T) -> Result<(), OutOfMemory> {
self.vec.push(value)
}
pub fn finish(mut self) -> Box<[T]> {
assert_eq!(self.init_len(), self.capacity());
let vec = mem::take(&mut self.vec);
mem::forget(self);
let (ptr, len, cap) = vec.into_raw_parts();
debug_assert_eq!(len, cap);
let ptr = core::ptr::slice_from_raw_parts_mut(ptr, len);
unsafe { Box::from_raw(ptr) }
}
pub fn shrink_to_fit(&mut self) -> Result<(), OutOfMemory> {
if self.init_len() == self.capacity() {
return Ok(());
}
let len = self.init_len();
let cap = self.capacity();
let vec = mem::take(&mut self.vec);
let old_layout = Layout::array::<T>(cap).expect(
"already have an allocation with this layout so should be able to recreate it",
);
let new_layout = Layout::array::<T>(len)
.expect("if `cap` is fine for an array layout, then `len` must be as well");
debug_assert_eq!(old_layout.align(), new_layout.align());
if new_layout.size() == 0 {
debug_assert!(mem::size_of::<T>() == 0 || len == 0);
if len == 0 {
debug_assert_eq!(self.capacity(), 0);
debug_assert_eq!(self.init_len(), 0);
} else {
debug_assert_eq!(mem::size_of::<T>(), 0);
let ptr = core::ptr::dangling_mut::<T>();
debug_assert!(!ptr.is_null());
debug_assert!(ptr.is_aligned());
self.vec = unsafe { Vec::from_raw_parts(ptr, len, len) };
}
debug_assert_eq!(self.capacity(), self.init_len());
return Ok(());
}
let (ptr, _len, _cap) = vec.into_raw_parts();
debug_assert_eq!(len, _len);
debug_assert_eq!(cap, _cap);
let new_ptr = unsafe {
std_alloc::alloc::realloc(ptr.cast::<u8>(), old_layout, new_layout.size())
};
if new_ptr.is_null() {
self.vec = unsafe { Vec::from_raw_parts(ptr, len, cap) };
Err(OutOfMemory::new(new_layout.size()))
} else {
let new_ptr = new_ptr.cast::<T>();
self.vec = unsafe { Vec::from_raw_parts(new_ptr, len, len) };
debug_assert_eq!(self.capacity(), self.init_len());
Ok(())
}
}
}
}
#[non_exhaustive]
#[derive(Debug, Clone, Copy)]
pub struct TooFewItems;
impl core::fmt::Display for TooFewItems {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str("iterator yielded too few items to fully initialize boxed slice")
}
}
impl core::error::Error for TooFewItems {}
#[derive(Debug)]
pub enum TooFewItemsOrOom {
TooFewItems(TooFewItems),
Oom(OutOfMemory),
}
impl TooFewItemsOrOom {
pub fn unwrap_oom(&self) -> OutOfMemory {
match self {
TooFewItemsOrOom::TooFewItems(_) => panic!("`unwrap_oom` on non-OOM error"),
TooFewItemsOrOom::Oom(oom) => *oom,
}
}
}
impl From<TooFewItems> for TooFewItemsOrOom {
fn from(e: TooFewItems) -> Self {
Self::TooFewItems(e)
}
}
impl From<OutOfMemory> for TooFewItemsOrOom {
fn from(oom: OutOfMemory) -> Self {
Self::Oom(oom)
}
}
impl core::fmt::Display for TooFewItemsOrOom {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::TooFewItems(_) => {
f.write_str("The iterator did not yield enough items to fill the boxed slice")
}
Self::Oom(_) => f.write_str("Failed to allocate space for the boxed slice"),
}
}
}
impl core::error::Error for TooFewItemsOrOom {
fn cause(&self) -> Option<&dyn core::error::Error> {
match self {
Self::TooFewItems(e) => Some(e),
Self::Oom(oom) => Some(oom),
}
}
}
pub fn boxed_slice_write_iter<T>(
boxed: Box<[MaybeUninit<T>]>,
iter: impl IntoIterator<Item = T>,
) -> Result<Box<[T]>, TooFewItems> {
let len = boxed.len();
let builder = BoxedSliceBuilder::from_boxed_slice(boxed);
assert_eq!(len, builder.capacity());
write_iter_into_builder(builder, iter)
}
pub fn new_boxed_slice_from_iter_with_len<T>(
len: usize,
iter: impl IntoIterator<Item = T>,
) -> Result<Box<[T]>, TooFewItemsOrOom> {
let builder = BoxedSliceBuilder::new(len)?;
assert_eq!(len, builder.capacity());
let boxed = write_iter_into_builder(builder, iter)?;
Ok(boxed)
}
fn write_iter_into_builder<T>(
mut builder: BoxedSliceBuilder<T>,
iter: impl IntoIterator<Item = T>,
) -> Result<Box<[T]>, TooFewItems> {
let len = builder.capacity();
for elem in iter.into_iter().take(len) {
builder.push(elem).expect("reserved capacity");
}
if builder.init_len() < builder.capacity() {
return Err(TooFewItems);
}
debug_assert_eq!(builder.init_len(), builder.capacity());
Ok(builder.finish())
}
#[derive(Debug)]
pub enum BoxedSliceFromFallibleIterError<E> {
IterError(E),
Oom(OutOfMemory),
}
impl<E> From<OutOfMemory> for BoxedSliceFromFallibleIterError<E> {
fn from(oom: OutOfMemory) -> Self {
Self::Oom(oom)
}
}
impl<E> core::fmt::Display for BoxedSliceFromFallibleIterError<E> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::IterError(_) => f.write_str("The fallible iterator produced an error"),
Self::Oom(_) => f.write_str("Failed to allocate space for the boxed slice"),
}
}
}
impl<E> core::error::Error for BoxedSliceFromFallibleIterError<E>
where
E: core::error::Error,
{
fn cause(&self) -> Option<&dyn core::error::Error> {
match self {
Self::IterError(e) => Some(e),
Self::Oom(oom) => Some(oom),
}
}
}
impl BoxedSliceFromFallibleIterError<OutOfMemory> {
pub fn flatten(self) -> OutOfMemory {
match self {
Self::IterError(oom) | Self::Oom(oom) => oom,
}
}
}
pub fn new_boxed_slice_from_fallible_iter<T, E>(
iter: impl IntoIterator<Item = Result<T, E>>,
) -> Result<Box<[T]>, BoxedSliceFromFallibleIterError<E>> {
let iter = iter.into_iter();
let (min, max) = iter.size_hint();
let len = max.unwrap_or_else(|| min);
let mut builder = BoxedSliceBuilder::new(len)?;
assert_eq!(len, builder.capacity());
for result in iter {
let elem = result.map_err(BoxedSliceFromFallibleIterError::IterError)?;
builder.push(elem)?;
}
debug_assert!(builder.init_len() <= builder.capacity());
builder.shrink_to_fit()?;
debug_assert_eq!(builder.init_len(), builder.capacity());
Ok(builder.finish())
}
pub fn new_boxed_slice_from_iter<T>(
iter: impl IntoIterator<Item = T>,
) -> Result<Box<[T]>, OutOfMemory> {
let iter = iter
.into_iter()
.map(Result::<T, core::convert::Infallible>::Ok);
new_boxed_slice_from_fallible_iter(iter).map_err(|e| match e {
BoxedSliceFromFallibleIterError::Oom(oom) => oom,
BoxedSliceFromFallibleIterError::IterError(_) => unreachable!(),
})
}
#[cfg(test)]
mod tests {
use super::*;
use core::cell::Cell;
use std_alloc::rc::Rc;
struct SetFlagOnDrop(Rc<Cell<bool>>);
impl Drop for SetFlagOnDrop {
fn drop(&mut self) {
let old_value = self.0.replace(true);
assert_eq!(old_value, false);
}
}
impl SetFlagOnDrop {
fn new() -> (Rc<Cell<bool>>, Self) {
let flag = Rc::new(Cell::new(false));
(flag.clone(), SetFlagOnDrop(flag))
}
}
#[test]
fn try_new() {
<Box<_> as TryNew>::try_new(4).unwrap();
}
#[test]
fn new_boxed_slice_from_iter_with_len_smoke_test() {
let slice = new_boxed_slice_from_iter_with_len(3, [42, 36, 1337]).unwrap();
assert_eq!(&*slice, &[42, 36, 1337]);
}
#[test]
fn new_boxed_slice_from_iter_with_len_with_too_few_elems() {
let (a_dropped, a) = SetFlagOnDrop::new();
let (b_dropped, b) = SetFlagOnDrop::new();
let (c_dropped, c) = SetFlagOnDrop::new();
match new_boxed_slice_from_iter_with_len(4, [a, b, c]) {
Err(TooFewItemsOrOom::TooFewItems(_)) => {}
Ok(_) | Err(TooFewItemsOrOom::Oom(_)) => unreachable!(),
}
assert!(a_dropped.get());
assert!(b_dropped.get());
assert!(c_dropped.get());
}
#[test]
fn new_boxed_slice_from_iter_with_len_with_too_many_elems() {
let (a_dropped, a) = SetFlagOnDrop::new();
let (b_dropped, b) = SetFlagOnDrop::new();
let (c_dropped, c) = SetFlagOnDrop::new();
let slice = new_boxed_slice_from_iter_with_len(2, [a, b, c]).unwrap();
assert!(!a_dropped.get());
assert!(!b_dropped.get());
assert!(c_dropped.get());
drop(slice);
assert!(a_dropped.get());
assert!(b_dropped.get());
assert!(c_dropped.get());
}
#[test]
fn new_boxed_slice_from_iter_smoke_test() {
let slice = new_boxed_slice_from_iter([10, 20, 30]).unwrap();
assert_eq!(&*slice, &[10, 20, 30]);
}
#[test]
fn new_boxed_slice_from_fallible_iter_smoke_test() {
let slice =
new_boxed_slice_from_fallible_iter::<_, &str>([Ok(10), Ok(20), Ok(30)]).unwrap();
assert_eq!(&*slice, &[10, 20, 30]);
}
#[test]
fn new_boxed_slice_from_fallible_iter_error() {
let result = new_boxed_slice_from_fallible_iter::<_, u32>([Ok(10), Ok(20), Err(30)]);
let Err(BoxedSliceFromFallibleIterError::IterError(err)) = result else {
panic!("unexpected result: {result:?}");
};
assert_eq!(err, 30);
}
#[test]
fn new_uninit_boxed_slice_smoke_test() {
let slice = new_uninit_boxed_slice::<u32>(5).unwrap();
assert_eq!(slice.len(), 5);
}
#[test]
fn boxed_slice_write_iter_smoke_test() {
let uninit = new_uninit_boxed_slice(3).unwrap();
let init = boxed_slice_write_iter(uninit, [10, 20, 30]).unwrap();
assert_eq!(&*init, &[10, 20, 30]);
}
#[test]
fn boxed_slice_write_iter_with_too_few_elems() {
let (a_dropped, a) = SetFlagOnDrop::new();
let (b_dropped, b) = SetFlagOnDrop::new();
let (c_dropped, c) = SetFlagOnDrop::new();
let uninit = new_uninit_boxed_slice(4).unwrap();
match boxed_slice_write_iter(uninit, [a, b, c]) {
Err(_) => {}
Ok(_) => unreachable!(),
}
assert!(a_dropped.get());
assert!(b_dropped.get());
assert!(c_dropped.get());
}
#[test]
fn boxed_slice_write_iter_with_too_many_elems() {
let (a_dropped, a) = SetFlagOnDrop::new();
let (b_dropped, b) = SetFlagOnDrop::new();
let (c_dropped, c) = SetFlagOnDrop::new();
let uninit = new_uninit_boxed_slice(2).unwrap();
let slice = boxed_slice_write_iter(uninit, [a, b, c]).unwrap();
assert!(!a_dropped.get());
assert!(!b_dropped.get());
assert!(c_dropped.get());
drop(slice);
assert!(a_dropped.get());
assert!(b_dropped.get());
assert!(c_dropped.get());
}
}