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