|
| 1 | +use crate::num::{NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize}; |
| 2 | +use crate::num::{NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize}; |
| 3 | + |
| 4 | +/// Types where `==` & `!=` are equivalent to comparing their underlying bytes. |
| 5 | +/// |
| 6 | +/// Importantly, this means no floating-point types, as those have different |
| 7 | +/// byte representations (like `-0` and `+0`) which compare as the same. |
| 8 | +/// Since byte arrays are `Eq`, that implies that these types are probably also |
| 9 | +/// `Eq`, but that's not technically required to use this trait. |
| 10 | +/// |
| 11 | +/// `Rhs` is *de facto* always `Self`, but the separate parameter is important |
| 12 | +/// to avoid the `specializing impl repeats parameter` error when consuming this. |
| 13 | +/// |
| 14 | +/// # Safety |
| 15 | +/// |
| 16 | +/// - `Self` and `Rhs` have no padding. |
| 17 | +/// - `Self` and `Rhs` have the same layout (size and alignment). |
| 18 | +/// - Neither `Self` nor `Rhs` have provenance, so integer comparisons are correct. |
| 19 | +/// - `<Self as PartialEq<Rhs>>::{eq,ne}` are equivalent to comparing the bytes. |
| 20 | +#[rustc_specialization_trait] |
| 21 | +pub(crate) unsafe trait BytewiseEq<Rhs = Self>: PartialEq<Rhs> + Sized {} |
| 22 | + |
| 23 | +macro_rules! is_bytewise_comparable { |
| 24 | + ($($t:ty),+ $(,)?) => {$( |
| 25 | + unsafe impl BytewiseEq for $t {} |
| 26 | + )+}; |
| 27 | +} |
| 28 | + |
| 29 | +// SAFETY: All the ordinary integer types have no padding, and are not pointers. |
| 30 | +is_bytewise_comparable!(u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize); |
| 31 | + |
| 32 | +// SAFETY: These have *niches*, but no *padding* and no *provenance*, |
| 33 | +// so we can compare them directly. |
| 34 | +is_bytewise_comparable!(bool, char, super::Ordering); |
| 35 | + |
| 36 | +// SAFETY: Similarly, the non-zero types have a niche, but no undef and no pointers, |
| 37 | +// and they compare like their underlying numeric type. |
| 38 | +is_bytewise_comparable!( |
| 39 | + NonZeroU8, |
| 40 | + NonZeroU16, |
| 41 | + NonZeroU32, |
| 42 | + NonZeroU64, |
| 43 | + NonZeroU128, |
| 44 | + NonZeroUsize, |
| 45 | + NonZeroI8, |
| 46 | + NonZeroI16, |
| 47 | + NonZeroI32, |
| 48 | + NonZeroI64, |
| 49 | + NonZeroI128, |
| 50 | + NonZeroIsize, |
| 51 | +); |
| 52 | + |
| 53 | +// SAFETY: The NonZero types have the "null" optimization guaranteed, and thus |
| 54 | +// are also safe to equality-compare bitwise inside an `Option`. |
| 55 | +// The way `PartialOrd` is defined for `Option` means that this wouldn't work |
| 56 | +// for `<` or `>` on the signed types, but since we only do `==` it's fine. |
| 57 | +is_bytewise_comparable!( |
| 58 | + Option<NonZeroU8>, |
| 59 | + Option<NonZeroU16>, |
| 60 | + Option<NonZeroU32>, |
| 61 | + Option<NonZeroU64>, |
| 62 | + Option<NonZeroU128>, |
| 63 | + Option<NonZeroUsize>, |
| 64 | + Option<NonZeroI8>, |
| 65 | + Option<NonZeroI16>, |
| 66 | + Option<NonZeroI32>, |
| 67 | + Option<NonZeroI64>, |
| 68 | + Option<NonZeroI128>, |
| 69 | + Option<NonZeroIsize>, |
| 70 | +); |
| 71 | + |
| 72 | +macro_rules! is_bytewise_comparable_array_length { |
| 73 | + ($($n:literal),+ $(,)?) => {$( |
| 74 | + // SAFETY: Arrays have no padding between elements, so if the elements are |
| 75 | + // `BytewiseEq`, then the whole array can be too. |
| 76 | + unsafe impl<T: BytewiseEq<U>, U> BytewiseEq<[U; $n]> for [T; $n] {} |
| 77 | + )+}; |
| 78 | +} |
| 79 | + |
| 80 | +// Frustratingly, this can't be made const-generic as it gets |
| 81 | +// error: specializing impl repeats parameter `N` |
| 82 | +// so just do it for a couple of plausibly-common ones. |
| 83 | +is_bytewise_comparable_array_length!(0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64); |
0 commit comments