mirror of
https://github.com/boostorg/unordered.git
synced 2025-07-29 19:07:15 +02:00
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