1 use super::{TryNew, try_alloc}; 2 use crate::error::OutOfMemory; 3 use core::{alloc::Layout, mem::MaybeUninit}; 4 use std_alloc::boxed::Box; 5 6 /// Allocate an `Box<MaybeUninit<T>>` with uninitialized contents, returning 7 /// `Err(OutOfMemory)` on allocation failure. 8 /// 9 /// You can initialize the resulting box's value via [`Box::write`]. 10 #[inline] 11 fn new_uninit_box<T>() -> Result<Box<MaybeUninit<T>>, OutOfMemory> { 12 let layout = Layout::new::<MaybeUninit<T>>(); 13 14 if layout.size() == 0 { 15 // NB: no actual allocation takes place when boxing zero-sized 16 // types. 17 return Ok(Box::new(MaybeUninit::uninit())); 18 } 19 20 // Safety: layout size is non-zero. 21 let ptr = unsafe { try_alloc(layout)? }; 22 23 let ptr = ptr.cast::<MaybeUninit<T>>(); 24 25 // Safety: The pointer's memory block was allocated by the global allocator. 26 Ok(unsafe { Box::from_raw(ptr.as_ptr()) }) 27 } 28 29 impl<T> TryNew for Box<T> { 30 type Value = T; 31 32 #[inline] 33 fn try_new(value: T) -> Result<Self, OutOfMemory> 34 where 35 Self: Sized, 36 { 37 let boxed = new_uninit_box::<T>()?; 38 Ok(Box::write(boxed, value)) 39 } 40 } 41 42 fn new_uninit_boxed_slice<T>(len: usize) -> Result<Box<[MaybeUninit<T>]>, OutOfMemory> { 43 let layout = Layout::array::<MaybeUninit<T>>(len) 44 .map_err(|_| OutOfMemory::new(len.saturating_mul(core::mem::size_of::<T>())))?; 45 46 if layout.size() == 0 { 47 // NB: no actual allocation takes place when boxing zero-sized 48 // types. 49 return Ok(Box::new_uninit_slice(len)); 50 } 51 52 // Safety: we just ensured that the new length is non-zero. 53 debug_assert_ne!(layout.size(), 0); 54 let ptr = unsafe { try_alloc(layout)? }; 55 56 let ptr = ptr.cast::<MaybeUninit<T>>().as_ptr(); 57 let ptr = core::ptr::slice_from_raw_parts_mut(ptr, len); 58 59 // Safety: `ptr` points to a memory block that is valid for 60 // `[MaybeUninit<T>; len]` and which was allocated by the global memory 61 // allocator. 62 let boxed = unsafe { Box::from_raw(ptr) }; 63 Ok(boxed) 64 } 65 66 /// An error returned by [`new_boxed_slice_from_iter`]. 67 #[derive(Debug)] 68 pub enum BoxedSliceFromIterError { 69 /// The iterator did not yield enough items to fill the boxed slice. 70 TooFewItems, 71 /// Failed to allocate space for the boxed slice. 72 Oom(OutOfMemory), 73 } 74 75 impl From<OutOfMemory> for BoxedSliceFromIterError { 76 fn from(oom: OutOfMemory) -> Self { 77 Self::Oom(oom) 78 } 79 } 80 81 impl core::fmt::Display for BoxedSliceFromIterError { 82 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 83 match self { 84 BoxedSliceFromIterError::TooFewItems => { 85 f.write_str("The iterator did not yield enough items to fill the boxed slice") 86 } 87 BoxedSliceFromIterError::Oom(_) => { 88 f.write_str("Failed to allocate space for the boxed slice") 89 } 90 } 91 } 92 } 93 94 impl core::error::Error for BoxedSliceFromIterError { 95 fn cause(&self) -> Option<&dyn core::error::Error> { 96 match self { 97 BoxedSliceFromIterError::TooFewItems => None, 98 BoxedSliceFromIterError::Oom(oom) => Some(oom), 99 } 100 } 101 } 102 103 /// Create a `Box<[T]>` of length `len` from the given iterator's elements. 104 /// 105 /// Returns an error on allocation failure, or if `iter` yields fewer than `len` 106 /// elements. 107 /// 108 /// The iterator is dropped after `len` elements have been yielded, this 109 /// function does not check that the iterator yields exactly `len` elements. 110 pub fn new_boxed_slice_from_iter<T>( 111 len: usize, 112 iter: impl IntoIterator<Item = T>, 113 ) -> Result<Box<[T]>, BoxedSliceFromIterError> { 114 /// RAII guard to handle dropping the initialized elements of the boxed 115 /// slice in the cases where we get too few items or the iterator panics. 116 struct DropGuard<T> { 117 boxed: Box<[MaybeUninit<T>]>, 118 init_len: usize, 119 } 120 121 impl<T> Drop for DropGuard<T> { 122 fn drop(&mut self) { 123 debug_assert!(self.init_len <= self.boxed.len()); 124 125 if !core::mem::needs_drop::<T>() { 126 return; 127 } 128 129 for elem in self.boxed.iter_mut().take(self.init_len) { 130 // Safety: the elements in `self.boxed[..self.init_len]` are 131 // valid and initialized and will not be used again. 132 unsafe { 133 core::ptr::drop_in_place(elem.as_mut_ptr()); 134 } 135 } 136 } 137 } 138 139 let mut guard = DropGuard { 140 boxed: new_uninit_boxed_slice(len)?, 141 init_len: 0, 142 }; 143 assert_eq!(len, guard.boxed.len()); 144 145 for (i, elem) in iter.into_iter().enumerate().take(len) { 146 debug_assert!(i < len); 147 debug_assert_eq!(guard.init_len, i); 148 guard.boxed[i].write(elem); 149 guard.init_len += 1; 150 } 151 152 debug_assert!(guard.init_len <= guard.boxed.len()); 153 154 if guard.init_len < guard.boxed.len() { 155 return Err(BoxedSliceFromIterError::TooFewItems); 156 } 157 158 debug_assert_eq!(guard.init_len, guard.boxed.len()); 159 160 // Take the boxed slice out of the guard. 161 let boxed = { 162 guard.init_len = 0; 163 let boxed = core::mem::take(&mut guard.boxed); 164 core::mem::forget(guard); 165 boxed 166 }; 167 168 // Safety: we initialized all elements. 169 let boxed = unsafe { boxed.assume_init() }; 170 171 Ok(boxed) 172 } 173 174 #[cfg(test)] 175 mod tests { 176 use super::*; 177 use core::cell::Cell; 178 use std_alloc::rc::Rc; 179 180 struct SetFlagOnDrop(Rc<Cell<bool>>); 181 182 impl Drop for SetFlagOnDrop { 183 fn drop(&mut self) { 184 let old_value = self.0.replace(true); 185 assert_eq!(old_value, false); 186 } 187 } 188 189 impl SetFlagOnDrop { 190 fn new() -> (Rc<Cell<bool>>, Self) { 191 let flag = Rc::new(Cell::new(false)); 192 (flag.clone(), SetFlagOnDrop(flag)) 193 } 194 } 195 196 #[test] 197 fn try_new() { 198 <Box<_> as TryNew>::try_new(4).unwrap(); 199 } 200 201 #[test] 202 fn new_boxed_slice_from_iter_smoke_test() { 203 let slice = new_boxed_slice_from_iter(3, [42, 36, 1337]).unwrap(); 204 assert_eq!(&*slice, &[42, 36, 1337]); 205 } 206 207 #[test] 208 fn new_boxed_slice_from_iter_with_too_few_elems() { 209 let (a_dropped, a) = SetFlagOnDrop::new(); 210 let (b_dropped, b) = SetFlagOnDrop::new(); 211 let (c_dropped, c) = SetFlagOnDrop::new(); 212 213 match new_boxed_slice_from_iter(4, [a, b, c]) { 214 Err(BoxedSliceFromIterError::TooFewItems) => {} 215 Ok(_) | Err(BoxedSliceFromIterError::Oom(_)) => unreachable!(), 216 } 217 218 assert!(a_dropped.get()); 219 assert!(b_dropped.get()); 220 assert!(c_dropped.get()); 221 } 222 223 #[test] 224 fn new_boxed_slice_from_iter_with_too_many_elems() { 225 let (a_dropped, a) = SetFlagOnDrop::new(); 226 let (b_dropped, b) = SetFlagOnDrop::new(); 227 let (c_dropped, c) = SetFlagOnDrop::new(); 228 229 let slice = new_boxed_slice_from_iter(2, [a, b, c]).unwrap(); 230 231 assert!(!a_dropped.get()); 232 assert!(!b_dropped.get()); 233 assert!(c_dropped.get()); 234 235 drop(slice); 236 237 assert!(a_dropped.get()); 238 assert!(b_dropped.get()); 239 assert!(c_dropped.get()); 240 } 241 } 242