Skip to main content

jsonld_core/utils/
hash.rs

1use foldhash::fast::FixedState;
2use std::hash::{BuildHasher, Hash, Hasher};
3
4/// Hashes a set of items, independently of their iteration order.
5///
6/// The standard library does not (yet) provide a `Hash` implementation
7/// for set types. This can be used instead.
8///
9/// Note that this function is not particularly strong and does
10/// not protect against `DoS` attacks.
11pub fn hash_set<S: IntoIterator, H: Hasher>(set: S, hasher: &mut H)
12where
13    S::Item: Hash,
14{
15    // See: https://github.com/rust-lang/rust/pull/48366
16    // Elements must be combined with an associative and commutative operation •.
17    // (u64, •, 0) must form a commutative monoid.
18    // This is satisfied by • = u64::wrapping_add.
19    //
20    // The inner hasher must be deterministic across calls so equal sets
21    // produce equal hashes; foldhash's FixedState gives a fixed-seed builder.
22    // `BuildHasher::hash_one` (stable since 1.71) does the same work without
23    // materializing a fresh hasher per item via `build_hasher()`.
24    let inner = FixedState::default();
25    let mut hash = 0u64;
26    for item in set {
27        hash = hash.wrapping_add(inner.hash_one(item));
28    }
29
30    hasher.write_u64(hash);
31}
32
33/// Hash an optional set of items.
34pub fn hash_set_opt<S: IntoIterator, H: Hasher>(set_opt: Option<S>, hasher: &mut H)
35where
36    S::Item: Hash,
37{
38    if let Some(set) = set_opt {
39        hash_set(set, hasher);
40    }
41}
42
43/// Hashes a map's entries, independently of their iteration order.
44///
45/// The standard library does not (yet) provide a `Hash` implementation
46/// for unordered map types. This can be used instead.
47///
48/// Note that this function is not particularly strong and does
49/// not protect against `DoS` attacks.
50pub fn hash_map<'a, K: 'a + Hash, V: 'a + Hash, H: Hasher>(map: impl 'a + IntoIterator<Item = (&'a K, &'a V)>, hasher: &mut H) {
51    let inner = FixedState::default();
52    let mut hash = 0u64;
53    for entry in map {
54        hash = hash.wrapping_add(inner.hash_one(entry));
55    }
56
57    hasher.write_u64(hash);
58}