From: Martin Quinson Date: Mon, 23 Sep 2019 07:55:56 +0000 (+0200) Subject: Add a fast hash function (mandates C++14 for MC) X-Git-Tag: v3.24~68^2^2~10 X-Git-Url: http://info.iut-bm.univ-fcomte.fr/pub/gitweb/simgrid.git/commitdiff_plain/724b608b9632663d62a553519ef1a32277ccf3ff Add a fast hash function (mandates C++14 for MC) This comes from https://github.com/RedSpah/xxhash_cpp/commit/a310676e1514b49d78f0c9ced0678efea4f82fd9 This requires C++14: xxhash.hpp:288:40: error: variable declaration in a constexpr function is a C++14 extension So bump the dependencies of the Model Checker to C++14. That's an intrusive move, but MC is experimental anyway, and C++14 isn't that modern anymore. --- diff --git a/src/include/xxhash.hpp b/src/include/xxhash.hpp new file mode 100644 index 0000000000..81e8207468 --- /dev/null +++ b/src/include/xxhash.hpp @@ -0,0 +1,719 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include + +#include + +/* +xxHash - Extremely Fast Hash algorithm +Header File +Copyright (C) 2012-2018, Yann Collet. +Copyright (C) 2017-2018, Piotr Pliszka. +All rights reserved. + +BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: +* Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +You can contact the author at : +- xxHash source repository : https://github.com/Cyan4973/xxHash +- xxHash C++ port repository : https://github.com/RedSpah/xxhash_cpp +*/ + +/* ************************************* +* Tuning parameters +***************************************/ +/*!XXH_FORCE_MEMORY_ACCESS : +* By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable. +* Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal. +* The below switch allow to select different access method for improved performance. +* Method 0 (default) : use `memcpy()`. Safe and portable. +* Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable). +* This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`. +* Method 2 : direct access. This method doesn't depend on compiler but violate C standard. +* It can generate buggy code on targets which do not support unaligned memory accesses. +* But in some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6) +* See http://stackoverflow.com/a/32095106/646947 for details. +* Prefer these methods in priority order (0 > 1 > 2) +*/ +#ifndef XXH_FORCE_MEMORY_ACCESS /* can be defined externally, on command line for example */ +# if defined(__GNUC__) && ( defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) ) +# define XXH_FORCE_MEMORY_ACCESS 2 +# elif defined(__INTEL_COMPILER) || (defined(__GNUC__) && ( defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7S__) )) +# define XXH_FORCE_MEMORY_ACCESS 1 +# endif +#endif + + +/*!XXH_FORCE_NATIVE_FORMAT : +* By default, xxHash library provides endian-independent Hash values, based on little-endian convention. +* Results are therefore identical for little-endian and big-endian CPU. +* This comes at a performance cost for big-endian CPU, since some swapping is required to emulate little-endian format. +* Should endian-independence be of no importance for your application, you may set the #define below to 1, +* to improve speed for Big-endian CPU. +* This option has no impact on Little_Endian CPU. +*/ +#if !defined(XXH_FORCE_NATIVE_FORMAT) || (XXH_FORCE_NATIVE_FORMAT == 0) /* can be defined externally */ +# define XXH_FORCE_NATIVE_FORMAT 0 +# define XXH_CPU_LITTLE_ENDIAN 1 +#endif + + +/*!XXH_FORCE_ALIGN_CHECK : +* This is a minor performance trick, only useful with lots of very small keys. +* It means : check for aligned/unaligned input. +* The check costs one initial branch per hash; +* set it to 0 when the input is guaranteed to be aligned, +* or when alignment doesn't matter for performance. +*/ +#ifndef XXH_FORCE_ALIGN_CHECK /* can be defined externally */ +# if defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64) +# define XXH_FORCE_ALIGN_CHECK 0 +# else +# define XXH_FORCE_ALIGN_CHECK 1 +# endif +#endif + +/*!XXH_CPU_LITTLE_ENDIAN : +* This is a CPU endian detection macro, will be +* automatically set to 1 (little endian) if XXH_FORCE_NATIVE_FORMAT +* is left undefined, XXH_FORCE_NATIVE_FORMAT is defined to 0, or if an x86/x86_64 compiler macro is defined. +* If left undefined, endianness will be determined at runtime, at the cost of a slight one-time overhead +* and a larger overhead due to get_endian() not being constexpr. +*/ +#ifndef XXH_CPU_LITTLE_ENDIAN +# if defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64) +# define XXH_CPU_LITTLE_ENDIAN 1 +# endif +#endif + +/* ************************************* +* Compiler Specific Options +***************************************/ +#define XXH_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) + +namespace xxh +{ + /* ************************************* + * Version + ***************************************/ + constexpr int cpp_version_major = 0; + constexpr int cpp_version_minor = 6; + constexpr int cpp_version_release = 5; + constexpr uint32_t version_number() { return cpp_version_major * 10000 + cpp_version_minor * 100 + cpp_version_release; } + + namespace hash_t_impl + { + /* ************************************* + * Basic Types - Detail + ***************************************/ + + using _hash32_underlying = uint32_t; + using _hash64_underlying = uint64_t; + + template + struct hash_type { using type = void; }; + template <> + struct hash_type<32> { using type = _hash32_underlying; }; + template <> + struct hash_type<64> { using type = _hash64_underlying; }; + } + + /* ************************************* + * Basic Types - Public + ***************************************/ + + template + using hash_t = typename hash_t_impl::hash_type::type; + using hash32_t = hash_t<32>; + using hash64_t = hash_t<64>; + + /* ************************************* + * Bit Functions - Public + ***************************************/ + + namespace bit_ops + { + /* **************************************** + * Intrinsics and Bit Operations + ******************************************/ + +#if defined(_MSC_VER) + inline uint32_t rotl32(uint32_t x, int32_t r) { return _rotl(x, r); } + inline uint64_t rotl64(uint64_t x, int32_t r) { return _rotl64(x, r); } +#else + inline uint32_t rotl32(uint32_t x, int32_t r) { return ((x << r) | (x >> (32 - r))); } + inline uint64_t rotl64(uint64_t x, int32_t r) { return ((x << r) | (x >> (64 - r))); } +#endif + +#if defined(_MSC_VER) /* Visual Studio */ + inline uint32_t swap32(uint32_t x) { return _byteswap_ulong(x); } + inline uint64_t swap64(uint64_t x) { return _byteswap_uint64(x); } +#elif XXH_GCC_VERSION >= 403 + inline uint32_t swap32(uint32_t x) { return __builtin_bswap32(x); } + inline uint64_t swap64(uint64_t x) { return __builtin_bswap64(x); } +#else + inline uint32_t swap32(uint32_t x) { return ((x << 24) & 0xff000000) | ((x << 8) & 0x00ff0000) | ((x >> 8) & 0x0000ff00) | ((x >> 24) & 0x000000ff); } + inline uint64_t swap64(uint64_t x) { return ((x << 56) & 0xff00000000000000ULL) | ((x << 40) & 0x00ff000000000000ULL) | ((x << 24) & 0x0000ff0000000000ULL) | ((x << 8) & 0x000000ff00000000ULL) | ((x >> 8) & 0x00000000ff000000ULL) | ((x >> 24) & 0x0000000000ff0000ULL) | ((x >> 40) & 0x000000000000ff00ULL) | ((x >> 56) & 0x00000000000000ffULL); } +#endif + template + inline hash_t rotl(hash_t n, int32_t r) {}; + + template <> + inline hash_t<32> rotl<32>(hash_t<32> n, int32_t r) + { + return rotl32(n, r); + }; + + template <> + inline hash_t<64> rotl<64>(hash_t<64> n, int32_t r) + { + return rotl64(n, r); + }; + + template + inline hash_t swap(hash_t n) {}; + + template <> + inline hash_t<32> swap<32>(hash_t<32> n) + { + return swap32(n); + }; + + template <> + inline hash_t<64> swap<64>(hash_t<64> n) + { + return swap64(n); + }; + } + + /* ************************************* + * Memory Functions - Public + ***************************************/ + + enum class alignment : uint8_t { aligned, unaligned }; + enum class endianness : uint8_t { big_endian = 0, little_endian = 1, unspecified = 2 }; + + namespace mem_ops + { + /* ************************************* + * Memory Access + ***************************************/ +#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2)) + + /* Force direct memory access. Only works on CPU which support unaligned memory access in hardware */ + template + inline hash_t read_unaligned(const void* memPtr) { return *(const hash_t*)memPtr; } + +#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1)) + + /* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */ + /* currently only defined for gcc and icc */ + template + using unalign = union { hash_t uval; } __attribute((packed)); + + template + inline hash_t read_unaligned(const void* memPtr) { return ((const unalign*)memPtr)->uval; } +#else + + /* portable and safe solution. Generally efficient. + * see : http://stackoverflow.com/a/32095106/646947 + */ + template + inline hash_t read_unaligned(const void* memPtr) + { + hash_t val; + memcpy(&val, memPtr, sizeof(val)); + return val; + } + +#endif /* XXH_FORCE_DIRECT_MEMORY_ACCESS */ + + inline hash_t<32> read32(const void* memPtr) { return read_unaligned<32>(memPtr); } + inline hash_t<64> read64(const void* memPtr) { return read_unaligned<64>(memPtr); } + + /* ************************************* + * Architecture Macros + ***************************************/ + + /* XXH_CPU_LITTLE_ENDIAN can be defined externally, for example on the compiler command line */ + +#ifndef XXH_CPU_LITTLE_ENDIAN + + inline endianness get_endian(endianness endian) + { + static struct _dummy_t + { + std::array endian_lookup = { endianness::big_endian, endianness::little_endian, endianness::unspecified }; + const int g_one = 1; + _dummy_t() + { + endian_lookup[2] = static_cast(*(const char*)(&g_one)); + } + } _dummy; + + return _dummy.endian_lookup[(uint8_t)endian]; + } + + inline bool is_little_endian() + { + return get_endian(endianness::unspecified) == endianness::little_endian; + } + +#else + constexpr endianness get_endian(endianness endian) + { + constexpr std::array endian_lookup = { endianness::big_endian, endianness::little_endian, (XXH_CPU_LITTLE_ENDIAN) ? endianness::little_endian : endianness::big_endian }; + return endian_lookup[static_cast(endian)]; + } + + constexpr bool is_little_endian() + { + return get_endian(endianness::unspecified) == endianness::little_endian; + } + +#endif + + + + /* *************************** + * Memory reads + *****************************/ + + + template + inline hash_t readLE_align(const void* ptr, endianness endian, alignment align) + { + if (align == alignment::unaligned) + { + return endian == endianness::little_endian ? read_unaligned(ptr) : bit_ops::swap(read_unaligned(ptr)); + } + else + { + return endian == endianness::little_endian ? *reinterpret_cast*>(ptr) : bit_ops::swap(*reinterpret_cast*>(ptr)); + } + } + + template + inline hash_t readLE(const void* ptr, endianness endian) + { + return readLE_align(ptr, endian, alignment::unaligned); + } + + template + inline hash_t readBE(const void* ptr) + { + return is_little_endian() ? bit_ops::swap(read_unaligned(ptr)) : read_unaligned(ptr); + } + + template + inline alignment get_alignment(const void* input) + { + return ((XXH_FORCE_ALIGN_CHECK) && ((reinterpret_cast(input) & ((N / 8) - 1)) == 0)) ? xxh::alignment::aligned : xxh::alignment::unaligned; + } + } + + /* ******************************************************************* + * Hash functions + *********************************************************************/ + + namespace detail + { + /* ******************************************************************* + * Hash functions - Implementation + *********************************************************************/ + + constexpr static std::array primes32 = { 2654435761U, 2246822519U, 3266489917U, 668265263U, 374761393U }; + constexpr static std::array primes64 = { 11400714785074694791ULL, 14029467366897019727ULL, 1609587929392839161ULL, 9650029242287828579ULL, 2870177450012600261ULL }; + + template + constexpr hash_t PRIME(int32_t n) {}; + + template <> + constexpr hash32_t PRIME<32>(int32_t n) + { + return primes32[n - 1]; + } + + template <> + constexpr hash64_t PRIME<64>(int32_t n) + { + return primes64[n - 1]; + } + + template + inline hash_t round(hash_t seed, hash_t input) + { + seed += input * PRIME(2); + seed = bit_ops::rotl(seed, ((N == 32) ? 13 : 31)); + seed *= PRIME(1); + return seed; + } + + inline hash64_t mergeRound64(hash64_t acc, hash64_t val) + { + val = round<64>(0, val); + acc ^= val; + acc = acc * PRIME<64>(1) + PRIME<64>(4); + return acc; + } + + template + inline void endian_align_sub_mergeround([[maybe_unused]] hash_t& hash_ret, hash_t v1, hash_t v2, hash_t v3, hash_t v4) {}; + + template <> + inline void endian_align_sub_mergeround<64>(hash_t<64>& hash_ret, hash_t<64> v1, hash_t<64> v2, hash_t<64> v3, hash_t<64> v4) + { + hash_ret = mergeRound64(hash_ret, v1); + hash_ret = mergeRound64(hash_ret, v2); + hash_ret = mergeRound64(hash_ret, v3); + hash_ret = mergeRound64(hash_ret, v4); + } + + template + inline hash_t endian_align_sub_ending(hash_t hash_ret, const uint8_t* p, const uint8_t* bEnd, xxh::endianness endian, xxh::alignment align) {}; + + template <> + inline hash_t<32> endian_align_sub_ending<32>(hash_t<32> hash_ret, const uint8_t* p, const uint8_t* bEnd, xxh::endianness endian, xxh::alignment align) + { + while ((p + 4) <= bEnd) + { + hash_ret += mem_ops::readLE_align<32>(p, endian, align) * PRIME<32>(3); + hash_ret = bit_ops::rotl<32>(hash_ret, 17) * PRIME<32>(4); + p += 4; + } + + while (p < bEnd) + { + hash_ret += (*p) * PRIME<32>(5); + hash_ret = bit_ops::rotl<32>(hash_ret, 11) * PRIME<32>(1); + p++; + } + + hash_ret ^= hash_ret >> 15; + hash_ret *= PRIME<32>(2); + hash_ret ^= hash_ret >> 13; + hash_ret *= PRIME<32>(3); + hash_ret ^= hash_ret >> 16; + + return hash_ret; + } + + template <> + inline hash_t<64> endian_align_sub_ending<64>(hash_t<64> hash_ret, const uint8_t* p, const uint8_t* bEnd, xxh::endianness endian, xxh::alignment align) + { + while (p + 8 <= bEnd) + { + const hash64_t k1 = round<64>(0, mem_ops::readLE_align<64>(p, endian, align)); + hash_ret ^= k1; + hash_ret = bit_ops::rotl<64>(hash_ret, 27) * PRIME<64>(1) + PRIME<64>(4); + p += 8; + } + + if (p + 4 <= bEnd) + { + hash_ret ^= static_cast(mem_ops::readLE_align<32>(p, endian, align)) * PRIME<64>(1); + hash_ret = bit_ops::rotl<64>(hash_ret, 23) * PRIME<64>(2) + PRIME<64>(3); + p += 4; + } + + while (p < bEnd) + { + hash_ret ^= (*p) * PRIME<64>(5); + hash_ret = bit_ops::rotl<64>(hash_ret, 11) * PRIME<64>(1); + p++; + } + + hash_ret ^= hash_ret >> 33; + hash_ret *= PRIME<64>(2); + hash_ret ^= hash_ret >> 29; + hash_ret *= PRIME<64>(3); + hash_ret ^= hash_ret >> 32; + + return hash_ret; + } + + template + inline hash_t endian_align(const void* input, size_t len, hash_t seed, xxh::endianness endian, xxh::alignment align) + { + static_assert(!(N != 32 && N != 64), "You can only call endian_align in 32 or 64 bit mode."); + + const uint8_t* p = static_cast(input); + const uint8_t* bEnd = p + len; + hash_t hash_ret; + + if (len >= (N / 2)) + { + const uint8_t* const limit = bEnd - (N / 2); + hash_t v1 = seed + PRIME(1) + PRIME(2); + hash_t v2 = seed + PRIME(2); + hash_t v3 = seed + 0; + hash_t v4 = seed - PRIME(1); + + do + { + v1 = round(v1, mem_ops::readLE_align(p, endian, align)); p += (N / 8); + v2 = round(v2, mem_ops::readLE_align(p, endian, align)); p += (N / 8); + v3 = round(v3, mem_ops::readLE_align(p, endian, align)); p += (N / 8); + v4 = round(v4, mem_ops::readLE_align(p, endian, align)); p += (N / 8); + } while (p <= limit); + + hash_ret = bit_ops::rotl(v1, 1) + bit_ops::rotl(v2, 7) + bit_ops::rotl(v3, 12) + bit_ops::rotl(v4, 18); + + endian_align_sub_mergeround(hash_ret, v1, v2, v3, v4); + } + else { hash_ret = seed + PRIME(5); } + + hash_ret += static_cast>(len); + + return endian_align_sub_ending(hash_ret, p, bEnd, endian, align); + } + } + + template + hash_t xxhash(const void* input, size_t len, hash_t seed = 0, endianness endian = endianness::unspecified) + { + static_assert(!(N != 32 && N != 64), "You can only call xxhash in 32 or 64 bit mode."); + return detail::endian_align(input, len, seed, mem_ops::get_endian(endian), mem_ops::get_alignment(input)); + } + + template + hash_t xxhash(const std::basic_string& input, hash_t seed = 0, endianness endian = endianness::unspecified) + { + static_assert(!(N != 32 && N != 64), "You can only call xxhash in 32 or 64 bit mode."); + return detail::endian_align(static_cast(input.data()), input.length() * sizeof(T), seed, mem_ops::get_endian(endian), mem_ops::get_alignment(static_cast(input.data()))); + } + + template + hash_t xxhash(ContiguousIterator begin, ContiguousIterator end, hash_t seed = 0, endianness endian = endianness::unspecified) + { + static_assert(!(N != 32 && N != 64), "You can only call xxhash in 32 or 64 bit mode."); + using T = typename std::decay_t; + return detail::endian_align(static_cast(&*begin), (end - begin) * sizeof(T), seed, mem_ops::get_endian(endian), mem_ops::get_alignment(static_cast(&*begin))); + } + + template + hash_t xxhash(const std::vector& input, hash_t seed = 0, endianness endian = endianness::unspecified) + { + static_assert(!(N != 32 && N != 64), "You can only call xxhash in 32 or 64 bit mode."); + return detail::endian_align(static_cast(input.data()), input.size() * sizeof(T), seed, mem_ops::get_endian(endian), mem_ops::get_alignment(static_cast(input.data()))); + } + + template + hash_t xxhash(const std::array& input, hash_t seed = 0, endianness endian = endianness::unspecified) + { + static_assert(!(N != 32 && N != 64), "You can only call xxhash in 32 or 64 bit mode."); + return detail::endian_align(static_cast(input.data()), AN * sizeof(T), seed, mem_ops::get_endian(endian), mem_ops::get_alignment(static_cast(input.data()))); + } + + template + hash_t xxhash(const std::initializer_list& input, hash_t seed = 0, endianness endian = endianness::unspecified) + { + static_assert(!(N != 32 && N != 64), "You can only call xxhash in 32 or 64 bit mode."); + return detail::endian_align(static_cast(input.begin()), input.size() * sizeof(T), seed, mem_ops::get_endian(endian), mem_ops::get_alignment(static_cast(input.begin()))); + } + + + /* ******************************************************************* + * Hash streaming + *********************************************************************/ + enum class error_code : uint8_t { ok = 0, error }; + + template + class hash_state_t { + + uint64_t total_len = 0; + hash_t v1 = 0, v2 = 0, v3 = 0, v4 = 0; + std::array, 4> mem = {{ 0,0,0,0 }}; + uint32_t memsize = 0; + + inline error_code _update_impl(const void* input, size_t length, endianness endian) + { + const uint8_t* p = reinterpret_cast(input); + const uint8_t* const bEnd = p + length; + + if (!input) { return xxh::error_code::error; } + + total_len += length; + + if (memsize + length < (N / 2)) + { /* fill in tmp buffer */ + memcpy(reinterpret_cast(mem.data()) + memsize, input, length); + memsize += static_cast(length); + return error_code::ok; + } + + if (memsize) + { /* some data left from previous update */ + memcpy(reinterpret_cast(mem.data()) + memsize, input, (N / 2) - memsize); + + const hash_t* ptr = mem.data(); + v1 = detail::round(v1, mem_ops::readLE(ptr, endian)); ptr++; + v2 = detail::round(v2, mem_ops::readLE(ptr, endian)); ptr++; + v3 = detail::round(v3, mem_ops::readLE(ptr, endian)); ptr++; + v4 = detail::round(v4, mem_ops::readLE(ptr, endian)); + + p += (N / 2) - memsize; + memsize = 0; + } + + if (p <= bEnd - (N / 2)) + { + const uint8_t* const limit = bEnd - (N / 2); + + do + { + v1 = detail::round(v1, mem_ops::readLE(p, endian)); p += (N / 8); + v2 = detail::round(v2, mem_ops::readLE(p, endian)); p += (N / 8); + v3 = detail::round(v3, mem_ops::readLE(p, endian)); p += (N / 8); + v4 = detail::round(v4, mem_ops::readLE(p, endian)); p += (N / 8); + } while (p <= limit); + } + + if (p < bEnd) + { + memcpy(mem.data(), p, static_cast(bEnd - p)); + memsize = static_cast(bEnd - p); + } + + return error_code::ok; + } + + inline hash_t _digest_impl(endianness endian) const + { + const uint8_t* p = reinterpret_cast(mem.data()); + const uint8_t* const bEnd = reinterpret_cast(mem.data()) + memsize; + hash_t hash_ret; + + if (total_len > (N / 2)) + { + hash_ret = bit_ops::rotl(v1, 1) + bit_ops::rotl(v2, 7) + bit_ops::rotl(v3, 12) + bit_ops::rotl(v4, 18); + + detail::endian_align_sub_mergeround(hash_ret, v1, v2, v3, v4); + } + else { hash_ret = v3 + detail::PRIME(5); } + + hash_ret += static_cast>(total_len); + + return detail::endian_align_sub_ending(hash_ret, p, bEnd, endian, alignment::unaligned); + } + + public: + hash_state_t(hash_t seed = 0) + { + static_assert(!(N != 32 && N != 64), "You can only stream hashing in 32 or 64 bit mode."); + v1 = seed + detail::PRIME(1) + detail::PRIME(2); + v2 = seed + detail::PRIME(2); + v3 = seed + 0; + v4 = seed - detail::PRIME(1); + }; + + hash_state_t operator=(hash_state_t& other) + { + memcpy(this, other, sizeof(hash_state_t)); + } + + error_code reset(hash_t seed = 0) + { + memset(this, 0, sizeof(hash_state_t)); + v1 = seed + detail::PRIME(1) + detail::PRIME(2); + v2 = seed + detail::PRIME(2); + v3 = seed + 0; + v4 = seed - detail::PRIME(1); + return error_code::ok; + } + + error_code update(const void* input, size_t length, endianness endian = endianness::unspecified) + { + return _update_impl(input, length, mem_ops::get_endian(endian)); + } + + template + error_code update(const std::basic_string& input, endianness endian = endianness::unspecified) + { + return _update_impl(static_cast(input.data()), input.length() * sizeof(T), mem_ops::get_endian(endian)); + } + + template + error_code update(ContiguousIterator begin, ContiguousIterator end, endianness endian = endianness::unspecified) + { + using T = typename std::decay_t; + return _update_impl(static_cast(&*begin), (end - begin) * sizeof(T), mem_ops::get_endian(endian)); + } + + template + error_code update(const std::vector& input, endianness endian = endianness::unspecified) + { + return _update_impl(static_cast(input.data()), input.size() * sizeof(T), mem_ops::get_endian(endian)); + } + + template + error_code update(const std::array& input, endianness endian = endianness::unspecified) + { + return _update_impl(static_cast(input.data()), AN * sizeof(T), mem_ops::get_endian(endian)); + } + + template + error_code update(const std::initializer_list& input, endianness endian = endianness::unspecified) + { + return _update_impl(static_cast(input.begin()), input.size() * sizeof(T), mem_ops::get_endian(endian)); + } + + hash_t digest(endianness endian = endianness::unspecified) + { + return _digest_impl(mem_ops::get_endian(endian)); + } + }; + + using hash_state32_t = hash_state_t<32>; + using hash_state64_t = hash_state_t<64>; + + + /* ******************************************************************* + * Canonical + *********************************************************************/ + + template + struct canonical_t + { + std::array digest;\ + + + + canonical_t(hash_t hash) + { + if (mem_ops::is_little_endian()) { hash = bit_ops::swap(hash); } + memcpy(digest.data(), &hash, sizeof(canonical_t)); + } + + hash_t get_hash() const + { + return mem_ops::readBE(&digest); + } + }; + + using canonical32_t = canonical_t<32>; + using canonical64_t = canonical_t<64>; +}