deterministic_hash/
lib.rs

1//! Tiny Rust library to create deterministic hashes regardless of architecture. This library is `no-std` compatible and uses no allocations or dependencies.
2//!
3//! The default `core::hash::Hasher` implementation ensures a platform dependant hashing of datastructures that use `#[derive(Hash)]`. Most notably by:
4//! * using `to_ne_bytes` for `u{8,16,32,64,128}`.
5//! * using the native bytelength of `usize`.
6//!
7//! The `DeterministicHasher` of this library forces the use of `to_le_bytes` and casts `usize` to `u64` regardless of your platform. Hence the hasher will be less efficient, but will be deterministic when using the same library in different architecture contexts. I use a common dataprotocol library both on ARM embedded systems, wasm and x64.
8//!
9//! From any hasher make it deterministic by inserting `DeterministicHasher` in between:
10//! ```
11//! let hasher = crc::crc32::Digest::new(crc::crc32::KOOPMAN);
12//! let hasher = deterministic_hash::DeterministicHasher::new(hasher);
13//! ```
14
15#![no_std]
16use core::hash::Hasher;
17
18/// Wrapper around any hasher to make it deterministic.
19///
20/// ```
21/// use core::hash::Hash;
22/// use crc::crc32::Hasher32;
23/// use deterministic_hash::DeterministicHasher;
24/// let mut hasher = DeterministicHasher::new(crc::crc32::Digest::new(crc::crc32::KOOPMAN));
25/// (0x1337 as usize).hash(&mut hasher);
26/// assert_eq!(hasher.as_inner().sum32(), 2482448842);
27/// ```
28#[derive(Default, Clone, Debug)]
29pub struct DeterministicHasher<T: Hasher>(T);
30
31impl<T: Hasher> DeterministicHasher<T> {
32    pub fn new(inner: T) -> Self {
33        Self(inner)
34    }
35
36    pub fn as_inner(&self) -> &T {
37        &self.0
38    }
39
40    pub fn into_inner(self) -> T {
41        self.0
42    }
43}
44
45/// Implementation of hasher that forces all bytes written to be platform agnostic.
46impl<T: Hasher> core::hash::Hasher for DeterministicHasher<T> {
47    fn finish(&self) -> u64 {
48        self.0.finish()
49    }
50
51    fn write(&mut self, bytes: &[u8]) {
52        self.0.write(bytes);
53    }
54
55    fn write_u8(&mut self, i: u8) {
56        self.write(&i.to_le_bytes())
57    }
58
59    fn write_u16(&mut self, i: u16) {
60        self.write(&i.to_le_bytes())
61    }
62
63    fn write_u32(&mut self, i: u32) {
64        self.write(&i.to_le_bytes())
65    }
66
67    fn write_u64(&mut self, i: u64) {
68        self.write(&i.to_le_bytes())
69    }
70
71    fn write_u128(&mut self, i: u128) {
72        self.write(&i.to_le_bytes())
73    }
74
75    fn write_usize(&mut self, i: usize) {
76        self.write(&(i as u64).to_le_bytes())
77    }
78
79    fn write_i8(&mut self, i: i8) {
80        self.write_u8(i as u8)
81    }
82
83    fn write_i16(&mut self, i: i16) {
84        self.write_u16(i as u16)
85    }
86
87    fn write_i32(&mut self, i: i32) {
88        self.write_u32(i as u32)
89    }
90
91    fn write_i64(&mut self, i: i64) {
92        self.write_u64(i as u64)
93    }
94
95    fn write_i128(&mut self, i: i128) {
96        self.write_u128(i as u128)
97    }
98
99    fn write_isize(&mut self, i: isize) {
100        self.write_usize(i as usize)
101    }
102}
103
104#[cfg(test)]
105mod tests {
106    #[test]
107    fn it_works() {
108        assert_eq!(2 + 2, 4);
109    }
110}