1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
#![cfg_attr(not(doctest), no_std)]
//! Helper trait to create instances of large structs with default value on heap directly
//! without going through stack.
//!
//! Similar to the unstable `box` syntax,
//! it semantically doesn't require creating the whole struct on stack then moving to heap,
//! and thus unlike [`copyless`][copyless] or [`boxext`][boxext],
//! it doesn't rely on optimization to eliminate building the struct on stack,
//! which may still face stack overflow on debug build when creating large struct.
//!
//! [copyless]: https://crates.io/crates/copyless
//! [boxext]: https://crates.io/crates/boxext
//!
//! ## Example
//!
//! ```
//! use default_boxed::DefaultBoxed;
//!
//! # #[cfg(not(miri))]
//! const BASE: usize = 1024;
//! # #[cfg(miri)]
//! # const BASE: usize = 1;
//!
//! #[derive(DefaultBoxed)]
//! struct Foo {
//! a: Bar,
//! b: [Bar; 1024 * BASE],
//! c: [u32; 1024 * BASE],
//! }
//!
//! struct Bar(u16);
//! impl Default for Bar {
//! fn default() -> Bar {
//! Bar(29)
//! }
//! }
//!
//! let foo = Foo::default_boxed();
//! assert_eq!(foo.a.0, 29);
//! assert_eq!(foo.b[128 * BASE].0, 29);
//! assert_eq!(foo.c[256 * BASE], 0);
//!
//! let foo_arr = Foo::default_boxed_array::<16>();
//! assert_eq!(foo_arr[15].a.0, 29);
//! ```
extern crate alloc;
use alloc::alloc::{alloc as alloc_raw, handle_alloc_error, Layout};
use alloc::boxed::Box;
use core::ptr;
pub use default_boxed_derive::DefaultBoxed;
/// Helper trait to create a boxed instance of the given type with a default value for each field.
///
/// This trait can be derived for structs.
///
/// To derive this trait, each field needs to also implement this trait, but all types which
/// implements `Default` implements this trait via the blanket `impl` already.
///
/// In addition, if a field is an array, only the item type needs to implement this trait, and each
/// item would be initialized separately.
pub trait DefaultBoxed {
/// Create a boxed instance with default value for each field.
fn default_boxed() -> Box<Self>
where
Self: Sized,
{
let layout = Layout::new::<Self>();
unsafe {
if layout.size() == 0 {
return Box::from_raw(ptr::NonNull::<Self>::dangling().as_ptr());
}
let raw = alloc_raw(layout) as *mut Self;
if raw.is_null() {
handle_alloc_error(layout)
} else {
Self::default_in_place(raw);
Box::from_raw(raw)
}
}
}
/// Create a boxed array of the given size with default value of the type.
///
/// ```
/// use default_boxed::DefaultBoxed;
/// let arr = u32::default_boxed_array::<32>();
/// assert_eq!(arr, Box::new([0; 32]));
/// ```
fn default_boxed_array<const N: usize>() -> Box<[Self; N]>
where
Self: Sized,
{
let layout = Layout::new::<[Self; N]>();
unsafe {
if layout.size() == 0 {
return Box::from_raw(ptr::NonNull::<[Self; N]>::dangling().as_ptr());
}
let raw = alloc_raw(layout) as *mut Self;
if raw.is_null() {
handle_alloc_error(layout)
} else {
for i in 0..N as isize {
Self::default_in_place(raw.offset(i));
}
Box::from_raw(raw as *mut [Self; N])
}
}
}
/// Fill the given memory location with default value.
///
/// # Safety
///
/// For callers, behavior is undefined if `ptr` is not valid for writes, or it is not properly
/// aligned.
///
/// For impls, behavior is undefined if this method reads from `ptr`.
unsafe fn default_in_place(ptr: *mut Self);
}
impl<T: Default> DefaultBoxed for T {
unsafe fn default_in_place(ptr: *mut Self) {
ptr::write(ptr, Default::default());
}
}