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}