diff --git a/Makefile.am b/Makefile.am index c46991ca..d6bdb398 100644 --- a/Makefile.am +++ b/Makefile.am @@ -52,28 +52,29 @@ UTIL_FILES = \ fourcc.hpp \ guid.cpp \ guid.hpp \ + hash.hpp \ + hash.cpp \ hash/adler.cpp \ hash/adler.hpp \ hash/bsdsum.cpp \ hash/bsdsum.hpp \ - hash.cpp \ hash/crc.cpp \ hash/crc.hpp \ + hash/fasthash.cpp \ hash/fletcher.hpp \ hash/hmac.cpp \ hash/hmac.hpp \ hash/hotp.cpp \ hash/hotp.hpp \ - hash.hpp \ hash/md2.cpp \ hash/md2.hpp \ hash/md4.cpp \ hash/md4.hpp \ hash/md5.cpp \ hash/md5.hpp \ - hash/murmur.hpp \ hash/murmur/common.cpp \ hash/murmur/common.hpp \ + hash/murmur.hpp \ hash/murmur/murmur1.cpp \ hash/murmur/murmur1.hpp \ hash/murmur/murmur2.cpp \ @@ -292,6 +293,7 @@ TEST_BIN = \ test/fixed \ test/float \ test/hash/murmur \ + test/hash/fasthash \ test/hmac \ test/hotp \ test/hton \ diff --git a/hash/fasthash.cpp b/hash/fasthash.cpp new file mode 100644 index 00000000..adf298a1 --- /dev/null +++ b/hash/fasthash.cpp @@ -0,0 +1,70 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright 2015 Danny Robson + */ + +#include "fasthash.hpp" + + +/////////////////////////////////////////////////////////////////////////////// +uint64_t +util::hash::fasthash::mix (uint64_t v) +{ + v ^= v >> 23; + v *= 0x2127599bf4325c37; + v ^= v >> 47; + + return v; +} + + +/////////////////////////////////////////////////////////////////////////////// +uint64_t +util::hash::fasthash::hash64 (const void *restrict data, size_t len, uint64_t seed) +{ + static const uint64_t m = 0x880355f21e6d1965; + + uint64_t result = seed ^ (len * m); + + auto cursor = reinterpret_cast (data); + auto last = cursor + len / sizeof (*cursor); + for (; cursor < last; ++cursor) { + result ^= mix (*cursor); + result *= m; + } + + size_t remain = len % sizeof (*cursor); + if (remain) { + auto tail = reinterpret_cast (cursor); + + uint64_t accum = 0; + for (size_t i = 0; i < remain; ++i) + accum ^= uint64_t {tail[i]} << i * 8; + + result ^= mix (accum); + result *= m; + } + + return mix (result); +} + + +//----------------------------------------------------------------------------- +uint32_t +util::hash::fasthash::hash32 (const void *restrict data, size_t len, uint32_t seed) +{ + uint64_t h = hash64 (data, len, seed); + return h - (h >> 32); +} + diff --git a/hash/fasthash.hpp b/hash/fasthash.hpp new file mode 100644 index 00000000..8545e92d --- /dev/null +++ b/hash/fasthash.hpp @@ -0,0 +1,33 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright 2015 Danny Robson + */ + +#ifndef __UTIL_HASH_FASTHASH_HPP +#define __UTIL_HASH_FASTHASH_HPP + +#include +#include + +// Zilong Tan's FastHash, via George Marsaglia's "Xorshift RNGs" +namespace util { namespace hash { namespace fasthash { + uint64_t mix (uint64_t); + + uint32_t hash32 (const void *restrict data, size_t len, uint32_t seed); + uint64_t hash64 (const void *restrict data, size_t len, uint64_t seed); +} } } + +#endif + + diff --git a/test/hash/fasthash.cpp b/test/hash/fasthash.cpp new file mode 100644 index 00000000..300914ae --- /dev/null +++ b/test/hash/fasthash.cpp @@ -0,0 +1,39 @@ +#include "hash/fasthash.hpp" +#include "tap.hpp" + +#include + +int +main (void) +{ + util::TAP::logger tap; + + static const struct { + uint32_t seed32; + uint32_t hash32; + uint64_t seed64; + uint64_t hash64; + const char *str; + } TESTS[] = { + { 0x00000000, 0x00000000, 0x0000000000000000, 0x0000000000000000, "" }, + { 0x00000001, 0xd30ac4de, 0x0000000000000001, 0x2127599bf4321e79, "" }, + { 0xffffffff, 0xf5c7b4b0, 0xffffffffffffffff, 0x9b4792000001368f, "" }, + { 0xf5c7b4b0, 0x228128b7, 0x9b4792000001368f, 0x67a642098cc81da6, "a" }, + { 0x228128b7, 0x8400568d, 0x67a642098cc81da6, 0xc906440e03ce99a8, "abc" }, + { 0x8400568d, 0x12b4858b, 0x67a642098cc81da6, 0x1a36fbf3d71b0737, "message digest" }, + { 0x12b4858b, 0x730b822e, 0x1a36fbf3d71b0737, 0x7b48e31e3ac40a0f, "abcdefghijklmnopqrstuvwxyz" }, + }; + + bool success32 = true; + bool success64 = true; + + for (const auto &t: TESTS) { + success32 = success32 && t.hash32 == util::hash::fasthash::hash32 (t.str, strlen (t.str), t.seed32); + success64 = success64 && t.hash64 == util::hash::fasthash::hash64 (t.str, strlen (t.str), t.seed64); + } + + tap.expect (success32, "fasthash32"); + tap.expect (success64, "fasthash64"); + + return tap.status (); +}