Skip to main content

graphql_tools/parser/
hash.rs

1use std::hash::{Hash, Hasher};
2use xxhash_rust::xxh3::Xxh3;
3
4use crate::parser::query::{Directive, Number, Type, Value};
5
6pub fn hash_list_unordered<TIterator: Iterator>(items: TIterator) -> u64
7where
8    TIterator::Item: Hash,
9{
10    let mut xor = 0u64;
11    let mut sum = 0u64;
12    let mut count = 0u64;
13    for item in items {
14        let mut hasher = Xxh3::new();
15        item.hash(&mut hasher);
16        let value = hasher.finish();
17        xor ^= value;
18        sum = sum.wrapping_add(value);
19        count = count.wrapping_add(1);
20    }
21    let mut hasher = Xxh3::new();
22    xor.hash(&mut hasher);
23    sum.hash(&mut hasher);
24    count.hash(&mut hasher);
25    hasher.finish()
26}
27
28impl Hash for Directive<'_, String> {
29    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
30        "Directive".hash(state);
31        self.name.hash(state);
32        hash_list_unordered(self.arguments.iter()).hash(state);
33    }
34}
35
36impl Hash for Value<'_, String> {
37    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
38        match self {
39            Value::Variable(v) => {
40                "Value::Variable".hash(state);
41                v.hash(state);
42            }
43            Value::Int(i) => {
44                "Value::Int".hash(state);
45                i.hash(state);
46            }
47            Value::Float(f) => {
48                "Value::Float".hash(state);
49                // We need to convert the float to a canonical form before hashing it,
50                // because different representations of the same number (e.g. 1.0 and 1.00)
51                // should hash to the same value.
52                let canonical = if f.is_nan() {
53                    f64::NAN
54                } else if f.is_infinite() {
55                    if f.is_sign_positive() {
56                        f64::INFINITY
57                    } else {
58                        f64::NEG_INFINITY
59                    }
60                } else {
61                    *f
62                };
63                canonical.to_bits().hash(state);
64            }
65            Value::String(s) => {
66                "Value::String".hash(state);
67                s.hash(state);
68            }
69            Value::Boolean(b) => {
70                "Value::Boolean".hash(state);
71                b.hash(state);
72            }
73            Value::Null => {
74                "Value::Null".hash(state);
75            }
76            Value::Enum(v) => {
77                "Value::Enum".hash(state);
78                v.hash(state);
79            }
80            Value::List(l) => {
81                "Value::List".hash(state);
82                l.len().hash(state);
83                for item in l {
84                    item.hash(state);
85                }
86            }
87            Value::Object(o) => {
88                "Value::Object".hash(state);
89
90                hash_list_unordered(o.iter()).hash(state);
91            }
92        }
93    }
94}
95
96impl Hash for Type<'_, String> {
97    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
98        match self {
99            Type::NamedType(n) => {
100                "Type::NamedType".hash(state);
101                n.hash(state);
102            }
103            Type::ListType(t) => {
104                "Type::ListType".hash(state);
105                t.hash(state);
106            }
107            Type::NonNullType(t) => {
108                "Type::NonNullType".hash(state);
109                t.hash(state);
110            }
111        }
112    }
113}
114
115impl Hash for Number {
116    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
117        self.0.hash(state);
118    }
119}