Skip to main content

turbo_persistence/
key.rs

1use std::{cmp::min, hash::Hasher};
2
3/// A trait for keys that can be used for hashing.
4pub trait KeyBase {
5    /// Returns the length of the key in bytes.
6    fn len(&self) -> usize;
7    fn is_empty(&self) -> bool {
8        self.len() == 0
9    }
10    /// Hashes the key. It should not include the structure of the key, only the data. E.g. `([1,
11    /// 2], [3, 4])` should hash the same as `[1, 2, 3, 4]`.
12    fn hash<H: Hasher>(&self, state: &mut H);
13}
14
15impl KeyBase for &'_ [u8] {
16    fn len(&self) -> usize {
17        <[u8]>::len(self)
18    }
19
20    fn is_empty(&self) -> bool {
21        <[u8]>::is_empty(self)
22    }
23
24    fn hash<H: Hasher>(&self, state: &mut H) {
25        state.write(self);
26    }
27}
28
29impl<const N: usize> KeyBase for [u8; N] {
30    fn len(&self) -> usize {
31        N
32    }
33
34    fn is_empty(&self) -> bool {
35        N > 0
36    }
37
38    fn hash<H: Hasher>(&self, state: &mut H) {
39        state.write(self);
40    }
41}
42
43impl KeyBase for Vec<u8> {
44    fn len(&self) -> usize {
45        self.len()
46    }
47
48    fn is_empty(&self) -> bool {
49        self.is_empty()
50    }
51
52    fn hash<H: Hasher>(&self, state: &mut H) {
53        state.write(self);
54    }
55}
56
57impl KeyBase for Box<[u8]> {
58    fn len(&self) -> usize {
59        (**self).len()
60    }
61
62    fn is_empty(&self) -> bool {
63        (**self).is_empty()
64    }
65
66    fn hash<H: Hasher>(&self, state: &mut H) {
67        state.write(self);
68    }
69}
70
71impl KeyBase for u8 {
72    fn len(&self) -> usize {
73        1
74    }
75
76    fn is_empty(&self) -> bool {
77        false
78    }
79
80    fn hash<H: Hasher>(&self, state: &mut H) {
81        state.write_u8(*self);
82    }
83}
84
85impl<A: KeyBase, B: KeyBase> KeyBase for (A, B) {
86    fn len(&self) -> usize {
87        let (a, b) = self;
88        a.len() + b.len()
89    }
90
91    fn is_empty(&self) -> bool {
92        let (a, b) = self;
93        a.is_empty() && b.is_empty()
94    }
95
96    fn hash<H: Hasher>(&self, state: &mut H) {
97        let (a, b) = self;
98        KeyBase::hash(a, state);
99        KeyBase::hash(b, state);
100    }
101}
102
103impl<T: KeyBase> KeyBase for &'_ T {
104    fn len(&self) -> usize {
105        (*self).len()
106    }
107
108    fn is_empty(&self) -> bool {
109        (*self).is_empty()
110    }
111
112    fn hash<H: Hasher>(&self, state: &mut H) {
113        (*self).hash(state)
114    }
115}
116
117/// A trait for keys that can be used to query the database. They need to allow hashing and
118/// comparison with a byte slice (total order).
119pub trait QueryKey: KeyBase {
120    fn cmp(&self, key: &[u8]) -> std::cmp::Ordering;
121}
122
123impl QueryKey for &'_ [u8] {
124    fn cmp(&self, key: &[u8]) -> std::cmp::Ordering {
125        Ord::cmp(self, &key)
126    }
127}
128
129impl<const N: usize> QueryKey for [u8; N] {
130    fn cmp(&self, key: &[u8]) -> std::cmp::Ordering {
131        Ord::cmp(&self[..], key)
132    }
133}
134
135impl QueryKey for Vec<u8> {
136    fn cmp(&self, key: &[u8]) -> std::cmp::Ordering {
137        Ord::cmp(&**self, key)
138    }
139}
140
141impl QueryKey for Box<[u8]> {
142    fn cmp(&self, key: &[u8]) -> std::cmp::Ordering {
143        Ord::cmp(&**self, key)
144    }
145}
146
147impl QueryKey for u8 {
148    fn cmp(&self, key: &[u8]) -> std::cmp::Ordering {
149        Ord::cmp(&[*self][..], key)
150    }
151}
152
153impl<A: QueryKey, B: QueryKey> QueryKey for (A, B) {
154    fn cmp(&self, mut key: &[u8]) -> std::cmp::Ordering {
155        let (a, b) = self;
156        let len = a.len();
157        let key_len = key.len();
158        let key_part = &key[..min(key_len, len)];
159        match a.cmp(key_part) {
160            std::cmp::Ordering::Equal => {
161                key = &key[len..];
162                b.cmp(key)
163            }
164            ord => ord,
165        }
166    }
167}
168
169impl<T: QueryKey> QueryKey for &'_ T {
170    fn cmp(&self, key: &[u8]) -> std::cmp::Ordering {
171        (*self).cmp(key)
172    }
173}
174
175/// A trait for keys that can be stored in the database. They need to allow hashing and comparison.
176pub trait StoreKey: KeyBase + Ord {
177    fn write_to(&self, buf: &mut Vec<u8>);
178}
179
180impl<const N: usize> StoreKey for [u8; N] {
181    fn write_to(&self, buf: &mut Vec<u8>) {
182        buf.extend_from_slice(&self[..]);
183    }
184}
185
186impl StoreKey for Vec<u8> {
187    fn write_to(&self, buf: &mut Vec<u8>) {
188        buf.extend_from_slice(self);
189    }
190}
191
192impl StoreKey for Box<[u8]> {
193    fn write_to(&self, buf: &mut Vec<u8>) {
194        buf.extend_from_slice(self);
195    }
196}
197
198impl StoreKey for &'_ [u8] {
199    fn write_to(&self, buf: &mut Vec<u8>) {
200        buf.extend_from_slice(self);
201    }
202}
203
204impl StoreKey for u8 {
205    fn write_to(&self, buf: &mut Vec<u8>) {
206        buf.push(*self);
207    }
208}
209
210impl<A: StoreKey, B: StoreKey> StoreKey for (A, B) {
211    fn write_to(&self, buf: &mut Vec<u8>) {
212        self.0.write_to(buf);
213        self.1.write_to(buf);
214    }
215}
216
217impl<T: StoreKey> StoreKey for &'_ T {
218    fn write_to(&self, buf: &mut Vec<u8>) {
219        (*self).write_to(buf);
220    }
221}
222
223/// Hashes a key with a fast, deterministic hash function.
224pub fn hash_key(key: &impl KeyBase) -> u64 {
225    let mut hasher = xxhash_rust::xxh3::Xxh3Default::new();
226    key.hash(&mut hasher);
227    hasher.finish()
228}
229
230#[cfg(test)]
231mod tests {
232    use std::cmp::Ordering;
233
234    use crate::{QueryKey, key::hash_key};
235
236    #[test]
237    fn tuple() {
238        let key = (&[1, 2], &[3, 4]);
239        assert_eq!(QueryKey::cmp(&key, &[1, 2, 3, 4]), Ordering::Equal);
240        assert_eq!(QueryKey::cmp(&key, &[1, 2, 3, 3]), Ordering::Greater);
241        assert_eq!(QueryKey::cmp(&key, &[1, 2, 3, 5]), Ordering::Less);
242        assert_eq!(QueryKey::cmp(&key, &[0, 2, 3, 4]), Ordering::Greater);
243        assert_eq!(QueryKey::cmp(&key, &[2, 2, 3, 4]), Ordering::Less);
244        assert_eq!(QueryKey::cmp(&key, &[1, 2, 3, 4, 5]), Ordering::Less);
245        assert_eq!(QueryKey::cmp(&key, &[1, 2, 3]), Ordering::Greater);
246        assert_eq!(QueryKey::cmp(&key, &[1, 2]), Ordering::Greater);
247        assert_eq!(QueryKey::cmp(&key, &[1]), Ordering::Greater);
248        assert_eq!(QueryKey::cmp(&key, &[]), Ordering::Greater);
249    }
250
251    #[test]
252    fn hash() {
253        let h1 = hash_key(&[1, 2, 3, 4]);
254        let h2 = hash_key(&(&[1, 2], &[3, 4]));
255        let h3 = hash_key(&(vec![1, 2, 3], 4u8));
256        assert_eq!(h2, h1);
257        assert_eq!(h3, h1);
258    }
259}