forked from boostorg/unordered
Unordered: Initial stab at siphash example.
[SVN r81973]
This commit is contained in:
18
examples/siphash/Jamfile.v2
Normal file
18
examples/siphash/Jamfile.v2
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
|
||||||
|
# Copyright 2012 Daniel James.
|
||||||
|
# Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
|
||||||
|
import testing ;
|
||||||
|
|
||||||
|
project unordered-test/example/siphash
|
||||||
|
: requirements
|
||||||
|
<warnings>all
|
||||||
|
<toolset>intel:<warnings>on
|
||||||
|
<toolset>gcc:<cxxflags>"-pedantic -Wstrict-aliasing -fstrict-aliasing -Wextra -Wsign-promo -Wunused-parameter -Wconversion -Wno-long-long -Wfloat-equal"
|
||||||
|
<toolset>darwin:<cxxflags>"-pedantic -Wstrict-aliasing -fstrict-aliasing -Wextra -Wsign-promo -Wunused-parameter -Wconversion -Wfloat-equal"
|
||||||
|
;
|
||||||
|
|
||||||
|
lib siphash : siphash.cpp siphash_generate.cpp /boost/random//boost_random ;
|
||||||
|
|
||||||
|
run siphash_example.cpp siphash ;
|
35
examples/siphash/README.txt
Normal file
35
examples/siphash/README.txt
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
Siphash example
|
||||||
|
---------------
|
||||||
|
|
||||||
|
Most of the commonly used hash functions are vunerable to
|
||||||
|
'hash-flooding' attacks. These can happen if the table is filled with
|
||||||
|
values which are likely to cause hash collisions. SipHash[1] is a fast
|
||||||
|
hash function proposed by Jean-Philippe Aumasson and Daniel J.
|
||||||
|
Bernstein2 to resist these attacks. While young and relatively untested,
|
||||||
|
it's been demonstrated that it's less vulnerable to attacks than popular
|
||||||
|
hash functions, such as MurmurHash and CityHash.
|
||||||
|
|
||||||
|
This a nice example, as it has a distinct practial use to boost::hash
|
||||||
|
and demonstrates a hash function with state. It also shows how a generic
|
||||||
|
hash function might be built on top of a binary hash function.
|
||||||
|
|
||||||
|
Note that this is an example, and far from fully polished. Some issues
|
||||||
|
with the current implementation:
|
||||||
|
|
||||||
|
- It's slow.
|
||||||
|
- Only works on little endian machines.
|
||||||
|
- Only supports a subset of the standard library.
|
||||||
|
|
||||||
|
I should improve it a bit before it's properly released. But if you wish
|
||||||
|
to implement something better, there are several C implementations
|
||||||
|
listed on the SipHash page[1]. I probably won't even try to compete with
|
||||||
|
them. If anyone is willing to implement a high quality SipHash library for
|
||||||
|
boost, that would be very much appreciated.
|
||||||
|
|
||||||
|
[1] https://131002.net/siphash/
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Copyright 2012 Daniel James.
|
||||||
|
Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
91
examples/siphash/siphash.cpp
Normal file
91
examples/siphash/siphash.cpp
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
|
||||||
|
// Copyright 2012 Daniel James.
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
|
||||||
|
// This is also released into the public domain.
|
||||||
|
|
||||||
|
#include "siphash.hpp"
|
||||||
|
|
||||||
|
namespace hash
|
||||||
|
{
|
||||||
|
const unsigned siphash_c = 2;
|
||||||
|
const unsigned siphash_d = 4;
|
||||||
|
|
||||||
|
siphash_state::siphash_state(sipkey const& key) :
|
||||||
|
v0(0x736f6d6570736575ull ^ key.k0),
|
||||||
|
v1(0x646f72616e646f6dull ^ key.k1),
|
||||||
|
v2(0x6c7967656e657261ull ^ key.k0),
|
||||||
|
v3(0x7465646279746573ull ^ key.k1),
|
||||||
|
buffered(0), b(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
inline boost::uint64_t rotate_left(boost::uint64_t x, unsigned c)
|
||||||
|
{
|
||||||
|
return (x << c) | (x >> (64-c));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void siphash_state::sip_round(unsigned rounds)
|
||||||
|
{
|
||||||
|
for (unsigned i = 0; i < rounds; ++i) {
|
||||||
|
v0 += v1;
|
||||||
|
v1 = rotate_left(v1, 13);
|
||||||
|
v1 ^= v0;
|
||||||
|
v0 = rotate_left(v0, 32);
|
||||||
|
|
||||||
|
v2 += v3;
|
||||||
|
v3 = rotate_left(v3, 16);
|
||||||
|
v3 ^= v2;
|
||||||
|
|
||||||
|
v2 += v1;
|
||||||
|
v1 = rotate_left(v1, 17);
|
||||||
|
v1 ^= v2;
|
||||||
|
v2 = rotate_left(v2, 32);
|
||||||
|
|
||||||
|
v0 += v3;
|
||||||
|
v3 = rotate_left(v3, 21);
|
||||||
|
v3 ^= v0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void siphash_state::update(void const* data, unsigned length)
|
||||||
|
{
|
||||||
|
char const* ptr = static_cast<char const*>(data);
|
||||||
|
|
||||||
|
while (length > 0) {
|
||||||
|
buffer[buffered++] = *ptr++;
|
||||||
|
++b;
|
||||||
|
--length;
|
||||||
|
|
||||||
|
if (buffered == 8)
|
||||||
|
{
|
||||||
|
v3 ^= m;
|
||||||
|
sip_round(siphash_c);
|
||||||
|
v0 ^= m;
|
||||||
|
buffered = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::uint64_t siphash_state::finalize()
|
||||||
|
{
|
||||||
|
// Compress the final 8 bytes.
|
||||||
|
while (buffered < 7) buffer[buffered++] = 0;
|
||||||
|
buffer[7] = b;
|
||||||
|
|
||||||
|
v3 ^= m;
|
||||||
|
sip_round(siphash_c);
|
||||||
|
v0 ^= m;
|
||||||
|
|
||||||
|
// Finalize the hash
|
||||||
|
|
||||||
|
v2 ^= 0xff;
|
||||||
|
sip_round(siphash_d);
|
||||||
|
|
||||||
|
return v0 ^ v1 ^ v2 ^ v3;
|
||||||
|
}
|
||||||
|
}
|
204
examples/siphash/siphash.hpp
Normal file
204
examples/siphash/siphash.hpp
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
|
||||||
|
// Copyright 2012 Daniel James.
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
|
||||||
|
// This is also released into the public domain.
|
||||||
|
|
||||||
|
#if !defined(BOOST_HASH_EXAMPLES_SIPHASH_HEADER)
|
||||||
|
#define BOOST_HASH_EXAMPLES_SIPHASH_HEADER
|
||||||
|
|
||||||
|
#include <boost/cstdint.hpp>
|
||||||
|
#include <boost/utility/enable_if.hpp>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
namespace hash
|
||||||
|
{
|
||||||
|
// Random key used to make sure the hash function isn't predictable.
|
||||||
|
|
||||||
|
struct sipkey
|
||||||
|
{
|
||||||
|
boost::uint64_t k0, k1;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Generate a secure sipkey.
|
||||||
|
// This only works when boost::random_device is available.
|
||||||
|
sipkey generate_sipkey();
|
||||||
|
|
||||||
|
// This is the class that does the actual hashing.
|
||||||
|
|
||||||
|
struct siphash_state
|
||||||
|
{
|
||||||
|
boost::uint64_t v0, v1, v2, v3;
|
||||||
|
union {
|
||||||
|
boost::uint64_t m;
|
||||||
|
unsigned char buffer[8];
|
||||||
|
};
|
||||||
|
unsigned char buffered;
|
||||||
|
unsigned char b;
|
||||||
|
|
||||||
|
explicit siphash_state(sipkey const&);
|
||||||
|
void update(void const*, unsigned);
|
||||||
|
boost::uint64_t finalize();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void sip_round(unsigned);
|
||||||
|
};
|
||||||
|
|
||||||
|
// The genereric hash function.
|
||||||
|
//
|
||||||
|
// Don't sepecialize this. Unless you really want to.
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct siphash
|
||||||
|
{
|
||||||
|
sipkey key;
|
||||||
|
|
||||||
|
/* implicit */ siphash(sipkey const& k) : key(k) {}
|
||||||
|
std::size_t operator()(T const&) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add support for a type by specializing this class.
|
||||||
|
//
|
||||||
|
// 'Enable' is used as a SFINAE hook.
|
||||||
|
|
||||||
|
template <typename T, typename Enable = void>
|
||||||
|
struct siphash_impl
|
||||||
|
{
|
||||||
|
static void update(siphash_state&, T const&);
|
||||||
|
};
|
||||||
|
|
||||||
|
// The implementation of the generic hash function.
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
std::size_t siphash<T>::operator()(T const& x) const
|
||||||
|
{
|
||||||
|
siphash_state state(key);
|
||||||
|
siphash_impl<T>::update(state, x);
|
||||||
|
return static_cast<std::size_t>(state.finalize());
|
||||||
|
}
|
||||||
|
|
||||||
|
// A couple of basic traits for hashing binary data.
|
||||||
|
|
||||||
|
struct enable_siphash_false { enum { value = false }; };
|
||||||
|
struct enable_siphash_true { enum { value = true }; };
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct enable_siphash_binary : enable_siphash_false {};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct enable_siphash_binary_array
|
||||||
|
{
|
||||||
|
enum { value = enable_siphash_binary<T>::value &&
|
||||||
|
sizeof(T[2]) == sizeof(T) * 2 };
|
||||||
|
};
|
||||||
|
|
||||||
|
// Some general purpose hash implementations, siphash_impl<T>
|
||||||
|
// can inherit from these.
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct siphash_binary_impl
|
||||||
|
{
|
||||||
|
static void update(siphash_state& state, int x)
|
||||||
|
{
|
||||||
|
state.update(&x, sizeof(x));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct siphash_container_impl
|
||||||
|
{
|
||||||
|
static void update(siphash_state& state, T const& x)
|
||||||
|
{
|
||||||
|
siphash_impl<typename T::value_type> value_impl;
|
||||||
|
|
||||||
|
for (typename T::const_iterator begin = x.begin(),
|
||||||
|
end = x.end(); begin != end; ++begin)
|
||||||
|
{
|
||||||
|
value_impl.update(state, *begin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, bool Enable = enable_siphash_binary_array<T>::value>
|
||||||
|
struct siphash_binary_container_impl;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct siphash_binary_container_impl<T, false> :
|
||||||
|
siphash_container_impl<T> {};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct siphash_binary_container_impl<T, true>
|
||||||
|
{
|
||||||
|
static void update(siphash_state& state, T const& x)
|
||||||
|
{
|
||||||
|
state.update(&*x.cbegin(), sizeof(T) * x.size());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Specialize siphash_impl for various types.
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct siphash_impl<T,
|
||||||
|
typename boost::enable_if_c<enable_siphash_binary<T>::value>::type
|
||||||
|
> : siphash_binary_impl<T> {};
|
||||||
|
|
||||||
|
template <typename T, typename Alloc>
|
||||||
|
struct siphash_impl<std::list<T, Alloc> > :
|
||||||
|
siphash_container_impl<T> {};
|
||||||
|
|
||||||
|
template <typename T, typename Alloc>
|
||||||
|
struct siphash_impl<std::vector<T, Alloc> > :
|
||||||
|
siphash_binary_container_impl<T> {};
|
||||||
|
|
||||||
|
template <typename T, typename Alloc>
|
||||||
|
struct siphash_impl<std::basic_string<T, std::char_traits<T>, Alloc> > :
|
||||||
|
siphash_binary_container_impl<T> {};
|
||||||
|
|
||||||
|
// Specialize the binary trait for builtin types.
|
||||||
|
|
||||||
|
template <> struct enable_siphash_binary<bool> :
|
||||||
|
enable_siphash_true {};
|
||||||
|
template <> struct enable_siphash_binary<char> :
|
||||||
|
enable_siphash_true {};
|
||||||
|
template <> struct enable_siphash_binary<unsigned char> :
|
||||||
|
enable_siphash_true {};
|
||||||
|
template <> struct enable_siphash_binary<signed char> :
|
||||||
|
enable_siphash_true {};
|
||||||
|
template <> struct enable_siphash_binary<short> :
|
||||||
|
enable_siphash_true {};
|
||||||
|
template <> struct enable_siphash_binary<unsigned short> :
|
||||||
|
enable_siphash_true {};
|
||||||
|
template <> struct enable_siphash_binary<int> :
|
||||||
|
enable_siphash_true {};
|
||||||
|
template <> struct enable_siphash_binary<unsigned int> :
|
||||||
|
enable_siphash_true {};
|
||||||
|
template <> struct enable_siphash_binary<long> :
|
||||||
|
enable_siphash_true {};
|
||||||
|
template <> struct enable_siphash_binary<unsigned long> :
|
||||||
|
enable_siphash_true {};
|
||||||
|
|
||||||
|
#if !defined(BOOST_NO_INTRINSIC_WCHAR_T)
|
||||||
|
template <> struct enable_siphash_binary<wchar_t> :
|
||||||
|
enable_siphash_true {};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(BOOST_NO_LONG_LONG)
|
||||||
|
template <> struct enable_siphash_binary<boost::long_long_type> :
|
||||||
|
enable_siphash_true {};
|
||||||
|
template <> struct enable_siphash_binary<boost::ulong_long_type> :
|
||||||
|
enable_siphash_true {};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(BOOST_HAS_INT128)
|
||||||
|
template <> struct enable_siphash_binary<boost::int128_type> :
|
||||||
|
boost::hash_detail::enable_hash_value {};
|
||||||
|
template <> struct enable_siphash_binary<boost::uint128_type> :
|
||||||
|
boost::hash_detail::enable_hash_value {};
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
38
examples/siphash/siphash_example.cpp
Normal file
38
examples/siphash/siphash_example.cpp
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
|
||||||
|
// Copyright 2012 Daniel James.
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
|
||||||
|
// This is also released into the public domain.
|
||||||
|
|
||||||
|
#include <boost/unordered_set.hpp>
|
||||||
|
#include <cassert>
|
||||||
|
#include "siphash.hpp"
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
boost::unordered_set<int, hash::siphash<int> > x1(0, hash::generate_sipkey());
|
||||||
|
boost::unordered_set<int, hash::siphash<int> > x2(0, hash::generate_sipkey());
|
||||||
|
|
||||||
|
assert(x1 == x2);
|
||||||
|
|
||||||
|
x1.insert(10);
|
||||||
|
x1.insert(30);
|
||||||
|
x1.insert(50);
|
||||||
|
assert(x1 != x2);
|
||||||
|
|
||||||
|
x2.insert(100);
|
||||||
|
assert(x1 != x2);
|
||||||
|
|
||||||
|
x1.insert(x2.begin(), x2.end());
|
||||||
|
x2.insert(x1.begin(), x1.end());
|
||||||
|
assert(x1 == x2);
|
||||||
|
|
||||||
|
assert(x1.bucket(10) != x2.bucket(10));
|
||||||
|
assert(x1.bucket(30) != x2.bucket(30));
|
||||||
|
|
||||||
|
x1 = x2;
|
||||||
|
|
||||||
|
assert(x1.bucket(10) == x2.bucket(10));
|
||||||
|
assert(x1.bucket(30) == x2.bucket(30));
|
||||||
|
}
|
27
examples/siphash/siphash_generate.cpp
Normal file
27
examples/siphash/siphash_generate.cpp
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
|
||||||
|
// Copyright 2012 Daniel James.
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
|
||||||
|
// This is also released into the public domain.
|
||||||
|
|
||||||
|
#include "siphash.hpp"
|
||||||
|
#include <boost/random/random_device.hpp>
|
||||||
|
#include <boost/random/variate_generator.hpp>
|
||||||
|
#include <boost/random/uniform_int.hpp>
|
||||||
|
|
||||||
|
namespace hash
|
||||||
|
{
|
||||||
|
sipkey generate_sipkey()
|
||||||
|
{
|
||||||
|
boost::random_device rng;
|
||||||
|
boost::variate_generator<boost::random_device&,
|
||||||
|
boost::uniform_int<boost::uint64_t> > gen(rng,
|
||||||
|
boost::uniform_int<boost::uint64_t>());
|
||||||
|
|
||||||
|
sipkey k;
|
||||||
|
k.k0 = gen();
|
||||||
|
k.k1 = gen();
|
||||||
|
return k;
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user