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
/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

//! Utilities for working with hashes.

use crate as gazebo;
use crate::dupe::Dupe;
use std::{
    cmp,
    collections::hash_map::DefaultHasher,
    fmt,
    fmt::Display,
    hash::{Hash, Hasher},
};

/// A type `T`, but with the hash computed in advance, so hashing is O(1).
#[derive(Dupe, Clone, Copy, PartialEq, Eq, Debug)]
pub struct Hashed<T> {
    hash: u64,
    value: T,
}

#[allow(clippy::derive_hash_xor_eq)]
impl<T> Hash for Hashed<T> {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.hash.hash(state)
    }
}

impl<T: Display> Display for Hashed<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.value.fmt(f)
    }
}

impl<T: PartialOrd> PartialOrd for Hashed<T> {
    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
        self.value.partial_cmp(&other.value)
    }
}

impl<T: Ord> Ord for Hashed<T> {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        self.value.cmp(&other.value)
    }
}

impl<T> AsRef<T> for Hashed<T> {
    fn as_ref(&self) -> &T {
        &self.value
    }
}

impl<T> AsMut<T> for Hashed<T> {
    fn as_mut(&mut self) -> &mut T {
        &mut self.value
    }
}

impl<T: Hash> From<T> for Hashed<T> {
    fn from(value: T) -> Self {
        Self::new(value)
    }
}

impl<T: Hash> Hashed<T> {
    pub fn new(value: T) -> Self {
        let mut hasher = DefaultHasher::new();
        value.hash(&mut hasher);
        Self {
            hash: hasher.finish(),
            value,
        }
    }

    pub fn into(self) -> T {
        self.value
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_hashed() {
        let v1 = Hashed::new("test");
        let v2 = Hashed::from("test");
        let v3 = Hashed::new("magic");
        assert_eq!(v1, v2);
        assert_ne!(v1, v3);
    }
}