wgpu/
cmp.rs

1//! We need to impl `PartialEq`, `Eq`, `PartialOrd`, `Ord`, and `Hash` for all handle types in wgpu.
2//!
3//! For types that have some already-unique property, we can use that property to implement these traits.
4//!
5//! For types (like WebGPU) that don't have such a property, we generate an identifier and use that.
6
7#[cfg(supports_64bit_atomics)]
8pub use core::sync::atomic::AtomicU64;
9#[cfg(not(supports_64bit_atomics))]
10pub use portable_atomic::AtomicU64;
11
12use core::{num::NonZeroU64, sync::atomic::Ordering};
13
14static NEXT_ID: AtomicU64 = AtomicU64::new(1);
15
16#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
17pub struct Identifier {
18    inner: NonZeroU64,
19}
20
21impl Identifier {
22    pub fn create() -> Self {
23        let id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
24        // Safety: Will take 7000+ years of constant incrementing to overflow. It's fine.
25        let inner = unsafe { NonZeroU64::new_unchecked(id) };
26        Self { inner }
27    }
28}
29
30/// Implements `PartialEq`, `Eq`, `PartialOrd`, `Ord`, and `Hash` for a type by proxying the operations to a single field.
31///
32/// ```ignore
33/// impl_eq_ord_hash_proxy!(MyType => .field);
34/// ```
35macro_rules! impl_eq_ord_hash_proxy {
36    ($type:ty => $($access:tt)*) => {
37        impl PartialEq for $type {
38            fn eq(&self, other: &Self) -> bool {
39                self $($access)* == other $($access)*
40            }
41        }
42
43        impl Eq for $type {}
44
45        impl PartialOrd for $type {
46            fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
47                Some(self.cmp(other))
48            }
49        }
50
51        impl Ord for $type {
52            fn cmp(&self, other: &Self) -> core::cmp::Ordering {
53                self $($access)*.cmp(&other $($access)*)
54            }
55        }
56
57        impl core::hash::Hash for $type {
58            fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
59                self $($access)*.hash(state)
60            }
61        }
62    };
63}
64
65/// Implements `PartialEq`, `Eq`, `PartialOrd`, `Ord`, and `Hash` for a type by comparing the addresses of the `Arc`s.
66///
67/// ```ignore
68/// impl_eq_ord_hash_arc_address!(MyType => .field);
69/// ```
70#[cfg_attr(not(any(wgpu_core, custom)), expect(unused_macros))]
71macro_rules! impl_eq_ord_hash_arc_address {
72    ($type:ty => $($access:tt)*) => {
73        impl PartialEq for $type {
74            fn eq(&self, other: &Self) -> bool {
75                let address_left = alloc::sync::Arc::as_ptr(&self $($access)*);
76                let address_right = alloc::sync::Arc::as_ptr(&other $($access)*);
77
78                address_left == address_right
79            }
80        }
81
82        impl Eq for $type {}
83
84        impl PartialOrd for $type {
85            fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
86                Some(self.cmp(other))
87            }
88        }
89
90        impl Ord for $type {
91            fn cmp(&self, other: &Self) -> core::cmp::Ordering {
92                let address_left = alloc::sync::Arc::as_ptr(&self $($access)*);
93                let address_right = alloc::sync::Arc::as_ptr(&other $($access)*);
94
95                address_left.cmp(&address_right)
96            }
97        }
98
99        impl core::hash::Hash for $type {
100            fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
101                let address = alloc::sync::Arc::as_ptr(&self $($access)*);
102                address.hash(state)
103            }
104        }
105    };
106}
107
108#[cfg_attr(not(any(wgpu_core, custom)), expect(unused_imports))]
109pub(crate) use {impl_eq_ord_hash_arc_address, impl_eq_ord_hash_proxy};