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};
9use core::num::NonZero;
10
11#[cfg(feature = "musli")]
12use musli::{Decode, Encode};
13use serde::{Deserialize, Serialize};
14use twox_hash::XxHash64;
15
16/// Error raised when too many parameters are added to a [`ParametersBuilder`].
17///
18/// # Examples
19///
20/// ```
21/// use rune::TypeHash;
22/// use rune::hash::ParametersBuilder;
23///
24/// let mut params = ParametersBuilder::new();
25///
26/// let err = 'outer: {
27///     for _ in 0.. {
28///         params = match params.add(i64::HASH) {
29///             Ok(params) => params,
30///             Err(err) => break 'outer err,
31///         };
32///     };
33///
34///     panic!("expected error");
35/// };
36///
37/// let err = err.to_string();
38/// # Ok::<_, rune::hash::TooManyParameters>(())
39/// ```
40#[derive(Debug)]
41#[non_exhaustive]
42pub struct TooManyParameters;
43
44impl fmt::Display for TooManyParameters {
45    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46        write!(f, "Only 32 type parameters are supported")
47    }
48}
49
50impl core::error::Error for TooManyParameters {}
51
52use crate::alloc;
53use crate::alloc::clone::TryClone;
54
55const SEP: u64 = 0x4bc94d6bd06053ad;
56const TYPE: u64 = 0x2fac10b63a6cc57c;
57const ASSOCIATED_FUNCTION_HASH: u64 = 0x5ea77ffbcdf5f302;
58const OBJECT_KEYS: u64 = 0x4473d7017aef7645;
59const IDENT: u64 = 0x1a095090689d4647;
60const INDEX: u64 = 0xe1b2378d7a937035;
61// Salt for type parameters.
62const TYPE_PARAMETERS: u32 = 16;
63// Salt for function parameters.
64const FUNCTION_PARAMETERS: u32 = 48;
65// A bunch of random hashes to mix in when calculating type parameters.
66const PARAMETERS: [u64; 32] = [
67    0x2d859a05306ebe33,
68    0x75ac929aabdda742,
69    0x18f4e51cd6f60e86,
70    0x3b47f977015b002,
71    0xd7e3b9e36d59b900,
72    0xad75a1d63593d47c,
73    0x8fc37a65ac89ed71,
74    0x39eb9ab133d1cf22,
75    0xa287885efb6bf688,
76    0x9eeef0c7395ea8ca,
77    0x26a193328114c317,
78    0x9edc35591d044a28,
79    0xbfa4e9a8eca88b80,
80    0x94a626c6aa89a686,
81    0x95970296235c5b91,
82    0xa8ab16ceff9068b8,
83    0x153e675e2a27db85,
84    0xa873a7e51dfe4205,
85    0xde401d82396a7876,
86    0x9dbbae67606eddc3,
87    0x23d51f8018d09e74,
88    0xb5bfa5d588fedcc6,
89    0x9702480ba16eeb96,
90    0x58549fb39441505c,
91    0xd88078065e88667d,
92    0x38a1d4efb147fe18,
93    0xf712c95e9ffd1ba5,
94    0x73c2249b2845a5e0,
95    0x8079aff8b0833e20,
96    0x7e708fb5e906bbb5,
97    0x22d363b1d55a5eec,
98    0x9e2d56cbbd4459f1,
99];
100
101/// The primitive hash that among other things is used to reference items,
102/// types, and native functions.
103///
104/// # Examples
105///
106/// ```
107/// use rune::Hash;
108///
109/// assert!(Hash::index(0).as_non_empty().is_some());
110/// ```
111#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
112#[cfg_attr(feature = "musli", derive(Decode, Encode), musli(transparent))]
113#[repr(transparent)]
114pub struct NonZeroHash(#[doc(hidden)] pub NonZero<u64>);
115
116impl NonZeroHash {
117    /// Get the value as a hash.
118    ///
119    /// # Examples
120    ///
121    /// ```
122    /// use rune::Hash;
123    ///
124    /// let hash = Hash::index(0).as_non_empty().ok_or("expected non-empty")?;
125    /// assert_eq!(hash.get(), Hash::index(0));
126    /// # Ok::<_, &'static str>(())
127    /// ```
128    pub const fn get(self) -> Hash {
129        Hash(self.0.get())
130    }
131}
132
133impl TryClone for NonZeroHash {
134    #[inline]
135    fn try_clone(&self) -> alloc::Result<Self> {
136        Ok(*self)
137    }
138}
139
140impl fmt::Display for NonZeroHash {
141    #[inline]
142    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
143        write!(f, "0x{:x}", self.0.get())
144    }
145}
146
147impl fmt::Debug for NonZeroHash {
148    #[inline]
149    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
150        write!(f, "0x{:x}", self.0.get())
151    }
152}
153
154impl PartialEq<Hash> for NonZeroHash {
155    #[inline]
156    fn eq(&self, other: &Hash) -> bool {
157        self.0.get() == other.0
158    }
159}
160
161/// The primitive hash that among other things is used to reference items,
162/// types, and native functions.
163///
164/// # Examples
165///
166/// ```
167/// use rune::Hash;
168///
169/// assert_ne!(Hash::index(0), Hash::index(1));
170/// ```
171#[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
172#[cfg_attr(feature = "musli", derive(Decode, Encode), musli(transparent))]
173#[repr(transparent)]
174pub struct Hash(#[doc(hidden)] pub u64);
175
176impl Hash {
177    /// The empty hash.
178    ///
179    /// # Examples
180    ///
181    /// ```
182    /// use rune::Hash;
183    ///
184    /// assert_ne!(Hash::index(0), Hash::EMPTY);
185    /// assert!(Hash::EMPTY.as_non_empty().is_none());
186    /// ```
187    pub const EMPTY: Self = Self(0);
188
189    /// Construct a new raw hash.
190    #[doc(hidden)]
191    pub const fn new(hash: u64) -> Self {
192        Self(hash)
193    }
194
195    /// Construct a new raw hash with the given parameters.
196    #[doc(hidden)]
197    pub const fn new_with_type_parameters(hash: u64, parameters: Hash) -> Self {
198        Self(hash).with_type_parameters(parameters)
199    }
200
201    /// Return the current hash if it is non-empty.
202    ///
203    /// # Examples
204    ///
205    /// ```
206    /// use rune::Hash;
207    ///
208    /// assert!(Hash::index(0).as_non_empty().is_some());
209    /// assert!(Hash::EMPTY.as_non_empty().is_none());
210    /// ```
211    #[inline]
212    pub fn as_non_empty(&self) -> Option<NonZeroHash> {
213        Some(NonZeroHash(NonZero::new(self.0)?))
214    }
215
216    /// Coerce a hash into its inner numerical value.
217    #[doc(hidden)]
218    pub const fn into_inner(self) -> u64 {
219        self.0
220    }
221
222    /// Test if hash is empty.
223    #[doc(hidden)]
224    pub const fn is_empty(&self) -> bool {
225        self.0 == 0
226    }
227
228    /// Construct a hash from an index.
229    ///
230    /// # Examples
231    ///
232    /// ```
233    /// use rune::Hash;
234    ///
235    /// assert_ne!(Hash::index(0), Hash::index(1));
236    /// ```
237    #[inline]
238    pub fn index(index: usize) -> Self {
239        Self(INDEX ^ (index as u64))
240    }
241
242    /// Get the hash corresponding to a string identifier like `function` or
243    /// `hello_world`.
244    ///
245    /// # Examples
246    ///
247    /// ```
248    /// use rune::Hash;
249    ///
250    /// assert_ne!(Hash::ident("foo"), Hash::index(0));
251    /// ```
252    pub fn ident(name: &str) -> Hash {
253        let mut hasher = Self::new_hasher();
254        name.hash(&mut hasher);
255        Self(IDENT ^ hasher.finish())
256    }
257
258    /// Get the hash of a type.
259    ///
260    /// # Examples
261    ///
262    /// ```
263    /// use rune::Hash;
264    ///
265    /// assert!(!Hash::type_hash(["main"]).is_empty());
266    /// assert!(!Hash::type_hash(["main", "other"]).is_empty());
267    /// ```
268    pub fn type_hash(path: impl ToTypeHash) -> Self {
269        path.to_type_hash()
270    }
271
272    /// Construct a hash to an instance function, where the instance is a
273    /// pre-determined type.
274    ///
275    /// # Examples
276    ///
277    /// ```
278    /// use rune::Hash;
279    /// use rune::runtime::Protocol;
280    ///
281    /// assert!(!Hash::associated_function("main", &Protocol::INTO_TYPE_NAME).is_empty());
282    /// ```
283    #[inline]
284    pub fn associated_function(type_hash: impl IntoHash, name: impl IntoHash) -> Self {
285        let type_hash = type_hash.into_hash();
286        let name = name.into_hash();
287        Self(ASSOCIATED_FUNCTION_HASH ^ (type_hash.0 ^ name.0))
288    }
289
290    /// Construct a hash corresponding to a field function.
291    ///
292    /// # Examples
293    ///
294    /// ```
295    /// use rune::{Hash, TypeHash};
296    /// use rune::runtime::Protocol;
297    ///
298    /// assert!(!Hash::field_function(&Protocol::ADD, i64::HASH, "field").is_empty());
299    /// ```
300    #[inline]
301    pub fn field_function(protocol: impl IntoHash, type_hash: Hash, name: impl IntoHash) -> Self {
302        let protocol = protocol.into_hash();
303        Self::associated_function(Hash(type_hash.0 ^ protocol.0), name)
304    }
305
306    /// Construct an index function.
307    ///
308    /// # Examples
309    ///
310    /// ```
311    /// use rune::{Hash, TypeHash};
312    /// use rune::runtime::Protocol;
313    ///
314    /// assert!(!Hash::index_function(&Protocol::ADD, i64::HASH, Hash::index(1)).is_empty());
315    /// ```
316    #[inline]
317    pub fn index_function(protocol: impl IntoHash, type_hash: Hash, index: Hash) -> Self {
318        let protocol = protocol.into_hash();
319        Self::associated_function(Hash(type_hash.0 ^ protocol.0), index)
320    }
321
322    /// Get the hash corresponding to a static byte array.
323    pub fn static_bytes(bytes: &[u8]) -> Hash {
324        let mut hasher = Self::new_hasher();
325        bytes.hash(&mut hasher);
326        Self(hasher.finish())
327    }
328
329    /// Hash the given iterator of object keys.
330    pub fn object_keys<I>(keys: I) -> Self
331    where
332        I: IntoIterator<Item: AsRef<str>>,
333    {
334        let mut hasher = Self::new_hasher();
335        OBJECT_KEYS.hash(&mut hasher);
336
337        for key in keys {
338            SEP.hash(&mut hasher);
339            key.as_ref().hash(&mut hasher);
340        }
341
342        Self(hasher.finish())
343    }
344
345    /// Mix in generics hash.
346    ///
347    /// The generics hash must be a combination of the output from
348    /// `with_type_parameters` and `with_function_parameters`.
349    pub const fn with_generics(self, generics: Self) -> Self {
350        Self(self.0 ^ generics.0)
351    }
352
353    /// Mix the current hash with type parameters.
354    pub const fn with_type_parameters(self, ty: Self) -> Self {
355        Self(self.0 ^ ty.0.wrapping_shl(TYPE_PARAMETERS))
356    }
357
358    /// Mix the current hash with function parameters.
359    pub const fn with_function_parameters(self, f: Self) -> Self {
360        Self(self.0 ^ f.0.wrapping_shl(FUNCTION_PARAMETERS))
361    }
362
363    /// Hash type parameters.
364    pub const fn parameters<const N: usize>(params: [Hash; N]) -> Self {
365        let mut builder = ParametersBuilder::new();
366
367        while builder.index < N {
368            let param = params[builder.index];
369
370            let Ok(b) = builder.add(param) else {
371                panic!("Only up to 32 type parameters are supported");
372            };
373
374            builder = b;
375        }
376
377        builder.finish()
378    }
379
380    /// Construct a new hasher.
381    fn new_hasher() -> XxHash64 {
382        BuildHasherDefault::<XxHash64>::default().build_hasher()
383    }
384}
385
386impl TryClone for Hash {
387    #[inline]
388    fn try_clone(&self) -> alloc::Result<Self> {
389        Ok(*self)
390    }
391}
392
393impl fmt::Display for Hash {
394    #[inline]
395    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
396        write!(f, "0x{:x}", self.0)
397    }
398}
399
400impl fmt::Debug for Hash {
401    #[inline]
402    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
403        write!(f, "0x{:x}", self.0)
404    }
405}
406
407/// Helper to build a parameters hash.
408///
409/// A collection of parameters are like the type parameters like `String` and
410/// `i64` in a signature like:
411///
412/// `::my_crate::Map<String, i64>`
413///
414/// # Examples
415///
416/// ```
417/// use rune::TypeHash;
418/// use rune::hash::ParametersBuilder;
419///
420/// let mut params = ParametersBuilder::new();
421///
422/// let params = params.add(String::HASH)?;
423/// let params = params.add(i64::HASH)?;
424///
425/// let hash = params.finish();
426/// # Ok::<_, rune::hash::TooManyParameters>(())
427/// ```
428#[derive(Default)]
429pub struct ParametersBuilder {
430    base: u64,
431    index: usize,
432    shift: u32,
433}
434
435impl ParametersBuilder {
436    /// Construct a new collection of parameters.
437    ///
438    /// # Examples
439    ///
440    /// ```
441    /// use rune::{Hash, TypeHash};
442    /// use rune::hash::ParametersBuilder;
443    ///
444    /// let mut params = ParametersBuilder::new();
445    ///
446    /// let hash = params.finish();
447    /// assert_eq!(hash, Hash::EMPTY);
448    /// # Ok::<_, rune::hash::TooManyParameters>(())
449    /// ```
450    pub const fn new() -> Self {
451        Self {
452            base: 0,
453            index: 0,
454            shift: 0,
455        }
456    }
457
458    /// Add a hash to the collection of parameters.
459    ///
460    /// # Errors
461    ///
462    /// Errors if too many parameters are added.
463    ///
464    /// # Examples
465    ///
466    /// ```
467    /// use rune::TypeHash;
468    /// use rune::hash::ParametersBuilder;
469    ///
470    /// let mut params = ParametersBuilder::new();
471    ///
472    /// let params = params.add(String::HASH)?;
473    /// let params = params.add(i64::HASH)?;
474    ///
475    /// let hash = params.finish();
476    /// # Ok::<_, rune::hash::TooManyParameters>(())
477    /// ```
478    pub const fn add(mut self, Hash(hash): Hash) -> Result<Self, TooManyParameters> {
479        if self.index >= PARAMETERS.len() {
480            self.shift += 8;
481            self.index = 0;
482
483            if self.shift >= u64::BITS {
484                return Err(TooManyParameters);
485            }
486        }
487
488        self.base ^= hash ^ PARAMETERS[self.index].wrapping_shl(self.shift);
489        self.index += 1;
490        Ok(self)
491    }
492
493    /// Finish building the parameters hash.
494    ///
495    /// # Examples
496    ///
497    /// ```
498    /// use rune::TypeHash;
499    /// use rune::hash::ParametersBuilder;
500    ///
501    /// let mut params = ParametersBuilder::new();
502    ///
503    /// let params = params.add(String::HASH)?;
504    /// let params = params.add(i64::HASH)?;
505    ///
506    /// let hash = params.finish();
507    /// # Ok::<_, rune::hash::TooManyParameters>(())
508    /// ```
509    pub const fn finish(self) -> Hash {
510        Hash::new(self.base)
511    }
512}
513
514impl PartialEq<NonZeroHash> for Hash {
515    #[inline]
516    fn eq(&self, other: &NonZeroHash) -> bool {
517        self.0 == other.0.get()
518    }
519}