/* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2016-2018 Danny Robson <danny@nerdcruft.net> */ #include "xxhash.hpp" #include "../bitwise.hpp" #include "../debug.hpp" #include "../endian.hpp" #include <cstring> using cruft::hash::xxhash; /////////////////////////////////////////////////////////////////////////////// template <typename T> static T read_le (const void *ptr) { return *static_cast<const T*> (ptr); } /////////////////////////////////////////////////////////////////////////////// template <typename T> struct constants { static const T prime[5]; static const T final_rotate[3]; static const T round_rotate; }; //----------------------------------------------------------------------------- template <> const uint32_t constants<uint32_t>::prime[5] = { 2654435761u, 2246822519u, 3266489917u, 668265263u, 374761393u, }; template <> const uint32_t constants<uint32_t>::final_rotate[3] = { 15, 13, 16 }; //----------------------------------------------------------------------------- template <> const uint32_t constants<uint32_t>::round_rotate = 13; //----------------------------------------------------------------------------- template <> const uint64_t constants<uint64_t>::prime[5] = { 11400714785074694791ull, 14029467366897019727ull, 1609587929392839161ull, 9650029242287828579ull, 2870177450012600261ull, }; template <> const uint64_t constants<uint64_t>::final_rotate[3] = { 33, 29, 32 }; //----------------------------------------------------------------------------- template <> const uint64_t constants<uint64_t>::round_rotate = 31; /////////////////////////////////////////////////////////////////////////////// template <typename T> static T round (T state, T input) { state += input * constants<T>::prime[1]; state = cruft::rotatel (state, constants<T>::round_rotate); state *= constants<T>::prime[0]; return state; } /////////////////////////////////////////////////////////////////////////////// template <typename WordT> xxhash<WordT>::xxhash (WordT _seed): m_seed (_seed) { ; } /////////////////////////////////////////////////////////////////////////////// template <typename WordT> typename xxhash<WordT>::digest_t xxhash<WordT>::operator() (const cruft::view<const uint8_t*> data) { word_t state[4] { m_seed + constants<WordT>::prime[0] + constants<WordT>::prime[1], m_seed + constants<WordT>::prime[1], m_seed, m_seed - constants<WordT>::prime[0], }; // consume block sized chunks while they're available. // process each state word independently per block. auto cursor = std::cbegin (data); const auto last = std::cend (data); while (last - cursor > block_bytes) { for (int i = 0; i < 4; ++i) { state[i] = round<word_t> (state[i], read_le<word_t> (cursor)); cursor += sizeof (word_t); } } // leave the remainder. it's used midway through finalisation. note that we // don't update the cursor as it's used to detect the remaining bytes // during finalisation. ; // compress the state and mix in the data size word_t h; if (data.size () < block_bytes) { h = state[2] + constants<WordT>::prime[4]; } else { h = rotatel (state[0], 1) + rotatel (state[1], 7) + rotatel (state[2], 12) + rotatel (state[3], 18); if constexpr (std::is_same_v<WordT,uint64_t>) { h = (h ^ round<WordT> (0, state[0])) * constants<WordT>::prime[0] + constants<WordT>::prime[3]; h = (h ^ round<WordT> (0, state[1])) * constants<WordT>::prime[0] + constants<WordT>::prime[3]; h = (h ^ round<WordT> (0, state[2])) * constants<WordT>::prime[0] + constants<WordT>::prime[3]; h = (h ^ round<WordT> (0, state[3])) * constants<WordT>::prime[0] + constants<WordT>::prime[3]; } } h += static_cast<WordT> (data.size ()); // drain the remainder of the data, first by words... while (cursor + sizeof (WordT) <= last) { if constexpr (std::is_same_v<WordT,uint32_t>) { h += read_le<WordT> (cursor) * constants<WordT>::prime[2]; h = rotatel (h, 17) * constants<WordT>::prime[3]; } else { h = rotatel ( h ^ round<WordT> (0, read_le<WordT> (cursor)), 27 ) * constants<WordT>::prime[0] + constants<WordT>::prime[3]; } cursor += sizeof (WordT); } // ...then maybe by half words... if constexpr (std::is_same_v<WordT,uint64_t>) { while (cursor + sizeof (uint32_t) <= last) { h = rotatel ( h ^ read_le<uint32_t> (cursor) * constants<WordT>::prime[0], 23 ) * constants<WordT>::prime[1] + constants<WordT>::prime[2]; cursor += sizeof (uint32_t); } } // ...then by bytes while (cursor != last) { if constexpr (std::is_same_v<WordT,uint32_t>) { h += *cursor * constants<WordT>::prime[4]; h = rotatel (h, 11) * constants<WordT>::prime[0]; } else { h = rotatel (h ^ *cursor * constants<WordT>::prime[4], 11) * constants<WordT>::prime[0]; } ++cursor; } // everything should have been consumed by now CHECK_EQ (cursor, std::cend (data)); // mix the result one last time before returning h ^= h >> constants<WordT>::final_rotate[0]; h *= constants<WordT>::prime[1]; h ^= h >> constants<WordT>::final_rotate[1]; h *= constants<WordT>::prime[2]; h ^= h >> constants<WordT>::final_rotate[2]; return h; }; /////////////////////////////////////////////////////////////////////////////// template class cruft::hash::xxhash<uint32_t>; template class cruft::hash::xxhash<uint64_t>;