1 use super::{TryNew, Vec, try_alloc}; 2 use crate::error::OutOfMemory; 3 use core::{ 4 alloc::Layout, 5 mem::{self, MaybeUninit}, 6 }; 7 use std_alloc::boxed::Box; 8 9 /// Allocate an `Box<MaybeUninit<T>>` with uninitialized contents, returning 10 /// `Err(OutOfMemory)` on allocation failure. 11 /// 12 /// You can initialize the resulting box's value via [`Box::write`]. 13 #[inline] 14 fn new_uninit_box<T>() -> Result<Box<MaybeUninit<T>>, OutOfMemory> { 15 let layout = Layout::new::<MaybeUninit<T>>(); 16 17 if layout.size() == 0 { 18 // NB: no actual allocation takes place when boxing zero-sized 19 // types. 20 return Ok(Box::new(MaybeUninit::uninit())); 21 } 22 23 // Safety: layout size is non-zero. 24 let ptr = unsafe { try_alloc(layout)? }; 25 26 let ptr = ptr.cast::<MaybeUninit<T>>(); 27 28 // Safety: The pointer's memory block was allocated by the global allocator. 29 Ok(unsafe { Box::from_raw(ptr.as_ptr()) }) 30 } 31 32 impl<T> TryNew for Box<T> { 33 type Value = T; 34 35 #[inline] 36 fn try_new(value: T) -> Result<Self, OutOfMemory> 37 where 38 Self: Sized, 39 { 40 let boxed = new_uninit_box::<T>()?; 41 Ok(Box::write(boxed, value)) 42 } 43 } 44 45 /// Allocate a new `Box<[MaybeUninit<T>]>` of the given length with 46 /// uninitialized contents, returning `Err(OutOfMemory)` on allocation failure. 47 /// 48 /// You can initialize the resulting boxed slice with 49 /// [`boxed_slice_write_iter`]. 50 pub fn new_uninit_boxed_slice<T>(len: usize) -> Result<Box<[MaybeUninit<T>]>, OutOfMemory> { 51 let layout = Layout::array::<MaybeUninit<T>>(len) 52 .map_err(|_| OutOfMemory::new(mem::size_of::<T>().saturating_mul(len)))?; 53 54 if layout.size() == 0 { 55 // NB: no actual allocation takes place when boxing zero-sized 56 // types. 57 return Ok(Box::new_uninit_slice(len)); 58 } 59 60 // Safety: layout size is non-zero. 61 let ptr = unsafe { try_alloc(layout)? }; 62 63 let ptr = ptr.cast::<MaybeUninit<T>>().as_ptr(); 64 let ptr = core::ptr::slice_from_raw_parts_mut(ptr, len); 65 66 // Safety: The pointer's memory block was allocated by the global allocator 67 // and holds room for `[T; len]`. 68 Ok(unsafe { Box::from_raw(ptr) }) 69 } 70 71 use boxed_slice_builder::BoxedSliceBuilder; 72 mod boxed_slice_builder { 73 use super::*; 74 75 /// Builder for constructing and initalizing a boxed slice. 76 /// 77 /// Also acts as an RAII guard to handle dropping the already-initialized 78 /// elements when we get too few items or an iterator panics during 79 /// construction. 80 pub struct BoxedSliceBuilder<T> { 81 vec: Vec<T>, 82 } 83 84 impl<T> BoxedSliceBuilder<T> { 85 pub fn new(len: usize) -> Result<Self, OutOfMemory> { 86 let mut vec = Vec::new(); 87 vec.reserve_exact(len)?; 88 Ok(Self { vec }) 89 } 90 91 pub fn from_boxed_slice(boxed: Box<[MaybeUninit<T>]>) -> Self { 92 let len = boxed.len(); 93 let ptr = Box::into_raw(boxed); 94 let ptr = ptr.cast::<T>(); 95 // Safety: the pointer was allocated by the global allocator and is 96 // valid for `[T; len]` since it was a boxed slice. 97 let vec = unsafe { Vec::from_raw_parts(ptr, 0, len) }; 98 Self { vec } 99 } 100 101 pub fn init_len(&self) -> usize { 102 self.vec.len() 103 } 104 105 pub fn capacity(&self) -> usize { 106 self.vec.capacity() 107 } 108 109 pub fn push(&mut self, value: T) -> Result<(), OutOfMemory> { 110 self.vec.push(value) 111 } 112 113 /// Finish this builder and take its boxed slice out. 114 /// 115 /// Panics if `self.init_len() != self.capacity()`. Call 116 /// `self.shrink_to_fit()` if necessary. 117 pub fn finish(mut self) -> Box<[T]> { 118 assert_eq!(self.init_len(), self.capacity()); 119 let vec = mem::take(&mut self.vec); 120 mem::forget(self); 121 let (ptr, len, cap) = vec.into_raw_parts(); 122 debug_assert_eq!(len, cap); 123 let ptr = core::ptr::slice_from_raw_parts_mut(ptr, len); 124 unsafe { Box::from_raw(ptr) } 125 } 126 127 /// Shrink this builder's allocation such that `self.init_len() == 128 /// self.capacity()`. 129 pub fn shrink_to_fit(&mut self) -> Result<(), OutOfMemory> { 130 if self.init_len() == self.capacity() { 131 return Ok(()); 132 } 133 134 let len = self.init_len(); 135 let cap = self.capacity(); 136 let vec = mem::take(&mut self.vec); 137 138 let old_layout = Layout::array::<T>(cap).expect( 139 "already have an allocation with this layout so should be able to recreate it", 140 ); 141 let new_layout = Layout::array::<T>(len) 142 .expect("if `cap` is fine for an array layout, then `len` must be as well"); 143 debug_assert_eq!(old_layout.align(), new_layout.align()); 144 145 // Handle zero-sized reallocations, since the global `realloc` function 146 // does not. 147 if new_layout.size() == 0 { 148 debug_assert!(mem::size_of::<T>() == 0 || len == 0); 149 if len == 0 { 150 debug_assert_eq!(self.capacity(), 0); 151 debug_assert_eq!(self.init_len(), 0); 152 } else { 153 debug_assert_eq!(mem::size_of::<T>(), 0); 154 let ptr = core::ptr::dangling_mut::<T>(); 155 debug_assert!(!ptr.is_null()); 156 debug_assert!(ptr.is_aligned()); 157 // Safety: T's dangling pointer is always non-null and aligned. 158 self.vec = unsafe { Vec::from_raw_parts(ptr, len, len) }; 159 } 160 debug_assert_eq!(self.capacity(), self.init_len()); 161 return Ok(()); 162 } 163 164 let (ptr, _len, _cap) = vec.into_raw_parts(); 165 debug_assert_eq!(len, _len); 166 debug_assert_eq!(cap, _cap); 167 168 // Safety: `ptr` was allocated by the global allocator, its memory block 169 // is described by `old_layout`, the new size is non-zero, and the new 170 // size will not overflow `isize::MAX` when rounded up to the layout's 171 // alignment (this is checked in the construction of `new_layout`). 172 let new_ptr = unsafe { 173 std_alloc::alloc::realloc(ptr.cast::<u8>(), old_layout, new_layout.size()) 174 }; 175 176 // Update `self` based on whether the reallocation succeeded or not, 177 // either inserting the new vec or reconstructing and replacing the 178 // old one. 179 if new_ptr.is_null() { 180 // Safety: The allocation failed so we retain ownership of `ptr`, 181 // which was a valid vec and we can safely make it a vec again. 182 self.vec = unsafe { Vec::from_raw_parts(ptr, len, cap) }; 183 Err(OutOfMemory::new(new_layout.size())) 184 } else { 185 let new_ptr = new_ptr.cast::<T>(); 186 // Safety: The allocation succeeded, `new_ptr` was reallocated by 187 // the global allocator and points to a valid boxed slice of length 188 // `len`. 189 self.vec = unsafe { Vec::from_raw_parts(new_ptr, len, len) }; 190 debug_assert_eq!(self.capacity(), self.init_len()); 191 Ok(()) 192 } 193 } 194 } 195 } 196 197 /// An error returned when an iterator yields too few items to fully initialize 198 /// a `Box<[MaybeUninit<T>]>`. 199 #[non_exhaustive] 200 #[derive(Debug, Clone, Copy)] 201 pub struct TooFewItems; 202 203 impl core::fmt::Display for TooFewItems { 204 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 205 f.write_str("iterator yielded too few items to fully initialize boxed slice") 206 } 207 } 208 209 impl core::error::Error for TooFewItems {} 210 211 /// An error returned by [`new_boxed_slice_from_iter`]. 212 #[derive(Debug)] 213 pub enum TooFewItemsOrOom { 214 /// The iterator did not yield enough items to fill the boxed slice. 215 TooFewItems(TooFewItems), 216 /// Failed to allocate space for the boxed slice. 217 Oom(OutOfMemory), 218 } 219 220 impl TooFewItemsOrOom { 221 /// Unwrap the inner `OutOfMemory` error, or panic if this is a different 222 /// error variant. 223 pub fn unwrap_oom(&self) -> OutOfMemory { 224 match self { 225 TooFewItemsOrOom::TooFewItems(_) => panic!("`unwrap_oom` on non-OOM error"), 226 TooFewItemsOrOom::Oom(oom) => *oom, 227 } 228 } 229 } 230 231 impl From<TooFewItems> for TooFewItemsOrOom { 232 fn from(e: TooFewItems) -> Self { 233 Self::TooFewItems(e) 234 } 235 } 236 237 impl From<OutOfMemory> for TooFewItemsOrOom { 238 fn from(oom: OutOfMemory) -> Self { 239 Self::Oom(oom) 240 } 241 } 242 243 impl core::fmt::Display for TooFewItemsOrOom { 244 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 245 match self { 246 Self::TooFewItems(_) => { 247 f.write_str("The iterator did not yield enough items to fill the boxed slice") 248 } 249 Self::Oom(_) => f.write_str("Failed to allocate space for the boxed slice"), 250 } 251 } 252 } 253 254 impl core::error::Error for TooFewItemsOrOom { 255 fn cause(&self) -> Option<&dyn core::error::Error> { 256 match self { 257 Self::TooFewItems(e) => Some(e), 258 Self::Oom(oom) => Some(oom), 259 } 260 } 261 } 262 263 /// Initialize a `Box<[MaybeUninit<T>]>` slice by writing the elements of the 264 /// given iterator into it. 265 pub fn boxed_slice_write_iter<T>( 266 boxed: Box<[MaybeUninit<T>]>, 267 iter: impl IntoIterator<Item = T>, 268 ) -> Result<Box<[T]>, TooFewItems> { 269 let len = boxed.len(); 270 let builder = BoxedSliceBuilder::from_boxed_slice(boxed); 271 assert_eq!(len, builder.capacity()); 272 write_iter_into_builder(builder, iter) 273 } 274 275 /// Create a `Box<[T]>` of length `len` from the given iterator's elements. 276 /// 277 /// Returns an error on allocation failure, or if `iter` yields fewer than `len` 278 /// elements. 279 /// 280 /// The iterator is dropped after `len` elements have been yielded, this 281 /// function does not check that the iterator yields exactly `len` elements. 282 pub fn new_boxed_slice_from_iter_with_len<T>( 283 len: usize, 284 iter: impl IntoIterator<Item = T>, 285 ) -> Result<Box<[T]>, TooFewItemsOrOom> { 286 let builder = BoxedSliceBuilder::new(len)?; 287 assert_eq!(len, builder.capacity()); 288 let boxed = write_iter_into_builder(builder, iter)?; 289 Ok(boxed) 290 } 291 292 fn write_iter_into_builder<T>( 293 mut builder: BoxedSliceBuilder<T>, 294 iter: impl IntoIterator<Item = T>, 295 ) -> Result<Box<[T]>, TooFewItems> { 296 let len = builder.capacity(); 297 298 for elem in iter.into_iter().take(len) { 299 builder.push(elem).expect("reserved capacity"); 300 } 301 302 if builder.init_len() < builder.capacity() { 303 return Err(TooFewItems); 304 } 305 306 debug_assert_eq!(builder.init_len(), builder.capacity()); 307 Ok(builder.finish()) 308 } 309 310 /// An error returned by [`new_boxed_slice_from_fallible_iter`]. 311 #[derive(Debug)] 312 pub enum BoxedSliceFromFallibleIterError<E> { 313 /// The fallible iterator produced an error. 314 IterError(E), 315 /// Failed to allocate space for the boxed slice. 316 Oom(OutOfMemory), 317 } 318 319 impl<E> From<OutOfMemory> for BoxedSliceFromFallibleIterError<E> { 320 fn from(oom: OutOfMemory) -> Self { 321 Self::Oom(oom) 322 } 323 } 324 325 impl<E> core::fmt::Display for BoxedSliceFromFallibleIterError<E> { 326 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 327 match self { 328 Self::IterError(_) => f.write_str("The fallible iterator produced an error"), 329 Self::Oom(_) => f.write_str("Failed to allocate space for the boxed slice"), 330 } 331 } 332 } 333 334 impl<E> core::error::Error for BoxedSliceFromFallibleIterError<E> 335 where 336 E: core::error::Error, 337 { 338 fn cause(&self) -> Option<&dyn core::error::Error> { 339 match self { 340 Self::IterError(e) => Some(e), 341 Self::Oom(oom) => Some(oom), 342 } 343 } 344 } 345 346 impl BoxedSliceFromFallibleIterError<OutOfMemory> { 347 /// Flatten this error into its inner OOM. 348 pub fn flatten(self) -> OutOfMemory { 349 match self { 350 Self::IterError(oom) | Self::Oom(oom) => oom, 351 } 352 } 353 } 354 355 /// Create a `Box<[T]>` from the given iterator's `Result<T, E>` items. 356 /// 357 /// Returns an error on allocation failure or if an iterator item is an `Err`. 358 pub fn new_boxed_slice_from_fallible_iter<T, E>( 359 iter: impl IntoIterator<Item = Result<T, E>>, 360 ) -> Result<Box<[T]>, BoxedSliceFromFallibleIterError<E>> { 361 let iter = iter.into_iter(); 362 363 let (min, max) = iter.size_hint(); 364 let len = max.unwrap_or_else(|| min); 365 366 let mut builder = BoxedSliceBuilder::new(len)?; 367 assert_eq!(len, builder.capacity()); 368 369 for result in iter { 370 let elem = result.map_err(BoxedSliceFromFallibleIterError::IterError)?; 371 builder.push(elem)?; 372 } 373 374 debug_assert!(builder.init_len() <= builder.capacity()); 375 builder.shrink_to_fit()?; 376 debug_assert_eq!(builder.init_len(), builder.capacity()); 377 378 Ok(builder.finish()) 379 } 380 381 /// Create a `Box<[T]>` from the given iterator's elements. 382 /// 383 /// Returns an error on allocation failure. 384 pub fn new_boxed_slice_from_iter<T>( 385 iter: impl IntoIterator<Item = T>, 386 ) -> Result<Box<[T]>, OutOfMemory> { 387 let iter = iter 388 .into_iter() 389 .map(Result::<T, core::convert::Infallible>::Ok); 390 new_boxed_slice_from_fallible_iter(iter).map_err(|e| match e { 391 BoxedSliceFromFallibleIterError::Oom(oom) => oom, 392 BoxedSliceFromFallibleIterError::IterError(_) => unreachable!(), 393 }) 394 } 395 396 #[cfg(test)] 397 mod tests { 398 use super::*; 399 use core::cell::Cell; 400 use std_alloc::rc::Rc; 401 402 struct SetFlagOnDrop(Rc<Cell<bool>>); 403 404 impl Drop for SetFlagOnDrop { 405 fn drop(&mut self) { 406 let old_value = self.0.replace(true); 407 assert_eq!(old_value, false); 408 } 409 } 410 411 impl SetFlagOnDrop { 412 fn new() -> (Rc<Cell<bool>>, Self) { 413 let flag = Rc::new(Cell::new(false)); 414 (flag.clone(), SetFlagOnDrop(flag)) 415 } 416 } 417 418 #[test] 419 fn try_new() { 420 <Box<_> as TryNew>::try_new(4).unwrap(); 421 } 422 423 #[test] 424 fn new_boxed_slice_from_iter_with_len_smoke_test() { 425 let slice = new_boxed_slice_from_iter_with_len(3, [42, 36, 1337]).unwrap(); 426 assert_eq!(&*slice, &[42, 36, 1337]); 427 } 428 429 #[test] 430 fn new_boxed_slice_from_iter_with_len_with_too_few_elems() { 431 let (a_dropped, a) = SetFlagOnDrop::new(); 432 let (b_dropped, b) = SetFlagOnDrop::new(); 433 let (c_dropped, c) = SetFlagOnDrop::new(); 434 435 match new_boxed_slice_from_iter_with_len(4, [a, b, c]) { 436 Err(TooFewItemsOrOom::TooFewItems(_)) => {} 437 Ok(_) | Err(TooFewItemsOrOom::Oom(_)) => unreachable!(), 438 } 439 440 assert!(a_dropped.get()); 441 assert!(b_dropped.get()); 442 assert!(c_dropped.get()); 443 } 444 445 #[test] 446 fn new_boxed_slice_from_iter_with_len_with_too_many_elems() { 447 let (a_dropped, a) = SetFlagOnDrop::new(); 448 let (b_dropped, b) = SetFlagOnDrop::new(); 449 let (c_dropped, c) = SetFlagOnDrop::new(); 450 451 let slice = new_boxed_slice_from_iter_with_len(2, [a, b, c]).unwrap(); 452 453 assert!(!a_dropped.get()); 454 assert!(!b_dropped.get()); 455 assert!(c_dropped.get()); 456 457 drop(slice); 458 459 assert!(a_dropped.get()); 460 assert!(b_dropped.get()); 461 assert!(c_dropped.get()); 462 } 463 464 #[test] 465 fn new_boxed_slice_from_iter_smoke_test() { 466 let slice = new_boxed_slice_from_iter([10, 20, 30]).unwrap(); 467 assert_eq!(&*slice, &[10, 20, 30]); 468 } 469 470 #[test] 471 fn new_boxed_slice_from_fallible_iter_smoke_test() { 472 let slice = 473 new_boxed_slice_from_fallible_iter::<_, &str>([Ok(10), Ok(20), Ok(30)]).unwrap(); 474 assert_eq!(&*slice, &[10, 20, 30]); 475 } 476 477 #[test] 478 fn new_boxed_slice_from_fallible_iter_error() { 479 let result = new_boxed_slice_from_fallible_iter::<_, u32>([Ok(10), Ok(20), Err(30)]); 480 let Err(BoxedSliceFromFallibleIterError::IterError(err)) = result else { 481 panic!("unexpected result: {result:?}"); 482 }; 483 assert_eq!(err, 30); 484 } 485 486 #[test] 487 fn new_uninit_boxed_slice_smoke_test() { 488 let slice = new_uninit_boxed_slice::<u32>(5).unwrap(); 489 assert_eq!(slice.len(), 5); 490 } 491 492 #[test] 493 fn boxed_slice_write_iter_smoke_test() { 494 let uninit = new_uninit_boxed_slice(3).unwrap(); 495 let init = boxed_slice_write_iter(uninit, [10, 20, 30]).unwrap(); 496 assert_eq!(&*init, &[10, 20, 30]); 497 } 498 499 #[test] 500 fn boxed_slice_write_iter_with_too_few_elems() { 501 let (a_dropped, a) = SetFlagOnDrop::new(); 502 let (b_dropped, b) = SetFlagOnDrop::new(); 503 let (c_dropped, c) = SetFlagOnDrop::new(); 504 505 let uninit = new_uninit_boxed_slice(4).unwrap(); 506 match boxed_slice_write_iter(uninit, [a, b, c]) { 507 Err(_) => {} 508 Ok(_) => unreachable!(), 509 } 510 511 assert!(a_dropped.get()); 512 assert!(b_dropped.get()); 513 assert!(c_dropped.get()); 514 } 515 516 #[test] 517 fn boxed_slice_write_iter_with_too_many_elems() { 518 let (a_dropped, a) = SetFlagOnDrop::new(); 519 let (b_dropped, b) = SetFlagOnDrop::new(); 520 let (c_dropped, c) = SetFlagOnDrop::new(); 521 522 let uninit = new_uninit_boxed_slice(2).unwrap(); 523 let slice = boxed_slice_write_iter(uninit, [a, b, c]).unwrap(); 524 525 assert!(!a_dropped.get()); 526 assert!(!b_dropped.get()); 527 assert!(c_dropped.get()); 528 529 drop(slice); 530 531 assert!(a_dropped.get()); 532 assert!(b_dropped.get()); 533 assert!(c_dropped.get()); 534 } 535 } 536