ergot_base/
nash.rs

1use core::num::NonZeroU32;
2
3use const_fnv1a_hash::fnv1a_hash_32;
4
5#[cfg_attr(feature = "defmt-v1", derive(defmt::Format))]
6#[derive(Debug, PartialEq, Clone, Copy)]
7#[repr(transparent)]
8pub struct NameHash {
9    hash: NonZeroU32,
10}
11
12impl NameHash {
13    pub const fn new(s: &str) -> Self {
14        // ensure that an empty string does NOT hash to zero
15        let _: () = const {
16            assert!(0 != fnv1a_hash_32(&[], None));
17        };
18
19        let hash32 = fnv1a_hash_32(s.as_bytes(), None);
20        match NonZeroU32::new(hash32) {
21            Some(hash) => Self { hash },
22            None => {
23                // If the hash comes to zero, instead use the len + first three characters
24                //
25                // We know the len is non-zero because we checked that an empty string above
26                // hashes to a non-zero value.
27                let len = if s.len() > 255 { 255 } else { s.len() } as u8;
28                let mut bytes = [len, 0, 0, 0];
29
30                // Awkward because const
31                match s.as_bytes() {
32                    [] => unreachable!(),
33                    [one] => {
34                        bytes[1] = *one;
35                        bytes[2] = *one;
36                        bytes[3] = *one;
37                    }
38                    [one, two] => {
39                        bytes[1] = *one;
40                        bytes[2] = *two;
41                        bytes[3] = *one;
42                    }
43                    [one, two, three] => {
44                        bytes[1] = *one;
45                        bytes[2] = *two;
46                        bytes[3] = *three;
47                    }
48                    [one, two, three, ..] => {
49                        bytes[1] = *one;
50                        bytes[2] = *two;
51                        bytes[3] = *three;
52                    }
53                }
54                let val = u32::from_le_bytes(bytes);
55
56                // Safety:
57                //
58                // We know the len is nonzero, which means at least one byte has nonzero
59                // contents.
60                let hash = unsafe { NonZeroU32::new_unchecked(val) };
61                Self { hash }
62            }
63        }
64    }
65
66    pub fn to_u32(&self) -> u32 {
67        self.hash.into()
68    }
69
70    pub fn from_u32(val: u32) -> Option<Self> {
71        let nz = NonZeroU32::new(val)?;
72        Some(Self { hash: nz })
73    }
74}
75
76#[cfg(test)]
77mod test {
78    use crate::nash::NameHash;
79
80    #[test]
81    fn transparent_works() {
82        assert_eq!(size_of::<NameHash>(), 4);
83        assert_eq!(size_of::<Option<NameHash>>(), 4);
84    }
85}