rune_core/
hash.rs

1pub use self::into_hash::IntoHash;
2mod into_hash;
3
4pub use self::to_type_hash::ToTypeHash;
5mod to_type_hash;
6
7use core::fmt;
8use core::hash::{BuildHasher, BuildHasherDefault, Hash as _, Hasher};
9
10#[cfg(feature = "musli")]
11use musli::{Decode, Encode};
12use serde::{Deserialize, Serialize};
13use twox_hash::XxHash64;
14
15#[derive(Debug)]
16#[non_exhaustive]
17pub struct TooManyParameters;
18
19impl fmt::Display for TooManyParameters {
20    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21        write!(f, "Only 32 type parameters are supported")
22    }
23}
24
25impl core::error::Error for TooManyParameters {}
26
27use crate::alloc;
28use crate::alloc::clone::TryClone;
29
30const SEP: u64 = 0x4bc94d6bd06053ad;
31const TYPE: u64 = 0x2fac10b63a6cc57c;
32const ASSOCIATED_FUNCTION_HASH: u64 = 0x5ea77ffbcdf5f302;
33const OBJECT_KEYS: u64 = 0x4473d7017aef7645;
34const IDENT: u64 = 0x1a095090689d4647;
35const INDEX: u64 = 0xe1b2378d7a937035;
36// Salt for type parameters.
37const TYPE_PARAMETERS: u32 = 16;
38// Salt for function parameters.
39const FUNCTION_PARAMETERS: u32 = 48;
40// A bunch of random hashes to mix in when calculating type parameters.
41const PARAMETERS: [u64; 32] = [
42    0x2d859a05306ebe33,
43    0x75ac929aabdda742,
44    0x18f4e51cd6f60e86,
45    0x3b47f977015b002,
46    0xd7e3b9e36d59b900,
47    0xad75a1d63593d47c,
48    0x8fc37a65ac89ed71,
49    0x39eb9ab133d1cf22,
50    0xa287885efb6bf688,
51    0x9eeef0c7395ea8ca,
52    0x26a193328114c317,
53    0x9edc35591d044a28,
54    0xbfa4e9a8eca88b80,
55    0x94a626c6aa89a686,
56    0x95970296235c5b91,
57    0xa8ab16ceff9068b8,
58    0x153e675e2a27db85,
59    0xa873a7e51dfe4205,
60    0xde401d82396a7876,
61    0x9dbbae67606eddc3,
62    0x23d51f8018d09e74,
63    0xb5bfa5d588fedcc6,
64    0x9702480ba16eeb96,
65    0x58549fb39441505c,
66    0xd88078065e88667d,
67    0x38a1d4efb147fe18,
68    0xf712c95e9ffd1ba5,
69    0x73c2249b2845a5e0,
70    0x8079aff8b0833e20,
71    0x7e708fb5e906bbb5,
72    0x22d363b1d55a5eec,
73    0x9e2d56cbbd4459f1,
74];
75
76/// The primitive hash that among other things is used to reference items,
77/// types, and native functions.
78#[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
79#[cfg_attr(feature = "musli", derive(Decode, Encode), musli(transparent))]
80#[repr(transparent)]
81pub struct Hash(#[doc(hidden)] pub u64);
82
83impl Hash {
84    /// The empty hash.
85    pub const EMPTY: Self = Self(0);
86
87    /// Construct a new raw hash.
88    #[doc(hidden)]
89    pub const fn new(hash: u64) -> Self {
90        Self(hash)
91    }
92
93    /// Construct a new raw hash with the given parameters.
94    #[doc(hidden)]
95    pub const fn new_with_type_parameters(hash: u64, parameters: Hash) -> Self {
96        Self(hash).with_type_parameters(parameters)
97    }
98
99    /// Return the current hash if it is non-empty.
100    #[inline]
101    pub fn as_non_empty(&self) -> Option<Self> {
102        if self.is_empty() {
103            None
104        } else {
105            Some(*self)
106        }
107    }
108
109    /// Coerce a hash into its inner numerical value.
110    #[doc(hidden)]
111    pub const fn into_inner(self) -> u64 {
112        self.0
113    }
114
115    /// Test if hash is empty.
116    #[doc(hidden)]
117    pub const fn is_empty(&self) -> bool {
118        self.0 == 0
119    }
120
121    /// Construct a hash from an index.
122    #[inline]
123    pub fn index(index: usize) -> Self {
124        Self(INDEX ^ (index as u64))
125    }
126
127    /// Get the hash corresponding to a string identifier like `function` or
128    /// `hello_world`.
129    pub fn ident(name: &str) -> Hash {
130        let mut hasher = Self::new_hasher();
131        name.hash(&mut hasher);
132        Self(IDENT ^ hasher.finish())
133    }
134
135    /// Get the hash of a type.
136    pub fn type_hash(path: impl ToTypeHash) -> Self {
137        path.to_type_hash()
138    }
139
140    /// Construct a hash to an instance function, where the instance is a
141    /// pre-determined type.
142    #[inline]
143    pub fn associated_function(type_hash: impl IntoHash, name: impl IntoHash) -> Self {
144        let type_hash = type_hash.into_hash();
145        let name = name.into_hash();
146        Self(ASSOCIATED_FUNCTION_HASH ^ (type_hash.0 ^ name.0))
147    }
148
149    /// Construct a hash corresponding to a field function.
150    #[inline]
151    pub fn field_function(protocol: impl IntoHash, type_hash: Hash, name: impl IntoHash) -> Self {
152        let protocol = protocol.into_hash();
153        Self::associated_function(Hash(type_hash.0 ^ protocol.0), name)
154    }
155
156    /// Construct an index function.
157    #[inline]
158    pub fn index_function(protocol: impl IntoHash, type_hash: Hash, index: Hash) -> Self {
159        let protocol = protocol.into_hash();
160        Self::associated_function(Hash(type_hash.0 ^ protocol.0), index)
161    }
162
163    /// Get the hash corresponding to a static byte array.
164    pub fn static_bytes(bytes: &[u8]) -> Hash {
165        let mut hasher = Self::new_hasher();
166        bytes.hash(&mut hasher);
167        Self(hasher.finish())
168    }
169
170    /// Hash the given iterator of object keys.
171    pub fn object_keys<I>(keys: I) -> Self
172    where
173        I: IntoIterator,
174        I::Item: AsRef<str>,
175    {
176        let mut hasher = Self::new_hasher();
177        OBJECT_KEYS.hash(&mut hasher);
178
179        for key in keys {
180            SEP.hash(&mut hasher);
181            key.as_ref().hash(&mut hasher);
182        }
183
184        Self(hasher.finish())
185    }
186
187    /// Mix in generics hash.
188    ///
189    /// The generics hash must be a combination of the output from
190    /// `with_type_parameters` and `with_function_parameters`.
191    pub const fn with_generics(self, generics: Self) -> Self {
192        Self(self.0 ^ generics.0)
193    }
194
195    /// Mix the current hash with type parameters.
196    pub const fn with_type_parameters(self, ty: Self) -> Self {
197        Self(self.0 ^ ty.0.wrapping_shl(TYPE_PARAMETERS))
198    }
199
200    /// Mix the current hash with function parameters.
201    pub const fn with_function_parameters(self, f: Self) -> Self {
202        Self(self.0 ^ f.0.wrapping_shl(FUNCTION_PARAMETERS))
203    }
204
205    /// Hash type parameters.
206    pub const fn parameters<const N: usize>(params: [Hash; N]) -> Self {
207        let mut builder = ParametersBuilder::new();
208
209        while builder.index < N {
210            let param = params[builder.index];
211
212            let Ok(b) = builder.add(param) else {
213                panic!("Only up to 32 type parameters are supported");
214            };
215
216            builder = b;
217        }
218
219        builder.finish()
220    }
221
222    /// Construct a new hasher.
223    fn new_hasher() -> XxHash64 {
224        BuildHasherDefault::<XxHash64>::default().build_hasher()
225    }
226}
227
228impl TryClone for Hash {
229    fn try_clone(&self) -> alloc::Result<Self> {
230        Ok(*self)
231    }
232}
233
234impl fmt::Display for Hash {
235    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
236        write!(f, "0x{:x}", self.0)
237    }
238}
239
240impl fmt::Debug for Hash {
241    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
242        write!(f, "0x{:x}", self.0)
243    }
244}
245
246/// Helper to build a parameters hash.
247///
248/// A collection of parameters are like the type parameters like `String` and
249/// `i64` in a signature like:
250///
251/// `::my_crate::Map<String, i64>`
252///
253/// # Examples
254///
255/// ```
256/// use rune::TypeHash;
257/// use rune::hash::ParametersBuilder;
258///
259/// let mut params = ParametersBuilder::new();
260///
261/// let params = params.add(String::HASH)?;
262/// let params = params.add(i64::HASH)?;
263///
264/// let hash = params.finish();
265/// # Ok::<_, rune::hash::TooManyParameters>(())
266/// ```
267#[derive(Default)]
268pub struct ParametersBuilder {
269    base: u64,
270    index: usize,
271    shift: u32,
272}
273
274impl ParametersBuilder {
275    /// Construct a new collection of parameters.
276    pub const fn new() -> Self {
277        Self {
278            base: 0,
279            index: 0,
280            shift: 0,
281        }
282    }
283
284    /// Add a hash to the collection of parameters.
285    ///
286    /// # Errors
287    ///
288    /// Errors if too many parameters are added.
289    pub const fn add(mut self, Hash(hash): Hash) -> Result<Self, TooManyParameters> {
290        if self.index >= PARAMETERS.len() {
291            self.shift += 8;
292            self.index = 0;
293
294            if self.shift >= u64::BITS {
295                return Err(TooManyParameters);
296            }
297        }
298
299        self.base ^= hash ^ PARAMETERS[self.index].wrapping_shl(self.shift);
300        self.index += 1;
301        Ok(self)
302    }
303
304    /// Finish building the parameters hash.
305    pub const fn finish(self) -> Hash {
306        Hash::new(self.base)
307    }
308}