1use std::{
4 num::NonZeroU64,
5 sync::atomic::{AtomicU64, Ordering},
6};
7
8static NEXT_NODE_ID: AtomicU64 = AtomicU64::new(1);
9
10#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
14pub struct NodeId(pub(crate) NonZeroU64);
15
16impl From<NodeId> for u64 {
17 fn from(id: NodeId) -> u64 {
18 id.0.get()
19 }
20}
21
22impl From<NodeId> for accesskit::NodeId {
23 fn from(value: NodeId) -> Self {
24 Self(value.0.get())
25 }
26}
27
28#[macro_export]
34macro_rules! id {
35 () => {
36 const { NodeId::__internal_new(file!(), line!(), column!()) }
37 };
38 ($($x:expr),+ $(,)?) => {{
39 let mut id = const { NodeId::__internal_new(file!(), line!(), column!()) };
40 $(
41 id = id.__internal_mix(($x).into());
42 )+
43 id
44 }};
45}
46
47impl Default for NodeId {
48 fn default() -> Self {
49 Self::next()
50 }
51}
52
53impl NodeId {
54 pub(crate) const FNV_OFFSET_BASIS: u64 = 0xcbf29ce484222325;
55 pub(crate) const FNV_PRIME: u64 = 0x100000001b3;
56
57 #[doc(hidden)]
58 pub const fn __internal_new(file: &'static str, line: u32, column: u32) -> Self {
59 const fn fnv1a_64(seed: Option<u64>, bytes: &[u8]) -> u64 {
60 let mut hash = if let Some(seed) = seed { seed } else { NodeId::FNV_OFFSET_BASIS };
61
62 let mut i = 0;
63 while i < bytes.len() {
64 hash ^= bytes[i] as u64;
65 hash = hash.wrapping_mul(NodeId::FNV_PRIME);
66 i += 1;
67 }
68
69 hash
70 }
71
72 let h1 = fnv1a_64(None, file.as_bytes());
73 let h2 = fnv1a_64(Some(h1), &line.to_le_bytes());
74 let h3 = fnv1a_64(Some(h2), &column.to_le_bytes());
75
76 let non_zero = match NonZeroU64::new(h3) {
77 Some(val) => val,
78 None => NonZeroU64::MAX,
79 };
80
81 NodeId(non_zero)
82 }
83
84 #[doc(hidden)]
85 pub const fn __internal_mix(self, rhs: u64) -> Self {
86 let mixed = (self.0.get() ^ rhs).wrapping_mul(NodeId::FNV_PRIME);
87 let non_zero = match NonZeroU64::new(mixed) {
88 Some(val) => val,
89 None => NonZeroU64::MAX,
90 };
91 NodeId(non_zero)
92 }
93
94 pub fn next() -> Self {
98 Self(NonZeroU64::new(NEXT_NODE_ID.fetch_add(1, Ordering::Relaxed)).unwrap_or(NonZeroU64::MAX))
100 }
101
102 pub fn set_counter(value: u64) {
108 assert!(value != 0, "NodeId counter cannot start at zero");
109 NEXT_NODE_ID.store(value, Ordering::SeqCst);
110 }
111
112 pub fn get_counter() -> u64 {
115 NEXT_NODE_ID.load(Ordering::SeqCst)
116 }
117}