hashers/
pigeon.rs

1//! # Hash functions by Steven Pigeon (https://hbfs.wordpress.com/)
2//!
3//! From:
4//!
5//! * https://hbfs.wordpress.com/2015/09/29/hash-functions-part-i/
6//! * https://hbfs.wordpress.com/2015/10/06/the-anatomy-of-hash-functions-hash-functions-part-ii/
7//! * https://hbfs.wordpress.com/2015/10/13/testing-hash-functions-hash-functions-part-iii/
8//! * https://hbfs.wordpress.com/2015/10/20/three-bad-functions-hash-functions-part-iv/
9//! * https://hbfs.wordpress.com/2015/10/27/three-somewhat-better-functions-hash-functions-part-v/
10//! * https://hbfs.wordpress.com/2015/11/17/and-a-good-one-hash-functions-part-vi/
11//!
12//! as well as
13//!
14//! * https://hbfs.wordpress.com/2011/11/08/mild-obfuscation/
15//!
16//! > In the previous entries, we learned that a good hash function for
17//! > look-ups should disperse bits as much as possible as well as being
18//! > unpredictable, that is, behave more or less like a pseudo-random
19//! > number generator. We had a few failed attempts, a few promising ones,
20//! > and now, a good one.
21//! >
22//! > One of the weak operations in the previous hash functions is the
23//! > combination operation, which is the addition. We remarked that it
24//! > isn’t very good because it is unlikely to provoke a global change in
25//! > the hash value. Indeed, if you add an 8-bit quantity, then you’re
26//! > reasonably certain that the value changes for the first 8 bits, but
27//! > after that, changes are operated only through the carry ripple, which
28//! > has only probability \frac{1}{2}^i of being propagated to the ith bit.
29//! > That is, it is very unlikely to ripple to the end of the word.
30//! >
31//! > So we need an operation (as simple as possible) to make sure that the
32//! > new bits are spread across, and affect, the hash value. Therefore,
33//! > we must scatter input bits. But how? Well, we could design some
34//! > bit-wise function that takes 8 bits and spread them, but that function
35//! > would be fixed, and independent of the input bits (if we consider a
36//! > permutation-type function). So we need a splatter that depends on
37//! > the input, but covers more or less all bits. Well, we can do that by
38//! > (integer) multiplying the next input byte by a large random-looking
39//! > number. A random-looking prime number, in fact. Why prime? It will not
40//! > introduce new common factors in the subsequent additions other than
41//! > those in the input.
42//! >
43//! > Let’s pick one:
44//! >
45//! > 173773926194192273.
46//! >
47//! > This number is 58 bits long. If you multiply an 8-bit value by a 56-bits
48//! > value, you’d get, most of the times, a 64-bits value. This time, it
49//! > is a bit bigger to compensate the fact the the 8-bit input doesn’t
50//! > necessarily use all of its 8 bits. Plus it’s prime. Why? How?
51//! >
52//! > ![random-typing](https://hbfs.files.wordpress.com/2015/11/random-typing.gif)
53//! >
54//! > (Yes. For real. That how I typed it. Not sorry.) Then let’s mix the
55//! > product. Let’s use the perfect_shuffle we’ve already used. Then
56//! > combine this value with a simple addition. The combination step being
57//! > strong enough now, we could use a simple confusion step. Let’s use
58//! > cut_deck, a function that swaps the high- and low part of the word,
59//! > without exchanging bits in each parts, for a bit more confusion.
60//!
61//! Unfortunately, although this *looks* like a good hash function, it is
62//! very slow, likely because it processes the input one byte at a time. If
63//! it were modified to correctly handle a larger block, it might actually
64//! be competitive.
65
66use std::hash::Hasher;
67
68#[inline]
69fn cut_deck(x: u64) -> u64 {
70    (x.wrapping_shl(32) | x.wrapping_shr(32))
71}
72
73#[inline]
74fn perfect_shuffle_32(mut x: u32) -> u32 {
75    x = (x & 0xff0000ffu32) | (x & 0x00ff0000u32).wrapping_shr(8) | (x & 0x0000ff00u32).wrapping_shl(8);
76    x = (x & 0xf00ff00fu32) | (x & 0x0f000f00u32).wrapping_shr(4) | (x & 0x00f000f0u32).wrapping_shl(4);
77    x = (x & 0xc3c3c3c3u32) | (x & 0x30303030u32).wrapping_shr(2) | (x & 0x0c0c0c0cu32).wrapping_shl(2);
78    x = (x & 0x99999999u32) | (x & 0x44444444u32).wrapping_shr(1) | (x & 0x22222222u32).wrapping_shl(1);
79    x
80}
81
82#[inline]
83fn perfect_shuffle_64(mut x: u64) -> u64 {
84    x = cut_deck(x);
85    let xh = perfect_shuffle_32(x.wrapping_shr(32) as u32) as u64;
86    let xl = perfect_shuffle_32(x as u32) as u64;
87    xh.wrapping_shl(32) | xl
88}
89
90pub struct Bricolage(u64);
91
92default_for_constant!(Bricolage, 0);
93
94const MAGIC: u64 = 173773926194192273u64;
95
96impl Hasher for Bricolage {
97    #[inline]
98    fn finish(&self) -> u64 {
99        self.0
100    }
101
102    #[inline]
103    fn write(&mut self, bytes: &[u8]) {
104        for byte in bytes.iter() {
105            let shuffled = perfect_shuffle_64((*byte as u64).wrapping_mul(MAGIC));
106            self.0 = cut_deck(self.0.wrapping_add(shuffled));
107        }
108    }
109}
110
111hasher_to_fcn!(
112    /// Provide access to Bricolage in a single call.
113    bricolage,
114    Bricolage
115);
116
117// ------------------------------------
118
119#[cfg(test)]
120mod bricolage_tests {
121    use super::*;
122
123    #[test]
124    fn basic() {
125        assert_eq!(bricolage(b""),   0);
126        assert_eq!(bricolage(b"a"),  17926483712435944715);
127        assert_eq!(bricolage(b"b"),  12457347154332739726);
128        assert_eq!(bricolage(b"ab"), 16461606921607156355);
129    }
130}
131