boxext/
lib.rs

1// Copyright 2018 Mike Hommey
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9#![deny(missing_docs)]
10
11//! # Extensions to the `Box` type
12//!
13//! This crate provides extra initializer methods for `Box`, working around the
14//! current (as of writing) shortcomings from `Box::new`:
15//!
16//! * Since Rust 1.12, constructs such as `Box::new([0; 4096])` first create a
17//! temporary object on the stack before copying it into the newly allocated
18//! space (e.g. [issue #50047]).
19//!
20//! * Constructs such as `Box::new(some_function_call())` first get the result
21//! from the function call on the stack before copying it into the newly
22//! allocated space.
23//!
24//! [issue #50047]: https://github.com/rust-lang/rust/issues/50047
25//!
26//! Both can be worked around with some contortion but with caveats. This crate
27//! provides helpers doing those contortions for you, but can't deal with the
28//! caveats. Those caveats are essentially the same as why the unstable
29//! placement features were removed in nightly 1.27, namely that there are no
30//! guarantees that things will actually happen in place (and they don't in
31//! debug builds).
32//!
33//! The crates adds the following helper methods to the `Box` type:
34//!
35//! * [`new_with`], which takes a function or closure returning the object that
36//! will be placed in the Box.
37//!
38//! * [`new_zeroed`], which creates an object filled with zeroes, possibly
39//! using [`calloc`]/[`HeapAlloc(..., HEAP_ZERO_MEMORY, ...)`]/
40//! [`mallocx(..., MALLOCX_ZERO)`] under the hood.
41//!
42//! * [`try_new`], [`try_new_with`], and [`try_new_zeroed`], which are equivalent
43//! to `new`, `new_with` and `new_zeroed`, but don't panic on allocation
44//! failure.
45//!
46//! [`new_with`]: trait.BoxExt.html#tymethod.new_with
47//! [`new_zeroed`]: trait.BoxExt.html#tymethod.new_zeroed
48//! [`try_new`]: trait.BoxExt.html#tymethod.try_new
49//! [`try_new_with`]: trait.BoxExt.html#tymethod.try_new_with
50//! [`try_new_zeroed`]: trait.BoxExt.html#tymethod.try_new_zeroed
51//! [`calloc`]: http://pubs.opengroup.org/onlinepubs/009695399/functions/calloc.html
52//! [`HeapAlloc(..., HEAP_ZERO_MEMORY, ...)`]: https://msdn.microsoft.com/en-us/library/windows/desktop/aa366597(v=vs.85).aspx#HEAP_ZERO_MEMORY
53//! [`mallocx(..., MALLOCX_ZERO)`]: http://jemalloc.net/jemalloc.3.html#MALLOCX_ZERO
54//!
55//! ## Features
56//!
57//! * `std` (enabled by default): Uses libstd. Can be disabled to allow use
58//! with `no_std` code, in which case `allocator_api` needs to be enabled.
59//!
60//! * `allocator_api`: Add similar helpers to the `Box` type from the
61//! `allocator_api` crate.
62
63#![cfg_attr(not(feature = "std"), no_std)]
64
65#[cfg(feature = "std")]
66use std::alloc::{handle_alloc_error, alloc, alloc_zeroed, Layout};
67
68#[cfg(feature = "allocator_api")]
69extern crate allocator_api;
70
71#[cfg(feature = "std")]
72extern crate core;
73
74#[cfg(feature = "std")]
75use core::ptr;
76
77#[cfg(feature = "allocator_api")]
78mod allocator_box;
79#[cfg(feature = "allocator_api")]
80pub use allocator_box::*;
81
82/// Extensions to the `Box` type
83pub trait BoxExt {
84    /// Type contained inside the `Box`.
85    type Inner;
86
87    /// Allocates memory on the heap and then places the result of `f` into it.
88    ///
89    /// This doesn't actually allocate if `Self::Inner` is zero-sized.
90    ///
91    /// When building with optimization enabled, this is expected to avoid
92    /// copies, contrary to `Box::new`.
93    ///
94    /// # Examples
95    ///
96    /// ```
97    /// extern crate boxext;
98    /// use boxext::BoxExt;
99    ///
100    /// #[derive(Debug, PartialEq)]
101    /// struct Foo(usize, usize);
102    ///
103    /// impl Foo {
104    ///     fn new(a: usize, b: usize) -> Self {
105    ///         Foo(a, b)
106    ///    }
107    /// }
108    ///
109    /// impl Default for Foo {
110    ///     fn default() -> Self {
111    ///         Foo::new(0, 1)
112    ///     }
113    /// }
114    ///
115    /// fn main() {
116    ///     // equivalent to `Box::new(Foo(1, 2))`
117    /// #   #[cfg(feature = "std")]
118    ///     let buf = Box::new_with(|| Foo(1, 2));
119    /// #   #[cfg(feature = "std")]
120    ///     assert_eq!(*buf, Foo(1, 2));
121    ///
122    ///     // equivalent to `Box::new(Foo::new(2, 3))`
123    /// #   #[cfg(feature = "std")]
124    ///     let buf = Box::new_with(|| Foo::new(2, 3));
125    /// #   #[cfg(feature = "std")]
126    ///     assert_eq!(*buf, Foo(2, 3));
127    ///
128    ///     // equivalent to `Box::new(Foo::default())`
129    /// #   #[cfg(feature = "std")]
130    ///     let buf = Box::new_with(Foo::default);
131    /// #   #[cfg(feature = "std")]
132    ///     assert_eq!(*buf, Foo::default());
133    /// }
134    /// ```
135    fn new_with<F: FnOnce() -> Self::Inner>(f: F) -> Self;
136
137    /// Allocates zeroed memory on the heap.
138    ///
139    /// This doesn't actually allocate if `Self::Inner` is zero-sized.
140    ///
141    /// This method will obtain zeroed memory directly from the underlying
142    /// allocator, through the use of [`calloc`], [`HeapAlloc(...,
143    /// HEAP_ZERO_MEMORY, ...)`] or [`mallocx(..., MALLOCX_ZERO)`], whichever
144    /// is used as a global allocator by the rust compiler.
145    ///
146    /// [`calloc`]: http://pubs.opengroup.org/onlinepubs/009695399/functions/calloc.html
147    /// [`HeapAlloc(..., HEAP_ZERO_MEMORY, ...)`]: https://msdn.microsoft.com/en-us/library/windows/desktop/aa366597(v=vs.85).aspx#HEAP_ZERO_MEMORY
148    /// [`mallocx(..., MALLOCX_ZERO)`]: http://jemalloc.net/jemalloc.3.html#MALLOCX_ZERO
149    ///
150    /// # Example
151    ///
152    /// ```
153    /// extern crate boxext;
154    /// use boxext::BoxExt;
155    ///
156    /// fn main() {
157    ///     // equivalent to `Box::new([0usize; 32])`
158    /// #   #[cfg(feature = "std")]
159    ///     let buf: Box<[usize; 32]> = Box::new_zeroed();
160    /// #   #[cfg(feature = "std")]
161    ///     assert_eq!(*buf, [0usize; 32]);
162    /// }
163    /// ```
164    ///
165    /// # Safety
166    ///
167    /// This method is only assumed safe for `Self::Inner` types implementing
168    /// the [`Zero`] trait, and not available otherwise. See the definition
169    /// of that trait.
170    ///
171    /// [`Zero`]: trait.Zero.html
172    fn new_zeroed() -> Self
173    where
174        Self: Sized,
175        Self::Inner: Zero;
176
177    /// Fallible [`Box::new`]
178    ///
179    /// [`Box::new`]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.new
180    ///
181    /// This returns `None` if memory couldn't be allocated.
182    ///
183    /// # Examples
184    ///
185    /// ```
186    /// extern crate boxext;
187    /// use boxext::BoxExt;
188    ///
189    /// fn main() {
190    /// #   #[cfg(feature = "std")]
191    ///     let five = Box::try_new(5).unwrap();
192    /// #   #[cfg(feature = "std")]
193    ///     assert_eq!(*five, 5);
194    /// }
195    /// ```
196    fn try_new(x: Self::Inner) -> Option<Self>
197    where
198        Self: Sized;
199
200    /// Fallible [`Box::new_with`]
201    ///
202    /// [`Box::new_with`]: #method.new_with
203    ///
204    /// This returns `None` if memory couldn't be allocated.
205    ///
206    /// # Examples
207    ///
208    /// ```
209    /// extern crate boxext;
210    /// use boxext::BoxExt;
211    ///
212    /// #[derive(Debug, PartialEq)]
213    /// struct Foo(usize, usize);
214    ///
215    /// impl Foo {
216    ///     fn new(a: usize, b: usize) -> Self {
217    ///         Foo(a, b)
218    ///    }
219    /// }
220    ///
221    /// impl Default for Foo {
222    ///     fn default() -> Self {
223    ///         Foo::new(0, 1)
224    ///     }
225    /// }
226    ///
227    /// fn main() {
228    ///     // equivalent to `Box::try_new(Foo(1, 2))`
229    /// #   #[cfg(feature = "std")]
230    ///     let buf = Box::try_new_with(|| Foo(1, 2)).unwrap();
231    /// #   #[cfg(feature = "std")]
232    ///     assert_eq!(*buf, Foo(1, 2));
233    ///
234    ///     // equivalent to `Box::try_new(Foo::new(2, 3))`
235    /// #   #[cfg(feature = "std")]
236    ///     let buf = Box::try_new_with(|| Foo::new(2, 3)).unwrap();
237    /// #   #[cfg(feature = "std")]
238    ///     assert_eq!(*buf, Foo(2, 3));
239    ///
240    ///     // equivalent to `Box::try_new(Foo::default())`
241    /// #   #[cfg(feature = "std")]
242    ///     let buf = Box::try_new_with(Foo::default).unwrap();
243    /// #   #[cfg(feature = "std")]
244    ///     assert_eq!(*buf, Foo::default());
245    /// }
246    /// ```
247    fn try_new_with<F: FnOnce() -> Self::Inner>(f: F) -> Option<Self>
248    where
249        Self: Sized;
250
251    /// Fallible [`Box::new_zeroed`]
252    ///
253    /// [`Box::new_zeroed`]: #method.new_zeroed
254    ///
255    /// This returns `None` if memory couldn't be allocated.
256    ///
257    /// # Example
258    ///
259    /// ```
260    /// extern crate boxext;
261    /// use boxext::BoxExt;
262    ///
263    /// fn main() {
264    ///     // equivalent to `Box::try_new([0usize; 32])`
265    /// #   #[cfg(feature = "std")]
266    ///     let buf: Box<[usize; 32]> = Box::try_new_zeroed().unwrap();
267    /// #   #[cfg(feature = "std")]
268    ///     assert_eq!(*buf, [0usize; 32]);
269    /// }
270    /// ```
271    ///
272    /// # Safety
273    ///
274    /// This method is only assumed safe for `Self::Inner` types implementing
275    /// the [`Zero`] trait, and not available otherwise. See the definition
276    /// of that trait.
277    ///
278    /// [`Zero`]: trait.Zero.html
279    fn try_new_zeroed() -> Option<Self>
280    where
281        Self: Sized,
282        Self::Inner: Zero;
283}
284
285#[cfg(feature = "std")]
286unsafe fn try_new_box<T>(zeroed: bool) -> Result<Box<T>, Layout> {
287    let layout = Layout::new::<T>();
288    let raw = if layout.size() == 0 {
289        ptr::NonNull::<T>::dangling().as_ptr()
290    } else if zeroed {
291        alloc_zeroed(layout) as *mut T
292    } else {
293        alloc(layout) as *mut T
294    };
295    if !raw.is_null() {
296        Ok(Box::from_raw(raw))
297    } else {
298        Err(layout)
299    }
300}
301
302#[cfg(feature = "std")]
303unsafe fn new_box<T>(zeroed: bool) -> Box<T> {
304    try_new_box::<T>(zeroed).unwrap_or_else(|l| handle_alloc_error(l))
305}
306
307#[cfg(feature = "std")]
308impl<T> BoxExt for Box<T> {
309    type Inner = T;
310
311    #[inline]
312    fn new_with<F: FnOnce() -> T>(f: F) -> Box<T> {
313        unsafe {
314            let mut b = new_box::<T>(false);
315            ptr::write(b.as_mut(), f());
316            b
317        }
318    }
319
320    #[inline]
321    fn new_zeroed() -> Box<T>
322    where
323        T: Zero,
324    {
325        unsafe { new_box(true) }
326    }
327
328    #[inline]
329    fn try_new(x: T) -> Option<Self> {
330        unsafe {
331            let mut b = try_new_box::<T>(false).ok()?;
332            ptr::write(b.as_mut(), x);
333            Some(b)
334        }
335    }
336
337    #[inline]
338    fn try_new_with<F: FnOnce() -> Self::Inner>(f: F) -> Option<Self> {
339        unsafe {
340            let mut b = try_new_box::<T>(false).ok()?;
341            ptr::write(b.as_mut(), f());
342            Some(b)
343        }
344    }
345
346    #[inline]
347    fn try_new_zeroed() -> Option<Self>
348    where
349        Self::Inner: Zero,
350    {
351        unsafe { try_new_box::<T>(true).ok() }
352    }
353}
354
355/// Trait indicating whether a value full of zeroes is valid.
356///
357/// This trait is used to enable the [`Box::new_zeroed`] method for types where
358/// it's safe to use.
359///
360/// [`Box::new_zeroed`]: trait.BoxExt.html#tymethod::new_zeroed
361///
362/// # Safety
363///
364/// Do **not** implement this trait for types where a raw byte array of 0
365/// doesn't represent a valid value for the type. Please double check it is
366/// valid and corresponds to what you want.
367///
368/// # Examples
369///
370/// ```
371/// extern crate boxext;
372/// use boxext::{BoxExt, Zero};
373///
374/// #[derive(Debug, PartialEq)]
375/// struct Foo(usize);
376///
377/// unsafe impl Zero for Foo {}
378///
379/// fn main() {
380///     // equivalent to `Box::new(Foo(0))`
381/// #   #[cfg(feature = "std")]
382///     let buf: Box<Foo> = Box::new_zeroed();
383/// #   #[cfg(feature = "std")]
384///     assert_eq!(*buf, Foo(0));
385/// }
386/// ```
387///
388/// For convenience, a `boxext_derive` crate is provided that provides a
389/// custom derive for `Zero`.
390///
391/// ```
392/// extern crate boxext;
393/// #[macro_use]
394/// extern crate boxext_derive;
395/// use boxext::BoxExt;
396///
397/// #[derive(Zero, Debug, PartialEq)]
398/// struct Foo(usize);
399///
400/// fn main() {
401///     // equivalent to `Box::new(Foo(0))`
402/// #   #[cfg(feature = "std")]
403///     let buf: Box<Foo> = Box::new_zeroed();
404/// #   #[cfg(feature = "std")]
405///     assert_eq!(*buf, Foo(0));
406/// }
407/// ```
408///
409/// ```compile_fail
410/// extern crate boxext;
411/// #[macro_use]
412/// extern crate boxext_derive;
413/// use boxext::BoxExt;
414///
415/// #[derive(Zero)]
416/// //          ^ the trait `boxext::Zero` is not implemented for `Bar`
417/// struct Foo(Bar);
418///
419/// struct Bar;
420///
421/// fn main() {
422///     // equivalent to `Box::new(Foo(0))`
423///     let buf: Box<Foo> = Box::new_zeroed();
424/// }
425/// ```
426///
427pub unsafe trait Zero: Sized {}
428
429macro_rules! zero_num_impl {
430    ($($t:ty)+) => { $(unsafe impl Zero for $t {})+ }
431}
432
433zero_num_impl! {
434    u8 u16 u32 u64 usize
435    i8 i16 i32 i64 isize
436    f32 f64
437}
438
439unsafe impl<T> Zero for *mut T {}
440
441unsafe impl<T> Zero for *const T {}
442
443macro_rules! zero_array_impl {
444    ($($n:expr)+) => {$(
445        unsafe impl<T: Zero> Zero for [T; $n] {}
446    )+};
447}
448
449zero_array_impl! {
450    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
451    17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
452    33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
453    49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
454    65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
455    81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
456    97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
457    113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
458    160 192 200 224 256 384 512 768 1024 2048 4096 8192 16384 32768
459}
460
461#[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))]
462zero_array_impl! {
463    65536 131072 262144 524288 1048576 2097152 4194304 8388608
464    16777216 33554432 67108864 134217728 268435456 536870912
465    1073741824 2147483648
466}
467
468#[cfg(target_pointer_width = "64")]
469zero_array_impl! {
470    4294967296
471}
472
473macro_rules! zero_tuple_impl {
474    ($t:ident $($u:ident)+) => {
475        zero_tuple_impl!(($t) $($u)+);
476    };
477    (($($t:ident)+) $u:ident $($v:ident)*) => {
478        zero_tuple_impl!(($($t)+));
479        zero_tuple_impl!(($($t)+ $u) $($v)*);
480    };
481    (($($t:ident)+)) => {
482        unsafe impl<$($t: Zero),+> Zero for ($($t,)+) {}
483    };
484}
485
486zero_tuple_impl! {
487    A B C D E F G H I J K L
488}