123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120 |
- // 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,
- );
|