cdragon_hashes/
bin.rs

1//! Hashes used in PROP files (bin files)
2//!
3//! Bin files use 32-bit FNV-1a hashes for several identifier names.
4//!
5//! This module provides methods to compute these hashes.
6use super::{HashKind, HashMapper};
7
8/// Compute a bin hash from a string
9///
10/// The input string is assumed to be ASCII only.
11pub fn compute_binhash(s: &str) -> u32 {
12    s.to_ascii_lowercase().bytes()
13        .fold(0x811c9dc5_u32, |h, b| (h ^ b as u32).wrapping_mul(0x01000193))
14}
15
16/// Same as `compute_binhash()` but const
17///
18/// Implementation is less straightforward due to current limited support of const.
19pub const fn compute_binhash_const(s: &str) -> u32 {
20    let mut h = 0x811c9dc5_u32;
21    let bytes = s.as_bytes();
22    let mut i = 0usize;
23    while i < bytes.len() {
24        let b = bytes[i].to_ascii_lowercase();
25        h = (h ^ b as u32).wrapping_mul(0x01000193);
26        i += 1;
27    }
28    h
29}
30
31/// Get a bin hash, either parsed from hex, or computed from a string
32///
33/// A hex hash can be surrounded by braces (e.g. `{012345678}`).
34///
35/// This method can be used to get a hash, known or not, from a user.
36pub fn binhash_from_str(s: &str) -> u32 {
37    let hash = {
38        if s.len() == 8 {
39            u32::from_str_radix(s, 16).ok()
40        } else if s.len() == 10 && s.starts_with('{') & s.ends_with('}') {
41            u32::from_str_radix(&s[1..9], 16).ok()
42        } else {
43            None
44        }
45    };
46    hash.unwrap_or_else(|| compute_binhash(s))
47}
48
49
50/// Mapper for bin hashes
51pub type BinHashMapper = HashMapper<u32, 32>;
52
53/// Enum with a variant for each kind of bin hash
54#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
55pub enum BinHashKind {
56    /// Hash of an entry path (`BinEntryPath`)
57    EntryPath,
58    /// Hash of an class name, used by entries, structs and embeds (`BinClassName`)
59    ClassName,
60    /// Hash of a field name (`BinFieldName`)
61    FieldName,
62    /// Hash of a hash value (`BinHashValue`)
63    HashValue,
64}
65
66impl BinHashKind {
67    /// All kinds of bin hashes
68    pub const VARIANTS: [Self; 4] = [
69        Self::EntryPath,
70        Self::ClassName,
71        Self::FieldName,
72        Self::HashValue,
73    ];
74}
75
76impl From<BinHashKind> for HashKind {
77    fn from(val: BinHashKind) -> Self {
78        match val {
79            BinHashKind::EntryPath => HashKind::BinEntryPath,
80            BinHashKind::ClassName => HashKind::BinClassName,
81            BinHashKind::FieldName => HashKind::BinFieldName,
82            BinHashKind::HashValue => HashKind::BinHashValue,
83        }
84    }
85}
86
87/// Helper for const, inline computation of bin hashes, with implicit conversion
88#[macro_export]
89macro_rules! binh {
90    ($e:expr) => { $crate::bin::compute_binhash_const($e).into() };
91    ($t:ident, $e:literal) => { $t { hash: $crate::bin::compute_binhash_const($e) } };
92}
93