nohash_hasher/
lib.rs

1// Copyright 2018-2020 Parity Technologies (UK) Ltd.
2//
3// Licensed under the Apache License, Version 2.0 or MIT license, at your option.
4//
5// A copy of the Apache License, Version 2.0 is included in the software as
6// LICENSE-APACHE and a copy of the MIT license is included in the software
7// as LICENSE-MIT. You may also obtain a copy of the Apache License, Version 2.0
8// at https://www.apache.org/licenses/LICENSE-2.0 and a copy of the MIT license
9// at https://opensource.org/licenses/MIT.
10
11#![cfg_attr(not(feature = "std"), no_std)]
12
13use core::{fmt, hash::{BuildHasherDefault, Hasher}, marker::PhantomData};
14
15/// A `HashMap` with an integer domain, using `NoHashHasher` to perform no hashing at all.
16///
17/// # Examples
18///
19/// See [`IsEnabled`] for use with custom types.
20///
21/// ```
22/// use nohash_hasher::IntMap;
23///
24/// let mut m: IntMap<u32, bool> = IntMap::default();
25///
26/// m.insert(0, false);
27/// m.insert(1, true);
28///
29/// assert!(m.contains_key(&0));
30/// assert!(m.contains_key(&1));
31/// ```
32#[cfg(feature = "std")]
33pub type IntMap<K, V> = std::collections::HashMap<K, V, BuildNoHashHasher<K>>;
34
35/// A `HashSet` of integers, using `NoHashHasher` to perform no hashing at all.
36///
37/// # Examples
38///
39/// See [`IsEnabled`] for use with custom types.
40///
41/// ```
42/// use nohash_hasher::IntSet;
43///
44/// let mut m = IntSet::default();
45///
46/// m.insert(0u32);
47/// m.insert(1u32);
48///
49/// assert!(m.contains(&0));
50/// assert!(m.contains(&1));
51/// ```
52#[cfg(feature = "std")]
53pub type IntSet<T> = std::collections::HashSet<T, BuildNoHashHasher<T>>;
54
55/// An alias for `BuildHasherDefault` for use with `NoHashHasher`.
56///
57/// # Examples
58///
59/// See also [`IntMap`] and [`IntSet`] for some easier usage examples.
60///
61/// ```
62/// use nohash_hasher::BuildNoHashHasher;
63/// use std::collections::HashMap;
64///
65/// let mut m: HashMap::<u8, char, BuildNoHashHasher<u8>> =
66///     HashMap::with_capacity_and_hasher(2, BuildNoHashHasher::default());
67///
68/// m.insert(0, 'a');
69/// m.insert(1, 'b');
70///
71/// assert_eq!(Some(&'a'), m.get(&0));
72/// assert_eq!(Some(&'b'), m.get(&1));
73/// ```
74pub type BuildNoHashHasher<T> = BuildHasherDefault<NoHashHasher<T>>;
75
76/// For an enabled type `T`, a `NoHashHasher<T>` implements `std::hash::Hasher` and
77/// uses the value set by one of the `write_{u8, u16, u32, u64, usize, i8, i16, i32,
78/// i64, isize}` methods as its hash output.
79///
80/// `NoHashHasher` does not implement any hashing algorithm and can only be used
81/// with types which can be mapped directly to a numeric value. Out of the box
82/// `NoHashHasher` is enabled for `u8`, `u16`, `u32`, `u64`, `usize`, `i8`, `i16`,
83/// `i32`, `i64`, and `isize`. Types that should be used with `NoHashHasher` need
84/// to implement [`IsEnabled`] and by doing so assert that their `Hash` impl invokes
85/// *only one* of the `Hasher::write_{u8, u16, u32, u64, usize, i8, i16, i32, i64,
86/// isize}` methods *exactly once*.
87///
88/// # Examples
89///
90/// See also [`BuildNoHashHasher`], [`IntMap`] and [`IntSet`] for some easier
91/// usage examples. See [`IsEnabled`] for use with custom types.
92///
93/// ```
94/// use nohash_hasher::NoHashHasher;
95/// use std::{collections::HashMap, hash::BuildHasherDefault};
96///
97/// let mut m: HashMap::<u8, char, BuildHasherDefault<NoHashHasher<u8>>> =
98///     HashMap::with_capacity_and_hasher(2, BuildHasherDefault::default());
99///
100/// m.insert(0, 'a');
101/// m.insert(1, 'b');
102///
103/// assert_eq!(Some(&'a'), m.get(&0));
104/// assert_eq!(Some(&'b'), m.get(&1));
105/// ```
106#[cfg(debug_assertions)]
107pub struct NoHashHasher<T>(u64, bool, PhantomData<T>);
108
109#[cfg(not(debug_assertions))]
110pub struct NoHashHasher<T>(u64, PhantomData<T>);
111
112impl<T> fmt::Debug for NoHashHasher<T> {
113    #[cfg(debug_assertions)]
114    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
115        f.debug_tuple("NoHashHasher").field(&self.0).field(&self.1).finish()
116    }
117
118    #[cfg(not(debug_assertions))]
119    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
120        f.debug_tuple("NoHashHasher").field(&self.0).finish()
121    }
122}
123
124impl<T> Default for NoHashHasher<T> {
125    #[cfg(debug_assertions)]
126    fn default() -> Self {
127        NoHashHasher(0, false, PhantomData)
128    }
129
130    #[cfg(not(debug_assertions))]
131    fn default() -> Self {
132        NoHashHasher(0, PhantomData)
133    }
134}
135
136impl<T> Clone for NoHashHasher<T> {
137    #[cfg(debug_assertions)]
138    fn clone(&self) -> Self {
139        NoHashHasher(self.0, self.1, self.2)
140    }
141
142    #[cfg(not(debug_assertions))]
143    fn clone(&self) -> Self {
144        NoHashHasher(self.0, self.1)
145    }
146}
147
148impl<T> Copy for NoHashHasher<T> {}
149
150/// Types which are safe to use with `NoHashHasher`.
151///
152/// This marker trait is an option for types to enable themselves for use
153/// with `NoHashHasher`. In order to be safe, the `Hash` impl needs to
154/// satisfy the following constraint:
155///
156/// > **One of the `Hasher::write_{u8,u16,u32,u64,usize,i8,i16,i32,i64,isize}`
157/// methods is invoked exactly once.**
158///
159/// The best way to ensure this is to write a custom `Hash` impl even when
160/// deriving `Hash` for a simple newtype of a single type which itself
161/// implements `IsEnabled` may work as well.
162///
163/// # Example
164///
165/// ```
166/// #[derive(PartialEq, Eq)]
167/// struct SomeType(u32);
168///
169/// impl std::hash::Hash for SomeType {
170///     fn hash<H: std::hash::Hasher>(&self, hasher: &mut H) {
171///         hasher.write_u32(self.0)
172///     }
173/// }
174///
175/// impl nohash_hasher::IsEnabled for SomeType {}
176///
177/// let mut m = nohash_hasher::IntMap::default();
178///
179/// m.insert(SomeType(1), 't');
180/// m.insert(SomeType(0), 'f');
181///
182/// assert_eq!(Some(&'t'), m.get(&SomeType(1)));
183/// assert_eq!(Some(&'f'), m.get(&SomeType(0)));
184/// ```
185pub trait IsEnabled {}
186
187impl IsEnabled for u8 {}
188impl IsEnabled for u16 {}
189impl IsEnabled for u32 {}
190impl IsEnabled for u64 {}
191impl IsEnabled for usize {}
192impl IsEnabled for i8 {}
193impl IsEnabled for i16 {}
194impl IsEnabled for i32 {}
195impl IsEnabled for i64 {}
196impl IsEnabled for isize {}
197
198#[cfg(not(debug_assertions))]
199impl<T: IsEnabled> Hasher for NoHashHasher<T> {
200    fn write(&mut self, _: &[u8]) {
201        panic!("Invalid use of NoHashHasher")
202    }
203
204    fn write_u8(&mut self, n: u8)       { self.0 = u64::from(n) }
205    fn write_u16(&mut self, n: u16)     { self.0 = u64::from(n) }
206    fn write_u32(&mut self, n: u32)     { self.0 = u64::from(n) }
207    fn write_u64(&mut self, n: u64)     { self.0 = n }
208    fn write_usize(&mut self, n: usize) { self.0 = n as u64 }
209
210    fn write_i8(&mut self, n: i8)       { self.0 = n as u64 }
211    fn write_i16(&mut self, n: i16)     { self.0 = n as u64 }
212    fn write_i32(&mut self, n: i32)     { self.0 = n as u64 }
213    fn write_i64(&mut self, n: i64)     { self.0 = n as u64 }
214    fn write_isize(&mut self, n: isize) { self.0 = n as u64 }
215
216    fn finish(&self) -> u64 { self.0 }
217}
218
219#[cfg(debug_assertions)]
220impl<T: IsEnabled> Hasher for NoHashHasher<T> {
221    fn write(&mut self, _: &[u8]) {
222        panic!("Invalid use of NoHashHasher")
223    }
224
225    fn write_u8(&mut self, n: u8) {
226        assert!(!self.1, "NoHashHasher: second write attempt detected.");
227        self.0 = u64::from(n);
228        self.1 = true
229    }
230
231    fn write_u16(&mut self, n: u16) {
232        assert!(!self.1, "NoHashHasher: second write attempt detected.");
233        self.0 = u64::from(n);
234        self.1 = true
235    }
236
237    fn write_u32(&mut self, n: u32) {
238        assert!(!self.1, "NoHashHasher: second write attempt detected.");
239        self.0 = u64::from(n);
240        self.1 = true
241    }
242
243    fn write_u64(&mut self, n: u64) {
244        assert!(!self.1, "NoHashHasher: second write attempt detected.");
245        self.0 = n;
246        self.1 = true
247    }
248
249    fn write_usize(&mut self, n: usize) {
250        assert!(!self.1, "NoHashHasher: second write attempt detected.");
251        self.0 = n as u64;
252        self.1 = true
253    }
254
255    fn write_i8(&mut self, n: i8) {
256        assert!(!self.1, "NoHashHasher: second write attempt detected.");
257        self.0 = n as u64;
258        self.1 = true
259    }
260
261    fn write_i16(&mut self, n: i16) {
262        assert!(!self.1, "NoHashHasher: second write attempt detected.");
263        self.0 = n as u64;
264        self.1 = true
265    }
266
267    fn write_i32(&mut self, n: i32) {
268        assert!(!self.1, "NoHashHasher: second write attempt detected.");
269        self.0 = n as u64;
270        self.1 = true
271    }
272
273    fn write_i64(&mut self, n: i64) {
274        assert!(!self.1, "NoHashHasher: second write attempt detected.");
275        self.0 = n as u64;
276        self.1 = true
277    }
278
279    fn write_isize(&mut self, n: isize) {
280        assert!(!self.1, "NoHashHasher: second write attempt detected.");
281        self.0 = n as u64;
282        self.1 = true
283    }
284
285    fn finish(&self) -> u64 {
286        self.0
287    }
288}
289
290#[cfg(test)]
291mod tests {
292    use super::*;
293
294    #[test]
295    fn ok() {
296        let mut h1 = NoHashHasher::<u8>::default();
297        h1.write_u8(42);
298        assert_eq!(42, h1.finish());
299
300        let mut h2 = NoHashHasher::<u16>::default();
301        h2.write_u16(42);
302        assert_eq!(42, h2.finish());
303
304        let mut h3 = NoHashHasher::<u32>::default();
305        h3.write_u32(42);
306        assert_eq!(42, h3.finish());
307
308        let mut h4 = NoHashHasher::<u64>::default();
309        h4.write_u64(42);
310        assert_eq!(42, h4.finish());
311
312        let mut h5 = NoHashHasher::<usize>::default();
313        h5.write_usize(42);
314        assert_eq!(42, h5.finish());
315
316        let mut h6 = NoHashHasher::<i8>::default();
317        h6.write_i8(42);
318        assert_eq!(42, h6.finish());
319
320        let mut h7 = NoHashHasher::<i16>::default();
321        h7.write_i16(42);
322        assert_eq!(42, h7.finish());
323
324        let mut h8 = NoHashHasher::<i32>::default();
325        h8.write_i32(42);
326        assert_eq!(42, h8.finish());
327
328        let mut h9 = NoHashHasher::<i64>::default();
329        h9.write_i64(42);
330        assert_eq!(42, h9.finish());
331
332        let mut h10 = NoHashHasher::<isize>::default();
333        h10.write_isize(42);
334        assert_eq!(42, h10.finish())
335    }
336
337    #[cfg(debug_assertions)]
338    #[test]
339    #[should_panic]
340    fn u8_double_usage() {
341        let mut h = NoHashHasher::<u8>::default();
342        h.write_u8(42);
343        h.write_u8(43);
344    }
345
346    #[cfg(debug_assertions)]
347    #[test]
348    #[should_panic]
349    fn u16_double_usage() {
350        let mut h = NoHashHasher::<u16>::default();
351        h.write_u16(42);
352        h.write_u16(43);
353    }
354
355    #[cfg(debug_assertions)]
356    #[test]
357    #[should_panic]
358    fn u32_double_usage() {
359        let mut h = NoHashHasher::<u32>::default();
360        h.write_u32(42);
361        h.write_u32(43);
362    }
363
364    #[cfg(debug_assertions)]
365    #[test]
366    #[should_panic]
367    fn u64_double_usage() {
368        let mut h = NoHashHasher::<u64>::default();
369        h.write_u64(42);
370        h.write_u64(43);
371    }
372
373    #[cfg(debug_assertions)]
374    #[test]
375    #[should_panic]
376    fn usize_double_usage() {
377        let mut h = NoHashHasher::<usize>::default();
378        h.write_usize(42);
379        h.write_usize(43);
380    }
381
382    #[cfg(debug_assertions)]
383    #[test]
384    #[should_panic]
385    fn i8_double_usage() {
386        let mut h = NoHashHasher::<i8>::default();
387        h.write_i8(42);
388        h.write_i8(43);
389    }
390
391    #[cfg(debug_assertions)]
392    #[test]
393    #[should_panic]
394    fn i16_double_usage() {
395        let mut h = NoHashHasher::<i16>::default();
396        h.write_i16(42);
397        h.write_i16(43);
398    }
399
400    #[cfg(debug_assertions)]
401    #[test]
402    #[should_panic]
403    fn i32_double_usage() {
404        let mut h = NoHashHasher::<i32>::default();
405        h.write_i32(42);
406        h.write_i32(43);
407    }
408
409    #[cfg(debug_assertions)]
410    #[test]
411    #[should_panic]
412    fn i64_double_usage() {
413        let mut h = NoHashHasher::<i64>::default();
414        h.write_i64(42);
415        h.write_i64(43);
416    }
417
418    #[cfg(debug_assertions)]
419    #[test]
420    #[should_panic]
421    fn isize_double_usage() {
422        let mut h = NoHashHasher::<isize>::default();
423        h.write_isize(42);
424        h.write_isize(43);
425    }
426}
427