Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | // SPDX-License-Identifier: Apache-2.0 OR MIT use crate::boxed::Box; #[rustc_specialization_trait] pub(super) unsafe trait IsZero { /// Whether this value's representation is all zeros fn is_zero(&self) -> bool; } macro_rules! impl_is_zero { ($t:ty, $is_zero:expr) => { unsafe impl IsZero for $t { #[inline] fn is_zero(&self) -> bool { $is_zero(*self) } } }; } impl_is_zero!(i16, |x| x == 0); impl_is_zero!(i32, |x| x == 0); impl_is_zero!(i64, |x| x == 0); impl_is_zero!(i128, |x| x == 0); impl_is_zero!(isize, |x| x == 0); impl_is_zero!(u16, |x| x == 0); impl_is_zero!(u32, |x| x == 0); impl_is_zero!(u64, |x| x == 0); impl_is_zero!(u128, |x| x == 0); impl_is_zero!(usize, |x| x == 0); impl_is_zero!(bool, |x| x == false); impl_is_zero!(char, |x| x == '\0'); impl_is_zero!(f32, |x: f32| x.to_bits() == 0); impl_is_zero!(f64, |x: f64| x.to_bits() == 0); unsafe impl<T> IsZero for *const T { #[inline] fn is_zero(&self) -> bool { (*self).is_null() } } unsafe impl<T> IsZero for *mut T { #[inline] fn is_zero(&self) -> bool { (*self).is_null() } } unsafe impl<T: IsZero, const N: usize> IsZero for [T; N] { #[inline] fn is_zero(&self) -> bool { // Because this is generated as a runtime check, it's not obvious that // it's worth doing if the array is really long. The threshold here // is largely arbitrary, but was picked because as of 2022-05-01 LLVM // can const-fold the check in `vec![[0; 32]; n]` but not in // `vec![[0; 64]; n]`: https://godbolt.org/z/WTzjzfs5b // Feel free to tweak if you have better evidence. N <= 32 && self.iter().all(IsZero::is_zero) } } // `Option<&T>` and `Option<Box<T>>` are guaranteed to represent `None` as null. // For fat pointers, the bytes that would be the pointer metadata in the `Some` // variant are padding in the `None` variant, so ignoring them and // zero-initializing instead is ok. // `Option<&mut T>` never implements `Clone`, so there's no need for an impl of // `SpecFromElem`. unsafe impl<T: ?Sized> IsZero for Option<&T> { #[inline] fn is_zero(&self) -> bool { self.is_none() } } unsafe impl<T: ?Sized> IsZero for Option<Box<T>> { #[inline] fn is_zero(&self) -> bool { self.is_none() } } // `Option<num::NonZeroU32>` and similar have a representation guarantee that // they're the same size as the corresponding `u32` type, as well as a guarantee // that transmuting between `NonZeroU32` and `Option<num::NonZeroU32>` works. // While the documentation officially makes it UB to transmute from `None`, // we're the standard library so we can make extra inferences, and we know that // the only niche available to represent `None` is the one that's all zeros. macro_rules! impl_is_zero_option_of_nonzero { ($($t:ident,)+) => {$( unsafe impl IsZero for Option<core::num::$t> { #[inline] fn is_zero(&self) -> bool { self.is_none() } } )+}; } impl_is_zero_option_of_nonzero!( NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroUsize, NonZeroIsize, ); |