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}