/* * 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 2016 Danny Robson */ #include "./xxhash.hpp" #include "./debug.hpp" #include "./bitwise.hpp" #include "./endian.hpp" using util::hash::xxhash; /////////////////////////////////////////////////////////////////////////////// template static T read_le (const void *ptr) { return *static_cast (ptr); } /////////////////////////////////////////////////////////////////////////////// template struct constants { static const T prime[5]; static const T round_rotl; }; //----------------------------------------------------------------------------- template <> const uint32_t constants::prime[5] = { 2654435761u, 2246822519u, 3266489917u, 668265263u, 374761393u, }; //----------------------------------------------------------------------------- template <> const uint32_t constants::round_rotl = 13; //----------------------------------------------------------------------------- template <> const uint64_t constants::prime[5] = { 11400714785074694791u, 14029467366897019727u, 1609587929392839161u, 9650029242287828579u, 2870177450012600261u, }; //----------------------------------------------------------------------------- template <> const uint64_t constants::round_rotl = 31; /////////////////////////////////////////////////////////////////////////////// constexpr uint32_t DEFAULT_SEED = 0; //----------------------------------------------------------------------------- template xxhash::xxhash (void): xxhash (DEFAULT_SEED) { ; } //----------------------------------------------------------------------------- template xxhash::xxhash (uint32_t _seed): m_seed (_seed) { reset (); } //----------------------------------------------------------------------------- template void xxhash::reset (void) { memset (&m_state, 0, sizeof (m_state)); m_state.v1 = m_seed + constants::prime[0] + constants::prime[1]; m_state.v2 = m_seed + constants::prime[1]; m_state.v3 = m_seed; m_state.v4 = m_seed - constants::prime[0]; } /////////////////////////////////////////////////////////////////////////////// template static T round (T seed, T input) { seed += input * constants::prime[1]; seed = util::rotatel (seed, constants::round_rotl); seed *= constants::prime[0]; return seed; } /////////////////////////////////////////////////////////////////////////////// template void xxhash::update (const uint8_t *restrict first, const uint8_t *restrict last) { CHECK (first); CHECK (last); CHECK_LE (first, last); //auto endian = XXH_littleEndian; size_t len = last - first; auto input = (const void*)first; auto p = reinterpret_cast (input); auto const bEnd = p + len; constexpr auto CHUNK = 4 * sizeof (T); m_state.total_len_32 += (unsigned)len; m_state.large_len |= (len >= CHUNK) | (m_state.total_len_32 >= CHUNK); if (m_state.memsize + len < CHUNK) { /* fill in tmp buffer */ memcpy ((uint8_t*)(m_state.mem32) + m_state.memsize, input, len); m_state.memsize += (unsigned)len; return; } if (m_state.memsize) { /* some data left from previous update */ memcpy ((uint8_t*)(m_state.mem32) + m_state.memsize, input, CHUNK - m_state.memsize); { const uint32_t* p32 = m_state.mem32; m_state.v1 = round (m_state.v1, ltoh (*p32)); p32++; m_state.v2 = round (m_state.v2, ltoh (*p32)); p32++; m_state.v3 = round (m_state.v3, ltoh (*p32)); p32++; m_state.v4 = round (m_state.v4, ltoh (*p32)); p32++; } p += CHUNK - m_state.memsize; m_state.memsize = 0; } if (p <= bEnd - CHUNK * sizeof (T)) { const uint8_t* const limit = bEnd - 4 * sizeof (T); T v1 = m_state.v1; T v2 = m_state.v2; T v3 = m_state.v3; T v4 = m_state.v4; do { v1 = round (v1, read_le (p)); p += sizeof (T); v2 = round (v2, read_le (p)); p += sizeof (T); v3 = round (v3, read_le (p)); p += sizeof (T); v4 = round (v4, read_le (p)); p += sizeof (T); } while (p <= limit); m_state.v1 = v1; m_state.v2 = v2; m_state.v3 = v3; m_state.v4 = v4; } if (p < bEnd) { memcpy (m_state.mem32, p, (size_t)(bEnd-p)); m_state.memsize = (unsigned)(bEnd-p); } } /////////////////////////////////////////////////////////////////////////////// template void xxhash::finish (void) { ; } /////////////////////////////////////////////////////////////////////////////// template typename xxhash::digest_t xxhash::digest (void) const { auto p = reinterpret_cast (m_state.mem32); auto last = p + m_state.memsize; T h; if (m_state.large_len) { h = rotatel (m_state.v1, T{ 1}) + rotatel (m_state.v2, T{ 7}) + rotatel (m_state.v3, T{12}) + rotatel (m_state.v4, T{18}); } else { h = m_state.v3 /* == seed */ + constants::prime[4]; } h += m_state.total_len_32; while (p + sizeof (T) <= last) { h += read_le (p) * constants::prime[2]; h = rotatel (h, 17) * constants::prime[3]; p += 4; } while (p < last) { h += (*p) * constants::prime[4]; h = rotatel (h, 11) * constants::prime[0]; p++; } h ^= h >> 15; h *= constants::prime[1]; h ^= h >> 13; h *= constants::prime[2]; h ^= h >> 16; return h; } /////////////////////////////////////////////////////////////////////////////// template class util::hash::xxhash; template class util::hash::xxhash;