Skip to main content

dotzuki_engine/
hash.rs

1//! no_std hash-map aliases with an atomic-free hasher.
2//!
3//! `std::collections::HashMap` is unavailable on bare-metal targets
4//! (thumbv4t / GBA), so the engine uses `hashbrown` instead. hashbrown's
5//! default hasher (foldhash) relies on `core::sync::atomic::AtomicUsize`,
6//! which the armv4t target does not provide, so this module supplies a
7//! fixed-seed [FxHasher] instead. FxHash is not cryptographically secure —
8//! irrelevant here: the maps key on trusted engine identifiers (tile ids,
9//! palette ids, event-flag names), not user input.
10//!
11//! The aliases are API-compatible with `std::collections::{HashMap, HashSet}`
12//! for the engine's usage (`new`, `entry`, `get`, `insert`, `remove`,
13//! `iter`, `keys`, `values`, `retain`, `drain`, …). Determinism note: unlike
14//! std's `RandomState`, the seed is fixed, so iteration order is stable
15//! across runs — a property the save/serialization paths benefit from.
16
17use core::hash::{BuildHasher, Hasher};
18
19/// FxHash — the rustc hash. Small, fast, allocation- and atomic-free.
20#[derive(Clone, Default)]
21pub struct FxHasher {
22    hash: u64,
23}
24
25const FX_K: u64 = 0x517c_c1b7_2722_0a95;
26
27impl FxHasher {
28    #[inline]
29    fn add_to_hash(&mut self, i: u64) {
30        self.hash = (self.hash.rotate_left(5) ^ i).wrapping_mul(FX_K);
31    }
32}
33
34impl Hasher for FxHasher {
35    #[inline]
36    fn write(&mut self, bytes: &[u8]) {
37        for b in bytes {
38            self.add_to_hash(*b as u64);
39        }
40    }
41
42    #[inline]
43    fn write_u8(&mut self, i: u8) {
44        self.add_to_hash(i as u64);
45    }
46
47    #[inline]
48    fn write_u16(&mut self, i: u16) {
49        self.add_to_hash(i as u64);
50    }
51
52    #[inline]
53    fn write_u32(&mut self, i: u32) {
54        self.add_to_hash(i as u64);
55    }
56
57    #[inline]
58    fn write_u64(&mut self, i: u64) {
59        self.add_to_hash(i);
60    }
61
62    #[inline]
63    fn write_usize(&mut self, i: usize) {
64        self.add_to_hash(i as u64);
65    }
66
67    #[inline]
68    fn write_u128(&mut self, i: u128) {
69        self.add_to_hash(i as u64);
70        self.add_to_hash((i >> 64) as u64);
71    }
72
73    #[inline]
74    fn finish(&self) -> u64 {
75        self.hash
76    }
77}
78
79/// Const-constructible builder for [FxHasher].
80///
81/// A unit struct (rather than `BuildHasherDefault<FxHasher>`) so empty maps
82/// can be declared as `static`s — `HashMap::with_hasher(FxBuildHasher)` is a
83/// const expression, no lazy init or atomics needed.
84#[derive(Clone, Copy, Default)]
85pub struct FxBuildHasher;
86
87impl BuildHasher for FxBuildHasher {
88    type Hasher = FxHasher;
89
90    #[inline]
91    fn build_hasher(&self) -> FxHasher {
92        FxHasher { hash: 0 }
93    }
94}
95
96/// Drop-in replacement for `std::collections::HashMap`.
97pub type HashMap<K, V> = hashbrown::HashMap<K, V, FxBuildHasher>;
98
99/// Drop-in replacement for `std::collections::HashSet`.
100pub type HashSet<T> = hashbrown::HashSet<T, FxBuildHasher>;