├── README.md ├── CMakeLists.txt ├── LICENSE ├── xxh32.hpp ├── test.cpp └── xxh64.hpp /README.md: -------------------------------------------------------------------------------- 1 | Compile-time xxhash implementation 2 | ================================== 3 | 4 | An implementation of the 64-bit xxhash algorithm (see https://github.com/Cyan4973/xxHash) 5 | as C++11 constexpr expression. 6 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project (xxhashct) 3 | 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 5 | 6 | add_executable (xxhashct_tests test.cpp) 7 | 8 | find_library(XXHASH_LIBRARY xxhash) 9 | find_path(XXHASH_INCLUDE_DIR xxhash.h) 10 | 11 | if (XXHASH_LIBRARY AND XXHASH_INCLUDE_DIR) 12 | target_link_libraries (xxhashct_tests ${XXHASH_LIBRARY}) 13 | target_include_directories (xxhashct_tests PRIVATE ${XXHASH_INCLUDE_DIR}) 14 | target_compile_definitions (xxhashct_tests PRIVATE HAVE_XXHASH) 15 | else () 16 | message (WARNING "xxhash library not found. Will only test against hardcoded hash values.") 17 | endif () 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2015 Daniel Kirchner 4 | Copyright (c) 2021 Zachary Arnaise 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /xxh32.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2021 Zachary Arnaise 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | #ifndef XXHASHCT_XXH32_HPP 26 | #define XXHASHCT_XXH32_HPP 27 | 28 | #include 29 | 30 | struct xxh32 { 31 | static constexpr uint32_t hash (const char *input, uint32_t len, uint32_t seed) { 32 | return finalize((len >= 16 ? h16bytes(input, len, seed) : seed + PRIME5) + len, (input) + (len & ~0xF), len & 0xF); 33 | } 34 | 35 | private: 36 | static constexpr uint32_t PRIME1 = 0x9E3779B1U; 37 | static constexpr uint32_t PRIME2 = 0x85EBCA77U; 38 | static constexpr uint32_t PRIME3 = 0xC2B2AE3DU; 39 | static constexpr uint32_t PRIME4 = 0x27D4EB2FU; 40 | static constexpr uint32_t PRIME5 = 0x165667B1U; 41 | 42 | // 32-bit rotate left. 43 | static constexpr uint32_t rotl (uint32_t x, int r) { 44 | return ((x << r) | (x >> (32 - r))); 45 | } 46 | // Normal stripe processing routine. 47 | static constexpr uint32_t round (uint32_t acc, const uint32_t input) { 48 | return rotl(acc + (input * PRIME2), 13) * PRIME1; 49 | } 50 | 51 | static constexpr uint32_t avalanche_step (const uint32_t h, const int rshift, const uint32_t prime) { 52 | return (h ^ (h >> rshift)) * prime; 53 | } 54 | // Mixes all bits to finalize the hash. 55 | static constexpr uint32_t avalanche (const uint32_t h) { 56 | return avalanche_step(avalanche_step(avalanche_step(h, 15, PRIME2), 13, PRIME3), 16, 1); 57 | } 58 | 59 | #ifdef XXH32_BIG_ENDIAN 60 | static constexpr uint32_t endian32 (const char*v) { 61 | return uint32_t(uint8_t(v[3]))|(uint32_t(uint8_t(v[2]))<<8) 62 | |(uint32_t(uint8_t(v[1]))<<16)|(uint32_t(uint8_t(v[0]))<<24); 63 | } 64 | #else 65 | static constexpr uint32_t endian32 (const char *v) { 66 | return uint32_t(uint8_t(v[0]))|(uint32_t(uint8_t(v[1])) << 8) 67 | |(uint32_t(uint8_t(v[2])) << 16)|(uint32_t(uint8_t(v[3])) << 24); 68 | } 69 | #endif // XXH32_BIG_ENDIAN 70 | 71 | static constexpr uint32_t fetch32 (const char *p, const uint32_t v) { 72 | return round(v, endian32(p)); 73 | } 74 | 75 | // Processes the last 0-15 bytes of p. 76 | static constexpr uint32_t finalize (const uint32_t h, const char *p, uint32_t len) { 77 | return 78 | (len >= 4) ? finalize(rotl(h + (endian32(p) * PRIME3), 17) * PRIME4, p + 4, len - 4) : 79 | (len > 0) ? finalize(rotl(h + (uint8_t(*p) * PRIME5), 11) * PRIME1, p + 1, len - 1) : 80 | avalanche(h); 81 | } 82 | 83 | static constexpr uint32_t h16bytes (const char *p, uint32_t len, const uint32_t v1, const uint32_t v2, const uint32_t v3, const uint32_t v4) { 84 | return 85 | (len >= 16) ? h16bytes(p + 16, len - 16, fetch32(p, v1), fetch32(p+4, v2), fetch32(p+8, v3), fetch32(p+12, v4)) : 86 | rotl(v1, 1) + rotl(v2, 7) + rotl(v3, 12) + rotl(v4, 18); 87 | } 88 | static constexpr uint32_t h16bytes (const char *p, uint32_t len, const uint32_t seed) { 89 | return h16bytes(p, len, seed + PRIME1 + PRIME2, seed + PRIME2, seed, seed - PRIME1); 90 | } 91 | }; 92 | 93 | #endif /* !defined XXHASHCT_XXH32_HPP */ 94 | -------------------------------------------------------------------------------- /test.cpp: -------------------------------------------------------------------------------- 1 | #include "xxh32.hpp" 2 | #include "xxh64.hpp" 3 | #include 4 | #include 5 | #include 6 | #ifdef HAVE_XXHASH 7 | #include 8 | #endif 9 | 10 | template 11 | struct constexpr_test { static_assert(value == expected, "Compile-time hash mismatch."); }; 12 | 13 | constexpr_test constexprTest_1; 14 | constexpr_test constexprTest_2; 15 | constexpr_test constexprTest_3; 16 | constexpr_test constexprTest_4; 17 | constexpr_test constexprTest32_5; 18 | constexpr_test constexprTest32_6; 19 | constexpr_test constexprTest64_7; 20 | constexpr_test constexprTest64_8; 21 | 22 | bool checkhash_32 (const void* ptr, int len, uint32_t seed, uint32_t expected) 23 | { 24 | const uint32_t hash = xxh32::hash (reinterpret_cast (ptr), len, seed); 25 | if (hash != expected) { 26 | std::cerr << "[xxh32] Test failed: 0x" << std::hex << hash << " != 0x" << std::hex << expected << std::endl; 27 | return false; 28 | } 29 | #ifdef HAVE_XXHASH 30 | uint64_t ref = XXH32(ptr, len, seed); 31 | if (hash != ref) { 32 | std::cerr << "Test against reference implementation failed: 0x" << std::hex << hash << " != 0x" << std::hex << ref << std::endl; 33 | return false; 34 | } 35 | #endif 36 | return true; 37 | } 38 | 39 | bool checkhash_64 (const void *ptr, int len, uint64_t seed, uint64_t expected) 40 | { 41 | uint64_t hash = xxh64::hash(reinterpret_cast (ptr), len, seed); 42 | if (hash != expected) { 43 | std::cerr << "[xxh64] Test failed: 0x" << std::hex << hash << " != 0x" << std::hex << expected << std::endl; 44 | return false; 45 | } 46 | #ifdef HAVE_XXHASH 47 | uint64_t ref = XXH64(ptr, len, seed); 48 | if (hash != ref) { 49 | std::cerr << "Test against reference implementation failed: 0x" << std::hex << hash << " != 0x" << std::hex << ref << std::endl; 50 | return false; 51 | } 52 | #endif 53 | return true; 54 | } 55 | 56 | int main (int argc, char *argv[]) 57 | { 58 | static std::array data; 59 | static constexpr uint32_t prime = 2654435761U; 60 | 61 | uint32_t byteGen = prime; 62 | for (auto i = 0; i < data.size (); i++) { 63 | data[i] = byteGen >> 24; 64 | byteGen *= byteGen; 65 | } 66 | 67 | bool result_32 = true; 68 | 69 | result_32 &= checkhash_32 (nullptr, 0, 0, 0x2CC5D05U); 70 | result_32 &= checkhash_32 (nullptr, 0, prime, 0x36B78AE7U); 71 | result_32 &= checkhash_32 (data.data (), 1, 0, 0xB85CBEE5U); 72 | result_32 &= checkhash_32 (data.data (), 1, prime, 0xD5845D64U); 73 | result_32 &= checkhash_32 (data.data (), 14, 0, 0xE5AA0AB4U); 74 | result_32 &= checkhash_32 (data.data (), 14, prime, 0x4481951DU); 75 | result_32 &= checkhash_32 (data.data (), 101, 0, 0x1F1AA412U); 76 | result_32 &= checkhash_32 (data.data (), 101, prime, 0x498EC8E2U); 77 | result_32 &= checkhash_32 (data.data (), data.size (), 0, 0x3931B56F); 78 | result_32 &= checkhash_32 (data.data (), data.size (), prime, 0x44857AA1); 79 | 80 | if (result_32) std::cout << "xxh32 tests passed successfully." << std::endl; 81 | 82 | bool result_64 = true; 83 | 84 | result_64 &= checkhash_64 (nullptr, 0, 0, 0xEF46DB3751D8E999ULL); 85 | result_64 &= checkhash_64 (nullptr, 0, prime, 0xAC75FDA2929B17EFULL); 86 | result_64 &= checkhash_64 (data.data (), 1, 0, 0x4FCE394CC88952D8ULL); 87 | result_64 &= checkhash_64 (data.data (), 1, prime, 0x739840CB819FA723ULL); 88 | result_64 &= checkhash_64 (data.data (), 14, 0, 0xCFFA8DB881BC3A3DULL); 89 | result_64 &= checkhash_64 (data.data (), 14, prime, 0x5B9611585EFCC9CBULL); 90 | result_64 &= checkhash_64 (data.data (), data.size (), 0, 0x7570A206CFC74b78ULL); 91 | result_64 &= checkhash_64 (data.data (), data.size (), prime, 0x95A9A4036AE9A784ULL); 92 | 93 | if (result_64) std::cout << "xxh64 tests passed successfully." << std::endl; 94 | 95 | if (result_32 && result_64) std::cout << "All tests passed successfully." << std::endl; 96 | return (result_32 && true) ? EXIT_SUCCESS : EXIT_FAILURE; 97 | } 98 | -------------------------------------------------------------------------------- /xxh64.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Daniel Kirchner 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | #ifndef XXH64_HPP 24 | #define XXH64_HPP 25 | 26 | #include 27 | 28 | struct xxh64 { 29 | static constexpr uint64_t hash (const char *p, uint64_t len, uint64_t seed) { 30 | return finalize ((len >= 32 ? h32bytes (p, len, seed) : seed + PRIME5) + len, p + (len & ~0x1F), len & 0x1F); 31 | } 32 | private: 33 | static constexpr uint64_t PRIME1 = 11400714785074694791ULL; 34 | static constexpr uint64_t PRIME2 = 14029467366897019727ULL; 35 | static constexpr uint64_t PRIME3 = 1609587929392839161ULL; 36 | static constexpr uint64_t PRIME4 = 9650029242287828579ULL; 37 | static constexpr uint64_t PRIME5 = 2870177450012600261ULL; 38 | 39 | static constexpr uint64_t rotl (uint64_t x, int r) { 40 | return ((x << r) | (x >> (64 - r))); 41 | } 42 | static constexpr uint64_t mix1 (const uint64_t h, const uint64_t prime, int rshift) { 43 | return (h ^ (h >> rshift)) * prime; 44 | } 45 | static constexpr uint64_t mix2 (const uint64_t p, const uint64_t v = 0) { 46 | return rotl (v + p * PRIME2, 31) * PRIME1; 47 | } 48 | static constexpr uint64_t mix3 (const uint64_t h, const uint64_t v) { 49 | return (h ^ mix2 (v)) * PRIME1 + PRIME4; 50 | } 51 | #ifdef XXH64_BIG_ENDIAN 52 | static constexpr uint32_t endian32 (const char *v) { 53 | return uint32_t(uint8_t(v[3]))|(uint32_t(uint8_t(v[2]))<<8) 54 | |(uint32_t(uint8_t(v[1]))<<16)|(uint32_t(uint8_t(v[0]))<<24); 55 | } 56 | static constexpr uint64_t endian64 (const char *v) 57 | { 58 | return uint64_t(uint8_t(v[7]))|(uint64_t(uint8_t(v[6]))<<8) 59 | |(uint64_t(uint8_t(v[5]))<<16)|(uint64_t(uint8_t(v[4]))<<24) 60 | |(uint64_t(uint8_t(v[3]))<<32)|(uint64_t(uint8_t(v[2]))<<40) 61 | |(uint64_t(uint8_t(v[1]))<<48)|(uint64_t(uint8_t(v[0]))<<56); 62 | } 63 | #else 64 | static constexpr uint32_t endian32 (const char *v) { 65 | return uint32_t(uint8_t(v[0]))|(uint32_t(uint8_t(v[1]))<<8) 66 | |(uint32_t(uint8_t(v[2]))<<16)|(uint32_t(uint8_t(v[3]))<<24); 67 | } 68 | static constexpr uint64_t endian64 (const char *v) { 69 | return uint64_t(uint8_t(v[0]))|(uint64_t(uint8_t(v[1]))<<8) 70 | |(uint64_t(uint8_t(v[2]))<<16)|(uint64_t(uint8_t(v[3]))<<24) 71 | |(uint64_t(uint8_t(v[4]))<<32)|(uint64_t(uint8_t(v[5]))<<40) 72 | |(uint64_t(uint8_t(v[6]))<<48)|(uint64_t(uint8_t(v[7]))<<56); 73 | } 74 | #endif 75 | static constexpr uint64_t fetch64 (const char *p, const uint64_t v = 0) { 76 | return mix2 (endian64 (p), v); 77 | } 78 | static constexpr uint64_t fetch32 (const char *p) { 79 | return uint64_t (endian32 (p)) * PRIME1; 80 | } 81 | static constexpr uint64_t fetch8 (const char *p) { 82 | return uint8_t (*p) * PRIME5; 83 | } 84 | static constexpr uint64_t finalize (const uint64_t h, const char *p, uint64_t len) { 85 | return (len >= 8) ? (finalize (rotl (h ^ fetch64 (p), 27) * PRIME1 + PRIME4, p + 8, len - 8)) : 86 | ((len >= 4) ? (finalize (rotl (h ^ fetch32 (p), 23) * PRIME2 + PRIME3, p + 4, len - 4)) : 87 | ((len > 0) ? (finalize (rotl (h ^ fetch8 (p), 11) * PRIME1, p + 1, len - 1)) : 88 | (mix1 (mix1 (mix1 (h, PRIME2, 33), PRIME3, 29), 1, 32)))); 89 | } 90 | static constexpr uint64_t h32bytes (const char *p, uint64_t len, const uint64_t v1,const uint64_t v2, const uint64_t v3, const uint64_t v4) { 91 | return (len >= 32) ? h32bytes (p + 32, len - 32, fetch64 (p, v1), fetch64 (p + 8, v2), fetch64 (p + 16, v3), fetch64 (p + 24, v4)) : 92 | mix3 (mix3 (mix3 (mix3 (rotl (v1, 1) + rotl (v2, 7) + rotl (v3, 12) + rotl (v4, 18), v1), v2), v3), v4); 93 | } 94 | static constexpr uint64_t h32bytes (const char *p, uint64_t len, const uint64_t seed) { 95 | return h32bytes (p, len, seed + PRIME1 + PRIME2, seed + PRIME2, seed, seed - PRIME1); 96 | } 97 | }; 98 | 99 | #endif /* !defined XXH64_HPP */ 100 | --------------------------------------------------------------------------------