xref: /openbmc/linux/rust/alloc/vec/is_zero.rs (revision 3ed03f4d)
1 // SPDX-License-Identifier: Apache-2.0 OR MIT
2 
3 use core::num::{Saturating, Wrapping};
4 
5 use crate::boxed::Box;
6 
7 #[rustc_specialization_trait]
8 pub(super) unsafe trait IsZero {
9     /// Whether this value's representation is all zeros,
10     /// or can be represented with all zeroes.
is_zero(&self) -> bool11     fn is_zero(&self) -> bool;
12 }
13 
14 macro_rules! impl_is_zero {
15     ($t:ty, $is_zero:expr) => {
16         unsafe impl IsZero for $t {
17             #[inline]
18             fn is_zero(&self) -> bool {
19                 $is_zero(*self)
20             }
21         }
22     };
23 }
24 
25 impl_is_zero!(i8, |x| x == 0); // It is needed to impl for arrays and tuples of i8.
26 impl_is_zero!(i16, |x| x == 0);
27 impl_is_zero!(i32, |x| x == 0);
28 impl_is_zero!(i64, |x| x == 0);
29 impl_is_zero!(i128, |x| x == 0);
30 impl_is_zero!(isize, |x| x == 0);
31 
32 impl_is_zero!(u8, |x| x == 0); // It is needed to impl for arrays and tuples of u8.
33 impl_is_zero!(u16, |x| x == 0);
34 impl_is_zero!(u32, |x| x == 0);
35 impl_is_zero!(u64, |x| x == 0);
36 impl_is_zero!(u128, |x| x == 0);
37 impl_is_zero!(usize, |x| x == 0);
38 
39 impl_is_zero!(bool, |x| x == false);
40 impl_is_zero!(char, |x| x == '\0');
41 
42 impl_is_zero!(f32, |x: f32| x.to_bits() == 0);
43 impl_is_zero!(f64, |x: f64| x.to_bits() == 0);
44 
45 unsafe impl<T> IsZero for *const T {
46     #[inline]
is_zero(&self) -> bool47     fn is_zero(&self) -> bool {
48         (*self).is_null()
49     }
50 }
51 
52 unsafe impl<T> IsZero for *mut T {
53     #[inline]
is_zero(&self) -> bool54     fn is_zero(&self) -> bool {
55         (*self).is_null()
56     }
57 }
58 
59 unsafe impl<T: IsZero, const N: usize> IsZero for [T; N] {
60     #[inline]
is_zero(&self) -> bool61     fn is_zero(&self) -> bool {
62         // Because this is generated as a runtime check, it's not obvious that
63         // it's worth doing if the array is really long. The threshold here
64         // is largely arbitrary, but was picked because as of 2022-07-01 LLVM
65         // fails to const-fold the check in `vec![[1; 32]; n]`
66         // See https://github.com/rust-lang/rust/pull/97581#issuecomment-1166628022
67         // Feel free to tweak if you have better evidence.
68 
69         N <= 16 && self.iter().all(IsZero::is_zero)
70     }
71 }
72 
73 // This is recursive macro.
74 macro_rules! impl_for_tuples {
75     // Stopper
76     () => {
77         // No use for implementing for empty tuple because it is ZST.
78     };
79     ($first_arg:ident $(,$rest:ident)*) => {
80         unsafe impl <$first_arg: IsZero, $($rest: IsZero,)*> IsZero for ($first_arg, $($rest,)*){
81             #[inline]
82             fn is_zero(&self) -> bool{
83                 // Destructure tuple to N references
84                 // Rust allows to hide generic params by local variable names.
85                 #[allow(non_snake_case)]
86                 let ($first_arg, $($rest,)*) = self;
87 
88                 $first_arg.is_zero()
89                     $( && $rest.is_zero() )*
90             }
91         }
92 
93         impl_for_tuples!($($rest),*);
94     }
95 }
96 
97 impl_for_tuples!(A, B, C, D, E, F, G, H);
98 
99 // `Option<&T>` and `Option<Box<T>>` are guaranteed to represent `None` as null.
100 // For fat pointers, the bytes that would be the pointer metadata in the `Some`
101 // variant are padding in the `None` variant, so ignoring them and
102 // zero-initializing instead is ok.
103 // `Option<&mut T>` never implements `Clone`, so there's no need for an impl of
104 // `SpecFromElem`.
105 
106 unsafe impl<T: ?Sized> IsZero for Option<&T> {
107     #[inline]
is_zero(&self) -> bool108     fn is_zero(&self) -> bool {
109         self.is_none()
110     }
111 }
112 
113 unsafe impl<T: ?Sized> IsZero for Option<Box<T>> {
114     #[inline]
is_zero(&self) -> bool115     fn is_zero(&self) -> bool {
116         self.is_none()
117     }
118 }
119 
120 // `Option<num::NonZeroU32>` and similar have a representation guarantee that
121 // they're the same size as the corresponding `u32` type, as well as a guarantee
122 // that transmuting between `NonZeroU32` and `Option<num::NonZeroU32>` works.
123 // While the documentation officially makes it UB to transmute from `None`,
124 // we're the standard library so we can make extra inferences, and we know that
125 // the only niche available to represent `None` is the one that's all zeros.
126 
127 macro_rules! impl_is_zero_option_of_nonzero {
128     ($($t:ident,)+) => {$(
129         unsafe impl IsZero for Option<core::num::$t> {
130             #[inline]
131             fn is_zero(&self) -> bool {
132                 self.is_none()
133             }
134         }
135     )+};
136 }
137 
138 impl_is_zero_option_of_nonzero!(
139     NonZeroU8,
140     NonZeroU16,
141     NonZeroU32,
142     NonZeroU64,
143     NonZeroU128,
144     NonZeroI8,
145     NonZeroI16,
146     NonZeroI32,
147     NonZeroI64,
148     NonZeroI128,
149     NonZeroUsize,
150     NonZeroIsize,
151 );
152 
153 macro_rules! impl_is_zero_option_of_num {
154     ($($t:ty,)+) => {$(
155         unsafe impl IsZero for Option<$t> {
156             #[inline]
157             fn is_zero(&self) -> bool {
158                 const {
159                     let none: Self = unsafe { core::mem::MaybeUninit::zeroed().assume_init() };
160                     assert!(none.is_none());
161                 }
162                 self.is_none()
163             }
164         }
165     )+};
166 }
167 
168 impl_is_zero_option_of_num!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128, usize, isize,);
169 
170 unsafe impl<T: IsZero> IsZero for Wrapping<T> {
171     #[inline]
is_zero(&self) -> bool172     fn is_zero(&self) -> bool {
173         self.0.is_zero()
174     }
175 }
176 
177 unsafe impl<T: IsZero> IsZero for Saturating<T> {
178     #[inline]
is_zero(&self) -> bool179     fn is_zero(&self) -> bool {
180         self.0.is_zero()
181     }
182 }
183 
184 macro_rules! impl_for_optional_bool {
185     ($($t:ty,)+) => {$(
186         unsafe impl IsZero for $t {
187             #[inline]
188             fn is_zero(&self) -> bool {
189                 // SAFETY: This is *not* a stable layout guarantee, but
190                 // inside `core` we're allowed to rely on the current rustc
191                 // behaviour that options of bools will be one byte with
192                 // no padding, so long as they're nested less than 254 deep.
193                 let raw: u8 = unsafe { core::mem::transmute(*self) };
194                 raw == 0
195             }
196         }
197     )+};
198 }
199 impl_for_optional_bool! {
200     Option<bool>,
201     Option<Option<bool>>,
202     Option<Option<Option<bool>>>,
203     // Could go further, but not worth the metadata overhead
204 }
205