Skip to main content

ferroui_base/controls/
resource_key.rs

1use crate::TypeInfo;
2use std::any::Any;
3use std::fmt;
4use std::hash::{BuildHasherDefault, Hash, Hasher};
5use std::rc::Rc;
6
7/// A value usable as a resource key: anything with value equality and a hash.
8///
9/// Implemented for every `Eq + Hash + 'static` type.
10pub trait ResourceKeyObject: Any {
11    fn as_any(&self) -> &dyn Any;
12    fn dyn_eq(&self, other: &dyn ResourceKeyObject) -> bool;
13    fn dyn_hash(&self, state: &mut dyn Hasher);
14    fn key_type_name(&self) -> &'static str;
15}
16
17impl<T: Eq + Hash + 'static> ResourceKeyObject for T {
18    fn as_any(&self) -> &dyn Any {
19        self
20    }
21
22    fn dyn_eq(&self, other: &dyn ResourceKeyObject) -> bool {
23        other.as_any().downcast_ref::<T>().is_some_and(|other| self == other)
24    }
25
26    fn dyn_hash(&self, mut state: &mut dyn Hasher) {
27        self.hash(&mut state)
28    }
29
30    fn key_type_name(&self) -> &'static str {
31        std::any::type_name::<T>()
32    }
33}
34
35/// The key of a resource in a resource dictionary.
36///
37/// The object model this library mirrors uses arbitrary objects with value
38/// equality as keys. The keys used in practice are strings and types (a
39/// control theme is keyed by the type it targets), which get their own cheap
40/// variants; any other hashable value can be used through
41/// [`ResourceKey::object`].
42#[derive(Clone)]
43pub enum ResourceKey {
44    /// A string key.
45    String(Rc<str>),
46    /// A type key.
47    Type(&'static TypeInfo),
48    /// Any other key.
49    Object(Rc<dyn ResourceKeyObject>),
50}
51
52impl ResourceKey {
53    /// Creates a key from any hashable value.
54    pub fn object<T: Eq + Hash + 'static>(value: T) -> Self {
55        ResourceKey::Object(Rc::new(value))
56    }
57
58    /// The key as a string, if it is one.
59    pub fn as_str(&self) -> Option<&str> {
60        match self {
61            ResourceKey::String(s) => Some(s),
62            _ => None,
63        }
64    }
65}
66
67impl PartialEq for ResourceKey {
68    fn eq(&self, other: &Self) -> bool {
69        match (self, other) {
70            (ResourceKey::String(a), ResourceKey::String(b)) => a == b,
71            (ResourceKey::Type(a), ResourceKey::Type(b)) => std::ptr::eq(*a, *b),
72            (ResourceKey::Object(a), ResourceKey::Object(b)) => (**a).dyn_eq(&**b),
73            _ => false,
74        }
75    }
76}
77
78impl Eq for ResourceKey {}
79
80impl Hash for ResourceKey {
81    fn hash<H: Hasher>(&self, state: &mut H) {
82        match self {
83            ResourceKey::String(s) => {
84                state.write_u8(0);
85                s.hash(state)
86            }
87            ResourceKey::Type(t) => {
88                state.write_u8(1);
89                t.hash(state)
90            }
91            ResourceKey::Object(o) => {
92                state.write_u8(2);
93                (**o).dyn_hash(state)
94            }
95        }
96    }
97}
98
99impl fmt::Debug for ResourceKey {
100    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101        fmt::Display::fmt(self, f)
102    }
103}
104
105impl fmt::Display for ResourceKey {
106    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
107        match self {
108            ResourceKey::String(s) => f.write_str(s),
109            ResourceKey::Type(t) => f.write_str(t.name()),
110            ResourceKey::Object(o) => f.write_str((**o).key_type_name()),
111        }
112    }
113}
114
115impl From<&str> for ResourceKey {
116    fn from(value: &str) -> Self {
117        ResourceKey::String(value.into())
118    }
119}
120
121impl From<String> for ResourceKey {
122    fn from(value: String) -> Self {
123        ResourceKey::String(value.into())
124    }
125}
126
127impl From<&'static TypeInfo> for ResourceKey {
128    fn from(value: &'static TypeInfo) -> Self {
129        ResourceKey::Type(value)
130    }
131}
132
133impl From<&ResourceKey> for ResourceKey {
134    fn from(value: &ResourceKey) -> Self {
135        value.clone()
136    }
137}
138
139/// The hasher of the tables keyed by [`ResourceKey`].
140///
141/// A resource lookup probes the table of every dictionary on the way to the
142/// root, and the keys are short: a type, or the name of a resource. The
143/// default hasher of the standard library is built to withstand keys chosen
144/// by an attacker, which the keys of a resource dictionary are not (they are
145/// written in the markup and the code of the application), and costs more
146/// per key than the probe itself. This one takes a word at a time: it
147/// multiplies, and folds the upper half of the product into the lower one,
148/// so that every bit of a word reaches both the bits the table takes its
149/// bucket from and the ones it tags an entry with. The same keys are equal
150/// and are found as before: only the placement in the table differs.
151#[derive(Clone, Copy, Default)]
152pub(crate) struct ResourceKeyHasher {
153    hash: u64,
154}
155
156/// Builds the [`ResourceKeyHasher`] of a table.
157pub(crate) type ResourceKeyBuildHasher = BuildHasherDefault<ResourceKeyHasher>;
158
159impl ResourceKeyHasher {
160    const MULTIPLIER: u64 = 0x51_7c_c1_b7_27_22_0a_95;
161
162    #[inline]
163    fn add(&mut self, word: u64) {
164        let product = (self.hash ^ word).wrapping_mul(Self::MULTIPLIER);
165        self.hash = product ^ (product >> 32);
166    }
167}
168
169impl Hasher for ResourceKeyHasher {
170    #[inline]
171    fn finish(&self) -> u64 {
172        self.hash
173    }
174
175    #[inline]
176    fn write(&mut self, bytes: &[u8]) {
177        let mut chunks = bytes.chunks_exact(8);
178        for chunk in &mut chunks {
179            let mut word = [0u8; 8];
180            word.copy_from_slice(chunk);
181            self.add(u64::from_le_bytes(word));
182        }
183        let rest = chunks.remainder();
184        if !rest.is_empty() {
185            // At most seven bytes are left: the last byte of the word takes
186            // their count, so that a zero byte at the end is not lost.
187            let mut word = [0u8; 8];
188            word[..rest.len()].copy_from_slice(rest);
189            word[7] = rest.len() as u8;
190            self.add(u64::from_le_bytes(word));
191        }
192    }
193
194    #[inline]
195    fn write_u8(&mut self, i: u8) {
196        self.add(u64::from(i));
197    }
198
199    #[inline]
200    fn write_u16(&mut self, i: u16) {
201        self.add(u64::from(i));
202    }
203
204    #[inline]
205    fn write_u32(&mut self, i: u32) {
206        self.add(u64::from(i));
207    }
208
209    #[inline]
210    fn write_u64(&mut self, i: u64) {
211        self.add(i);
212    }
213
214    #[inline]
215    fn write_usize(&mut self, i: usize) {
216        self.add(i as u64);
217    }
218}