1use std::collections::HashMap;
4use std::fmt;
5use std::hash::{BuildHasherDefault, Hasher};
6
7#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
13pub struct WidgetId(u64);
14
15impl WidgetId {
16 pub const ROOT: Self = Self(0xcbf2_9ce4_8422_2325);
18
19 pub(crate) fn child(self, key: &Key, type_name: &str) -> Self {
20 let mut hash = Fnv(self.0);
21 match key {
22 Key::Index(index) => {
23 hash.write(b"#");
24 hash.write(&index.to_le_bytes());
25 hash.write(type_name.as_bytes());
26 }
27 Key::Named(name) => {
28 hash.write(b"@");
29 hash.write(name.as_bytes());
30 }
31 }
32 Self(hash.0)
33 }
34}
35
36impl fmt::Debug for WidgetId {
37 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38 write!(f, "WidgetId({:016x})", self.0)
39 }
40}
41
42#[derive(Debug, Clone, PartialEq, Eq)]
44pub(crate) enum Key {
45 Index(usize),
46 Named(String),
47}
48
49pub(crate) type IdMap<K, V> = HashMap<K, V, BuildHasherDefault<IdHasher>>;
51
52#[derive(Debug, Default, Clone, Copy)]
57pub(crate) struct IdHasher(u64);
58
59impl IdHasher {
60 fn add(&mut self, value: u64) {
61 self.0 = (self.0.rotate_left(5) ^ value).wrapping_mul(0x517c_c1b7_2722_0a95);
62 }
63}
64
65impl Hasher for IdHasher {
66 fn finish(&self) -> u64 {
67 self.0
68 }
69
70 fn write(&mut self, bytes: &[u8]) {
71 for byte in bytes {
72 self.add(u64::from(*byte));
73 }
74 }
75
76 fn write_u16(&mut self, value: u16) {
77 self.add(u64::from(value));
78 }
79
80 fn write_u32(&mut self, value: u32) {
81 self.add(u64::from(value));
82 }
83
84 fn write_u64(&mut self, value: u64) {
85 self.add(value);
86 }
87
88 fn write_usize(&mut self, value: usize) {
89 self.add(value as u64);
90 }
91}
92
93struct Fnv(u64);
94
95impl Fnv {
96 fn write(&mut self, bytes: &[u8]) {
97 for byte in bytes {
98 self.0 ^= u64::from(*byte);
99 self.0 = self.0.wrapping_mul(0x0100_0000_01b3);
100 }
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107
108 #[test]
109 fn ids_depend_on_parent_key_and_type() {
110 let a = WidgetId::ROOT.child(&Key::Index(0), "Button");
111 assert_eq!(a, WidgetId::ROOT.child(&Key::Index(0), "Button"));
112 assert_ne!(a, WidgetId::ROOT.child(&Key::Index(1), "Button"));
113 assert_ne!(a, WidgetId::ROOT.child(&Key::Index(0), "Text"));
114 let named = WidgetId::ROOT.child(&Key::Named("save".into()), "Button");
115 assert_eq!(named, WidgetId::ROOT.child(&Key::Named("save".into()), "Text"));
116 assert_ne!(a.child(&Key::Index(0), "Text"), named.child(&Key::Index(0), "Text"));
117 }
118}