1use std::hash::{Hash, Hasher};
2
3use crate::nulls::IsNull;
4
5#[inline(always)]
7pub fn _boost_hash_combine(l: u64, r: u64) -> u64 {
8 l ^ r.wrapping_add(0x9e3779b9u64.wrapping_add(l << 6).wrapping_add(r >> 2))
9}
10
11#[inline(always)]
12pub const fn folded_multiply(a: u64, b: u64) -> u64 {
13 let full = (a as u128).wrapping_mul(b as u128);
14 (full as u64) ^ ((full >> 64) as u64)
15}
16
17#[derive(Eq, Copy, Clone, Debug)]
21pub struct BytesHash<'a> {
22 payload: Option<&'a [u8]>,
23 pub(super) hash: u64,
24}
25
26impl<'a> BytesHash<'a> {
27 #[inline]
28 pub fn new(s: Option<&'a [u8]>, hash: u64) -> Self {
29 Self { payload: s, hash }
30 }
31}
32
33impl<'a> IsNull for BytesHash<'a> {
34 const HAS_NULLS: bool = true;
35 type Inner = BytesHash<'a>;
36
37 #[inline(always)]
38 fn is_null(&self) -> bool {
39 self.payload.is_none()
40 }
41
42 fn unwrap_inner(self) -> Self::Inner {
43 assert!(self.payload.is_some());
44 self
45 }
46}
47
48impl Hash for BytesHash<'_> {
49 #[inline]
50 fn hash<H: Hasher>(&self, state: &mut H) {
51 state.write_u64(self.hash)
52 }
53}
54
55impl PartialEq for BytesHash<'_> {
56 #[inline]
57 fn eq(&self, other: &Self) -> bool {
58 (self.hash == other.hash) && (self.payload == other.payload)
59 }
60}
61
62#[inline(always)]
63pub fn hash_to_partition(h: u64, n_partitions: usize) -> usize {
64 ((h as u128 * n_partitions as u128) >> 64) as usize
70}
71
72#[derive(Clone)]
73pub struct HashPartitioner {
74 num_partitions: usize,
75 seed: u64,
76}
77
78impl HashPartitioner {
79 #[inline]
82 pub fn new(num_partitions: usize, mut seed: u64) -> Self {
83 assert!(num_partitions > 0);
84 const ARBITRARY1: u64 = 0x85921e81c41226a0;
86 const ARBITRARY2: u64 = 0x3bc1d0faba166294;
87 const ARBITRARY3: u64 = 0xfbde893e21a73756;
88 seed = folded_multiply(seed ^ ARBITRARY1, ARBITRARY2);
89 seed = folded_multiply(seed, ARBITRARY3);
90 seed |= 1;
91 Self {
92 seed,
93 num_partitions,
94 }
95 }
96
97 #[inline(always)]
102 pub fn hash_to_partition(&self, hash: u64) -> usize {
103 let shuffled = hash.wrapping_mul(self.seed);
109 ((shuffled as u128 * self.num_partitions as u128) >> 64) as usize
110 }
111
112 #[inline(always)]
114 pub fn null_partition(&self) -> usize {
115 0
116 }
117
118 #[inline(always)]
119 pub fn num_partitions(&self) -> usize {
120 self.num_partitions
121 }
122}
123
124pub trait DirtyHash {
126 fn dirty_hash(&self) -> u64;
129}
130
131const RANDOM_ODD: u64 = 0x55fbfd6bfc5458e9;
134
135macro_rules! impl_hash_partition_as_u64 {
136 ($T: ty) => {
137 impl DirtyHash for $T {
138 #[inline(always)]
139 fn dirty_hash(&self) -> u64 {
140 (*self as u64).wrapping_mul(RANDOM_ODD)
141 }
142 }
143 };
144}
145
146impl_hash_partition_as_u64!(u8);
147impl_hash_partition_as_u64!(u16);
148impl_hash_partition_as_u64!(u32);
149impl_hash_partition_as_u64!(u64);
150impl_hash_partition_as_u64!(i8);
151impl_hash_partition_as_u64!(i16);
152impl_hash_partition_as_u64!(i32);
153impl_hash_partition_as_u64!(i64);
154
155impl DirtyHash for u128 {
156 #[inline(always)]
157 fn dirty_hash(&self) -> u64 {
158 (*self as u64)
159 .wrapping_mul(RANDOM_ODD)
160 .wrapping_add((*self >> 64) as u64)
161 }
162}
163
164impl DirtyHash for i128 {
165 #[inline(always)]
166 fn dirty_hash(&self) -> u64 {
167 (*self as u64)
168 .wrapping_mul(RANDOM_ODD)
169 .wrapping_add((*self >> 64) as u64)
170 }
171}
172
173impl DirtyHash for BytesHash<'_> {
174 #[inline(always)]
175 fn dirty_hash(&self) -> u64 {
176 self.hash
177 }
178}
179
180impl<T: DirtyHash + ?Sized> DirtyHash for &T {
181 #[inline(always)]
182 fn dirty_hash(&self) -> u64 {
183 (*self).dirty_hash()
184 }
185}
186
187impl<T: DirtyHash> DirtyHash for Option<T> {
190 #[inline(always)]
191 fn dirty_hash(&self) -> u64 {
192 self.as_ref().map(|s| s.dirty_hash()).unwrap_or(0)
193 }
194}