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
#![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; //! //! #[derive(DefaultBoxed)] //! struct Foo { //! a: Bar, //! b: [Bar; 1024 * 1024], //! c: [u32; 1024 * 1024], //! } //! //! 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 * 1024].0, 29); //! assert_eq!(foo.c[256 * 1024], 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()); } }