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}