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 130 131 132 133
#![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.
///
/// # Safety
///
/// Implementations must ensure that `default_in_place` initializes the value on the given pointer.
pub unsafe 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);
}
unsafe impl<T: Default> DefaultBoxed for T {
unsafe fn default_in_place(ptr: *mut Self) {
ptr::write(ptr, Default::default());
}
}