1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
//! Tiny Rust library to create deterministic hashes regardless of architecture. This library is `no-std` compatible and uses no allocations or dependencies.
//!
//! The default `core::hash::Hasher` implementation ensures a platform dependant hashing of datastructures that use `#[derive(Hash)]`. Most notably by:
//! * using `to_ne_bytes` for `u{8,16,32,64,128}`.
//! * using the native bytelength of `usize`.
//!
//! 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.
//!
//! From any hasher make it deterministic by inserting `DeterministicHasher` in between:
//! ```
//! let hasher = crc::crc32::Digest::new(crc::crc32::KOOPMAN);
//! let hasher = deterministic_hash::DeterministicHasher::new(hasher);
//! ```

#![no_std]
use core::hash::Hasher;

/// Wrapper around any hasher to make it deterministic.
///
/// ```
/// use core::hash::Hash;
/// use crc::crc32::Hasher32;
/// use deterministic_hash::DeterministicHasher;
/// let mut hasher = DeterministicHasher::new(crc::crc32::Digest::new(crc::crc32::KOOPMAN));
/// (0x1337 as usize).hash(&mut hasher);
/// assert_eq!(hasher.as_inner().sum32(), 2482448842);
/// ```
pub struct DeterministicHasher<T: Hasher>(T);

impl<T: Hasher> DeterministicHasher<T> {
    pub fn new(inner: T) -> Self {
        Self(inner)
    }

    pub fn as_inner(&self) -> &T {
        &self.0
    }

    pub fn into_inner(self) -> T {
        self.0
    }
}

/// Implementation of hasher that forces all bytes written to be platform agnostic.
impl<T: Hasher> core::hash::Hasher for DeterministicHasher<T> {
    fn finish(&self) -> u64 {
        self.0.finish()
    }

    fn write(&mut self, bytes: &[u8]) {
        self.0.write(bytes);
    }

    fn write_u8(&mut self, i: u8) {
        self.write(&i.to_le_bytes())
    }

    fn write_u16(&mut self, i: u16) {
        self.write(&i.to_le_bytes())
    }

    fn write_u32(&mut self, i: u32) {
        self.write(&i.to_le_bytes())
    }

    fn write_u64(&mut self, i: u64) {
        self.write(&i.to_le_bytes())
    }

    fn write_u128(&mut self, i: u128) {
        self.write(&i.to_le_bytes())
    }

    fn write_usize(&mut self, i: usize) {
        self.write(&(i as u64).to_le_bytes())
    }

    fn write_i8(&mut self, i: i8) {
        self.write_u8(i as u8)
    }

    fn write_i16(&mut self, i: i16) {
        self.write_u16(i as u16)
    }

    fn write_i32(&mut self, i: i32) {
        self.write_u32(i as u32)
    }

    fn write_i64(&mut self, i: i64) {
        self.write_u64(i as u64)
    }

    fn write_i128(&mut self, i: i128) {
        self.write_u128(i as u128)
    }

    fn write_isize(&mut self, i: isize) {
        self.write_usize(i as usize)
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
        assert_eq!(2 + 2, 4);
    }
}