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>;