1 // SPDX-License-Identifier: Apache-2.0 OR MIT
2 
3 use crate::boxed::Box;
4 
5 #[rustc_specialization_trait]
6 pub(super) unsafe trait IsZero {
7     /// Whether this value's representation is all zeros
is_zero(&self) -> bool8     fn is_zero(&self) -> bool;
9 }
10 
11 macro_rules! impl_is_zero {
12     ($t:ty, $is_zero:expr) => {
13         unsafe impl IsZero for $t {
14             #[inline]
15             fn is_zero(&self) -> bool {
16                 $is_zero(*self)
17             }
18         }
19     };
20 }
21 
22 impl_is_zero!(i16, |x| x == 0);
23 impl_is_zero!(i32, |x| x == 0);
24 impl_is_zero!(i64, |x| x == 0);
25 impl_is_zero!(i128, |x| x == 0);
26 impl_is_zero!(isize, |x| x == 0);
27 
28 impl_is_zero!(u16, |x| x == 0);
29 impl_is_zero!(u32, |x| x == 0);
30 impl_is_zero!(u64, |x| x == 0);
31 impl_is_zero!(u128, |x| x == 0);
32 impl_is_zero!(usize, |x| x == 0);
33 
34 impl_is_zero!(bool, |x| x == false);
35 impl_is_zero!(char, |x| x == '\0');
36 
37 impl_is_zero!(f32, |x: f32| x.to_bits() == 0);
38 impl_is_zero!(f64, |x: f64| x.to_bits() == 0);
39 
40 unsafe impl<T> IsZero for *const T {
41     #[inline]
is_zero(&self) -> bool42     fn is_zero(&self) -> bool {
43         (*self).is_null()
44     }
45 }
46 
47 unsafe impl<T> IsZero for *mut T {
48     #[inline]
is_zero(&self) -> bool49     fn is_zero(&self) -> bool {
50         (*self).is_null()
51     }
52 }
53 
54 unsafe impl<T: IsZero, const N: usize> IsZero for [T; N] {
55     #[inline]
is_zero(&self) -> bool56     fn is_zero(&self) -> bool {
57         // Because this is generated as a runtime check, it's not obvious that
58         // it's worth doing if the array is really long.  The threshold here
59         // is largely arbitrary, but was picked because as of 2022-05-01 LLVM
60         // can const-fold the check in `vec![[0; 32]; n]` but not in
61         // `vec![[0; 64]; n]`: https://godbolt.org/z/WTzjzfs5b
62         // Feel free to tweak if you have better evidence.
63 
64         N <= 32 && self.iter().all(IsZero::is_zero)
65     }
66 }
67 
68 // `Option<&T>` and `Option<Box<T>>` are guaranteed to represent `None` as null.
69 // For fat pointers, the bytes that would be the pointer metadata in the `Some`
70 // variant are padding in the `None` variant, so ignoring them and
71 // zero-initializing instead is ok.
72 // `Option<&mut T>` never implements `Clone`, so there's no need for an impl of
73 // `SpecFromElem`.
74 
75 unsafe impl<T: ?Sized> IsZero for Option<&T> {
76     #[inline]
is_zero(&self) -> bool77     fn is_zero(&self) -> bool {
78         self.is_none()
79     }
80 }
81 
82 unsafe impl<T: ?Sized> IsZero for Option<Box<T>> {
83     #[inline]
is_zero(&self) -> bool84     fn is_zero(&self) -> bool {
85         self.is_none()
86     }
87 }
88 
89 // `Option<num::NonZeroU32>` and similar have a representation guarantee that
90 // they're the same size as the corresponding `u32` type, as well as a guarantee
91 // that transmuting between `NonZeroU32` and `Option<num::NonZeroU32>` works.
92 // While the documentation officially makes it UB to transmute from `None`,
93 // we're the standard library so we can make extra inferences, and we know that
94 // the only niche available to represent `None` is the one that's all zeros.
95 
96 macro_rules! impl_is_zero_option_of_nonzero {
97     ($($t:ident,)+) => {$(
98         unsafe impl IsZero for Option<core::num::$t> {
99             #[inline]
100             fn is_zero(&self) -> bool {
101                 self.is_none()
102             }
103         }
104     )+};
105 }
106 
107 impl_is_zero_option_of_nonzero!(
108     NonZeroU8,
109     NonZeroU16,
110     NonZeroU32,
111     NonZeroU64,
112     NonZeroU128,
113     NonZeroI8,
114     NonZeroI16,
115     NonZeroI32,
116     NonZeroI64,
117     NonZeroI128,
118     NonZeroUsize,
119     NonZeroIsize,
120 );
121