Path: blob/main/crates/core/src/alloc/boxed.rs
3071 views
use super::{TryClone, TryNew, Vec, try_alloc};1use crate::{alloc::str_ptr_from_slice_ptr, error::OutOfMemory};2use core::{3alloc::Layout,4mem::{self, MaybeUninit},5};6use std_alloc::boxed::Box;78/// Allocate an `Box<MaybeUninit<T>>` with uninitialized contents, returning9/// `Err(OutOfMemory)` on allocation failure.10///11/// You can initialize the resulting box's value via [`Box::write`].12#[inline]13fn new_uninit_box<T>() -> Result<Box<MaybeUninit<T>>, OutOfMemory> {14let layout = Layout::new::<MaybeUninit<T>>();1516if layout.size() == 0 {17// NB: no actual allocation takes place when boxing zero-sized18// types.19return Ok(Box::new(MaybeUninit::uninit()));20}2122// Safety: layout size is non-zero.23let ptr = unsafe { try_alloc(layout)? };2425let ptr = ptr.cast::<MaybeUninit<T>>();2627// Safety: The pointer's memory block was allocated by the global allocator.28Ok(unsafe { Box::from_raw(ptr.as_ptr()) })29}3031impl<T> TryNew for Box<T> {32type Value = T;3334#[inline]35fn try_new(value: T) -> Result<Self, OutOfMemory>36where37Self: Sized,38{39let boxed = new_uninit_box::<T>()?;40Ok(Box::write(boxed, value))41}42}4344impl<T> TryClone for Box<T>45where46T: TryClone,47{48fn try_clone(&self) -> Result<Self, OutOfMemory> {49let b = new_uninit_box::<T>()?;50let v = (**self).try_clone()?;51Ok(Box::write(b, v))52}53}5455impl<T> TryClone for Box<[T]>56where57T: TryClone,58{59fn try_clone(&self) -> Result<Self, OutOfMemory> {60let mut builder = BoxedSliceBuilder::new(self.len())?;61for v in &*self {62builder.push(v.try_clone()?).expect("reserved capacity");63}64debug_assert_eq!(builder.init_len(), builder.capacity());65Ok(builder.finish())66}67}6869impl TryClone for Box<str> {70fn try_clone(&self) -> Result<Self, OutOfMemory> {71let mut builder = BoxedSliceBuilder::new(self.len())?;72for b in self.as_bytes() {73builder.push(*b).expect("reserved capacity");74}75debug_assert_eq!(builder.init_len(), builder.capacity());76let boxed = builder.finish();77let ptr = Box::into_raw(boxed);78let ptr = str_ptr_from_slice_ptr(ptr);79// SAFETY: the pointer is allocated with the global allocator and points80// to a valid utf8 sequence.81let boxed = unsafe { Box::from_raw(ptr) };82Ok(boxed)83}84}8586/// Allocate a new `Box<[MaybeUninit<T>]>` of the given length with87/// uninitialized contents, returning `Err(OutOfMemory)` on allocation failure.88///89/// You can initialize the resulting boxed slice with90/// [`boxed_slice_write_iter`].91pub fn new_uninit_boxed_slice<T>(len: usize) -> Result<Box<[MaybeUninit<T>]>, OutOfMemory> {92let layout = Layout::array::<MaybeUninit<T>>(len)93.map_err(|_| OutOfMemory::new(mem::size_of::<T>().saturating_mul(len)))?;9495if layout.size() == 0 {96// NB: no actual allocation takes place when boxing zero-sized97// types.98return Ok(Box::new_uninit_slice(len));99}100101// Safety: layout size is non-zero.102let ptr = unsafe { try_alloc(layout)? };103104let ptr = ptr.cast::<MaybeUninit<T>>().as_ptr();105let ptr = core::ptr::slice_from_raw_parts_mut(ptr, len);106107// Safety: The pointer's memory block was allocated by the global allocator108// and holds room for `[T; len]`.109Ok(unsafe { Box::from_raw(ptr) })110}111112use boxed_slice_builder::BoxedSliceBuilder;113mod boxed_slice_builder {114use super::*;115116/// Builder for constructing and initalizing a boxed slice.117///118/// Also acts as an RAII guard to handle dropping the already-initialized119/// elements when we get too few items or an iterator panics during120/// construction.121pub struct BoxedSliceBuilder<T> {122vec: Vec<T>,123}124125impl<T> BoxedSliceBuilder<T> {126pub fn new(len: usize) -> Result<Self, OutOfMemory> {127let mut vec = Vec::new();128vec.reserve_exact(len)?;129Ok(Self { vec })130}131132pub fn from_boxed_slice(boxed: Box<[MaybeUninit<T>]>) -> Self {133let len = boxed.len();134let ptr = Box::into_raw(boxed);135let ptr = ptr.cast::<T>();136// Safety: the pointer was allocated by the global allocator and is137// valid for `[T; len]` since it was a boxed slice.138let vec = unsafe { Vec::from_raw_parts(ptr, 0, len) };139Self { vec }140}141142pub fn init_len(&self) -> usize {143self.vec.len()144}145146pub fn capacity(&self) -> usize {147self.vec.capacity()148}149150pub fn push(&mut self, value: T) -> Result<(), OutOfMemory> {151self.vec.push(value)152}153154/// Finish this builder and take its boxed slice out.155///156/// Panics if `self.init_len() != self.capacity()`. Call157/// `self.shrink_to_fit()` if necessary.158pub fn finish(mut self) -> Box<[T]> {159assert_eq!(self.init_len(), self.capacity());160let vec = mem::take(&mut self.vec);161mem::forget(self);162let (ptr, len, cap) = vec.into_raw_parts();163debug_assert_eq!(len, cap);164let ptr = core::ptr::slice_from_raw_parts_mut(ptr, len);165unsafe { Box::from_raw(ptr) }166}167168/// Shrink this builder's allocation such that `self.init_len() ==169/// self.capacity()`.170pub fn shrink_to_fit(&mut self) -> Result<(), OutOfMemory> {171if self.init_len() == self.capacity() {172return Ok(());173}174175let len = self.init_len();176let cap = self.capacity();177let vec = mem::take(&mut self.vec);178179let old_layout = Layout::array::<T>(cap).expect(180"already have an allocation with this layout so should be able to recreate it",181);182let new_layout = Layout::array::<T>(len)183.expect("if `cap` is fine for an array layout, then `len` must be as well");184debug_assert_eq!(old_layout.align(), new_layout.align());185186// Handle zero-sized reallocations, since the global `realloc` function187// does not.188if new_layout.size() == 0 {189debug_assert!(mem::size_of::<T>() == 0 || len == 0);190if len == 0 {191debug_assert_eq!(self.capacity(), 0);192debug_assert_eq!(self.init_len(), 0);193} else {194debug_assert_eq!(mem::size_of::<T>(), 0);195let ptr = core::ptr::dangling_mut::<T>();196debug_assert!(!ptr.is_null());197debug_assert!(ptr.is_aligned());198// Safety: T's dangling pointer is always non-null and aligned.199self.vec = unsafe { Vec::from_raw_parts(ptr, len, len) };200}201debug_assert_eq!(self.capacity(), self.init_len());202return Ok(());203}204205let (ptr, _len, _cap) = vec.into_raw_parts();206debug_assert_eq!(len, _len);207debug_assert_eq!(cap, _cap);208209// Safety: `ptr` was allocated by the global allocator, its memory block210// is described by `old_layout`, the new size is non-zero, and the new211// size will not overflow `isize::MAX` when rounded up to the layout's212// alignment (this is checked in the construction of `new_layout`).213let new_ptr = unsafe {214std_alloc::alloc::realloc(ptr.cast::<u8>(), old_layout, new_layout.size())215};216217// Update `self` based on whether the reallocation succeeded or not,218// either inserting the new vec or reconstructing and replacing the219// old one.220if new_ptr.is_null() {221// Safety: The allocation failed so we retain ownership of `ptr`,222// which was a valid vec and we can safely make it a vec again.223self.vec = unsafe { Vec::from_raw_parts(ptr, len, cap) };224Err(OutOfMemory::new(new_layout.size()))225} else {226let new_ptr = new_ptr.cast::<T>();227// Safety: The allocation succeeded, `new_ptr` was reallocated by228// the global allocator and points to a valid boxed slice of length229// `len`.230self.vec = unsafe { Vec::from_raw_parts(new_ptr, len, len) };231debug_assert_eq!(self.capacity(), self.init_len());232Ok(())233}234}235}236}237238/// An error returned when an iterator yields too few items to fully initialize239/// a `Box<[MaybeUninit<T>]>`.240#[non_exhaustive]241#[derive(Debug, Clone, Copy)]242pub struct TooFewItems;243244impl core::fmt::Display for TooFewItems {245fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {246f.write_str("iterator yielded too few items to fully initialize boxed slice")247}248}249250impl core::error::Error for TooFewItems {}251252/// An error returned by [`new_boxed_slice_from_iter`].253#[derive(Debug)]254pub enum TooFewItemsOrOom {255/// The iterator did not yield enough items to fill the boxed slice.256TooFewItems(TooFewItems),257/// Failed to allocate space for the boxed slice.258Oom(OutOfMemory),259}260261impl TooFewItemsOrOom {262/// Unwrap the inner `OutOfMemory` error, or panic if this is a different263/// error variant.264pub fn unwrap_oom(&self) -> OutOfMemory {265match self {266TooFewItemsOrOom::TooFewItems(_) => panic!("`unwrap_oom` on non-OOM error"),267TooFewItemsOrOom::Oom(oom) => *oom,268}269}270}271272impl From<TooFewItems> for TooFewItemsOrOom {273fn from(e: TooFewItems) -> Self {274Self::TooFewItems(e)275}276}277278impl From<OutOfMemory> for TooFewItemsOrOom {279fn from(oom: OutOfMemory) -> Self {280Self::Oom(oom)281}282}283284impl core::fmt::Display for TooFewItemsOrOom {285fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {286match self {287Self::TooFewItems(_) => {288f.write_str("The iterator did not yield enough items to fill the boxed slice")289}290Self::Oom(_) => f.write_str("Failed to allocate space for the boxed slice"),291}292}293}294295impl core::error::Error for TooFewItemsOrOom {296fn cause(&self) -> Option<&dyn core::error::Error> {297match self {298Self::TooFewItems(e) => Some(e),299Self::Oom(oom) => Some(oom),300}301}302}303304/// Initialize a `Box<[MaybeUninit<T>]>` slice by writing the elements of the305/// given iterator into it.306pub fn boxed_slice_write_iter<T>(307boxed: Box<[MaybeUninit<T>]>,308iter: impl IntoIterator<Item = T>,309) -> Result<Box<[T]>, TooFewItems> {310let len = boxed.len();311let builder = BoxedSliceBuilder::from_boxed_slice(boxed);312assert_eq!(len, builder.capacity());313write_iter_into_builder(builder, iter)314}315316/// Create a `Box<[T]>` of length `len` from the given iterator's elements.317///318/// Returns an error on allocation failure, or if `iter` yields fewer than `len`319/// elements.320///321/// The iterator is dropped after `len` elements have been yielded, this322/// function does not check that the iterator yields exactly `len` elements.323pub fn new_boxed_slice_from_iter_with_len<T>(324len: usize,325iter: impl IntoIterator<Item = T>,326) -> Result<Box<[T]>, TooFewItemsOrOom> {327let builder = BoxedSliceBuilder::new(len)?;328assert_eq!(len, builder.capacity());329let boxed = write_iter_into_builder(builder, iter)?;330Ok(boxed)331}332333fn write_iter_into_builder<T>(334mut builder: BoxedSliceBuilder<T>,335iter: impl IntoIterator<Item = T>,336) -> Result<Box<[T]>, TooFewItems> {337let len = builder.capacity();338339for elem in iter.into_iter().take(len) {340builder.push(elem).expect("reserved capacity");341}342343if builder.init_len() < builder.capacity() {344return Err(TooFewItems);345}346347debug_assert_eq!(builder.init_len(), builder.capacity());348Ok(builder.finish())349}350351/// An error returned by [`new_boxed_slice_from_fallible_iter`].352#[derive(Debug)]353pub enum BoxedSliceFromFallibleIterError<E> {354/// The fallible iterator produced an error.355IterError(E),356/// Failed to allocate space for the boxed slice.357Oom(OutOfMemory),358}359360impl<E> From<OutOfMemory> for BoxedSliceFromFallibleIterError<E> {361fn from(oom: OutOfMemory) -> Self {362Self::Oom(oom)363}364}365366impl<E> core::fmt::Display for BoxedSliceFromFallibleIterError<E> {367fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {368match self {369Self::IterError(_) => f.write_str("The fallible iterator produced an error"),370Self::Oom(_) => f.write_str("Failed to allocate space for the boxed slice"),371}372}373}374375impl<E> core::error::Error for BoxedSliceFromFallibleIterError<E>376where377E: core::error::Error,378{379fn cause(&self) -> Option<&dyn core::error::Error> {380match self {381Self::IterError(e) => Some(e),382Self::Oom(oom) => Some(oom),383}384}385}386387impl BoxedSliceFromFallibleIterError<OutOfMemory> {388/// Flatten this error into its inner OOM.389pub fn flatten(self) -> OutOfMemory {390match self {391Self::IterError(oom) | Self::Oom(oom) => oom,392}393}394}395396/// Create a `Box<[T]>` from the given iterator's `Result<T, E>` items.397///398/// Returns an error on allocation failure or if an iterator item is an `Err`.399pub fn new_boxed_slice_from_fallible_iter<T, E>(400iter: impl IntoIterator<Item = Result<T, E>>,401) -> Result<Box<[T]>, BoxedSliceFromFallibleIterError<E>> {402let iter = iter.into_iter();403404let (min, max) = iter.size_hint();405let len = max.unwrap_or_else(|| min);406407let mut builder = BoxedSliceBuilder::new(len)?;408assert_eq!(len, builder.capacity());409410for result in iter {411let elem = result.map_err(BoxedSliceFromFallibleIterError::IterError)?;412builder.push(elem)?;413}414415debug_assert!(builder.init_len() <= builder.capacity());416builder.shrink_to_fit()?;417debug_assert_eq!(builder.init_len(), builder.capacity());418419Ok(builder.finish())420}421422/// Create a `Box<[T]>` from the given iterator's elements.423///424/// Returns an error on allocation failure.425pub fn new_boxed_slice_from_iter<T>(426iter: impl IntoIterator<Item = T>,427) -> Result<Box<[T]>, OutOfMemory> {428let iter = iter429.into_iter()430.map(Result::<T, core::convert::Infallible>::Ok);431new_boxed_slice_from_fallible_iter(iter).map_err(|e| match e {432BoxedSliceFromFallibleIterError::Oom(oom) => oom,433BoxedSliceFromFallibleIterError::IterError(_) => unreachable!(),434})435}436437#[cfg(test)]438mod tests {439use super::*;440use core::cell::Cell;441use std_alloc::rc::Rc;442443struct SetFlagOnDrop(Rc<Cell<bool>>);444445impl Drop for SetFlagOnDrop {446fn drop(&mut self) {447let old_value = self.0.replace(true);448assert_eq!(old_value, false);449}450}451452impl SetFlagOnDrop {453fn new() -> (Rc<Cell<bool>>, Self) {454let flag = Rc::new(Cell::new(false));455(flag.clone(), SetFlagOnDrop(flag))456}457}458459#[test]460fn try_new() {461<Box<_> as TryNew>::try_new(4).unwrap();462}463464#[test]465fn new_boxed_slice_from_iter_with_len_smoke_test() {466let slice = new_boxed_slice_from_iter_with_len(3, [42, 36, 1337]).unwrap();467assert_eq!(&*slice, &[42, 36, 1337]);468}469470#[test]471fn new_boxed_slice_from_iter_with_len_with_too_few_elems() {472let (a_dropped, a) = SetFlagOnDrop::new();473let (b_dropped, b) = SetFlagOnDrop::new();474let (c_dropped, c) = SetFlagOnDrop::new();475476match new_boxed_slice_from_iter_with_len(4, [a, b, c]) {477Err(TooFewItemsOrOom::TooFewItems(_)) => {}478Ok(_) | Err(TooFewItemsOrOom::Oom(_)) => unreachable!(),479}480481assert!(a_dropped.get());482assert!(b_dropped.get());483assert!(c_dropped.get());484}485486#[test]487fn new_boxed_slice_from_iter_with_len_with_too_many_elems() {488let (a_dropped, a) = SetFlagOnDrop::new();489let (b_dropped, b) = SetFlagOnDrop::new();490let (c_dropped, c) = SetFlagOnDrop::new();491492let slice = new_boxed_slice_from_iter_with_len(2, [a, b, c]).unwrap();493494assert!(!a_dropped.get());495assert!(!b_dropped.get());496assert!(c_dropped.get());497498drop(slice);499500assert!(a_dropped.get());501assert!(b_dropped.get());502assert!(c_dropped.get());503}504505#[test]506fn new_boxed_slice_from_iter_smoke_test() {507let slice = new_boxed_slice_from_iter([10, 20, 30]).unwrap();508assert_eq!(&*slice, &[10, 20, 30]);509}510511#[test]512fn new_boxed_slice_from_fallible_iter_smoke_test() {513let slice =514new_boxed_slice_from_fallible_iter::<_, &str>([Ok(10), Ok(20), Ok(30)]).unwrap();515assert_eq!(&*slice, &[10, 20, 30]);516}517518#[test]519fn new_boxed_slice_from_fallible_iter_error() {520let result = new_boxed_slice_from_fallible_iter::<_, u32>([Ok(10), Ok(20), Err(30)]);521let Err(BoxedSliceFromFallibleIterError::IterError(err)) = result else {522panic!("unexpected result: {result:?}");523};524assert_eq!(err, 30);525}526527#[test]528fn new_uninit_boxed_slice_smoke_test() {529let slice = new_uninit_boxed_slice::<u32>(5).unwrap();530assert_eq!(slice.len(), 5);531}532533#[test]534fn boxed_slice_write_iter_smoke_test() {535let uninit = new_uninit_boxed_slice(3).unwrap();536let init = boxed_slice_write_iter(uninit, [10, 20, 30]).unwrap();537assert_eq!(&*init, &[10, 20, 30]);538}539540#[test]541fn boxed_slice_write_iter_with_too_few_elems() {542let (a_dropped, a) = SetFlagOnDrop::new();543let (b_dropped, b) = SetFlagOnDrop::new();544let (c_dropped, c) = SetFlagOnDrop::new();545546let uninit = new_uninit_boxed_slice(4).unwrap();547match boxed_slice_write_iter(uninit, [a, b, c]) {548Err(_) => {}549Ok(_) => unreachable!(),550}551552assert!(a_dropped.get());553assert!(b_dropped.get());554assert!(c_dropped.get());555}556557#[test]558fn boxed_slice_write_iter_with_too_many_elems() {559let (a_dropped, a) = SetFlagOnDrop::new();560let (b_dropped, b) = SetFlagOnDrop::new();561let (c_dropped, c) = SetFlagOnDrop::new();562563let uninit = new_uninit_boxed_slice(2).unwrap();564let slice = boxed_slice_write_iter(uninit, [a, b, c]).unwrap();565566assert!(!a_dropped.get());567assert!(!b_dropped.get());568assert!(c_dropped.get());569570drop(slice);571572assert!(a_dropped.get());573assert!(b_dropped.get());574assert!(c_dropped.get());575}576}577578579