Skip to main content

prosa_utils/
hash.rs

1//! Module for hashing helpers
2
3use std::{
4    hash::{BuildHasherDefault, Hasher},
5    marker::PhantomData,
6    num::{
7        NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroIsize, NonZeroU8, NonZeroU16,
8        NonZeroU32, NonZeroU64, NonZeroUsize,
9    },
10};
11
12mod sealed {
13    pub trait SealedInteger {}
14}
15
16/// Int Hasher
17#[derive(Debug, Default, Clone, Copy)]
18pub struct IntHasher<T: IsInteger>(u64, PhantomData<T>);
19
20/// IntHasher builder use for
21/// - [`IntHashSet`]
22/// - [`IntHashMap`]
23pub type BuildIntHasher<T> = BuildHasherDefault<IntHasher<T>>;
24
25/// IntHashSet for integer HashSet
26///
27/// ```
28/// use prosa_utils::hash::{BuildIntHasher, IntHashSet};
29///
30/// let mut int_hashset: IntHashSet<u32> = IntHashSet::with_capacity_and_hasher(1, BuildIntHasher::default());
31/// assert!(int_hashset.insert(2));
32/// assert!(int_hashset.insert(3));
33/// assert!(int_hashset.capacity() >= 2);
34/// ```
35pub type IntHashSet<T> = std::collections::HashSet<T, BuildIntHasher<T>>;
36
37/// IntHashMap for integer HashMap
38///
39/// ```
40/// use prosa_utils::hash::{BuildIntHasher, IntHashMap};
41///
42/// let mut int_hashmap: IntHashMap<i16, String> = IntHashMap::with_capacity_and_hasher(1, BuildIntHasher::default());
43/// assert!(int_hashmap.insert(2, "test2".to_string()).is_none());
44/// assert!(int_hashmap.insert(3, "test3".to_string()).is_none());
45/// assert!(int_hashmap.capacity() >= 2);
46/// ```
47pub type IntHashMap<K, V> = std::collections::HashMap<K, V, BuildIntHasher<K>>;
48
49/// Trait to identify integer types for implementations.
50///
51/// This trait is sealed and can only be implemented by `prosa_utils` for supported integer types.
52///
53/// ```
54/// use prosa_utils::hash::IsInteger;
55///
56/// /// When you implement the trait, P need to be an integer
57/// pub trait IntegerGetter<P: IsInteger> {
58///     /// Returns an integer
59///     fn get_int(&self) -> P;
60/// }
61/// ```
62///
63/// ```compile_fail
64/// struct CustomInteger(u64);
65///
66/// impl prosa_utils::hash::IsInteger for CustomInteger {}
67/// ```
68pub trait IsInteger: sealed::SealedInteger {}
69
70macro_rules! impl_is_integer {
71    ( $($integer:ty),+ $(,)? ) => {
72        $(
73            impl sealed::SealedInteger for $integer {}
74            impl IsInteger for $integer {}
75        )+
76    };
77}
78
79impl_is_integer!(
80    u8,
81    u16,
82    u32,
83    u64,
84    usize,
85    i8,
86    i16,
87    i32,
88    i64,
89    isize,
90    NonZeroU8,
91    NonZeroU16,
92    NonZeroU32,
93    NonZeroU64,
94    NonZeroUsize,
95    NonZeroI8,
96    NonZeroI16,
97    NonZeroI32,
98    NonZeroI64,
99    NonZeroIsize,
100);
101
102impl<T: IsInteger> Hasher for IntHasher<T> {
103    fn write(&mut self, _: &[u8]) {
104        panic!("Invalid use of IntHasher")
105    }
106
107    fn write_u8(&mut self, n: u8) {
108        self.0 = u64::from(n)
109    }
110    fn write_u16(&mut self, n: u16) {
111        self.0 = u64::from(n)
112    }
113    fn write_u32(&mut self, n: u32) {
114        self.0 = u64::from(n)
115    }
116    fn write_u64(&mut self, n: u64) {
117        self.0 = n
118    }
119    fn write_usize(&mut self, n: usize) {
120        self.0 = n as u64
121    }
122
123    fn write_i8(&mut self, n: i8) {
124        self.0 = n as u64
125    }
126    fn write_i16(&mut self, n: i16) {
127        self.0 = n as u64
128    }
129    fn write_i32(&mut self, n: i32) {
130        self.0 = n as u64
131    }
132    fn write_i64(&mut self, n: i64) {
133        self.0 = n as u64
134    }
135    fn write_isize(&mut self, n: isize) {
136        self.0 = n as u64
137    }
138
139    fn finish(&self) -> u64 {
140        self.0
141    }
142}
143
144#[cfg(test)]
145mod tests {
146    use super::*;
147
148    #[test]
149    fn test_hashset() {
150        let mut int_hashset =
151            IntHashSet::<u32>::with_capacity_and_hasher(1, BuildIntHasher::default());
152        assert!(int_hashset.insert(2));
153        assert!(int_hashset.insert(3));
154        assert!(int_hashset.capacity() >= 2);
155    }
156
157    #[test]
158    fn test_hashmap() {
159        let mut int_hashmap =
160            IntHashMap::<i16, String>::with_capacity_and_hasher(1, BuildIntHasher::default());
161        assert!(int_hashmap.insert(2, "test2".to_string()).is_none());
162        assert!(int_hashmap.insert(3, "test3".to_string()).is_none());
163        assert!(int_hashmap.capacity() >= 2);
164    }
165}