is_zero.rs 3.3 KB

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