]> git.lizzy.rs Git - rust.git/blob - library/alloc/src/vec/is_zero.rs
Rollup merge of #101385 - BartMassey-upstream:file-doc, r=thomcc
[rust.git] / library / alloc / src / vec / is_zero.rs
1 use crate::boxed::Box;
2
3 #[rustc_specialization_trait]
4 pub(super) unsafe trait IsZero {
5     /// Whether this value's representation is all zeros
6     fn is_zero(&self) -> bool;
7 }
8
9 macro_rules! impl_is_zero {
10     ($t:ty, $is_zero:expr) => {
11         unsafe impl IsZero for $t {
12             #[inline]
13             fn is_zero(&self) -> bool {
14                 $is_zero(*self)
15             }
16         }
17     };
18 }
19
20 impl_is_zero!(i8, |x| x == 0); // It is needed to impl for arrays and tuples of i8.
21 impl_is_zero!(i16, |x| x == 0);
22 impl_is_zero!(i32, |x| x == 0);
23 impl_is_zero!(i64, |x| x == 0);
24 impl_is_zero!(i128, |x| x == 0);
25 impl_is_zero!(isize, |x| x == 0);
26
27 impl_is_zero!(u8, |x| x == 0); // It is needed to impl for arrays and tuples of u8.
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]
42     fn is_zero(&self) -> bool {
43         (*self).is_null()
44     }
45 }
46
47 unsafe impl<T> IsZero for *mut T {
48     #[inline]
49     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]
56     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-07-01 LLVM
60         // fails to const-fold the check in `vec![[1; 32]; n]`
61         // See https://github.com/rust-lang/rust/pull/97581#issuecomment-1166628022
62         // Feel free to tweak if you have better evidence.
63
64         N <= 16 && self.iter().all(IsZero::is_zero)
65     }
66 }
67
68 // This is recursive macro.
69 macro_rules! impl_for_tuples {
70     // Stopper
71     () => {
72         // No use for implementing for empty tuple because it is ZST.
73     };
74     ($first_arg:ident $(,$rest:ident)*) => {
75         unsafe impl <$first_arg: IsZero, $($rest: IsZero,)*> IsZero for ($first_arg, $($rest,)*){
76             #[inline]
77             fn is_zero(&self) -> bool{
78                 // Destructure tuple to N references
79                 // Rust allows to hide generic params by local variable names.
80                 #[allow(non_snake_case)]
81                 let ($first_arg, $($rest,)*) = self;
82
83                 $first_arg.is_zero()
84                     $( && $rest.is_zero() )*
85             }
86         }
87
88         impl_for_tuples!($($rest),*);
89     }
90 }
91
92 impl_for_tuples!(A, B, C, D, E, F, G, H);
93
94 // `Option<&T>` and `Option<Box<T>>` are guaranteed to represent `None` as null.
95 // For fat pointers, the bytes that would be the pointer metadata in the `Some`
96 // variant are padding in the `None` variant, so ignoring them and
97 // zero-initializing instead is ok.
98 // `Option<&mut T>` never implements `Clone`, so there's no need for an impl of
99 // `SpecFromElem`.
100
101 unsafe impl<T: ?Sized> IsZero for Option<&T> {
102     #[inline]
103     fn is_zero(&self) -> bool {
104         self.is_none()
105     }
106 }
107
108 unsafe impl<T: ?Sized> IsZero for Option<Box<T>> {
109     #[inline]
110     fn is_zero(&self) -> bool {
111         self.is_none()
112     }
113 }
114
115 // `Option<num::NonZeroU32>` and similar have a representation guarantee that
116 // they're the same size as the corresponding `u32` type, as well as a guarantee
117 // that transmuting between `NonZeroU32` and `Option<num::NonZeroU32>` works.
118 // While the documentation officially makes it UB to transmute from `None`,
119 // we're the standard library so we can make extra inferences, and we know that
120 // the only niche available to represent `None` is the one that's all zeros.
121
122 macro_rules! impl_is_zero_option_of_nonzero {
123     ($($t:ident,)+) => {$(
124         unsafe impl IsZero for Option<core::num::$t> {
125             #[inline]
126             fn is_zero(&self) -> bool {
127                 self.is_none()
128             }
129         }
130     )+};
131 }
132
133 impl_is_zero_option_of_nonzero!(
134     NonZeroU8,
135     NonZeroU16,
136     NonZeroU32,
137     NonZeroU64,
138     NonZeroU128,
139     NonZeroI8,
140     NonZeroI16,
141     NonZeroI32,
142     NonZeroI64,
143     NonZeroI128,
144     NonZeroUsize,
145     NonZeroIsize,
146 );