molframe_query/execution/
fingerprint.rs1use crate::ast::{Expr, GeometricExpr};
4use std::fmt;
5use std::hash::{Hash, Hasher};
6
7const OFFSET: u64 = 0xcbf2_9ce4_8422_2325;
8const PRIME: u64 = 0x0000_0100_0000_01b3;
9
10#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
12pub struct QueryFingerprint(u64);
13
14impl QueryFingerprint {
15 pub(super) fn of(expression: &Expr) -> Self {
16 let mut encoder = Encoder(OFFSET);
17 encoder.expression(expression);
18 Self(encoder.finish())
19 }
20
21 #[must_use]
23 pub const fn get(self) -> u64 {
24 self.0
25 }
26}
27
28impl fmt::Display for QueryFingerprint {
29 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
30 write!(formatter, "query-fnv1a64:{:016x}", self.0)
31 }
32}
33
34impl fmt::Debug for QueryFingerprint {
35 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
36 fmt::Display::fmt(self, formatter)
37 }
38}
39
40struct Encoder(u64);
41
42impl Encoder {
43 fn expression(&mut self, expression: &Expr) {
44 match expression {
45 Expr::All => self.tag(0),
46 Expr::None => self.tag(1),
47 Expr::And(left, right) => self.binary(2, left, right),
48 Expr::Or(left, right) => self.binary(3, left, right),
49 Expr::Not(value) => self.unary(4, value),
50 Expr::Global(value) => self.unary(5, value),
51 Expr::ByResidue(value) => self.unary(6, value),
52 Expr::Same { key, target } => {
53 self.tag(7);
54 key.hash(self);
55 self.expression(target);
56 }
57 Expr::Bonded { depth, target } => {
58 self.tag(8);
59 self.write_u32(*depth);
60 self.expression(target);
61 }
62 Expr::Geometric(value) => self.geometric(value),
63 Expr::Comparison {
64 column,
65 operator,
66 value,
67 absolute,
68 } => {
69 self.tag(10);
70 column.hash(self);
71 operator.hash(self);
72 self.write_u64(value.to_bits());
73 self.write_u8(u8::from(*absolute));
74 }
75 Expr::Membership { column, values } => {
76 self.tag(11);
77 column.hash(self);
78 self.write_usize(values.len());
79 for value in values {
80 self.text(value);
81 }
82 }
83 Expr::Group(value) => self.tagged_text(12, value),
84 Expr::Atom {
85 segment,
86 residue,
87 name,
88 } => {
89 self.tag(13);
90 self.text(segment);
91 self.write_i32(*residue);
92 self.text(name);
93 }
94 Expr::Macro(value) => {
95 self.tag(14);
96 value.hash(self);
97 }
98 Expr::Chirality(value) => self.tagged_text(15, value),
99 Expr::Smarts { pattern: value, .. } => {
100 self.tag(16);
101 value.hash(self);
102 }
103 }
104 }
105
106 fn geometric(&mut self, expression: &GeometricExpr) {
107 self.tag(9);
108 match expression {
109 GeometricExpr::Within { radius, target } => self.radius_target(0, *radius, target),
110 GeometricExpr::Beyond { radius, target } => self.radius_target(1, *radius, target),
111 GeometricExpr::Around { radius, target } => self.radius_target(2, *radius, target),
112 GeometricExpr::SphereZone { radius, target } => {
113 self.radius_target(3, *radius, target);
114 }
115 GeometricExpr::SphereLayer {
116 inner,
117 outer,
118 target,
119 } => self.layer_target(4, [*inner, *outer], target),
120 GeometricExpr::IsoLayer {
121 inner,
122 outer,
123 target,
124 } => self.layer_target(5, [*inner, *outer], target),
125 GeometricExpr::CylinderZone {
126 radius,
127 z_max,
128 z_min,
129 target,
130 } => self.layer_target(6, [*radius, *z_max, *z_min], target),
131 GeometricExpr::CylinderLayer {
132 inner,
133 outer,
134 z_max,
135 z_min,
136 target,
137 } => self.layer_target(7, [*inner, *outer, *z_max, *z_min], target),
138 GeometricExpr::Point { point, radius } => {
139 self.tag(8);
140 self.floats(point);
141 self.write_u32(radius.to_bits());
142 }
143 }
144 }
145
146 fn unary(&mut self, tag: u8, value: &Expr) {
147 self.tag(tag);
148 self.expression(value);
149 }
150
151 fn binary(&mut self, tag: u8, left: &Expr, right: &Expr) {
152 self.tag(tag);
153 self.expression(left);
154 self.expression(right);
155 }
156
157 fn radius_target(&mut self, tag: u8, radius: f32, target: &Expr) {
158 self.tag(tag);
159 self.write_u32(radius.to_bits());
160 self.expression(target);
161 }
162
163 fn layer_target<const N: usize>(&mut self, tag: u8, values: [f32; N], target: &Expr) {
164 self.tag(tag);
165 self.floats(&values);
166 self.expression(target);
167 }
168
169 fn floats(&mut self, values: &[f32]) {
170 for value in values {
171 self.write_u32(value.to_bits());
172 }
173 }
174
175 fn tagged_text(&mut self, tag: u8, value: &str) {
176 self.tag(tag);
177 self.text(value);
178 }
179
180 fn text(&mut self, value: &str) {
181 self.write_usize(value.len());
182 self.write(value.as_bytes());
183 }
184
185 fn tag(&mut self, tag: u8) {
186 self.write_u8(tag);
187 }
188}
189
190impl Hasher for Encoder {
191 fn finish(&self) -> u64 {
192 self.0
193 }
194
195 fn write(&mut self, bytes: &[u8]) {
196 for byte in bytes {
197 self.0 ^= u64::from(*byte);
198 self.0 = self.0.wrapping_mul(PRIME);
199 }
200 }
201}