]> git.lizzy.rs Git - rust.git/blob - library/alloc/src/vec/is_zero.rs
implement IsZero for Saturating and Wrapping
[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     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!(i8, |x| x == 0); // It is needed to impl for arrays and tuples of i8.
23 impl_is_zero!(i16, |x| x == 0);
24 impl_is_zero!(i32, |x| x == 0);
25 impl_is_zero!(i64, |x| x == 0);
26 impl_is_zero!(i128, |x| x == 0);
27 impl_is_zero!(isize, |x| x == 0);
28
29 impl_is_zero!(u8, |x| x == 0); // It is needed to impl for arrays and tuples of u8.
30 impl_is_zero!(u16, |x| x == 0);
31 impl_is_zero!(u32, |x| x == 0);
32 impl_is_zero!(u64, |x| x == 0);
33 impl_is_zero!(u128, |x| x == 0);
34 impl_is_zero!(usize, |x| x == 0);
35
36 impl_is_zero!(bool, |x| x == false);
37 impl_is_zero!(char, |x| x == '\0');
38
39 impl_is_zero!(f32, |x: f32| x.to_bits() == 0);
40 impl_is_zero!(f64, |x: f64| x.to_bits() == 0);
41
42 unsafe impl<T> IsZero for *const T {
43     #[inline]
44     fn is_zero(&self) -> bool {
45         (*self).is_null()
46     }
47 }
48
49 unsafe impl<T> IsZero for *mut T {
50     #[inline]
51     fn is_zero(&self) -> bool {
52         (*self).is_null()
53     }
54 }
55
56 unsafe impl<T: IsZero, const N: usize> IsZero for [T; N] {
57     #[inline]
58     fn is_zero(&self) -> bool {
59         // Because this is generated as a runtime check, it's not obvious that
60         // it's worth doing if the array is really long.  The threshold here
61         // is largely arbitrary, but was picked because as of 2022-07-01 LLVM
62         // fails to const-fold the check in `vec![[1; 32]; n]`
63         // See https://github.com/rust-lang/rust/pull/97581#issuecomment-1166628022
64         // Feel free to tweak if you have better evidence.
65
66         N <= 16 && self.iter().all(IsZero::is_zero)
67     }
68 }
69
70 // This is recursive macro.
71 macro_rules! impl_for_tuples {
72     // Stopper
73     () => {
74         // No use for implementing for empty tuple because it is ZST.
75     };
76     ($first_arg:ident $(,$rest:ident)*) => {
77         unsafe impl <$first_arg: IsZero, $($rest: IsZero,)*> IsZero for ($first_arg, $($rest,)*){
78             #[inline]
79             fn is_zero(&self) -> bool{
80                 // Destructure tuple to N references
81                 // Rust allows to hide generic params by local variable names.
82                 #[allow(non_snake_case)]
83                 let ($first_arg, $($rest,)*) = self;
84
85                 $first_arg.is_zero()
86                     $( && $rest.is_zero() )*
87             }
88         }
89
90         impl_for_tuples!($($rest),*);
91     }
92 }
93
94 impl_for_tuples!(A, B, C, D, E, F, G, H);
95
96 // `Option<&T>` and `Option<Box<T>>` are guaranteed to represent `None` as null.
97 // For fat pointers, the bytes that would be the pointer metadata in the `Some`
98 // variant are padding in the `None` variant, so ignoring them and
99 // zero-initializing instead is ok.
100 // `Option<&mut T>` never implements `Clone`, so there's no need for an impl of
101 // `SpecFromElem`.
102
103 unsafe impl<T: ?Sized> IsZero for Option<&T> {
104     #[inline]
105     fn is_zero(&self) -> bool {
106         self.is_none()
107     }
108 }
109
110 unsafe impl<T: ?Sized> IsZero for Option<Box<T>> {
111     #[inline]
112     fn is_zero(&self) -> bool {
113         self.is_none()
114     }
115 }
116
117 // `Option<num::NonZeroU32>` and similar have a representation guarantee that
118 // they're the same size as the corresponding `u32` type, as well as a guarantee
119 // that transmuting between `NonZeroU32` and `Option<num::NonZeroU32>` works.
120 // While the documentation officially makes it UB to transmute from `None`,
121 // we're the standard library so we can make extra inferences, and we know that
122 // the only niche available to represent `None` is the one that's all zeros.
123
124 macro_rules! impl_is_zero_option_of_nonzero {
125     ($($t:ident,)+) => {$(
126         unsafe impl IsZero for Option<core::num::$t> {
127             #[inline]
128             fn is_zero(&self) -> bool {
129                 self.is_none()
130             }
131         }
132     )+};
133 }
134
135 impl_is_zero_option_of_nonzero!(
136     NonZeroU8,
137     NonZeroU16,
138     NonZeroU32,
139     NonZeroU64,
140     NonZeroU128,
141     NonZeroI8,
142     NonZeroI16,
143     NonZeroI32,
144     NonZeroI64,
145     NonZeroI128,
146     NonZeroUsize,
147     NonZeroIsize,
148 );
149
150 unsafe impl<T: IsZero> IsZero for Wrapping<T> {
151     #[inline]
152     fn is_zero(&self) -> bool {
153         self.0.is_zero()
154     }
155 }
156
157 unsafe impl<T: IsZero> IsZero for Saturating<T> {
158     #[inline]
159     fn is_zero(&self) -> bool {
160         self.0.is_zero()
161     }
162 }