is_zero.rs - source
Description: Source of the Rust file `library/alloc/src/vec/is_zero.rs`.

#
alloc/vec/
is_zero.rs

    1use core::mem::SizedTypeProperties;
    2use core::num::{NonZero, Saturating, Wrapping};
    3
    4use crate::boxed::Box;
    5
    6#[rustc_specialization_trait]
    7pub(super) unsafe trait IsZero {
    8    /// Whether this value's representation is all zeros,
    9    /// or can be represented with all zeroes.
    10    fn is_zero(&self) -> bool;
    11}
    12
    13macro_rules! impl_is_zero {
    14    ($t:ty, $is_zero:expr) => {
    15        unsafe impl IsZero for $t {
    16            #[inline]
    17            fn is_zero(&self) -> bool {
    18                $is_zero(*self)
    19            }
    20        }
    21    };
    22}
    23
    24impl_is_zero!((), |_: ()| true); // It is needed to impl for arrays and tuples of ().
    25
    26impl_is_zero!(i8, |x| x == 0); // It is needed to impl for arrays and tuples of i8.
    27impl_is_zero!(i16, |x| x == 0);
    28impl_is_zero!(i32, |x| x == 0);
    29impl_is_zero!(i64, |x| x == 0);
    30impl_is_zero!(i128, |x| x == 0);
    31impl_is_zero!(isize, |x| x == 0);
    32
    33impl_is_zero!(u8, |x| x == 0); // It is needed to impl for arrays and tuples of u8.
    34impl_is_zero!(u16, |x| x == 0);
    35impl_is_zero!(u32, |x| x == 0);
    36impl_is_zero!(u64, |x| x == 0);
    37impl_is_zero!(u128, |x| x == 0);
    38impl_is_zero!(usize, |x| x == 0);
    39
    40impl_is_zero!(bool, |x| x == false);
    41impl_is_zero!(char, |x| x == '\0');
    42
    43impl_is_zero!(f32, |x: f32| x.to_bits() == 0);
    44impl_is_zero!(f64, |x: f64| x.to_bits() == 0);
    45
    46// `IsZero` cannot be soundly implemented for pointers because of provenance
    47// (see #135338).
    48
    49unsafe impl<T, const N: usize> IsZero for [T; N] {
    50    #[inline]
    51    default fn is_zero(&self) -> bool {
    52        // If the array is of length zero,
    53        // then it doesn't actually contain any `T`s,
    54        // so `T::clone` doesn't need to be called,
    55        // and we can "zero-initialize" all zero bytes of the array.
    56        N == 0
    57    }
    58}
    59
    60unsafe impl<T: IsZero, const N: usize> IsZero for [T; N] {
    61    #[inline]
    62    fn is_zero(&self) -> bool {
    63        if T::IS_ZST {
    64            // If T is a ZST, then there is at most one possible value of `T`,
    65            // so we only need to check one element for zeroness.
    66            // We can't unconditionally return `true` here, since, e.g.
    67            // `T = [NonTrivialCloneZst; 5]` is a ZST that implements `IsZero`
    68            // due to the generic array impl, but `T::is_zero` returns `false`
    69            // since the length is not 0.
    70            self.get(0).is_none_or(IsZero::is_zero)
    71        } else {
    72            // Because this is generated as a runtime check, it's not obvious that
    73            // it's worth doing if the array is really long. The threshold here
    74            // is largely arbitrary, but was picked because as of 2022-07-01 LLVM
    75            // fails to const-fold the check in `vec![[1; 32]; n]`
    76            // See https://github.com/rust-lang/rust/pull/97581#issuecomment-1166628022
    77            // Feel free to tweak if you have better evidence.
    78
    79            N <= 16 && self.iter().all(IsZero::is_zero)
    80        }
    81    }
    82}
    83
    84// This is recursive macro.
    85macro_rules! impl_is_zero_tuples {
    86    // Stopper
    87    () => {
    88        // We already have an impl for () above.
    89    };
    90    ($first_arg:ident $(,$rest:ident)*) => {
    91        unsafe impl <$first_arg: IsZero, $($rest: IsZero,)*> IsZero for ($first_arg, $($rest,)*){
    92            #[inline]
    93            fn is_zero(&self) -> bool{
    94                // Destructure tuple to N references
    95                // Rust allows to hide generic params by local variable names.
    96                #[allow(non_snake_case)]
    97                let ($first_arg, $($rest,)*) = self;
    98
    99                $first_arg.is_zero()
    100                    $( && $rest.is_zero() )*
    101            }
    102        }
    103
    104        impl_is_zero_tuples!($($rest),*);
    105    }
    106}
    107
    108impl_is_zero_tuples!(A, B, C, D, E, F, G, H);
    109
    110// `Option<&T>` and `Option<Box<T>>` are guaranteed to represent `None` as null.
    111// For fat pointers, the bytes that would be the pointer metadata in the `Some`
    112// variant are padding in the `None` variant, so ignoring them and
    113// zero-initializing instead is ok.
    114// `Option<&mut T>` never implements `Clone`, so there's no need for an impl of
    115// `SpecFromElem`.
    116
    117unsafe impl<T: ?Sized> IsZero for Option<&T> {
    118    #[inline]
    119    fn is_zero(&self) -> bool {
    120        self.is_none()
    121    }
    122}
    123
    124unsafe impl<T: ?Sized> IsZero for Option<Box<T>> {
    125    #[inline]
    126    fn is_zero(&self) -> bool {
    127        self.is_none()
    128    }
    129}
    130
    131// `Option<NonZero<u32>>` and similar have a representation guarantee that
    132// they're the same size as the corresponding `u32` type, as well as a guarantee
    133// that transmuting between `NonZero<u32>` and `Option<NonZero<u32>>` works.
    134// While the documentation officially makes it UB to transmute from `None`,
    135// we're the standard library so we can make extra inferences, and we know that
    136// the only niche available to represent `None` is the one that's all zeros.
    137macro_rules! impl_is_zero_option_of_nonzero_int {
    138    ($($t:ty),+ $(,)?) => {$(
    139        unsafe impl IsZero for Option<NonZero<$t>> {
    140            #[inline]
    141            fn is_zero(&self) -> bool {
    142                self.is_none()
    143            }
    144        }
    145    )+};
    146}
    147
    148impl_is_zero_option_of_nonzero_int!(u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize);
    149
    150macro_rules! impl_is_zero_option_of_int {
    151    ($($t:ty),+ $(,)?) => {$(
    152        unsafe impl IsZero for Option<$t> {
    153            #[inline]
    154            fn is_zero(&self) -> bool {
    155                const {
    156                    let none: Self = unsafe { core::mem::MaybeUninit::zeroed().assume_init() };
    157                    assert!(none.is_none());
    158                }
    159                self.is_none()
    160            }
    161        }
    162    )+};
    163}
    164
    165impl_is_zero_option_of_int!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128, usize, isize);
    166
    167unsafe impl<T: IsZero> IsZero for Wrapping<T> {
    168    #[inline]
    169    fn is_zero(&self) -> bool {
    170        self.0.is_zero()
    171    }
    172}
    173
    174unsafe impl<T: IsZero> IsZero for Saturating<T> {
    175    #[inline]
    176    fn is_zero(&self) -> bool {
    177        self.0.is_zero()
    178    }
    179}
    180
    181macro_rules! impl_is_zero_option_of_bool {
    182    ($($t:ty),+ $(,)?) => {$(
    183        unsafe impl IsZero for $t {
    184            #[inline]
    185            fn is_zero(&self) -> bool {
    186                // SAFETY: This is *not* a stable layout guarantee, but
    187                // inside `core` we're allowed to rely on the current rustc
    188                // behavior that options of bools will be one byte with
    189                // no padding, so long as they're nested less than 254 deep.
    190                let raw: u8 = unsafe { core::mem::transmute(*self) };
    191                raw == 0
    192            }
    193        }
    194    )+};
    195}
    196
    197impl_is_zero_option_of_bool! {
    198    Option<bool>,
    199    Option<Option<bool>>,
    200    Option<Option<Option<bool>>>,
    201    // Could go further, but not worth the metadata overhead.
    202}