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