hydrate_base/
string_hash.rs

1use std::fmt;
2use std::hash::{Hash, Hasher};
3use std::sync::Arc;
4
5#[derive(Clone, Debug)]
6pub enum StringHashContents {
7    Static(&'static str),
8    Runtime(Arc<String>),
9    Unknown,
10}
11
12/// Store a hash and optionally the string used to create it. The string may not be available if:
13///  - The hash was created directly without a string
14///  - The `strip-stringhash-strings` feature is enabled in the crate
15///
16/// This allows for debugging ease normally but can be used in release to save memory and avoid
17/// leaking data in strings
18#[derive(Clone)]
19pub struct StringHash {
20    hash: u128,
21    // The contents are a debugging aid that may be stripped
22    #[cfg(not(feature = "strip-stringhash-strings"))]
23    contents: StringHashContents,
24}
25
26impl fmt::Debug for StringHash {
27    fn fmt(
28        &self,
29        f: &mut fmt::Formatter<'_>,
30    ) -> fmt::Result {
31        #[cfg(feature = "strip-stringhash-strings")]
32        {
33            f.debug_struct("StringHash")
34                .field("hash", &format!("{:0>32x}", self.hash))
35                .finish()
36        }
37
38        #[cfg(not(feature = "strip-stringhash-strings"))]
39        {
40            f.debug_struct("StringHash")
41                .field("hash", &format!("{:0>32x}", self.hash))
42                .field("contents", &self.contents)
43                .finish()
44        }
45    }
46}
47
48impl PartialEq for StringHash {
49    fn eq(
50        &self,
51        other: &Self,
52    ) -> bool {
53        self.hash.eq(&other.hash)
54    }
55}
56
57impl Eq for StringHash {}
58
59impl Hash for StringHash {
60    fn hash<H: Hasher>(
61        &self,
62        state: &mut H,
63    ) {
64        self.hash.hash(state);
65    }
66}
67
68impl StringHash {
69    pub const fn from_static_str(s: &'static str) -> Self {
70        let hash = if s.is_empty() {
71            0u128
72        } else {
73            const_fnv1a_hash::fnv1a_hash_str_128(s) | 1u128
74        };
75
76        #[cfg(not(feature = "strip-stringhash-strings"))]
77        let contents = StringHashContents::Static(s);
78
79        StringHash {
80            hash,
81            #[cfg(not(feature = "strip-stringhash-strings"))]
82            contents,
83        }
84    }
85
86    pub fn from_runtime_str(s: &str) -> Self {
87        let hash = if s.is_empty() {
88            0u128
89        } else {
90            const_fnv1a_hash::fnv1a_hash_str_128(s) | 1u128
91        };
92
93        #[cfg(not(feature = "strip-stringhash-strings"))]
94        let contents = StringHashContents::Runtime(Arc::new(s.to_string()));
95
96        StringHash {
97            hash,
98            #[cfg(not(feature = "strip-stringhash-strings"))]
99            contents,
100        }
101    }
102
103    pub fn from_hash(hash: u128) -> Self {
104        StringHash {
105            hash,
106            #[cfg(not(feature = "strip-stringhash-strings"))]
107            contents: StringHashContents::Unknown,
108        }
109    }
110
111    pub fn hash(&self) -> u128 {
112        self.hash
113    }
114
115    #[cfg(not(feature = "strip-stringhash-strings"))]
116    pub fn contents(&self) -> &StringHashContents {
117        &self.contents
118    }
119}