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); |
84 | |