diff --git a/CMakeLists.txt b/CMakeLists.txt index a03fea3e2..f8da68d36 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -205,6 +205,7 @@ file (GLOB CXBXR_SOURCE_COMMON "${CXBXR_ROOT_DIR}/src/common/Timer.cpp" "${CXBXR_ROOT_DIR}/src/common/util/crc32c.cpp" "${CXBXR_ROOT_DIR}/src/common/util/CxbxUtil.cpp" + "${CXBXR_ROOT_DIR}/src/common/util/hasher.cpp" "${CXBXR_ROOT_DIR}/src/common/win32/DInputController.cpp" "${CXBXR_ROOT_DIR}/src/common/win32/EmuShared.cpp" "${CXBXR_ROOT_DIR}/src/common/win32/InlineFunc.cpp" diff --git a/projects/cxbx/CMakeLists.txt b/projects/cxbx/CMakeLists.txt index cb869a8ab..b277805c2 100644 --- a/projects/cxbx/CMakeLists.txt +++ b/projects/cxbx/CMakeLists.txt @@ -32,10 +32,9 @@ link_directories( if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") add_compile_definitions( - _CRT_SECURE_NO_WARNINGS + _CRT_SECURE_NO_WARNINGS # Windows 7 minimum requirement _WIN32_WINNT=0x0601 - LTM_DESC USE_LTM LTC_NO_TEST @@ -45,6 +44,9 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") LTC_NO_PRNGS LTC_NO_MISC LTC_NO_PROTOTYPES + + # Use inline XXHash version + XXH_INLINE_ALL ) # Reference: https://docs.microsoft.com/en-us/cpp/build/reference/compiler-options-listed-alphabetically diff --git a/src/common/util/hasher.cpp b/src/common/util/hasher.cpp new file mode 100644 index 000000000..541818ec4 --- /dev/null +++ b/src/common/util/hasher.cpp @@ -0,0 +1,42 @@ +#include "hasher.h" + +#include "xxhash.h" +#include "crc32c.h" +#include + +enum { + HASH_NONE = 0, + HASH_XXH3, + HASH_CRC32C +}; + +static int g_HashAlgorithm = HASH_NONE; + +void InitHasher() +{ + // Detect the best hashing algorithm to use for the host machine + // TODO/Future Improvement: This could be expanded to support even more hash algorithims + // And we could hash a random buffer to calculate the fastest hash to use on a given host + printf("Selecting hash algorithm: "); + if (crc32c_hw_available()) { + printf("CRC32C\n"); + g_HashAlgorithm = HASH_CRC32C; + } else { + printf("XXH3\n"); + g_HashAlgorithm = HASH_XXH3; + } +} + +__forceinline uint64_t ComputeHash(void* data, size_t len) +{ + if (g_HashAlgorithm == HASH_NONE) { + InitHasher(); + } + + switch (g_HashAlgorithm) { + case HASH_XXH3: return XXH3_64bits(data, len); + case HASH_CRC32C: return crc32c_append(0, (uint8_t*)data, len); + } + + return 0; +} diff --git a/src/common/util/hasher.h b/src/common/util/hasher.h new file mode 100644 index 000000000..8ec92c14f --- /dev/null +++ b/src/common/util/hasher.h @@ -0,0 +1,34 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com +// ****************************************************************** +// * +// * This file is part of the Cxbx project. +// * +// * Cxbx and Cxbe are free software; you can redistribute them +// * and/or modify them under the terms of the GNU General Public +// * License as published by the Free Software Foundation; either +// * version 2 of the license, or (at your option) any later version. +// * +// * This program is distributed in the hope that it will be useful, +// * but WITHOUT ANY WARRANTY; without even the implied warranty of +// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// * GNU General Public License for more details. +// * +// * You should have recieved a copy of the GNU General Public License +// * along with this program; see the file COPYING. +// * If not, write to the Free Software Foundation, Inc., +// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. +// * +// * (c) 2019 - Luke Usher +// * +// * All rights reserved +// * +// ****************************************************************** +#ifndef _HASHER_H +#define _HASHER_H + +#include + +extern __forceinline uint64_t ComputeHash(void* data, size_t len); + +#endif diff --git a/src/common/util/xxh3.h b/src/common/util/xxh3.h new file mode 100644 index 000000000..9d4ad5999 --- /dev/null +++ b/src/common/util/xxh3.h @@ -0,0 +1,1222 @@ +/* + xxHash - Extremely Fast Hash algorithm + Development source file for `xxh3` + Copyright (C) 2019-present, Yann Collet. + + 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 +*/ + +/* Note : + This file is separated for development purposes. + It will be integrated into `xxhash.c` when development phase is complete. +*/ + +#ifndef XXH3_H +#define XXH3_H + + +/* === Dependencies === */ + +#undef XXH_INLINE_ALL /* in case it's already defined */ +#define XXH_INLINE_ALL +#include "xxhash.h" + +#undef NDEBUG /* avoid redefinition */ +#define NDEBUG /* disable assert (release mode) */ +#include +#ifndef NDEBUG +# define NDEBUG +# define UNDEF_NDEBUG +# include +#endif + + +/* === Compiler specifics === */ + +#if !(defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) /* < C99 */ +# define restrict /* disable */ +#endif + +#if defined(__GNUC__) +# if defined(__AVX2__) +# include +# elif defined(__SSE2__) +# include +# elif defined(__ARM_NEON__) || defined(__ARM_NEON) +# define inline __inline__ /* clang bug */ +# include +# undef inline +# endif +#elif defined(_MSC_VER) +# include +#endif + + + +/* ========================================== + * Vectorization detection + * ========================================== */ +#define XXH_SCALAR 0 +#define XXH_SSE2 1 +#define XXH_AVX2 2 +#define XXH_NEON 3 +#define XXH_VSX 4 + +#ifndef XXH_VECTOR /* can be defined on command line */ +# if defined(__AVX2__) +# define XXH_VECTOR XXH_AVX2 +# elif defined(__SSE2__) || defined(_M_AMD64) || defined(_M_X64) || (defined(_M_IX86_FP) && (_M_IX86_FP == 2)) +# define XXH_VECTOR XXH_SSE2 +# elif defined(__GNUC__) /* msvc support maybe later */ \ + && (defined(__ARM_NEON__) || defined(__ARM_NEON)) \ + && defined(__LITTLE_ENDIAN__) /* ARM big endian is a thing */ +# define XXH_VECTOR XXH_NEON +# elif defined(__PPC64__) && defined(__VSX__) && defined(__GNUC__) +# define XXH_VECTOR XXH_VSX +# else +# define XXH_VECTOR XXH_SCALAR +# endif +#endif + +/* control alignment of accumulator, + * for compatibility with fast vector loads */ +#ifndef XXH_ACC_ALIGN +# if XXH_VECTOR == 0 /* scalar */ +# define XXH_ACC_ALIGN 8 +# elif XXH_VECTOR == 1 /* sse2 */ +# define XXH_ACC_ALIGN 16 +# elif XXH_VECTOR == 2 /* avx2 */ +# define XXH_ACC_ALIGN 32 +# elif XXH_VECTOR == 3 /* neon */ +# define XXH_ACC_ALIGN 16 +# elif XXH_VECTOR == 4 /* vsx */ +# define XXH_ACC_ALIGN 16 +# endif +#endif + +/* U64 XXH_mult32to64(U32 a, U64 b) { return (U64)a * (U64)b; } */ +#ifdef _MSC_VER +# include + /* MSVC doesn't do a good job with the mull detection. */ +# define XXH_mult32to64 __emulu +#else +# define XXH_mult32to64(x, y) ((U64)((x) & 0xFFFFFFFF) * (U64)((y) & 0xFFFFFFFF)) +#endif + +/* VSX stuff */ +#if XXH_VECTOR == XXH_VSX +# include +# undef vector +typedef __vector unsigned long long U64x2; +typedef __vector unsigned U32x4; +/* Adapted from https://github.com/google/highwayhash/blob/master/highwayhash/hh_vsx.h. */ +XXH_FORCE_INLINE U64x2 XXH_vsxMultOdd(U32x4 a, U32x4 b) { + U64x2 result; + __asm__("vmulouw %0, %1, %2" : "=v" (result) : "v" (a), "v" (b)); + return result; +} +XXH_FORCE_INLINE U64x2 XXH_vsxMultEven(U32x4 a, U32x4 b) { + U64x2 result; + __asm__("vmuleuw %0, %1, %2" : "=v" (result) : "v" (a), "v" (b)); + return result; +} +#endif + + +/* ========================================== + * XXH3 default settings + * ========================================== */ + +#define XXH_SECRET_DEFAULT_SIZE 192 /* minimum XXH_SECRET_SIZE_MIN */ + +#if (XXH_SECRET_DEFAULT_SIZE < XXH_SECRET_SIZE_MIN) +# error "default keyset is not large enough" +#endif + +XXH_ALIGN(64) static const BYTE kSecret[XXH_SECRET_DEFAULT_SIZE] = { + 0xb8, 0xfe, 0x6c, 0x39, 0x23, 0xa4, 0x4b, 0xbe, 0x7c, 0x01, 0x81, 0x2c, 0xf7, 0x21, 0xad, 0x1c, + 0xde, 0xd4, 0x6d, 0xe9, 0x83, 0x90, 0x97, 0xdb, 0x72, 0x40, 0xa4, 0xa4, 0xb7, 0xb3, 0x67, 0x1f, + 0xcb, 0x79, 0xe6, 0x4e, 0xcc, 0xc0, 0xe5, 0x78, 0x82, 0x5a, 0xd0, 0x7d, 0xcc, 0xff, 0x72, 0x21, + 0xb8, 0x08, 0x46, 0x74, 0xf7, 0x43, 0x24, 0x8e, 0xe0, 0x35, 0x90, 0xe6, 0x81, 0x3a, 0x26, 0x4c, + 0x3c, 0x28, 0x52, 0xbb, 0x91, 0xc3, 0x00, 0xcb, 0x88, 0xd0, 0x65, 0x8b, 0x1b, 0x53, 0x2e, 0xa3, + 0x71, 0x64, 0x48, 0x97, 0xa2, 0x0d, 0xf9, 0x4e, 0x38, 0x19, 0xef, 0x46, 0xa9, 0xde, 0xac, 0xd8, + 0xa8, 0xfa, 0x76, 0x3f, 0xe3, 0x9c, 0x34, 0x3f, 0xf9, 0xdc, 0xbb, 0xc7, 0xc7, 0x0b, 0x4f, 0x1d, + 0x8a, 0x51, 0xe0, 0x4b, 0xcd, 0xb4, 0x59, 0x31, 0xc8, 0x9f, 0x7e, 0xc9, 0xd9, 0x78, 0x73, 0x64, + + 0xea, 0xc5, 0xac, 0x83, 0x34, 0xd3, 0xeb, 0xc3, 0xc5, 0x81, 0xa0, 0xff, 0xfa, 0x13, 0x63, 0xeb, + 0x17, 0x0d, 0xdd, 0x51, 0xb7, 0xf0, 0xda, 0x49, 0xd3, 0x16, 0x55, 0x26, 0x29, 0xd4, 0x68, 0x9e, + 0x2b, 0x16, 0xbe, 0x58, 0x7d, 0x47, 0xa1, 0xfc, 0x8f, 0xf8, 0xb8, 0xd1, 0x7a, 0xd0, 0x31, 0xce, + 0x45, 0xcb, 0x3a, 0x8f, 0x95, 0x16, 0x04, 0x28, 0xaf, 0xd7, 0xfb, 0xca, 0xbb, 0x4b, 0x40, 0x7e, +}; + + +#if defined(__GNUC__) && defined(__i386__) +/* GCC is stupid and tries to vectorize this. + * This tells GCC that it is wrong. */ +__attribute__((__target__("no-sse"))) +#endif +static U64 +XXH3_mul128_fold64(U64 ll1, U64 ll2) +{ +#if defined(__SIZEOF_INT128__) || (defined(_INTEGRAL_MAX_BITS) && _INTEGRAL_MAX_BITS >= 128) + + __uint128_t lll = (__uint128_t)ll1 * ll2; + return (U64)lll ^ (U64)(lll >> 64); + +#elif defined(_M_X64) || defined(_M_IA64) + +#ifndef _MSC_VER +# pragma intrinsic(_umul128) +#endif + U64 llhigh; + U64 const lllow = _umul128(ll1, ll2, &llhigh); + return lllow ^ llhigh; + + /* We have to do it out manually on 32-bit. + * This is a modified, unrolled, widened, and optimized version of the + * mulqdu routine from Hacker's Delight. + * + * https://www.hackersdelight.org/hdcodetxt/mulqdu.c.txt + * + * This was modified to use U32->U64 multiplication instead + * of U16->U32, to add the high and low values in the end, + * be endian-independent, and I added a partial assembly + * implementation for ARM. */ + + /* An easy 128-bit folding multiply on ARMv6T2 and ARMv7-A/R can be done with + * the mighty umaal (Unsigned Multiply Accumulate Accumulate Long) which takes 4 cycles + * or less, doing a long multiply and adding two 32-bit integers: + * + * void umaal(U32 *RdLo, U32 *RdHi, U32 Rn, U32 Rm) + * { + * U64 prodAcc = (U64)Rn * (U64)Rm; + * prodAcc += *RdLo; + * prodAcc += *RdHi; + * *RdLo = prodAcc & 0xFFFFFFFF; + * *RdHi = prodAcc >> 32; + * } + * + * This is compared to umlal which adds to a single 64-bit integer: + * + * void umlal(U32 *RdLo, U32 *RdHi, U32 Rn, U32 Rm) + * { + * U64 prodAcc = (U64)Rn * (U64)Rm; + * prodAcc += (*RdLo | ((U64)*RdHi << 32); + * *RdLo = prodAcc & 0xFFFFFFFF; + * *RdHi = prodAcc >> 32; + * } + * + * Getting the compiler to emit them is like pulling teeth, and checking + * for it is annoying because ARMv7-M lacks this instruction. However, it + * is worth it, because this is an otherwise expensive operation. */ + + /* GCC-compatible, ARMv6t2 or ARMv7+, non-M variant, and 32-bit */ +#elif defined(__GNUC__) /* GCC-compatible */ \ + && defined(__ARM_ARCH) && !defined(__aarch64__) && !defined(__arm64__) /* 32-bit ARM */\ + && !defined(__ARM_ARCH_7M__) /* <- Not ARMv7-M vv*/ \ + && !(defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM == 0 && __TARGET_ARCH_THUMB == 4) \ + && (defined(__ARM_ARCH_6T2__) || __ARM_ARCH > 6) /* ARMv6T2 or later */ + + U32 w[4] = { 0 }; + U32 u[2] = { (U32)(ll1 >> 32), (U32)ll1 }; + U32 v[2] = { (U32)(ll2 >> 32), (U32)ll2 }; + U32 k; + + /* U64 t = (U64)u[1] * (U64)v[1]; + * w[3] = t & 0xFFFFFFFF; + * k = t >> 32; */ + __asm__("umull %0, %1, %2, %3" + : "=r" (w[3]), "=r" (k) + : "r" (u[1]), "r" (v[1])); + + /* t = (U64)u[0] * (U64)v[1] + w[2] + k; + * w[2] = t & 0xFFFFFFFF; + * k = t >> 32; */ + __asm__("umaal %0, %1, %2, %3" + : "+r" (w[2]), "+r" (k) + : "r" (u[0]), "r" (v[1])); + w[1] = k; + k = 0; + + /* t = (U64)u[1] * (U64)v[0] + w[2] + k; + * w[2] = t & 0xFFFFFFFF; + * k = t >> 32; */ + __asm__("umaal %0, %1, %2, %3" + : "+r" (w[2]), "+r" (k) + : "r" (u[1]), "r" (v[0])); + + /* t = (U64)u[0] * (U64)v[0] + w[1] + k; + * w[1] = t & 0xFFFFFFFF; + * k = t >> 32; */ + __asm__("umaal %0, %1, %2, %3" + : "+r" (w[1]), "+r" (k) + : "r" (u[0]), "r" (v[0])); + w[0] = k; + + return (w[1] | ((U64)w[0] << 32)) ^ (w[3] | ((U64)w[2] << 32)); + +#else /* Portable scalar version */ + + /* emulate 64x64->128b multiplication, using four 32x32->64 */ + U32 const h1 = (U32)(ll1 >> 32); + U32 const h2 = (U32)(ll2 >> 32); + U32 const l1 = (U32)ll1; + U32 const l2 = (U32)ll2; + + U64 const llh = XXH_mult32to64(h1, h2); + U64 const llm1 = XXH_mult32to64(l1, h2); + U64 const llm2 = XXH_mult32to64(h1, l2); + U64 const lll = XXH_mult32to64(l1, l2); + + U64 const t = lll + (llm1 << 32); + U64 const carry1 = t < lll; + + U64 const lllow = t + (llm2 << 32); + U64 const carry2 = lllow < t; + U64 const llhigh = llh + (llm1 >> 32) + (llm2 >> 32) + carry1 + carry2; + + return llhigh ^ lllow; + +#endif +} + + +static XXH64_hash_t XXH3_avalanche(U64 h64) +{ + h64 ^= h64 >> 37; + h64 *= PRIME64_3; + h64 ^= h64 >> 32; + return h64; +} + + +/* ========================================== + * Short keys + * ========================================== */ + +XXH_FORCE_INLINE XXH64_hash_t +XXH3_len_1to3_64b(const void* data, size_t len, const void* keyPtr, XXH64_hash_t seed) +{ + assert(data != NULL); + assert(len > 1 && len <= 3); + assert(keyPtr != NULL); + { BYTE const c1 = ((const BYTE*)data)[0]; + BYTE const c2 = ((const BYTE*)data)[len >> 1]; + BYTE const c3 = ((const BYTE*)data)[len - 1]; + U32 const combined = ((U32)c1) + (((U32)c2) << 8) + (((U32)c3) << 16) + (((U32)len) << 24); + U64 const keyed = (U64)combined ^ (XXH_readLE64(keyPtr) + seed); + U64 const mixed = keyed * PRIME64_1; + return XXH3_avalanche(mixed); + } +} + +XXH_FORCE_INLINE XXH64_hash_t +XXH3_len_4to8_64b(const void* data, size_t len, const void* keyPtr, XXH64_hash_t seed) +{ + assert(data != NULL); + assert(key != NULL); + assert(len >= 4 && len <= 8); + { U32 const in1 = XXH_readLE32(data); + U32 const in2 = XXH_readLE32((const BYTE*)data + len - 4); + U64 const in64 = in1 + ((U64)in2 << 32); + U64 const keyed = in64 ^ (XXH_readLE64(keyPtr) + seed); + U64 const mix64 = len + XXH3_mul128_fold64(keyed, PRIME64_1); + return XXH3_avalanche(mix64); + } +} + +XXH_FORCE_INLINE XXH64_hash_t +XXH3_len_9to16_64b(const void* data, size_t len, const void* keyPtr, XXH64_hash_t seed) +{ + assert(data != NULL); + assert(key != NULL); + assert(len >= 9 && len <= 16); + { const U64* const key64 = (const U64*) keyPtr; + U64 const ll1 = XXH_readLE64(data) ^ (XXH_readLE64(key64) + seed); + U64 const ll2 = XXH_readLE64((const BYTE*)data + len - 8) ^ (XXH_readLE64(key64+1) - seed); + U64 const acc = len + (ll1 + ll2) + XXH3_mul128_fold64(ll1, ll2); + return XXH3_avalanche(acc); + } +} + +XXH_FORCE_INLINE XXH64_hash_t +XXH3_len_0to16_64b(const void* data, size_t len, const void* keyPtr, XXH64_hash_t seed) +{ + assert(data != NULL); + assert(len <= 16); + { if (len > 8) return XXH3_len_9to16_64b(data, len, keyPtr, seed); + if (len >= 4) return XXH3_len_4to8_64b(data, len, keyPtr, seed); + if (len) return XXH3_len_1to3_64b(data, len, keyPtr, seed); + return 0; + } +} + + +/* === Long Keys === */ + +#define STRIPE_LEN 64 +#define XXH_SECRET_CONSUME_RATE 8 /* nb of secret bytes consumed at each accumulation */ +#define ACC_NB (STRIPE_LEN / sizeof(U64)) + +XXH_FORCE_INLINE void +XXH3_accumulate_512(void* restrict acc, const void* restrict data, const void* restrict key) +{ +#if (XXH_VECTOR == XXH_AVX2) + + assert(((size_t)acc) & 31 == 0); + { XXH_ALIGN(32) __m256i* const xacc = (__m256i *) acc; + const __m256i* const xdata = (const __m256i *) data; /* not really aligned, just for ptr arithmetic, and because _mm256_loadu_si256() requires this type */ + const __m256i* const xkey = (const __m256i *) key; /* not really aligned, just for ptr arithmetic, and because _mm256_loadu_si256() requires this type */ + + size_t i; + for (i=0; i < STRIPE_LEN/sizeof(__m256i); i++) { + __m256i const d = _mm256_loadu_si256 (xdata+i); + __m256i const k = _mm256_loadu_si256 (xkey+i); + __m256i const dk = _mm256_xor_si256 (d,k); /* uint32 dk[8] = {d0+k0, d1+k1, d2+k2, d3+k3, ...} */ + __m256i const mul = _mm256_mul_epu32 (dk, _mm256_shuffle_epi32 (dk, 0x31)); /* uint64 mul[4] = {dk0*dk1, dk2*dk3, ...} */ + __m256i const add = _mm256_add_epi64(d, xacc[i]); + xacc[i] = _mm256_add_epi64(mul, add); + } } + +#elif (XXH_VECTOR == XXH_SSE2) + + assert(((size_t)acc) & 15 == 0); + { XXH_ALIGN(16) __m128i* const xacc = (__m128i *) acc; /* presumed */ + const __m128i* const xdata = (const __m128i *) data; /* not really aligned, just for ptr arithmetic, and because _mm_loadu_si128() requires this type */ + const __m128i* const xkey = (const __m128i *) key; /* not really aligned, just for ptr arithmetic, and because _mm_loadu_si128() requires this type */ + + size_t i; + for (i=0; i < STRIPE_LEN/sizeof(__m128i); i++) { + __m128i const d = _mm_loadu_si128 (xdata+i); + __m128i const k = _mm_loadu_si128 (xkey+i); + __m128i const dk = _mm_xor_si128 (d,k); /* uint32 dk[4] = {d0+k0, d1+k1, d2+k2, d3+k3} */ + __m128i const mul = _mm_mul_epu32 (dk, _mm_shuffle_epi32 (dk, 0x31)); /* uint64 mul[2] = {dk0*dk1,dk2*dk3} */ + __m128i const add = _mm_add_epi64(d, xacc[i]); + xacc[i] = _mm_add_epi64(mul, add); + } } + +#elif (XXH_VECTOR == XXH_NEON) + + assert((((size_t)acc) & 15) == 0); + { + XXH_ALIGN(16) uint64x2_t* const xacc = (uint64x2_t *) acc; + /* We don't use a uint32x4_t pointer because it causes bus errors on ARMv7. */ + uint32_t const* const xdata = (const uint32_t *) data; + uint32_t const* const xkey = (const uint32_t *) key; + + size_t i; + for (i=0; i < STRIPE_LEN / sizeof(uint64x2_t); i++) { +#if !defined(__aarch64__) && !defined(__arm64__) && defined(__GNUC__) /* ARM32-specific hack */ + /* vzip on ARMv7 Clang generates a lot of vmovs (technically vorrs) without this. + * vzip on 32-bit ARM NEON will overwrite the original register, and I think that Clang + * assumes I don't want to destroy it and tries to make a copy. This slows down the code + * a lot. + * aarch64 not only uses an entirely different syntax, but it requires three + * instructions... + * ext v1.16B, v0.16B, #8 // select high bits because aarch64 can't address them directly + * zip1 v3.2s, v0.2s, v1.2s // first zip + * zip2 v2.2s, v0.2s, v1.2s // second zip + * ...to do what ARM does in one: + * vzip.32 d0, d1 // Interleave high and low bits and overwrite. */ + + /* data_vec = xdata[i]; */ + uint32x4_t const data_vec = vld1q_u32(xdata + (i * 4)); + /* key_vec = xkey[i]; */ + uint32x4_t const key_vec = vld1q_u32(xkey + (i * 4)); + /* data_key = data_vec ^ key_vec; */ + uint32x4_t data_key; + /* Add first to prevent register swaps */ + /* xacc[i] += data_vec; */ + xacc[i] = vaddq_u64(xacc[i], vreinterpretq_u64_u32(data_vec)); + + data_key = veorq_u32(data_vec, key_vec); + + /* Here's the magic. We use the quirkiness of vzip to shuffle data_key in place. + * shuffle: data_key[0, 1, 2, 3] = data_key[0, 2, 1, 3] */ + __asm__("vzip.32 %e0, %f0" : "+w" (data_key)); + /* xacc[i] += (uint64x2_t) data_key[0, 1] * (uint64x2_t) data_key[2, 3]; */ + xacc[i] = vmlal_u32(xacc[i], vget_low_u32(data_key), vget_high_u32(data_key)); +#else + /* On aarch64, vshrn/vmovn seems to be equivalent to, if not faster than, the vzip method. */ + + /* data_vec = xdata[i]; */ + uint32x4_t const data_vec = vld1q_u32(xdata + (i * 4)); + /* key_vec = xkey[i]; */ + uint32x4_t const key_vec = vld1q_u32(xkey + (i * 4)); + /* data_key = data_vec ^ key_vec; */ + uint32x4_t const data_key = veorq_u32(data_vec, key_vec); + /* data_key_lo = (uint32x2_t) (data_key & 0xFFFFFFFF); */ + uint32x2_t const data_key_lo = vmovn_u64 (vreinterpretq_u64_u32(data_key)); + /* data_key_hi = (uint32x2_t) (data_key >> 32); */ + uint32x2_t const data_key_hi = vshrn_n_u64 (vreinterpretq_u64_u32(data_key), 32); + /* xacc[i] += data_vec; */ + xacc[i] = vaddq_u64 (xacc[i], vreinterpretq_u64_u32(data_vec)); + /* xacc[i] += (uint64x2_t) data_key_lo * (uint64x2_t) data_key_hi; */ + xacc[i] = vmlal_u32 (xacc[i], data_key_lo, data_key_hi); +#endif + } + } + +#elif XXH_VECTOR == XXH_VSX + U64x2* const xacc = (U64x2*) acc; /* presumed aligned */ + U64x2 const* const xdata = (U64x2 const*) data; /* no alignment restriction */ + U64x2 const* const xkey = (U64x2 const*) key; /* no alignment restriction */ + U64x2 const v32 = { 32, 32 }; + + size_t i; + for (i = 0; i < STRIPE_LEN / sizeof(U64x2); i++) { + /* data_vec = xdata[i]; */ + /* key_vec = xkey[i]; */ +#ifdef __BIG_ENDIAN__ + /* byteswap */ + U64x2 const data_vec = vec_revb(vec_vsx_ld(0, xdata + i)); + /* swap 32-bit words */ + U64x2 const key_vec = vec_rl(vec_vsx_ld(0, xkey + i), v32); +#else + U64x2 const data_vec = vec_vsx_ld(0, xdata + i); + U64x2 const key_vec = vec_vsx_ld(0, xkey + i); +#endif + U64x2 data_key = data_vec ^ key_vec; + /* shuffled = (data_key << 32) | (data_key >> 32); */ + U32x4 shuffled = (U32x4)vec_rl(data_key, v32); + /* product = ((U64x2)data_key & 0xFFFFFFFF) * ((U64x2)shuffled & 0xFFFFFFFF); */ + U64x2 product = XXH_vsxMultOdd((U32x4)data_key, shuffled); + + xacc[i] += product; + xacc[i] += data_vec; + } + +#else /* scalar variant of Accumulator - universal */ + + XXH_ALIGN(32) U64* const xacc = (U64*) acc; /* presumed aligned on 32-bytes boundaries, little hint for the auto-vectorizer */ + const char* const xdata = (const char*) data; /* no alignment restriction */ + const char* const xkey = (const char*) key; /* no alignment restriction */ + size_t i; + assert(((size_t)acc & 31) == 0); + for (i=0; i < ACC_NB; i++) { + U64 const data_val = XXH_readLE64(xdata + 8*i); + U64 const key_val = XXH_readLE64(xkey + 8*i); + U64 const data_key = key_val ^ data_val; + xacc[i] += XXH_mult32to64(data_key & 0xFFFFFFFF, data_key >> 32); + xacc[i] += data_val; + } + +#endif +} + +XXH_FORCE_INLINE void +XXH3_scrambleAcc(void* restrict acc, const void* restrict key) +{ +#if (XXH_VECTOR == XXH_AVX2) + + assert(((size_t)acc) & 31 == 0); + { XXH_ALIGN(32) __m256i* const xacc = (__m256i*) acc; + const __m256i* const xkey = (const __m256i *) key; /* not really aligned, just for ptr arithmetic, and because _mm256_loadu_si256() requires this argument type */ + const __m256i k1 = _mm256_set1_epi32((int)PRIME32_1); + + size_t i; + for (i=0; i < STRIPE_LEN/sizeof(__m256i); i++) { + __m256i data = xacc[i]; + __m256i const shifted = _mm256_srli_epi64(data, 47); + data = _mm256_xor_si256(data, shifted); + + { __m256i const k = _mm256_loadu_si256 (xkey+i); + __m256i const dk = _mm256_xor_si256 (data, k); /* U32 dk[4] = {d0+k0, d1+k1, d2+k2, d3+k3} */ + + __m256i const dk1 = _mm256_mul_epu32 (dk, k1); + + __m256i const d2 = _mm256_shuffle_epi32 (dk, 0x31); + __m256i const dk2 = _mm256_mul_epu32 (d2, k1); + __m256i const dk2h= _mm256_slli_epi64 (dk2, 32); + + xacc[i] = _mm256_add_epi64(dk1, dk2h); + } } + } + +#elif (XXH_VECTOR == XXH_SSE2) + + { XXH_ALIGN(16) __m128i* const xacc = (__m128i*) acc; + const __m128i* const xkey = (const __m128i *) key; /* not really aligned, just for ptr arithmetic */ + const __m128i k1 = _mm_set1_epi32((int)PRIME32_1); + + size_t i; + for (i=0; i < STRIPE_LEN/sizeof(__m128i); i++) { + __m128i data = xacc[i]; + __m128i const shifted = _mm_srli_epi64(data, 47); + data = _mm_xor_si128(data, shifted); + + { __m128i const k = _mm_loadu_si128 (xkey+i); + __m128i const dk = _mm_xor_si128 (data,k); + + __m128i const dk1 = _mm_mul_epu32 (dk,k1); + + __m128i const d2 = _mm_shuffle_epi32 (dk, 0x31); + __m128i const dk2 = _mm_mul_epu32 (d2,k1); + __m128i const dk2h= _mm_slli_epi64(dk2, 32); + + xacc[i] = _mm_add_epi64(dk1, dk2h); + } } + } + +#elif (XXH_VECTOR == XXH_NEON) + + assert(((size_t)acc) & 15 == 0); + + { uint64x2_t* const xacc = (uint64x2_t*) acc; + uint32_t const* const xkey = (uint32_t const*) key; + uint32x2_t const prime = vdup_n_u32 (PRIME32_1); + + size_t i; + for (i=0; i < STRIPE_LEN/sizeof(uint64x2_t); i++) { + /* data_vec = xacc[i] ^ (xacc[i] >> 47); */ + uint64x2_t const acc_vec = xacc[i]; + uint64x2_t const shifted = vshrq_n_u64 (acc_vec, 47); + uint64x2_t const data_vec = veorq_u64 (acc_vec, shifted); + + /* key_vec = xkey[i]; */ + uint32x4_t const key_vec = vld1q_u32 (xkey + (i * 4)); + /* data_key = data_vec ^ key_vec; */ + uint32x4_t const data_key = veorq_u32 (vreinterpretq_u32_u64(data_vec), key_vec); + /* shuffled = { data_key[0, 2], data_key[1, 3] }; */ + uint32x2x2_t const shuffled = vzip_u32 (vget_low_u32(data_key), vget_high_u32(data_key)); + + /* data_key *= PRIME32_1 */ + + /* prod_hi = (data_key >> 32) * PRIME32_1; */ + uint64x2_t const prod_hi = vmull_u32 (shuffled.val[1], prime); + /* xacc[i] = prod_hi << 32; */ + xacc[i] = vshlq_n_u64(prod_hi, 32); + /* xacc[i] += (prod_hi & 0xFFFFFFFF) * PRIME32_1; */ + xacc[i] = vmlal_u32(xacc[i], shuffled.val[0], prime); + } } + +#elif (XXH_VECTOR == XXH_VSX) + + U64x2* const xacc = (U64x2*) acc; + const U64x2* const xkey = (const U64x2*) key; + /* constants */ + U64x2 const v32 = { 32, 32 }; + U64x2 const v47 = { 47, 47 }; + U32x4 const prime = { PRIME32_1, PRIME32_1, PRIME32_1, PRIME32_1 }; + size_t i; + + for (i = 0; i < STRIPE_LEN / sizeof(U64x2); i++) { + U64x2 const acc_vec = xacc[i]; + U64x2 const data_vec = acc_vec ^ (acc_vec >> v47); + /* key_vec = xkey[i]; */ +#ifdef __BIG_ENDIAN__ + /* swap 32-bit words */ + U64x2 const key_vec = vec_rl(vec_vsx_ld(0, xkey + i), v32); +#else + U64x2 const key_vec = vec_vsx_ld(0, xkey + i); +#endif + U64x2 const data_key = data_vec ^ key_vec; + + /* data_key *= PRIME32_1 */ + + /* prod_lo = ((U64x2)data_key & 0xFFFFFFFF) * ((U64x2)prime & 0xFFFFFFFF); */ + U64x2 const prod_lo = XXH_vsxMultOdd((U32x4)data_key, prime); + /* prod_hi = ((U64x2)data_key >> 32) * ((U64x2)prime >> 32); */ + U64x2 const prod_hi = XXH_vsxMultEven((U32x4)data_key, prime); + xacc[i] = prod_lo + (prod_hi << v32); + } + +#else /* scalar variant of Scrambler - universal */ + + XXH_ALIGN(32) U64* const xacc = (U64*) acc; /* presumed aligned on 32-bytes boundaries, little hint for the auto-vectorizer */ + const char* const xkey = (const char*) key; /* no alignment restriction */ + int i; + assert((((size_t)acc) & 31) == 0); + + for (i=0; i < (int)ACC_NB; i++) { + U64 const key64 = XXH_readLE64(xkey + 8*i); + U64 acc64 = xacc[i]; + acc64 ^= acc64 >> 47; + acc64 ^= key64; + acc64 *= PRIME32_1; + xacc[i] = acc64; + } + +#endif +} + +/* assumption : nbStripes will not overflow secret size */ +XXH_FORCE_INLINE void +XXH3_accumulate(U64* restrict acc, const void* restrict data, + const void* restrict secret, size_t nbStripes) +{ + size_t n; + /* Clang doesn't unroll this loop without the pragma. Unrolling can be up to 1.4x faster. + * note from @aras-p : this unroll statement is actually detrimental for WASM. + * need to find a detection macro to exclude this case. */ +#if defined(__clang__) && !defined(__OPTIMIZE_SIZE__) && !defined(__ARM_ARCH) +# pragma clang loop unroll(enable) +#endif + for (n = 0; n < nbStripes; n++ ) { + XXH3_accumulate_512(acc, + (const char*)data + n*STRIPE_LEN, + (const char*)secret + n*XXH_SECRET_CONSUME_RATE); + } +} + +/* note : clang auto-vectorizes well in SS2 mode _if_ this function is `static`, + * and doesn't auto-vectorize it at all if it is `FORCE_INLINE`. + * However, it auto-vectorizes better AVX2 if it is `FORCE_INLINE` + * Pretty much every other modes and compilers prefer `FORCE_INLINE`. + */ +#if defined(__clang__) && (XXH_VECTOR==0) && !defined(__AVX2__) +static void +#else +XXH_FORCE_INLINE void +#endif +XXH3_hashLong_internal_loop( U64* restrict acc, + const void* restrict data, size_t len, + const void* restrict secret, size_t secretSize) +{ + size_t const nb_rounds = (secretSize - STRIPE_LEN) / XXH_SECRET_CONSUME_RATE; + size_t const block_len = STRIPE_LEN * nb_rounds; + size_t const nb_blocks = len / block_len; + + size_t n; + + assert(secretSize >= XXH_SECRET_SIZE_MIN); + + for (n = 0; n < nb_blocks; n++) { + XXH3_accumulate(acc, (const char*)data + n*block_len, secret, nb_rounds); + XXH3_scrambleAcc(acc, (const char*)secret + secretSize - STRIPE_LEN); + } + + /* last partial block */ + assert(len > STRIPE_LEN); + { size_t const nbStripes = (len - (block_len * nb_blocks)) / STRIPE_LEN; + assert(nbStripes <= (secretSize / XXH_SECRET_CONSUME_RATE)); + XXH3_accumulate(acc, (const char*)data + nb_blocks*block_len, secret, nbStripes); + + /* last stripe */ + if (len & (STRIPE_LEN - 1)) { + const void* const p = (const char*)data + len - STRIPE_LEN; +#define XXH_SECRET_LASTACC_START 7 /* do not align on 8, so that secret is different from scrambler */ + XXH3_accumulate_512(acc, p, (const char*)secret + secretSize - STRIPE_LEN - XXH_SECRET_LASTACC_START); + } } +} + +XXH_FORCE_INLINE U64 +XXH3_mix2Accs(const U64* restrict acc, const void* restrict secret) +{ + const U64* const key64 = (const U64*)secret; + return XXH3_mul128_fold64( + acc[0] ^ XXH_readLE64(key64), + acc[1] ^ XXH_readLE64(key64+1) ); +} + +static XXH64_hash_t +XXH3_mergeAccs(const U64* restrict acc, const void* restrict secret, U64 start) +{ + U64 result64 = start; + + result64 += XXH3_mix2Accs(acc+0, (const char*)secret + 0); + result64 += XXH3_mix2Accs(acc+2, (const char*)secret + 16); + result64 += XXH3_mix2Accs(acc+4, (const char*)secret + 32); + result64 += XXH3_mix2Accs(acc+6, (const char*)secret + 48); + + return XXH3_avalanche(result64); +} + +XXH_FORCE_INLINE XXH64_hash_t +XXH3_hashLong_internal(const void* restrict data, size_t len, + const void* restrict secret, size_t secretSize) +{ + XXH_ALIGN(XXH_ACC_ALIGN) U64 acc[ACC_NB] = { PRIME32_3, PRIME64_1, PRIME64_2, PRIME64_3, PRIME64_4, PRIME32_2, PRIME64_5, PRIME32_1 }; + + XXH3_hashLong_internal_loop(acc, data, len, secret, secretSize); + + /* converge into final hash */ + XXH_STATIC_ASSERT(sizeof(acc) == 64); +#define XXH_SECRET_MERGEACCS_START 11 /* do not align on 8, so that secret is different from accumulator */ + assert(secretSize >= sizeof(acc) + XXH_SECRET_MERGEACCS_START); + return XXH3_mergeAccs(acc, (const char*)secret + XXH_SECRET_MERGEACCS_START, (U64)len * PRIME64_1); +} + + +XXH_NO_INLINE XXH64_hash_t /* It's important for performance that XXH3_hashLong is not inlined. Not sure why (uop cache maybe ?), but difference is large and easily measurable */ +XXH3_hashLong_64b_defaultSecret(const void* restrict data, size_t len) +{ + return XXH3_hashLong_internal(data, len, kSecret, sizeof(kSecret)); +} + +XXH_NO_INLINE XXH64_hash_t /* It's important for performance that XXH3_hashLong is not inlined. Not sure why (uop cache maybe ?), but difference is large and easily measurable */ +XXH3_hashLong_64b_withSecret(const void* restrict data, size_t len, const void* restrict secret, size_t secretSize) +{ + return XXH3_hashLong_internal(data, len, secret, secretSize); +} + + +XXH_FORCE_INLINE void XXH_writeLE64(void* dst, U64 v64) +{ + if (!XXH_CPU_LITTLE_ENDIAN) v64 = XXH_swap64(v64); + memcpy(dst, &v64, sizeof(v64)); +} + +/* XXH3_initKeySeed() : + * destination `customSecret` is presumed allocated and same size as `kSecret`. + */ +XXH_FORCE_INLINE void XXH3_initKeySeed(void* customSecret, U64 seed64) +{ + char* const dst = (char*)customSecret; + const char* const src = (const char*)kSecret; + int const nbRounds = XXH_SECRET_DEFAULT_SIZE / 16; + int i; + + XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE & 15) == 0); + + for (i=0; i < nbRounds; i++) { + XXH_writeLE64(dst + 16*i, XXH_readLE64(src + 16*i) + seed64); + XXH_writeLE64(dst + 16*i + 8, XXH_readLE64(src + 16*i + 8) - seed64); + } +} + + +/* XXH3_hashLong_64b_withSeed() : + * Generate a custom key, + * based on alteration of default kSecret with the seed, + * and then use this key for long mode hashing. + * This operation is decently fast but nonetheless costs a little bit of time. + * Try to avoid it whenever possible (typically when seed==0). + */ +XXH_NO_INLINE XXH64_hash_t /* It's important for performance that XXH3_hashLong is not inlined. Not sure why (uop cache maybe ?), but difference is large and easily measurable */ +XXH3_hashLong_64b_withSeed(const void* data, size_t len, XXH64_hash_t seed) +{ + XXH_ALIGN(8) char secret[XXH_SECRET_DEFAULT_SIZE]; + XXH3_initKeySeed(secret, seed); + return XXH3_hashLong_internal(data, len, secret, sizeof(secret)); +} + + +XXH_FORCE_INLINE U64 XXH3_mix16B(const void* restrict data, const void* restrict key, U64 seed64) +{ + const U64* const key64 = (const U64*)key; + U64 const ll1 = XXH_readLE64(data); + U64 const ll2 = XXH_readLE64((const BYTE*)data+8); + return XXH3_mul128_fold64( + ll1 ^ (XXH_readLE64(key64) + seed64), + ll2 ^ (XXH_readLE64(key64+1) - seed64) ) ; +} + + +XXH_FORCE_INLINE XXH64_hash_t +XXH3_len_17to128_64b(const void* restrict data, size_t len, const void* restrict secret, size_t secretSize, XXH64_hash_t seed) +{ + const BYTE* const p = (const BYTE*)data; + const char* const key = (const char*)secret; + + assert(secretSize >= XXH_SECRET_SIZE_MIN); (void)secretSize; + assert(16 < len && len <= 128); + + { U64 acc = len * PRIME64_1; + if (len > 32) { + if (len > 64) { + if (len > 96) { + acc += XXH3_mix16B(p+48, key+96, seed); + acc += XXH3_mix16B(p+len-64, key+112, seed); + } + acc += XXH3_mix16B(p+32, key+64, seed); + acc += XXH3_mix16B(p+len-48, key+80, seed); + } + acc += XXH3_mix16B(p+16, key+32, seed); + acc += XXH3_mix16B(p+len-32, key+48, seed); + } + acc += XXH3_mix16B(p+0, key+0, seed); + acc += XXH3_mix16B(p+len-16, key+16, seed); + + return XXH3_avalanche(acc); + } +} + + +/* === Public entry point === */ + +XXH_PUBLIC_API XXH64_hash_t XXH3_64bits(const void* data, size_t len) +{ + if (len <= 16) return XXH3_len_0to16_64b(data, len, kSecret, 0); + if (len > 128) return XXH3_hashLong_64b_defaultSecret(data, len); + return XXH3_len_17to128_64b(data, len, kSecret, sizeof(kSecret), 0); +} + +XXH_PUBLIC_API XXH64_hash_t +XXH3_64bits_withSecret(const void* data, size_t len, const void* secret, size_t secretSize) +{ + assert(secretSize >= XXH_SECRET_SIZE_MIN); + assert(((size_t)secret % 8) == 0); + /* if an action must be taken should `secret` conditions not be respected, + * it should be done here. + * For now, it's a contract pre-condition. + * Adding a check and a branch here would cost performance at every hash */ + if (len <= 16) return XXH3_len_0to16_64b(data, len, secret, 0); + if (len > 128) return XXH3_hashLong_64b_withSecret(data, len, secret, secretSize); + return XXH3_len_17to128_64b(data, len, secret, secretSize, 0); +} + +XXH_PUBLIC_API XXH64_hash_t +XXH3_64bits_withSeed(const void* data, size_t len, XXH64_hash_t seed) +{ + /* note : opened question : would it be faster to + * route to XXH3_64bits_withSecret_internal() + * when `seed == 0` ? + * This would add a branch though. + * Maybe do it into XXH3_hashLong_64b_withSeed() instead, + * since that's where it matters */ + if (len <= 16) return XXH3_len_0to16_64b(data, len, kSecret, seed); + if (len > 128) return XXH3_hashLong_64b_withSeed(data, len, seed); + return XXH3_len_17to128_64b(data, len, kSecret, sizeof(kSecret), seed); +} + +/* === XXH3 streaming === */ + +XXH_PUBLIC_API XXH3_state_t* XXH3_64bits_createState(void) +{ + return (XXH3_state_t*)XXH_malloc(sizeof(XXH3_state_t)); +} + +XXH_PUBLIC_API XXH_errorcode XXH3_64bits_freeState(XXH3_state_t* statePtr) +{ + XXH_free(statePtr); + return XXH_OK; +} + +XXH_PUBLIC_API void +XXH3_64bits_copyState(XXH3_state_t* dst_state, const XXH3_state_t* src_state) +{ + memcpy(dst_state, src_state, sizeof(*dst_state)); +} + +static void +XXH3_64bits_reset_internal(XXH3_state_t* statePtr, + XXH64_hash_t seed, + const void* secret, size_t secretSize) +{ + assert(statePtr != NULL); + memset(statePtr, 0, sizeof(*statePtr)); + statePtr->acc[0] = PRIME32_3; + statePtr->acc[1] = PRIME64_1; + statePtr->acc[2] = PRIME64_2; + statePtr->acc[3] = PRIME64_3; + statePtr->acc[4] = PRIME64_4; + statePtr->acc[5] = PRIME32_2; + statePtr->acc[6] = PRIME64_5; + statePtr->acc[7] = PRIME32_1; + statePtr->seed = seed; + assert(secret != NULL); + statePtr->secret = secret; + assert(secretSize >= XXH_SECRET_SIZE_MIN); + statePtr->secretLimit = (XXH32_hash_t)(secretSize - STRIPE_LEN); + statePtr->nbStripesPerBlock = statePtr->secretLimit / XXH_SECRET_CONSUME_RATE; +} + +XXH_PUBLIC_API XXH_errorcode +XXH3_64bits_reset(XXH3_state_t* statePtr) +{ + if (statePtr == NULL) return XXH_ERROR; + XXH3_64bits_reset_internal(statePtr, 0, kSecret, XXH_SECRET_DEFAULT_SIZE); + return XXH_OK; +} + +XXH_PUBLIC_API XXH_errorcode +XXH3_64bits_reset_withSecret(XXH3_state_t* statePtr, const void* secret, size_t secretSize) +{ + if (statePtr == NULL) return XXH_ERROR; + XXH3_64bits_reset_internal(statePtr, 0, secret, secretSize); + if (secret == NULL) return XXH_ERROR; + if (secretSize < XXH_SECRET_SIZE_MIN) return XXH_ERROR; + return XXH_OK; +} + +XXH_PUBLIC_API XXH_errorcode +XXH3_64bits_reset_withSeed(XXH3_state_t* statePtr, XXH64_hash_t seed) +{ + if (statePtr == NULL) return XXH_ERROR; + XXH3_64bits_reset_internal(statePtr, seed, kSecret, XXH_SECRET_DEFAULT_SIZE); + XXH3_initKeySeed(statePtr->customSecret, seed); + statePtr->secret = statePtr->customSecret; + return XXH_OK; +} + +static void +XXH3_64bits_consumeStripes( U64* acc, + XXH32_hash_t* nbStripesSoFarPtr, XXH32_hash_t nbStripesPerBlock, + const void* data, size_t totalStripes, + const void* secret, size_t secretLimit) +{ + assert(*nbStripesSoFarPtr < nbStripesPerBlock); + if (nbStripesPerBlock - *nbStripesSoFarPtr <= totalStripes) { + /* need a scrambling operation */ + size_t const nbStripes = nbStripesPerBlock - *nbStripesSoFarPtr; + XXH3_accumulate(acc, data, (const char*)secret + nbStripesSoFarPtr[0] * XXH_SECRET_CONSUME_RATE, nbStripes); + XXH3_scrambleAcc(acc, (const char*)secret + secretLimit); + XXH3_accumulate(acc, (const char*)data + nbStripes * STRIPE_LEN, secret, totalStripes - nbStripes); + *nbStripesSoFarPtr = (XXH32_hash_t)(totalStripes - nbStripes); + } else { + XXH3_accumulate(acc, data, (const char*)secret + nbStripesSoFarPtr[0] * XXH_SECRET_CONSUME_RATE, totalStripes); + *nbStripesSoFarPtr += (XXH32_hash_t)totalStripes; + } +} + +XXH_PUBLIC_API XXH_errorcode +XXH3_64bits_update(XXH3_state_t* state, const void* input, size_t len) +{ + if (input==NULL) +#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1) + return XXH_OK; +#else + return XXH_ERROR; +#endif + + { const BYTE* p = (const BYTE*)input; + const BYTE* const bEnd = p + len; + + state->totalLen += len; + + if (state->bufferedSize + len <= XXH3_INTERNALBUFFER_SIZE) { /* fill in tmp buffer */ + XXH_memcpy(state->buffer + state->bufferedSize, input, len); + state->bufferedSize += (XXH32_hash_t)len; + return XXH_OK; + } + /* input now > XXH3_INTERNALBUFFER_SIZE */ + + #define XXH3_INTERNALBUFFER_STRIPES (XXH3_INTERNALBUFFER_SIZE / STRIPE_LEN) + XXH_STATIC_ASSERT(XXH3_INTERNALBUFFER_SIZE % STRIPE_LEN == 0); /* clean multiple */ + + if (state->bufferedSize) { /* some data within internal buffer: fill then consume it */ + size_t const loadSize = XXH3_INTERNALBUFFER_SIZE - state->bufferedSize; + XXH_memcpy(state->buffer + state->bufferedSize, input, loadSize); + p += loadSize; + XXH3_64bits_consumeStripes(state->acc, + &state->nbStripesSoFar, state->nbStripesPerBlock, + state->buffer, XXH3_INTERNALBUFFER_STRIPES, + state->secret, state->secretLimit); + } + + /* consume input by full buffer quantities */ + if (p+XXH3_INTERNALBUFFER_SIZE <= bEnd) { + const BYTE* const limit = bEnd - XXH3_INTERNALBUFFER_SIZE; + do { + XXH3_64bits_consumeStripes(state->acc, + &state->nbStripesSoFar, state->nbStripesPerBlock, + p, XXH3_INTERNALBUFFER_STRIPES, + state->secret, state->secretLimit); + p += XXH3_INTERNALBUFFER_SIZE; + } while (p<=limit); + } + + if (p < bEnd) { /* some remaining input data : buffer it */ + XXH_memcpy(state->buffer, p, (size_t)(bEnd-p)); + state->bufferedSize = (XXH32_hash_t)(bEnd-p); + } + } + + return XXH_OK; +} + + +XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_digest (const XXH3_state_t* state) +{ + if (state->totalLen > XXH3_INTERNALBUFFER_SIZE) { + XXH_ALIGN(XXH_ACC_ALIGN) XXH64_hash_t acc[ACC_NB]; + memcpy(acc, state->acc, sizeof(acc)); /* digest locally, state remains unaltered, and can continue ingesting more data afterwards */ + if (state->bufferedSize >= STRIPE_LEN) { + size_t const totalNbStripes = state->bufferedSize / STRIPE_LEN; + XXH32_hash_t nbStripesSoFar = state->nbStripesSoFar; + XXH3_64bits_consumeStripes(acc, + &nbStripesSoFar, state->nbStripesPerBlock, + state->buffer, totalNbStripes, + state->secret, state->secretLimit); + if (state->bufferedSize % STRIPE_LEN) { /* one last partial stripe */ + XXH3_accumulate_512(acc, state->buffer + state->bufferedSize - STRIPE_LEN, (const char*)state->secret + state->secretLimit - XXH_SECRET_LASTACC_START); + } + } else { /* bufferedSize < STRIPE_LEN */ + if (state->bufferedSize) { /* one last stripe */ + char lastStripe[STRIPE_LEN]; + size_t const catchupSize = STRIPE_LEN - state->bufferedSize; + memcpy(lastStripe, (const char*)state->buffer + sizeof(state->buffer) - catchupSize, catchupSize); + memcpy(lastStripe + catchupSize, state->buffer, state->bufferedSize); + XXH3_accumulate_512(acc, lastStripe, (const char*)state->secret + state->secretLimit - XXH_SECRET_LASTACC_START); + } } + return XXH3_mergeAccs(acc, (const char*)state->secret + XXH_SECRET_MERGEACCS_START, (U64)state->totalLen * PRIME64_1); + } + /* len <= XXH3_INTERNALBUFFER_SIZE : short code */ + if (state->seed) + return XXH3_64bits_withSeed(state->buffer, (size_t)state->totalLen, state->seed); + return XXH3_64bits_withSecret(state->buffer, (size_t)(state->totalLen), state->secret, state->secretLimit + STRIPE_LEN); +} + + +/* ========================================== + * XXH3 128 bits (=> XXH128) + * ========================================== */ + +XXH_FORCE_INLINE XXH128_hash_t +XXH3_len_1to3_128b(const void* data, size_t len, const void* keyPtr, XXH64_hash_t seed) +{ + assert(data != NULL); + assert(len > 0 && len <= 3); + assert(keyPtr != NULL); + { const U32* const key32 = (const U32*) keyPtr; + BYTE const c1 = ((const BYTE*)data)[0]; + BYTE const c2 = ((const BYTE*)data)[len >> 1]; + BYTE const c3 = ((const BYTE*)data)[len - 1]; + U32 const l1 = (U32)(c1) + ((U32)(c2) << 8); + U32 const l2 = (U32)(len) + ((U32)(c3) << 2); + U64 const ll11 = XXH_mult32to64((unsigned int)(l1 + seed + key32[0]), (unsigned int)(l2 + key32[1])); + U64 const ll12 = XXH_mult32to64((unsigned int)(l1 + key32[2]), (unsigned int)(l2 - seed + key32[3])); + XXH128_hash_t const h128 = { XXH3_avalanche(ll11), XXH3_avalanche(ll12) }; + return h128; + } +} + + +XXH_FORCE_INLINE XXH128_hash_t +XXH3_len_4to8_128b(const void* data, size_t len, const void* keyPtr, XXH64_hash_t seed) +{ + assert(data != NULL); + assert(len >= 4 && len <= 8); + { const U32* const key32 = (const U32*) keyPtr; + U32 const l1 = XXH_readLE32(data) + (U32)seed + key32[0]; + U32 const l2 = XXH_readLE32((const BYTE*)data + len - 4) + (U32)(seed >> 32) + key32[1]; + U64 const acc1 = len + l1 + ((U64)l2 << 32) + XXH_mult32to64(l1, l2); + U64 const acc2 = len*PRIME64_1 + l1*PRIME64_2 + l2*PRIME64_3; + { XXH128_hash_t const h128 = { XXH3_avalanche(acc1), XXH3_avalanche(acc2) }; + return h128; + } + } +} + +XXH_FORCE_INLINE XXH128_hash_t +XXH3_len_9to16_128b(const void* data, size_t len, const void* keyPtr, XXH64_hash_t seed) +{ + assert(data != NULL); + assert(key != NULL); + assert(len >= 9 && len <= 16); + { const U64* const key64 = (const U64*) keyPtr; + U64 acc1 = PRIME64_1 * ((U64)len + seed); + U64 acc2 = PRIME64_2 * ((U64)len - seed); + U64 const ll1 = XXH_readLE64(data); + U64 const ll2 = XXH_readLE64((const BYTE*)data + len - 8); + acc1 += XXH3_mul128_fold64(ll1 + XXH_readLE64(key64+0), ll2 + XXH_readLE64(key64+1)); + acc2 += XXH3_mul128_fold64(ll1 + XXH_readLE64(key64+2), ll2 + XXH_readLE64(key64+3)); + { XXH128_hash_t const h128 = { XXH3_avalanche(acc1), XXH3_avalanche(acc2) }; + return h128; + } + } +} + +XXH_FORCE_INLINE XXH128_hash_t +XXH3_len_0to16_128b(const void* data, size_t len, XXH64_hash_t seed) +{ + assert(data != NULL); + assert(len <= 16); + { if (len > 8) return XXH3_len_9to16_128b(data, len, kSecret, seed); + if (len >= 4) return XXH3_len_4to8_128b(data, len, kSecret, seed); + if (len) return XXH3_len_1to3_128b(data, len, kSecret, seed); + { XXH128_hash_t const h128 = { seed, (XXH64_hash_t)0 - seed }; + return h128; + } + } +} + +XXH_NO_INLINE XXH128_hash_t /* It's important for performance that XXH3_hashLong is not inlined. Not sure why (uop cache maybe ?), but difference is large and easily measurable */ +XXH3_hashLong_128b(const void* data, size_t len, XXH64_hash_t seed) +{ + XXH_ALIGN(64) U64 acc[ACC_NB] = { seed, PRIME64_1, PRIME64_2, PRIME64_3, PRIME64_4, PRIME64_5, (U64)0 - seed, 0 }; + assert(len > 128); + + XXH3_hashLong_internal_loop(acc, data, len, kSecret, sizeof(kSecret)); + + /* converge into final hash */ + assert(sizeof(acc) == 64); + { U64 const low64 = XXH3_mergeAccs(acc, kSecret, (U64)len * PRIME64_1); + U64 const high64 = XXH3_mergeAccs(acc, kSecret+16, ((U64)len+1) * PRIME64_2); + XXH128_hash_t const h128 = { low64, high64 }; + return h128; + } +} + +XXH_PUBLIC_API XXH128_hash_t +XXH3_128bits_withSeed(const void* data, size_t len, XXH64_hash_t seed) +{ + if (len <= 16) return XXH3_len_0to16_128b(data, len, seed); + + { U64 acc1 = PRIME64_1 * (len + seed); + U64 acc2 = 0; + const BYTE* const p = (const BYTE*)data; + const char* const key = (const char*)kSecret; + if (len > 32) { + if (len > 64) { + if (len > 96) { + if (len > 128) return XXH3_hashLong_128b(data, len, seed); + + acc1 += XXH3_mix16B(p+48, key+96, seed); + acc2 += XXH3_mix16B(p+len-64, key+112, seed); + } + + acc1 += XXH3_mix16B(p+32, key+64, seed); + acc2 += XXH3_mix16B(p+len-48, key+80, seed); + } + + acc1 += XXH3_mix16B(p+16, key+32, seed); + acc2 += XXH3_mix16B(p+len-32, key+48, seed); + } + + acc1 += XXH3_mix16B(p+0, key+0, seed); + acc2 += XXH3_mix16B(p+len-16, key+16, seed); + + { U64 const part1 = acc1 + acc2; + U64 const part2 = (acc1 * PRIME64_3) + (acc2 * PRIME64_4) + ((len - seed) * PRIME64_2); + XXH128_hash_t const h128 = { XXH3_avalanche(part1), (XXH64_hash_t)0 - XXH3_avalanche(part2) }; + return h128; + } + } +} + + +XXH_PUBLIC_API XXH128_hash_t XXH3_128bits(const void* data, size_t len) +{ + return XXH3_128bits_withSeed(data, len, 0); +} + + +XXH_PUBLIC_API XXH128_hash_t XXH128(const void* data, size_t len, XXH64_hash_t seed) +{ + return XXH3_128bits_withSeed(data, len, seed); +} + + +#ifdef UNDEF_NDEBUG +# undef NDEBUG +#endif + +#endif /* XXH3_H */ diff --git a/src/common/util/xxhash.c b/src/common/util/xxhash.c new file mode 100644 index 000000000..00b5aefb3 --- /dev/null +++ b/src/common/util/xxhash.c @@ -0,0 +1,1024 @@ +/* +* xxHash - Fast Hash algorithm +* Copyright (C) 2012-2016, Yann Collet +* +* 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 homepage: http://www.xxhash.com +* - xxHash source repository : https://github.com/Cyan4973/xxHash +*/ + + +/* ************************************* +* 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(_WIN32)) || \ + (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_ACCEPT_NULL_INPUT_POINTER : + * If input pointer is NULL, xxHash default behavior is to dereference it, triggering a segfault. + * When this macro is enabled, xxHash actively checks input for null pointer. + * It it is, result for null input pointers is the same as a null-length input. + */ +#ifndef XXH_ACCEPT_NULL_INPUT_POINTER /* can be defined externally */ +# define XXH_ACCEPT_NULL_INPUT_POINTER 0 +#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 + + +/* ************************************* +* Includes & Memory related functions +***************************************/ +/*! Modify the local functions below should you wish to use some other memory routines +* for malloc(), free() */ +#include +static void* XXH_malloc(size_t s) { return malloc(s); } +static void XXH_free (void* p) { free(p); } +/*! and for memcpy() */ +#include +static void* XXH_memcpy(void* dest, const void* src, size_t size) { return memcpy(dest,src,size); } + +#include /* assert */ + +#define XXH_STATIC_LINKING_ONLY +#include "xxhash.h" + + +/* ************************************* +* Compiler Specific Options +***************************************/ +#ifdef _MSC_VER /* Visual Studio */ +# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ +# define XXH_FORCE_INLINE static __forceinline +# define XXH_NO_INLINE static __declspec(noinline) +#else +# if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ +# ifdef __GNUC__ +# define XXH_FORCE_INLINE static inline __attribute__((always_inline)) +# define XXH_NO_INLINE static __attribute__((noinline)) +# else +# define XXH_FORCE_INLINE static inline +# define XXH_NO_INLINE static +# endif +# else +# define XXH_FORCE_INLINE static +# define XXH_NO_INLINE static +# endif /* __STDC_VERSION__ */ +#endif + + +/* ************************************* +* Basic Types +***************************************/ +#ifndef MEM_MODULE +# if !defined (__VMS) \ + && (defined (__cplusplus) \ + || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) +# include + typedef uint8_t BYTE; + typedef uint16_t U16; + typedef uint32_t U32; +# else + typedef unsigned char BYTE; + typedef unsigned short U16; + typedef unsigned int U32; +# endif +#endif + + +/* === 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 */ +static U32 XXH_read32(const void* memPtr) { return *(const U32*) 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 */ +typedef union { U32 u32; } __attribute__((packed)) unalign; +static U32 XXH_read32(const void* ptr) { return ((const unalign*)ptr)->u32; } + +#else + +/* portable and safe solution. Generally efficient. + * see : http://stackoverflow.com/a/32095106/646947 + */ +static U32 XXH_read32(const void* memPtr) +{ + U32 val; + memcpy(&val, memPtr, sizeof(val)); + return val; +} + +#endif /* XXH_FORCE_DIRECT_MEMORY_ACCESS */ + + +/* === Endianess === */ +typedef enum { XXH_bigEndian=0, XXH_littleEndian=1 } XXH_endianess; + +/* XXH_CPU_LITTLE_ENDIAN can be defined externally, for example on the compiler command line */ +#ifndef XXH_CPU_LITTLE_ENDIAN +static int XXH_isLittleEndian(void) +{ + const union { U32 u; BYTE c[4]; } one = { 1 }; /* don't use static : performance detrimental */ + return one.c[0]; +} +# define XXH_CPU_LITTLE_ENDIAN XXH_isLittleEndian() +#endif + + + + +/* **************************************** +* Compiler-specific Functions and Macros +******************************************/ +#define XXH_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) + +/* Note : although _rotl exists for minGW (GCC under windows), performance seems poor */ +#if defined(_MSC_VER) +# define XXH_rotl32(x,r) _rotl(x,r) +# define XXH_rotl64(x,r) _rotl64(x,r) +#else +# define XXH_rotl32(x,r) (((x) << (r)) | ((x) >> (32 - (r)))) +# define XXH_rotl64(x,r) (((x) << (r)) | ((x) >> (64 - (r)))) +#endif + +#if defined(_MSC_VER) /* Visual Studio */ +# define XXH_swap32 _byteswap_ulong +#elif XXH_GCC_VERSION >= 403 +# define XXH_swap32 __builtin_bswap32 +#else +static U32 XXH_swap32 (U32 x) +{ + return ((x << 24) & 0xff000000 ) | + ((x << 8) & 0x00ff0000 ) | + ((x >> 8) & 0x0000ff00 ) | + ((x >> 24) & 0x000000ff ); +} +#endif + + +/* *************************** +* Memory reads +*****************************/ +typedef enum { XXH_aligned, XXH_unaligned } XXH_alignment; + +XXH_FORCE_INLINE U32 XXH_readLE32(const void* ptr) +{ + return XXH_CPU_LITTLE_ENDIAN ? XXH_read32(ptr) : XXH_swap32(XXH_read32(ptr)); +} + +static U32 XXH_readBE32(const void* ptr) +{ + return XXH_CPU_LITTLE_ENDIAN ? XXH_swap32(XXH_read32(ptr)) : XXH_read32(ptr); +} + +XXH_FORCE_INLINE U32 +XXH_readLE32_align(const void* ptr, XXH_alignment align) +{ + if (align==XXH_unaligned) { + return XXH_readLE32(ptr); + } else { + return XXH_CPU_LITTLE_ENDIAN ? *(const U32*)ptr : XXH_swap32(*(const U32*)ptr); + } +} + + +/* ************************************* +* Macros +***************************************/ +#define XXH_STATIC_ASSERT(c) { enum { XXH_sa = 1/(int)(!!(c)) }; } /* use after variable declarations */ +XXH_PUBLIC_API unsigned XXH_versionNumber (void) { return XXH_VERSION_NUMBER; } + + +/* ******************************************************************* +* 32-bit hash functions +*********************************************************************/ +static const U32 PRIME32_1 = 2654435761U; /* 0b10011110001101110111100110110001 */ +static const U32 PRIME32_2 = 2246822519U; /* 0b10000101111010111100101001110111 */ +static const U32 PRIME32_3 = 3266489917U; /* 0b11000010101100101010111000111101 */ +static const U32 PRIME32_4 = 668265263U; /* 0b00100111110101001110101100101111 */ +static const U32 PRIME32_5 = 374761393U; /* 0b00010110010101100110011110110001 */ + +static U32 XXH32_round(U32 acc, U32 input) +{ + acc += input * PRIME32_2; + acc = XXH_rotl32(acc, 13); + acc *= PRIME32_1; +#if defined(__GNUC__) && defined(__SSE4_1__) && !defined(XXH_ENABLE_AUTOVECTORIZE) + /* UGLY HACK: + * This inline assembly hack forces acc into a normal register. This is the + * only thing that prevents GCC and Clang from autovectorizing the XXH32 loop + * (pragmas and attributes don't work for some resason) without globally + * disabling SSE4.1. + * + * The reason we want to avoid vectorization is because despite working on + * 4 integers at a time, there are multiple factors slowing XXH32 down on + * SSE4: + * - There's a ridiculous amount of lag from pmulld (10 cycles of latency on newer chips!) + * making it slightly slower to multiply four integers at once compared to four + * integers independently. Even when pmulld was fastest, Sandy/Ivy Bridge, it is + * still not worth it to go into SSE just to multiply unless doing a long operation. + * + * - Four instructions are required to rotate, + * movqda tmp, v // not required with VEX encoding + * pslld tmp, 13 // tmp <<= 13 + * psrld v, 19 // x >>= 19 + * por v, tmp // x |= tmp + * compared to one for scalar: + * roll v, 13 // reliably fast across the board + * shldl v, v, 13 // Sandy Bridge and later prefer this for some reason + * + * - Instruction level parallelism is actually more beneficial here because the + * SIMD actually serializes this operation: While v1 is rotating, v2 can load data, + * while v3 can multiply. SSE forces them to operate together. + * + * How this hack works: + * __asm__("" // Declare an assembly block but don't declare any instructions + * : // However, as an Input/Output Operand, + * "+r" // constrain a read/write operand (+) as a general purpose register (r). + * (acc) // and set acc as the operand + * ); + * + * Because of the 'r', the compiler has promised that seed will be in a + * general purpose register and the '+' says that it will be 'read/write', + * so it has to assume it has changed. It is like volatile without all the + * loads and stores. + * + * Since the argument has to be in a normal register (not an SSE register), + * each time XXH32_round is called, it is impossible to vectorize. */ + __asm__("" : "+r" (acc)); +#endif + return acc; +} + +/* mix all bits */ +static U32 XXH32_avalanche(U32 h32) +{ + h32 ^= h32 >> 15; + h32 *= PRIME32_2; + h32 ^= h32 >> 13; + h32 *= PRIME32_3; + h32 ^= h32 >> 16; + return(h32); +} + +#define XXH_get32bits(p) XXH_readLE32_align(p, align) + +static U32 +XXH32_finalize(U32 h32, const void* ptr, size_t len, XXH_alignment align) + +{ + const BYTE* p = (const BYTE*)ptr; + +#define PROCESS1 \ + h32 += (*p++) * PRIME32_5; \ + h32 = XXH_rotl32(h32, 11) * PRIME32_1 ; + +#define PROCESS4 \ + h32 += XXH_get32bits(p) * PRIME32_3; \ + p+=4; \ + h32 = XXH_rotl32(h32, 17) * PRIME32_4 ; + + switch(len&15) /* or switch(bEnd - p) */ + { + case 12: PROCESS4; + /* fallthrough */ + case 8: PROCESS4; + /* fallthrough */ + case 4: PROCESS4; + return XXH32_avalanche(h32); + + case 13: PROCESS4; + /* fallthrough */ + case 9: PROCESS4; + /* fallthrough */ + case 5: PROCESS4; + PROCESS1; + return XXH32_avalanche(h32); + + case 14: PROCESS4; + /* fallthrough */ + case 10: PROCESS4; + /* fallthrough */ + case 6: PROCESS4; + PROCESS1; + PROCESS1; + return XXH32_avalanche(h32); + + case 15: PROCESS4; + /* fallthrough */ + case 11: PROCESS4; + /* fallthrough */ + case 7: PROCESS4; + /* fallthrough */ + case 3: PROCESS1; + /* fallthrough */ + case 2: PROCESS1; + /* fallthrough */ + case 1: PROCESS1; + /* fallthrough */ + case 0: return XXH32_avalanche(h32); + } + assert(0); + return h32; /* reaching this point is deemed impossible */ +} + +XXH_FORCE_INLINE U32 +XXH32_endian_align(const void* input, size_t len, U32 seed, XXH_alignment align) +{ + const BYTE* p = (const BYTE*)input; + const BYTE* bEnd = p + len; + U32 h32; + +#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1) + if (p==NULL) { + len=0; + bEnd=p=(const BYTE*)(size_t)16; + } +#endif + + if (len>=16) { + const BYTE* const limit = bEnd - 15; + U32 v1 = seed + PRIME32_1 + PRIME32_2; + U32 v2 = seed + PRIME32_2; + U32 v3 = seed + 0; + U32 v4 = seed - PRIME32_1; + + do { + v1 = XXH32_round(v1, XXH_get32bits(p)); p+=4; + v2 = XXH32_round(v2, XXH_get32bits(p)); p+=4; + v3 = XXH32_round(v3, XXH_get32bits(p)); p+=4; + v4 = XXH32_round(v4, XXH_get32bits(p)); p+=4; + } while (p < limit); + + h32 = XXH_rotl32(v1, 1) + XXH_rotl32(v2, 7) + + XXH_rotl32(v3, 12) + XXH_rotl32(v4, 18); + } else { + h32 = seed + PRIME32_5; + } + + h32 += (U32)len; + + return XXH32_finalize(h32, p, len&15, align); +} + + +XXH_PUBLIC_API unsigned int XXH32 (const void* input, size_t len, unsigned int seed) +{ +#if 0 + /* Simple version, good for code maintenance, but unfortunately slow for small inputs */ + XXH32_state_t state; + XXH32_reset(&state, seed); + XXH32_update(&state, input, len); + return XXH32_digest(&state); + +#else + + if (XXH_FORCE_ALIGN_CHECK) { + if ((((size_t)input) & 3) == 0) { /* Input is 4-bytes aligned, leverage the speed benefit */ + return XXH32_endian_align(input, len, seed, XXH_aligned); + } } + + return XXH32_endian_align(input, len, seed, XXH_unaligned); +#endif +} + + + +/*====== Hash streaming ======*/ + +XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void) +{ + return (XXH32_state_t*)XXH_malloc(sizeof(XXH32_state_t)); +} +XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr) +{ + XXH_free(statePtr); + return XXH_OK; +} + +XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dstState, const XXH32_state_t* srcState) +{ + memcpy(dstState, srcState, sizeof(*dstState)); +} + +XXH_PUBLIC_API XXH_errorcode XXH32_reset(XXH32_state_t* statePtr, unsigned int seed) +{ + XXH32_state_t state; /* using a local state to memcpy() in order to avoid strict-aliasing warnings */ + memset(&state, 0, sizeof(state)); + state.v1 = seed + PRIME32_1 + PRIME32_2; + state.v2 = seed + PRIME32_2; + state.v3 = seed + 0; + state.v4 = seed - PRIME32_1; + /* do not write into reserved, planned to be removed in a future version */ + memcpy(statePtr, &state, sizeof(state) - sizeof(state.reserved)); + return XXH_OK; +} + + +XXH_PUBLIC_API XXH_errorcode +XXH32_update(XXH32_state_t* state, const void* input, size_t len) +{ + if (input==NULL) +#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1) + return XXH_OK; +#else + return XXH_ERROR; +#endif + + { const BYTE* p = (const BYTE*)input; + const BYTE* const bEnd = p + len; + + state->total_len_32 += (XXH32_hash_t)len; + state->large_len |= (XXH32_hash_t)((len>=16) | (state->total_len_32>=16)); + + if (state->memsize + len < 16) { /* fill in tmp buffer */ + XXH_memcpy((BYTE*)(state->mem32) + state->memsize, input, len); + state->memsize += (XXH32_hash_t)len; + return XXH_OK; + } + + if (state->memsize) { /* some data left from previous update */ + XXH_memcpy((BYTE*)(state->mem32) + state->memsize, input, 16-state->memsize); + { const U32* p32 = state->mem32; + state->v1 = XXH32_round(state->v1, XXH_readLE32(p32)); p32++; + state->v2 = XXH32_round(state->v2, XXH_readLE32(p32)); p32++; + state->v3 = XXH32_round(state->v3, XXH_readLE32(p32)); p32++; + state->v4 = XXH32_round(state->v4, XXH_readLE32(p32)); + } + p += 16-state->memsize; + state->memsize = 0; + } + + if (p <= bEnd-16) { + const BYTE* const limit = bEnd - 16; + U32 v1 = state->v1; + U32 v2 = state->v2; + U32 v3 = state->v3; + U32 v4 = state->v4; + + do { + v1 = XXH32_round(v1, XXH_readLE32(p)); p+=4; + v2 = XXH32_round(v2, XXH_readLE32(p)); p+=4; + v3 = XXH32_round(v3, XXH_readLE32(p)); p+=4; + v4 = XXH32_round(v4, XXH_readLE32(p)); p+=4; + } while (p<=limit); + + state->v1 = v1; + state->v2 = v2; + state->v3 = v3; + state->v4 = v4; + } + + if (p < bEnd) { + XXH_memcpy(state->mem32, p, (size_t)(bEnd-p)); + state->memsize = (unsigned)(bEnd-p); + } + } + + return XXH_OK; +} + + +XXH_PUBLIC_API unsigned int XXH32_digest (const XXH32_state_t* state) +{ + U32 h32; + + if (state->large_len) { + h32 = XXH_rotl32(state->v1, 1) + + XXH_rotl32(state->v2, 7) + + XXH_rotl32(state->v3, 12) + + XXH_rotl32(state->v4, 18); + } else { + h32 = state->v3 /* == seed */ + PRIME32_5; + } + + h32 += state->total_len_32; + + return XXH32_finalize(h32, state->mem32, state->memsize, XXH_aligned); +} + + +/*====== Canonical representation ======*/ + +/*! Default XXH result types are basic unsigned 32 and 64 bits. +* The canonical representation follows human-readable write convention, aka big-endian (large digits first). +* These functions allow transformation of hash result into and from its canonical format. +* This way, hash values can be written into a file or buffer, remaining comparable across different systems. +*/ + +XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash) +{ + XXH_STATIC_ASSERT(sizeof(XXH32_canonical_t) == sizeof(XXH32_hash_t)); + if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap32(hash); + memcpy(dst, &hash, sizeof(*dst)); +} + +XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src) +{ + return XXH_readBE32(src); +} + + +#ifndef XXH_NO_LONG_LONG + +/* ******************************************************************* +* 64-bit hash functions +*********************************************************************/ + +/*====== Memory access ======*/ + +#ifndef MEM_MODULE +# define MEM_MODULE +# if !defined (__VMS) \ + && (defined (__cplusplus) \ + || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) +# include + typedef uint64_t U64; +# else + /* if compiler doesn't support unsigned long long, replace by another 64-bit type */ + typedef unsigned long long U64; +# endif +#endif + + +#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 */ +static U64 XXH_read64(const void* memPtr) { return *(const U64*) 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 */ +typedef union { U32 u32; U64 u64; } __attribute__((packed)) unalign64; +static U64 XXH_read64(const void* ptr) { return ((const unalign64*)ptr)->u64; } + +#else + +/* portable and safe solution. Generally efficient. + * see : http://stackoverflow.com/a/32095106/646947 + */ + +static U64 XXH_read64(const void* memPtr) +{ + U64 val; + memcpy(&val, memPtr, sizeof(val)); + return val; +} + +#endif /* XXH_FORCE_DIRECT_MEMORY_ACCESS */ + +#if defined(_MSC_VER) /* Visual Studio */ +# define XXH_swap64 _byteswap_uint64 +#elif XXH_GCC_VERSION >= 403 +# define XXH_swap64 __builtin_bswap64 +#else +static U64 XXH_swap64 (U64 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 + +XXH_FORCE_INLINE U64 XXH_readLE64(const void* ptr) +{ + return XXH_CPU_LITTLE_ENDIAN ? XXH_read64(ptr) : XXH_swap64(XXH_read64(ptr)); +} + +static U64 XXH_readBE64(const void* ptr) +{ + return XXH_CPU_LITTLE_ENDIAN ? XXH_swap64(XXH_read64(ptr)) : XXH_read64(ptr); +} + +XXH_FORCE_INLINE U64 +XXH_readLE64_align(const void* ptr, XXH_alignment align) +{ + if (align==XXH_unaligned) + return XXH_readLE64(ptr); + else + return XXH_CPU_LITTLE_ENDIAN ? *(const U64*)ptr : XXH_swap64(*(const U64*)ptr); +} + + +/*====== xxh64 ======*/ + +static const U64 PRIME64_1 = 11400714785074694791ULL; /* 0b1001111000110111011110011011000110000101111010111100101010000111 */ +static const U64 PRIME64_2 = 14029467366897019727ULL; /* 0b1100001010110010101011100011110100100111110101001110101101001111 */ +static const U64 PRIME64_3 = 1609587929392839161ULL; /* 0b0001011001010110011001111011000110011110001101110111100111111001 */ +static const U64 PRIME64_4 = 9650029242287828579ULL; /* 0b1000010111101011110010100111011111000010101100101010111001100011 */ +static const U64 PRIME64_5 = 2870177450012600261ULL; /* 0b0010011111010100111010110010111100010110010101100110011111000101 */ + +static U64 XXH64_round(U64 acc, U64 input) +{ + acc += input * PRIME64_2; + acc = XXH_rotl64(acc, 31); + acc *= PRIME64_1; + return acc; +} + +static U64 XXH64_mergeRound(U64 acc, U64 val) +{ + val = XXH64_round(0, val); + acc ^= val; + acc = acc * PRIME64_1 + PRIME64_4; + return acc; +} + +static U64 XXH64_avalanche(U64 h64) +{ + h64 ^= h64 >> 33; + h64 *= PRIME64_2; + h64 ^= h64 >> 29; + h64 *= PRIME64_3; + h64 ^= h64 >> 32; + return h64; +} + + +#define XXH_get64bits(p) XXH_readLE64_align(p, align) + +static U64 +XXH64_finalize(U64 h64, const void* ptr, size_t len, XXH_alignment align) +{ + const BYTE* p = (const BYTE*)ptr; + +#define PROCESS1_64 \ + h64 ^= (*p++) * PRIME64_5; \ + h64 = XXH_rotl64(h64, 11) * PRIME64_1; + +#define PROCESS4_64 \ + h64 ^= (U64)(XXH_get32bits(p)) * PRIME64_1; \ + p+=4; \ + h64 = XXH_rotl64(h64, 23) * PRIME64_2 + PRIME64_3; + +#define PROCESS8_64 { \ + U64 const k1 = XXH64_round(0, XXH_get64bits(p)); \ + p+=8; \ + h64 ^= k1; \ + h64 = XXH_rotl64(h64,27) * PRIME64_1 + PRIME64_4; \ +} + + switch(len&31) { + case 24: PROCESS8_64; + /* fallthrough */ + case 16: PROCESS8_64; + /* fallthrough */ + case 8: PROCESS8_64; + return XXH64_avalanche(h64); + + case 28: PROCESS8_64; + /* fallthrough */ + case 20: PROCESS8_64; + /* fallthrough */ + case 12: PROCESS8_64; + /* fallthrough */ + case 4: PROCESS4_64; + return XXH64_avalanche(h64); + + case 25: PROCESS8_64; + /* fallthrough */ + case 17: PROCESS8_64; + /* fallthrough */ + case 9: PROCESS8_64; + PROCESS1_64; + return XXH64_avalanche(h64); + + case 29: PROCESS8_64; + /* fallthrough */ + case 21: PROCESS8_64; + /* fallthrough */ + case 13: PROCESS8_64; + /* fallthrough */ + case 5: PROCESS4_64; + PROCESS1_64; + return XXH64_avalanche(h64); + + case 26: PROCESS8_64; + /* fallthrough */ + case 18: PROCESS8_64; + /* fallthrough */ + case 10: PROCESS8_64; + PROCESS1_64; + PROCESS1_64; + return XXH64_avalanche(h64); + + case 30: PROCESS8_64; + /* fallthrough */ + case 22: PROCESS8_64; + /* fallthrough */ + case 14: PROCESS8_64; + /* fallthrough */ + case 6: PROCESS4_64; + PROCESS1_64; + PROCESS1_64; + return XXH64_avalanche(h64); + + case 27: PROCESS8_64; + /* fallthrough */ + case 19: PROCESS8_64; + /* fallthrough */ + case 11: PROCESS8_64; + PROCESS1_64; + PROCESS1_64; + PROCESS1_64; + return XXH64_avalanche(h64); + + case 31: PROCESS8_64; + /* fallthrough */ + case 23: PROCESS8_64; + /* fallthrough */ + case 15: PROCESS8_64; + /* fallthrough */ + case 7: PROCESS4_64; + /* fallthrough */ + case 3: PROCESS1_64; + /* fallthrough */ + case 2: PROCESS1_64; + /* fallthrough */ + case 1: PROCESS1_64; + /* fallthrough */ + case 0: return XXH64_avalanche(h64); + } + + /* impossible to reach */ + assert(0); + return 0; /* unreachable, but some compilers complain without it */ +} + +XXH_FORCE_INLINE U64 +XXH64_endian_align(const void* input, size_t len, U64 seed, XXH_alignment align) +{ + const BYTE* p = (const BYTE*)input; + const BYTE* bEnd = p + len; + U64 h64; + +#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1) + if (p==NULL) { + len=0; + bEnd=p=(const BYTE*)(size_t)32; + } +#endif + + if (len>=32) { + const BYTE* const limit = bEnd - 32; + U64 v1 = seed + PRIME64_1 + PRIME64_2; + U64 v2 = seed + PRIME64_2; + U64 v3 = seed + 0; + U64 v4 = seed - PRIME64_1; + + do { + v1 = XXH64_round(v1, XXH_get64bits(p)); p+=8; + v2 = XXH64_round(v2, XXH_get64bits(p)); p+=8; + v3 = XXH64_round(v3, XXH_get64bits(p)); p+=8; + v4 = XXH64_round(v4, XXH_get64bits(p)); p+=8; + } while (p<=limit); + + h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18); + h64 = XXH64_mergeRound(h64, v1); + h64 = XXH64_mergeRound(h64, v2); + h64 = XXH64_mergeRound(h64, v3); + h64 = XXH64_mergeRound(h64, v4); + + } else { + h64 = seed + PRIME64_5; + } + + h64 += (U64) len; + + return XXH64_finalize(h64, p, len, align); +} + + +XXH_PUBLIC_API XXH64_hash_t XXH64 (const void* input, size_t len, unsigned long long seed) +{ +#if 0 + /* Simple version, good for code maintenance, but unfortunately slow for small inputs */ + XXH64_state_t state; + XXH64_reset(&state, seed); + XXH64_update(&state, input, len); + return XXH64_digest(&state); + +#else + + if (XXH_FORCE_ALIGN_CHECK) { + if ((((size_t)input) & 7)==0) { /* Input is aligned, let's leverage the speed advantage */ + return XXH64_endian_align(input, len, seed, XXH_aligned); + } } + + return XXH64_endian_align(input, len, seed, XXH_unaligned); + +#endif +} + +/*====== Hash Streaming ======*/ + +XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void) +{ + return (XXH64_state_t*)XXH_malloc(sizeof(XXH64_state_t)); +} +XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr) +{ + XXH_free(statePtr); + return XXH_OK; +} + +XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* dstState, const XXH64_state_t* srcState) +{ + memcpy(dstState, srcState, sizeof(*dstState)); +} + +XXH_PUBLIC_API XXH_errorcode XXH64_reset(XXH64_state_t* statePtr, unsigned long long seed) +{ + XXH64_state_t state; /* using a local state to memcpy() in order to avoid strict-aliasing warnings */ + memset(&state, 0, sizeof(state)); + state.v1 = seed + PRIME64_1 + PRIME64_2; + state.v2 = seed + PRIME64_2; + state.v3 = seed + 0; + state.v4 = seed - PRIME64_1; + /* do not write into reserved, might be removed in a future version */ + memcpy(statePtr, &state, sizeof(state) - sizeof(state.reserved)); + return XXH_OK; +} + +XXH_PUBLIC_API XXH_errorcode +XXH64_update (XXH64_state_t* state, const void* input, size_t len) +{ + if (input==NULL) +#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1) + return XXH_OK; +#else + return XXH_ERROR; +#endif + + { const BYTE* p = (const BYTE*)input; + const BYTE* const bEnd = p + len; + + state->total_len += len; + + if (state->memsize + len < 32) { /* fill in tmp buffer */ + XXH_memcpy(((BYTE*)state->mem64) + state->memsize, input, len); + state->memsize += (U32)len; + return XXH_OK; + } + + if (state->memsize) { /* tmp buffer is full */ + XXH_memcpy(((BYTE*)state->mem64) + state->memsize, input, 32-state->memsize); + state->v1 = XXH64_round(state->v1, XXH_readLE64(state->mem64+0)); + state->v2 = XXH64_round(state->v2, XXH_readLE64(state->mem64+1)); + state->v3 = XXH64_round(state->v3, XXH_readLE64(state->mem64+2)); + state->v4 = XXH64_round(state->v4, XXH_readLE64(state->mem64+3)); + p += 32-state->memsize; + state->memsize = 0; + } + + if (p+32 <= bEnd) { + const BYTE* const limit = bEnd - 32; + U64 v1 = state->v1; + U64 v2 = state->v2; + U64 v3 = state->v3; + U64 v4 = state->v4; + + do { + v1 = XXH64_round(v1, XXH_readLE64(p)); p+=8; + v2 = XXH64_round(v2, XXH_readLE64(p)); p+=8; + v3 = XXH64_round(v3, XXH_readLE64(p)); p+=8; + v4 = XXH64_round(v4, XXH_readLE64(p)); p+=8; + } while (p<=limit); + + state->v1 = v1; + state->v2 = v2; + state->v3 = v3; + state->v4 = v4; + } + + if (p < bEnd) { + XXH_memcpy(state->mem64, p, (size_t)(bEnd-p)); + state->memsize = (unsigned)(bEnd-p); + } + } + + return XXH_OK; +} + + +XXH_PUBLIC_API XXH64_hash_t XXH64_digest (const XXH64_state_t* state) +{ + U64 h64; + + if (state->total_len >= 32) { + U64 const v1 = state->v1; + U64 const v2 = state->v2; + U64 const v3 = state->v3; + U64 const v4 = state->v4; + + h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18); + h64 = XXH64_mergeRound(h64, v1); + h64 = XXH64_mergeRound(h64, v2); + h64 = XXH64_mergeRound(h64, v3); + h64 = XXH64_mergeRound(h64, v4); + } else { + h64 = state->v3 /*seed*/ + PRIME64_5; + } + + h64 += (U64) state->total_len; + + return XXH64_finalize(h64, state->mem64, (size_t)state->total_len, XXH_aligned); +} + + +/*====== Canonical representation ======*/ + +XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash) +{ + XXH_STATIC_ASSERT(sizeof(XXH64_canonical_t) == sizeof(XXH64_hash_t)); + if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap64(hash); + memcpy(dst, &hash, sizeof(*dst)); +} + +XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src) +{ + return XXH_readBE64(src); +} + + + +/* ********************************************************************* +* XXH3 +* New generation hash designed for speed on small keys and vectorization +************************************************************************ */ + +#include "xxh3.h" + + +#endif /* XXH_NO_LONG_LONG */ diff --git a/src/common/util/xxhash.h b/src/common/util/xxhash.h new file mode 100644 index 000000000..d01ac4091 --- /dev/null +++ b/src/common/util/xxhash.h @@ -0,0 +1,512 @@ +/* + xxHash - Extremely Fast Hash algorithm + Header File + Copyright (C) 2012-2016, Yann Collet. + + 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 +*/ + +/* Notice extracted from xxHash homepage : + +xxHash is an extremely fast Hash algorithm, running at RAM speed limits. +It also successfully passes all tests from the SMHasher suite. + +Comparison (single thread, Windows Seven 32 bits, using SMHasher on a Core 2 Duo @3GHz) + +Name Speed Q.Score Author +xxHash 5.4 GB/s 10 +CrapWow 3.2 GB/s 2 Andrew +MumurHash 3a 2.7 GB/s 10 Austin Appleby +SpookyHash 2.0 GB/s 10 Bob Jenkins +SBox 1.4 GB/s 9 Bret Mulvey +Lookup3 1.2 GB/s 9 Bob Jenkins +SuperFastHash 1.2 GB/s 1 Paul Hsieh +CityHash64 1.05 GB/s 10 Pike & Alakuijala +FNV 0.55 GB/s 5 Fowler, Noll, Vo +CRC32 0.43 GB/s 9 +MD5-32 0.33 GB/s 10 Ronald L. Rivest +SHA1-32 0.28 GB/s 10 + +Q.Score is a measure of quality of the hash function. +It depends on successfully passing SMHasher test set. +10 is a perfect score. + +A 64-bit version, named XXH64, is available since r35. +It offers much better speed, but for 64-bit applications only. +Name Speed on 64 bits Speed on 32 bits +XXH64 13.8 GB/s 1.9 GB/s +XXH32 6.8 GB/s 6.0 GB/s +*/ + +#ifndef XXHASH_H_5627135585666179 +#define XXHASH_H_5627135585666179 1 + +#if defined (__cplusplus) +extern "C" { +#endif + + +/* **************************** +* Definitions +******************************/ +#include /* size_t */ +typedef enum { XXH_OK=0, XXH_ERROR } XXH_errorcode; + + +/* **************************** + * API modifier + ******************************/ +/** XXH_INLINE_ALL (and XXH_PRIVATE_API) + * This is useful to include xxhash functions in `static` mode + * in order to inline them, and remove their symbol from the public list. + * Inlining can offer dramatic performance improvement on small keys. + * Methodology : + * #define XXH_INLINE_ALL + * #include "xxhash.h" + * `xxhash.c` is automatically included. + * It's not useful to compile and link it as a separate module. + */ +#if defined(XXH_INLINE_ALL) || defined(XXH_PRIVATE_API) +# ifndef XXH_STATIC_LINKING_ONLY +# define XXH_STATIC_LINKING_ONLY +# endif +# if defined(__GNUC__) +# define XXH_PUBLIC_API static __inline __attribute__((unused)) +# elif defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) +# define XXH_PUBLIC_API static inline +# elif defined(_MSC_VER) +# define XXH_PUBLIC_API static __inline +# else + /* this version may generate warnings for unused static functions */ +# define XXH_PUBLIC_API static +# endif +#else +# if defined(WIN32) && defined(_MSC_VER) && (defined(XXH_IMPORT) || defined(XXH_EXPORT)) +# ifdef XXH_EXPORT +# define XXH_PUBLIC_API __declspec(dllexport) +# elif XXH_IMPORT +# define XXH_PUBLIC_API __declspec(dllimport) +# endif +# else +# define XXH_PUBLIC_API /* do nothing */ +# endif +#endif /* XXH_INLINE_ALL || XXH_PRIVATE_API */ + +/*! XXH_NAMESPACE, aka Namespace Emulation : + * + * If you want to include _and expose_ xxHash functions from within your own library, + * but also want to avoid symbol collisions with other libraries which may also include xxHash, + * + * you can use XXH_NAMESPACE, to automatically prefix any public symbol from xxhash library + * with the value of XXH_NAMESPACE (therefore, avoid NULL and numeric values). + * + * Note that no change is required within the calling program as long as it includes `xxhash.h` : + * regular symbol name will be automatically translated by this header. + */ +#ifdef XXH_NAMESPACE +# define XXH_CAT(A,B) A##B +# define XXH_NAME2(A,B) XXH_CAT(A,B) +# define XXH_versionNumber XXH_NAME2(XXH_NAMESPACE, XXH_versionNumber) +# define XXH32 XXH_NAME2(XXH_NAMESPACE, XXH32) +# define XXH32_createState XXH_NAME2(XXH_NAMESPACE, XXH32_createState) +# define XXH32_freeState XXH_NAME2(XXH_NAMESPACE, XXH32_freeState) +# define XXH32_reset XXH_NAME2(XXH_NAMESPACE, XXH32_reset) +# define XXH32_update XXH_NAME2(XXH_NAMESPACE, XXH32_update) +# define XXH32_digest XXH_NAME2(XXH_NAMESPACE, XXH32_digest) +# define XXH32_copyState XXH_NAME2(XXH_NAMESPACE, XXH32_copyState) +# define XXH32_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH32_canonicalFromHash) +# define XXH32_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH32_hashFromCanonical) +# define XXH64 XXH_NAME2(XXH_NAMESPACE, XXH64) +# define XXH64_createState XXH_NAME2(XXH_NAMESPACE, XXH64_createState) +# define XXH64_freeState XXH_NAME2(XXH_NAMESPACE, XXH64_freeState) +# define XXH64_reset XXH_NAME2(XXH_NAMESPACE, XXH64_reset) +# define XXH64_update XXH_NAME2(XXH_NAMESPACE, XXH64_update) +# define XXH64_digest XXH_NAME2(XXH_NAMESPACE, XXH64_digest) +# define XXH64_copyState XXH_NAME2(XXH_NAMESPACE, XXH64_copyState) +# define XXH64_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH64_canonicalFromHash) +# define XXH64_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH64_hashFromCanonical) +#endif + + +/* ************************************* +* Version +***************************************/ +#define XXH_VERSION_MAJOR 0 +#define XXH_VERSION_MINOR 7 +#define XXH_VERSION_RELEASE 0 +#define XXH_VERSION_NUMBER (XXH_VERSION_MAJOR *100*100 + XXH_VERSION_MINOR *100 + XXH_VERSION_RELEASE) +XXH_PUBLIC_API unsigned XXH_versionNumber (void); + + +/*-********************************************************************** +* 32-bit hash +************************************************************************/ +#if !defined (__VMS) \ + && (defined (__cplusplus) \ + || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) +# include + typedef uint32_t XXH32_hash_t; +#else + typedef unsigned int XXH32_hash_t; +#endif + +/*! XXH32() : + Calculate the 32-bit hash of sequence "length" bytes stored at memory address "input". + The memory between input & input+length must be valid (allocated and read-accessible). + "seed" can be used to alter the result predictably. + Speed on Core 2 Duo @ 3 GHz (single thread, SMHasher benchmark) : 5.4 GB/s */ +XXH_PUBLIC_API XXH32_hash_t XXH32 (const void* input, size_t length, unsigned int seed); + +/*====== Streaming ======*/ +typedef struct XXH32_state_s XXH32_state_t; /* incomplete type */ +XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void); +XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr); +XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dst_state, const XXH32_state_t* src_state); + +XXH_PUBLIC_API XXH_errorcode XXH32_reset (XXH32_state_t* statePtr, unsigned int seed); +XXH_PUBLIC_API XXH_errorcode XXH32_update (XXH32_state_t* statePtr, const void* input, size_t length); +XXH_PUBLIC_API XXH32_hash_t XXH32_digest (const XXH32_state_t* statePtr); + +/* + * Streaming functions generate the xxHash of an input provided in multiple segments. + * Note that, for small input, they are slower than single-call functions, due to state management. + * For small inputs, prefer `XXH32()` and `XXH64()`, which are better optimized. + * + * XXH state must first be allocated, using XXH*_createState() . + * + * Start a new hash by initializing state with a seed, using XXH*_reset(). + * + * Then, feed the hash state by calling XXH*_update() as many times as necessary. + * The function returns an error code, with 0 meaning OK, and any other value meaning there is an error. + * + * Finally, a hash value can be produced anytime, by using XXH*_digest(). + * This function returns the nn-bits hash as an int or long long. + * + * It's still possible to continue inserting input into the hash state after a digest, + * and generate some new hashes later on, by calling again XXH*_digest(). + * + * When done, free XXH state space if it was allocated dynamically. + */ + +/*====== Canonical representation ======*/ + +typedef struct { unsigned char digest[4]; } XXH32_canonical_t; +XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash); +XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src); + +/* Default result type for XXH functions are primitive unsigned 32 and 64 bits. + * The canonical representation uses human-readable write convention, aka big-endian (large digits first). + * These functions allow transformation of hash result into and from its canonical format. + * This way, hash values can be written into a file / memory, and remain comparable on different systems and programs. + */ + + +#ifndef XXH_NO_LONG_LONG +/*-********************************************************************** +* 64-bit hash +************************************************************************/ +#if !defined (__VMS) \ + && (defined (__cplusplus) \ + || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) +# include + typedef uint64_t XXH64_hash_t; +#else + typedef unsigned long long XXH64_hash_t; +#endif + +/*! XXH64() : + Calculate the 64-bit hash of sequence of length "len" stored at memory address "input". + "seed" can be used to alter the result predictably. + This function runs faster on 64-bit systems, but slower on 32-bit systems (see benchmark). +*/ +XXH_PUBLIC_API XXH64_hash_t XXH64 (const void* input, size_t length, unsigned long long seed); + +/*====== Streaming ======*/ +typedef struct XXH64_state_s XXH64_state_t; /* incomplete type */ +XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void); +XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr); +XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* dst_state, const XXH64_state_t* src_state); + +XXH_PUBLIC_API XXH_errorcode XXH64_reset (XXH64_state_t* statePtr, unsigned long long seed); +XXH_PUBLIC_API XXH_errorcode XXH64_update (XXH64_state_t* statePtr, const void* input, size_t length); +XXH_PUBLIC_API XXH64_hash_t XXH64_digest (const XXH64_state_t* statePtr); + +/*====== Canonical representation ======*/ +typedef struct { unsigned char digest[8]; } XXH64_canonical_t; +XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash); +XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src); + + +#endif /* XXH_NO_LONG_LONG */ + + + +#ifdef XXH_STATIC_LINKING_ONLY + +/* ================================================================================================ + This section contains declarations which are not guaranteed to remain stable. + They may change in future versions, becoming incompatible with a different version of the library. + These declarations should only be used with static linking. + Never use them in association with dynamic linking ! +=================================================================================================== */ + +/* These definitions are only present to allow + * static allocation of XXH state, on stack or in a struct for example. + * Never **ever** use members directly. */ + +struct XXH32_state_s { + XXH32_hash_t total_len_32; + XXH32_hash_t large_len; + XXH32_hash_t v1; + XXH32_hash_t v2; + XXH32_hash_t v3; + XXH32_hash_t v4; + XXH32_hash_t mem32[4]; + XXH32_hash_t memsize; + XXH32_hash_t reserved; /* never read nor write, might be removed in a future version */ +}; /* typedef'd to XXH32_state_t */ + +#ifndef XXH_NO_LONG_LONG /* remove 64-bit support */ +struct XXH64_state_s { + XXH64_hash_t total_len; + XXH64_hash_t v1; + XXH64_hash_t v2; + XXH64_hash_t v3; + XXH64_hash_t v4; + XXH64_hash_t mem64[4]; + XXH32_hash_t memsize; + XXH32_hash_t reserved[2]; /* never read nor write, might be removed in a future version */ +}; /* typedef'd to XXH64_state_t */ +#endif /* XXH_NO_LONG_LONG */ + + +/*-********************************************************************** +* XXH3 +* New experimental hash +************************************************************************/ +#ifndef XXH_NO_LONG_LONG + + +/* ============================================ + * XXH3 is a new hash algorithm, + * featuring vastly improved speed performance + * for both small and large inputs. + * See full speed analysis at : http://fastcompression.blogspot.com/2019/03/presenting-xxh3.html + * In general, expect XXH3 to run about ~2x faster on large inputs, + * and >3x faster on small ones, though exact difference depend on platform. + * + * The algorithm is portable, will generate the same hash on all platforms. + * It benefits greatly from vectorization units, but does not require it. + * + * XXH3 offers 2 variants, _64bits and _128bits. + * When only 64 bits are needed, prefer calling the _64bits variant : + * it reduces the amount of mixing, resulting in faster speed on small inputs. + * It's also generally simpler to manipulate a scalar return type than a struct. + * + * The XXH3 algorithm is still considered experimental. + * Produced results can still change between versions. + * It's possible to use it for ephemeral data, but avoid storing long-term values for later re-use. + * + * The API currently supports one-shot hashing only. + * The full version will include streaming capability, and canonical representation. + * + * There are still a number of opened questions that community can influence during the experimental period. + * I'm trying to list a few of them below, though don't consider this list as complete. + * + * - 128-bits output type : currently defined as a structure of 2 64-bits fields. + * That's because 128-bit values do not exist in C standard. + * Note that it means that, at byte level, result is not identical depending on endianess. + * However, at field level, they are identical on all platforms. + * The canonical representation will solve the issue of identical byte-level representation across platforms, + * which is necessary for serialization. + * Would there be a better representation for a 128-bit hash result ? + * Are the names of the inner 64-bit fields important ? Should they be changed ? + * + * - Canonical representation : for the 64-bit variant, canonical representation is the same as XXH64() (aka big-endian). + * What should it be for the 128-bit variant ? + * Since it's no longer a scalar value, big-endian representation is no longer an obvious choice. + * One possibility : represent it as the concatenation of two 64-bits canonical representation (aka 2x big-endian) + * Another one : represent it in the same order as natural order in the struct for little-endian platforms. + * Less consistent with existing convention for XXH32/XXH64, but may be more natural for little-endian platforms. + * + * - Associated functions for 128-bit hash : simple things, such as checking if 2 hashes are equal, become more difficult with struct. + * Granted, it's not terribly difficult to create a comparator, but it's still a workload. + * Would it be beneficial to declare and define a comparator function for XXH128_hash_t ? + * Are there other operations on XXH128_hash_t which would be desirable ? + * + * - Seed type for 128-bits variant : currently, it's a single 64-bit value, like the 64-bit variant. + * It could be argued that it's more logical to offer a 128-bit seed input parameter for a 128-bit hash. + * Although it's also more difficult to use, since it requires to declare and pass a structure instead of a value. + * It would either replace current choice, or add a new one. + * Farmhash, for example, offers both variants (the 128-bits seed variant is called `doubleSeed`). + * If both 64-bit and 128-bit seeds are possible, which variant should be called XXH128 ? + * + * - Result for len==0 : Currently, the result of hashing a zero-length input is `0`. + * It seems okay as a return value when using all "default" secret and seed (it used to be a request for XXH32/XXH64). + * But is it still fine to return `0` when secret or seed are non-default ? + * Are there use case which would depend on a different hash result when the secret is different ? + */ + +#ifdef XXH_NAMESPACE +# define XXH3_64bits XXH_NAME2(XXH_NAMESPACE, XXH3_64bits) +# define XXH3_64bits_withSecret XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_withSecret) +# define XXH3_64bits_withSeed XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_withSeed) + +# define XXH3_64bits_createState XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_createState) +# define XXH3_64bits_freeState XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_freeState) +# define XXH3_64bits_copyState XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_copyState) +# define XXH3_64bits_reset XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_reset) +# define XXH3_64bits_reset_withSeed XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_reset_withSeed) +# define XXH3_64bits_reset_withSecret XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_reset_withSecret) +# define XXH3_64bits_update XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_update) +# define XXH3_64bits_digest XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_digest) + +# define XXH3_128bits XXH_NAME2(XXH_NAMESPACE, XXH3_128bits) +# define XXH3_128bits_withSeed XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_withSeed) +# define XXH128 XXH_NAME2(XXH_NAMESPACE, XXH128) +#endif + +/* XXH3_64bits() : + * default 64-bit variant, using default secret and default seed of 0. + * it's also the fastest one. */ +XXH_PUBLIC_API XXH64_hash_t XXH3_64bits(const void* data, size_t len); + +/* XXH3_64bits_withSecret() : + * It's possible to provide any blob of bytes as a "secret" to generate the hash. + * This makes it more difficult for an external actor to prepare an intentional collision. + * The secret *must* be large enough (>= XXH_SECRET_SIZE_MIN). + * It should consist of random bytes. + * Avoid repeating same character, and especially avoid swathes of \0. + * Avoid repeating sequences of bytes within the secret. + * Failure to respect these conditions will result in a bad quality hash. + */ +#define XXH_SECRET_SIZE_MIN 136 +XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_withSecret(const void* data, size_t len, const void* secret, size_t secretSize); + +/* XXH3_64bits_withSeed() : + * This variant generates on the fly a custom secret, + * based on the default secret, altered using the `seed` value. + * While this operation is decently fast, note that it's not completely free. + * note : seed==0 produces same results as XXH3_64bits() */ +XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_withSeed(const void* data, size_t len, XXH64_hash_t seed); + + +/* streaming 64-bit */ + +#if defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) /* C11+ */ +# include +# define XXH_ALIGN(n) alignas(n) +#elif defined(__GNUC__) +# define XXH_ALIGN(n) __attribute__ ((aligned(n))) +#elif defined(_MSC_VER) +# define XXH_ALIGN(n) __declspec(align(n)) +#else +# define XXH_ALIGN(n) /* disabled */ +#endif + +typedef struct XXH3_state_s XXH3_state_t; + +#define XXH3_SECRET_DEFAULT_SIZE 192 /* minimum XXH_SECRET_SIZE_MIN */ +#define XXH3_INTERNALBUFFER_SIZE 128 +struct XXH3_state_s { + XXH_ALIGN(64) XXH64_hash_t acc[8]; + XXH_ALIGN(64) char customSecret[XXH3_SECRET_DEFAULT_SIZE]; /* used to store a custom secret generated from the seed. Makes state larger. Design might change */ + XXH_ALIGN(64) char buffer[XXH3_INTERNALBUFFER_SIZE]; + const void* secret; + XXH32_hash_t bufferedSize; + XXH32_hash_t nbStripesPerBlock; + XXH32_hash_t nbStripesSoFar; + XXH32_hash_t reserved32; + XXH32_hash_t reserved32_2; + XXH32_hash_t secretLimit; + XXH64_hash_t totalLen; + XXH64_hash_t seed; + XXH64_hash_t reserved64; +}; /* typedef'd to XXH3_state_t */ + +/* Streaming requires state maintenance. + * This operation costs memory and cpu. + * As a consequence, streaming is slower than one-shot hashing. + * For better performance, prefer using one-short functions anytime possible. */ + +XXH_PUBLIC_API XXH3_state_t* XXH3_64bits_createState(void); +XXH_PUBLIC_API XXH_errorcode XXH3_64bits_freeState(XXH3_state_t* statePtr); +XXH_PUBLIC_API void XXH3_64bits_copyState(XXH3_state_t* dst_state, const XXH3_state_t* src_state); + +/* XXH3_64bits_reset() : + * initialize with default parameters. + * result will be equivalent to `XXH3_64bits()` */ +XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset(XXH3_state_t* statePtr); +/* XXH3_64bits_reset_withSeed() : + * generate a custom secret from `seed`, and store it into state. + * digest will be equivalent to `XXH3_64bits_withSeed()` */ +XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset_withSeed(XXH3_state_t* statePtr, XXH64_hash_t seed); +/* XXH3_64bits_reset_withSecret() : + * `secret` is referenced, and must outlive the hash streaming session. + * secretSize must be >= XXH_SECRET_SIZE_MIN. + */ +XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset_withSecret(XXH3_state_t* statePtr, const void* secret, size_t secretSize); + +XXH_PUBLIC_API XXH_errorcode XXH3_64bits_update (XXH3_state_t* statePtr, const void* input, size_t length); +XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_digest (const XXH3_state_t* statePtr); + + +/* 128-bit */ + +typedef struct { + XXH64_hash_t low64; + XXH64_hash_t high64; +} XXH128_hash_t; + +XXH_PUBLIC_API XXH128_hash_t XXH3_128bits(const void* data, size_t len); +XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_withSeed(const void* data, size_t len, XXH64_hash_t seed); /* == XXH128() */ +XXH_PUBLIC_API XXH128_hash_t XXH128(const void* data, size_t len, XXH64_hash_t seed); + + + +#endif /* XXH_NO_LONG_LONG */ + + +/*-********************************************************************** +* XXH_INLINE_ALL +************************************************************************/ +#if defined(XXH_INLINE_ALL) || defined(XXH_PRIVATE_API) +# include "xxhash.c" /* include xxhash function bodies as `static`, for inlining */ +#endif + + + +#endif /* XXH_STATIC_LINKING_ONLY */ + + +#if defined (__cplusplus) +} +#endif + +#endif /* XXHASH_H_5627135585666179 */ diff --git a/src/common/util/xxhash32.h b/src/common/util/xxhash32.h deleted file mode 100644 index 0cd5cafe9..000000000 --- a/src/common/util/xxhash32.h +++ /dev/null @@ -1,167 +0,0 @@ -// ////////////////////////////////////////////////////////// -// xxhash32.h -// Copyright (c) 2016 Stephan Brumme. All rights reserved. -// see https://create.stephan-brumme.com/disclaimer.html -// -#pragma once -#include // for uint32_t and uint64_t -#include "crc32c.h" -/// XXHash (32 bit), based on Yann Collet's descriptions, see https://cyan4973.github.io/xxHash/ -/** How to use: -uint32_t myseed = 0; -XXHash32 myhash(myseed); -myhash.add(pointerToSomeBytes, numberOfBytes); -myhash.add(pointerToSomeMoreBytes, numberOfMoreBytes); // call add() as often as you like to ... -// and compute hash: -uint32_t result = myhash.hash(); -// or all of the above in one single line: -uint32_t result2 = XXHash32::hash(mypointer, numBytes, myseed); -Note: my code is NOT endian-aware ! -**/ -class XXHash32 -{ -public: - /// create new XXHash (32 bit) - /** @param seed your seed value, even zero is a valid seed and e.g. used by LZ4 **/ - explicit XXHash32(uint32_t seed) - { - state[0] = seed + Prime1 + Prime2; - state[1] = seed + Prime2; - state[2] = seed; - state[3] = seed - Prime1; - bufferSize = 0; - totalLength = 0; - } - /// add a chunk of bytes - /** @param input pointer to a continuous block of data - @param length number of bytes - @return false if parameters are invalid / zero **/ - bool add(const void* input, uint64_t length) - { - // no data ? - if (!input || length == 0) - return false; - totalLength += length; - // byte-wise access - const unsigned char* data = (const unsigned char*)input; - // unprocessed old data plus new data still fit in temporary buffer ? - if (bufferSize + length < MaxBufferSize) - { - // just add new data - while (length-- > 0) - buffer[bufferSize++] = *data++; - return true; - } - // point beyond last byte - const unsigned char* stop = data + length; - const unsigned char* stopBlock = stop - MaxBufferSize; - // some data left from previous update ? - if (bufferSize > 0) - { - // make sure temporary buffer is full (16 bytes) - while (bufferSize < MaxBufferSize) - buffer[bufferSize++] = *data++; - // process these 16 bytes (4x4) - process(buffer, state[0], state[1], state[2], state[3]); - } - // copying state to local variables helps optimizer A LOT - uint32_t s0 = state[0], s1 = state[1], s2 = state[2], s3 = state[3]; - // 16 bytes at once - while (data <= stopBlock) - { - // local variables s0..s3 instead of state[0]..state[3] are much faster - process(data, s0, s1, s2, s3); - data += 16; - } - // copy back - state[0] = s0; state[1] = s1; state[2] = s2; state[3] = s3; - // copy remainder to temporary buffer - bufferSize = stop - data; - for (unsigned int i = 0; i < bufferSize; i++) - buffer[i] = data[i]; - // done - return true; - } - /// get current hash - /** @return 32 bit XXHash **/ - uint32_t hash() const - { - uint32_t result = (uint32_t)totalLength; - // fold 128 bit state into one single 32 bit value - if (totalLength >= MaxBufferSize) - result += rotateLeft(state[0], 1) + - rotateLeft(state[1], 7) + - rotateLeft(state[2], 12) + - rotateLeft(state[3], 18); - else - // internal state wasn't set in add(), therefore original seed is still stored in state2 - result += state[2] + Prime5; - // process remaining bytes in temporary buffer - const unsigned char* data = buffer; - // point beyond last byte - const unsigned char* stop = data + bufferSize; - // at least 4 bytes left ? => eat 4 bytes per step - for (; data + 4 <= stop; data += 4) - result = rotateLeft(result + *(uint32_t*)data * Prime3, 17) * Prime4; - // take care of remaining 0..3 bytes, eat 1 byte per step - while (data != stop) - result = rotateLeft(result + (*data++) * Prime5, 11) * Prime1; - // mix bits - result ^= result >> 15; - result *= Prime2; - result ^= result >> 13; - result *= Prime3; - result ^= result >> 16; - return result; - } - /// combine constructor, add() and hash() in one static function (C style) - /** @param input pointer to a continuous block of data - @param length number of bytes - @param seed your seed value, e.g. zero is a valid seed and used by LZ4 - @return 32 bit XXHash **/ - static uint32_t hash(const void* input, uint64_t length, uint32_t seed) - { - // Some modern CPUs support hardware accellerated CRC32 - // This is significantly faster than xxHash, in some cases, by more than double - // So now we check for this capability and use it if it exists. - // This significantly reduces the impact of hashing on CPUs supporting SSE4.2 - // but also keeps xxHash present as a fast fallback, for those who don't support it - static bool bHardwareCrc32 = crc32c_hw_available(); // Cache the result in a static variable to avoid _cpuid every call - static bool bCrc32Init = false; - if (bHardwareCrc32) { - return crc32c_append(seed, (uint8_t*)input, (size_t)length); - } - - XXHash32 hasher(seed); - hasher.add(input, length); - return hasher.hash(); - } -private: - /// magic constants :-) - static const uint32_t Prime1 = 2654435761U; - static const uint32_t Prime2 = 2246822519U; - static const uint32_t Prime3 = 3266489917U; - static const uint32_t Prime4 = 668265263U; - static const uint32_t Prime5 = 374761393U; - /// temporarily store up to 15 bytes between multiple add() calls - static const uint32_t MaxBufferSize = 15 + 1; - // internal state and temporary buffer - uint32_t state[4]; // state[2] == seed if totalLength < MaxBufferSize - unsigned char buffer[MaxBufferSize]; - unsigned int bufferSize; - uint64_t totalLength; - /// rotate bits, should compile to a single CPU instruction (ROL) - static inline uint32_t rotateLeft(uint32_t x, unsigned char bits) - { - return (x << bits) | (x >> (32 - bits)); - } - /// process a block of 4x4 bytes, this is the main part of the XXHash32 algorithm - static inline void process(const void* data, uint32_t& state0, uint32_t& state1, uint32_t& state2, uint32_t& state3) - { - const uint32_t* block = (const uint32_t*)data; - state0 = rotateLeft(state0 + block[0] * Prime2, 13) * Prime1; - state1 = rotateLeft(state1 + block[1] * Prime2, 13) * Prime1; - state2 = rotateLeft(state2 + block[2] * Prime2, 13) * Prime1; - state3 = rotateLeft(state3 + block[3] * Prime2, 13) * Prime1; - } -}; \ No newline at end of file diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp index 989a8e772..607288dce 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp @@ -27,7 +27,7 @@ #define _XBOXKRNL_DEFEXTRN_ #define LOG_PREFIX CXBXR_MODULE::D3D8 -#include "common\util\xxhash32.h" +#include "common\util\hasher.h" #include // prevent name collisions @@ -150,6 +150,8 @@ static DWORD g_VBLastSwap = 0; static XTL::D3DSWAPDATA g_SwapData = {0}; static DWORD g_SwapLast = 0; +static XTL::CxbxVertexBufferConverter VertexBufferConverter = {}; + // cached Direct3D state variable(s) static XTL::IDirect3DIndexBuffer *pClosingLineLoopIndexBuffer = nullptr; @@ -716,7 +718,7 @@ typedef struct { DWORD dwXboxResourceType = 0; void* pXboxData = nullptr; size_t szXboxDataSize = 0; - uint32_t hash = 0; + uint64_t hash = 0; bool forceRehash = false; std::chrono::time_point nextHashTime; std::chrono::milliseconds hashLifeTime = 1ms; @@ -866,8 +868,8 @@ bool HostResourceRequiresUpdate(resource_key_t key, DWORD dwSize) auto now = std::chrono::high_resolution_clock::now(); if (now > it->second.nextHashTime || it->second.forceRehash) { - uint32_t oldHash = it->second.hash; - it->second.hash = XXHash32::hash(it->second.pXboxData, it->second.szXboxDataSize, 0); + uint64_t oldHash = it->second.hash; + it->second.hash = ComputeHash(it->second.pXboxData, it->second.szXboxDataSize); if (it->second.hash != oldHash) { // The data changed, so reset the hash lifetime @@ -905,7 +907,7 @@ void SetHostResource(XTL::X_D3DResource* pXboxResource, XTL::IDirect3DResource* resourceInfo.dwXboxResourceType = GetXboxCommonResourceType(pXboxResource); resourceInfo.pXboxData = GetDataFromXboxResource(pXboxResource); resourceInfo.szXboxDataSize = dwSize > 0 ? dwSize : GetXboxResourceSize(pXboxResource); - resourceInfo.hash = XXHash32::hash(resourceInfo.pXboxData, resourceInfo.szXboxDataSize, 0); + resourceInfo.hash = ComputeHash(resourceInfo.pXboxData, resourceInfo.szXboxDataSize); resourceInfo.hashLifeTime = 1ms; resourceInfo.lastUpdate = std::chrono::high_resolution_clock::now(); resourceInfo.nextHashTime = resourceInfo.lastUpdate + resourceInfo.hashLifeTime; @@ -1640,6 +1642,10 @@ static LRESULT WINAPI EmuMsgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lPar ToggleFauxFullscreen(hWnd); } } + else if (wParam == VK_F1) + { + VertexBufferConverter.PrintStats(); + } else if (wParam == VK_F6) { // For some unknown reason, F6 isn't handled in WndMain::WndProc @@ -2332,7 +2338,7 @@ static void EmuVerifyResourceIsRegistered(XTL::X_D3DResource *pResource, DWORD D } typedef struct { - DWORD Hash = 0; + uint64_t Hash = 0; DWORD IndexCount = 0; XTL::IDirect3DIndexBuffer* pHostIndexBuffer = nullptr; } ConvertedIndexBuffer; @@ -2388,7 +2394,7 @@ void CxbxUpdateActiveIndexBuffer } // If the data needs updating, do so - uint32_t uiHash = XXHash32::hash(pIndexData, IndexCount * 2, 0); + uint64_t uiHash = ComputeHash(pIndexData, IndexCount * 2); if (uiHash != indexBuffer.Hash) { // Update the Index Count and the hash indexBuffer.IndexCount = IndexCount; @@ -7053,13 +7059,11 @@ void XTL::CxbxDrawIndexed(CxbxDrawContext &DrawContext) CxbxUpdateActiveIndexBuffer(DrawContext.pIndexData, DrawContext.dwVertexCount); - CxbxVertexBufferConverter VertexBufferConverter = {}; - //Walk through index buffer // Determine highest and lowest index in use : INDEX16 LowIndex, HighIndex; WalkIndexBuffer(LowIndex, HighIndex, &(DrawContext.pIndexData[DrawContext.dwStartVertex]), DrawContext.dwVertexCount); - VertexBufferConverter.Apply(&DrawContext, LowIndex); + VertexBufferConverter.Apply(&DrawContext); if (DrawContext.XboxPrimitiveType == X_D3DPT_QUADLIST) { UINT uiStartIndex = 0; @@ -7135,7 +7139,6 @@ void XTL::CxbxDrawPrimitiveUP(CxbxDrawContext &DrawContext) assert(DrawContext.uiXboxVertexStreamZeroStride > 0); assert(DrawContext.dwIndexBase == 0); // No IndexBase under Draw*UP - CxbxVertexBufferConverter VertexBufferConverter = {}; VertexBufferConverter.Apply(&DrawContext); if (DrawContext.XboxPrimitiveType == X_D3DPT_QUADLIST) { // LOG_TEST_CASE("X_D3DPT_QUADLIST"); // X-Marbles and XDK Sample PlayField hits this case @@ -7329,7 +7332,7 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_DrawVertices) DrawContext.dwVertexCount = VertexCount; DrawContext.dwStartVertex = StartVertex; DrawContext.hVertexShader = g_CurrentXboxVertexShaderHandle; - CxbxVertexBufferConverter VertexBufferConverter = {}; + VertexBufferConverter.Apply(&DrawContext); if (DrawContext.XboxPrimitiveType == X_D3DPT_QUADLIST) { // LOG_TEST_CASE("X_D3DPT_QUADLIST"); // ?X-Marbles and XDK Sample (Cartoon, ?maybe PlayField?) hits this case @@ -7538,7 +7541,6 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_DrawIndexedVerticesUP) DrawContext.hVertexShader = g_CurrentXboxVertexShaderHandle; // Don't set DrawContext.pIndexData = (INDEX16*)pIndexData; // Used by GetVerticesInBuffer - CxbxVertexBufferConverter VertexBufferConverter = {}; VertexBufferConverter.Apply(&DrawContext); if (DrawContext.XboxPrimitiveType == X_D3DPT_QUADLIST) { // Indexed quadlist can be drawn using unpatched indexes via multiple draws of 2 'strip' triangles : diff --git a/src/core/hle/D3D8/XbVertexBuffer.cpp b/src/core/hle/D3D8/XbVertexBuffer.cpp index a0d47ed53..e3aa5f446 100644 --- a/src/core/hle/D3D8/XbVertexBuffer.cpp +++ b/src/core/hle/D3D8/XbVertexBuffer.cpp @@ -28,19 +28,17 @@ #define _XBOXKRNL_DEFEXTRN_ #define LOG_PREFIX CXBXR_MODULE::VTXB +#include #include "core\kernel\memory-manager\VMManager.h" -#include "common\util\xxhash32.h" // For XXHash32::hash() +#include "common\util\hasher.h" #include "core\kernel\support\Emu.h" #include "core\kernel\support\EmuXTL.h" #include "core\hle\D3D8\ResourceTracker.h" #include -#include #include #include -#define HASH_SEED 0 - #define MAX_STREAM_NOT_USED_TIME (2 * CLOCKS_PER_SEC) // TODO: Trim the not used time // Inline vertex buffer emulation @@ -60,137 +58,54 @@ extern XTL::X_D3DVertexBuffer*g_D3DStreams[16]; extern UINT g_D3DStreamStrides[16]; void *GetDataFromXboxResource(XTL::X_D3DResource *pXboxResource); -typedef struct { - XTL::IDirect3DVertexBuffer* pHostVertexBuffer; - size_t uiSize; - std::chrono::time_point lastUsed; -} cached_vertex_buffer_object; - -std::unordered_map g_HostVertexBuffers; - -// This caches Vertex Buffer Objects, but not the containing data -// This prevents unnecessary allocation and releasing of Vertex Buffers when -// we can use an existing just fine. This gives a (slight) performance boost -// Returns true if the existing vertex buffer was trashed/made invalid -bool GetCachedVertexBufferObject(DWORD pXboxDataPtr, DWORD size, XTL::IDirect3DVertexBuffer** pVertexBuffer) -{ - // TODO: If the vertex buffer object cache becomes too large, - // free the least recently used vertex buffers - - auto it = g_HostVertexBuffers.find(pXboxDataPtr); - if (it == g_HostVertexBuffers.end()) { - // Create new vertex buffer and return - cached_vertex_buffer_object newBuffer; - newBuffer.uiSize = size; - newBuffer.lastUsed = std::chrono::high_resolution_clock::now(); - - HRESULT hRet = g_pD3DDevice->CreateVertexBuffer( - size, - D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC, - 0, - XTL::D3DPOOL_DEFAULT, - &newBuffer.pHostVertexBuffer, - nullptr - ); - if (FAILED(hRet)) { - CxbxKrnlCleanup("Failed to create vertex buffer"); - } - - g_HostVertexBuffers[pXboxDataPtr] = newBuffer; - - *pVertexBuffer = newBuffer.pHostVertexBuffer; - return false; - } - - auto buffer = &it->second; - buffer->lastUsed = std::chrono::high_resolution_clock::now(); - - // Return the existing vertex buffer, if possible - if (size <= buffer->uiSize) { - *pVertexBuffer = buffer->pHostVertexBuffer; - return false; - } - - // If execution reached here, we need to release and re-create the vertex buffer.. - buffer->pHostVertexBuffer->Release(); - buffer->uiSize = size; - HRESULT hRet = g_pD3DDevice->CreateVertexBuffer( - size, - D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC, - 0, - XTL::D3DPOOL_DEFAULT, - &buffer->pHostVertexBuffer, - nullptr - ); - if (FAILED(hRet)) { - CxbxKrnlCleanup("Failed to create vertex buffer"); - } - - *pVertexBuffer = buffer->pHostVertexBuffer; - return true; -} - -void ActivatePatchedStream -( - XTL::CxbxDrawContext *pDrawContext, - UINT uiStream, - XTL::CxbxPatchedStream *pPatchedStream, - bool bRelease -) +void XTL::CxbxPatchedStream::Activate(XTL::CxbxDrawContext *pDrawContext, UINT uiStream) const { //LOG_INIT // Allows use of DEBUG_D3DRESULT // Use the cached stream values on the host - if (pPatchedStream->bCacheIsStreamZeroDrawUP) { + if (bCacheIsStreamZeroDrawUP) { // Set the UserPointer variables in the drawing context - pDrawContext->pHostVertexStreamZeroData = pPatchedStream->pCachedHostVertexStreamZeroData; - pDrawContext->uiHostVertexStreamZeroStride = pPatchedStream->uiCachedHostVertexStride; + pDrawContext->pHostVertexStreamZeroData = pCachedHostVertexStreamZeroData; + pDrawContext->uiHostVertexStreamZeroStride = uiCachedHostVertexStride; } else { HRESULT hRet = g_pD3DDevice->SetStreamSource( uiStream, - pPatchedStream->pCachedHostVertexBuffer, + pCachedHostVertexBuffer, 0, // OffsetInBytes - pPatchedStream->uiCachedHostVertexStride); + uiCachedHostVertexStride); //DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetStreamSource"); if (FAILED(hRet)) { CxbxKrnlCleanup("Failed to set the type patched buffer as the new stream source!\n"); // TODO : Cartoon hits the above case when the vertex cache size is 0. } - - // TODO : The following doesn't fix that - find our why and fix it for real - if (bRelease) { - // Always release to prevent leaks when it wasn't read from cache: - pPatchedStream->pCachedHostVertexBuffer->Release(); - // NOTE : Even this doesn't prevent Cartoon breaking : g_pD3DDevice->ResourceManagerDiscardBytes(0); - } } +} + +XTL::CxbxPatchedStream::CxbxPatchedStream() +{ + isValid = false; } -void ReleasePatchedStream(XTL::CxbxPatchedStream *pPatchedStream) +XTL::CxbxPatchedStream::~CxbxPatchedStream() { - if (pPatchedStream->bCachedHostVertexStreamZeroDataIsAllocated) { - free(pPatchedStream->pCachedHostVertexStreamZeroData); - pPatchedStream->bCachedHostVertexStreamZeroDataIsAllocated = false; - } - pPatchedStream->pCachedHostVertexStreamZeroData = nullptr; - pPatchedStream->pCachedHostVertexBuffer = nullptr; // g_HostVertexBuffers owns these nowadays -} + if (bCachedHostVertexStreamZeroDataIsAllocated) { + free(pCachedHostVertexStreamZeroData); + bCachedHostVertexStreamZeroDataIsAllocated = false; + } + + pCachedHostVertexStreamZeroData = nullptr; + + if (pCachedHostVertexBuffer != nullptr) { + pCachedHostVertexBuffer->Release(); + pCachedHostVertexBuffer = nullptr; + } +} XTL::CxbxVertexBufferConverter::CxbxVertexBufferConverter() { - this->m_uiNbrStreams = 0; - ZeroMemory(this->m_PatchedStreams, sizeof(CxbxPatchedStream) * MAX_NBR_STREAMS); - this->m_bAllocatedStreamZeroData = false; - this->m_pNewVertexStreamZeroData = NULL; - this->m_pVertexShaderInfo = NULL; -} - -XTL::CxbxVertexBufferConverter::~CxbxVertexBufferConverter() -{ - for (int i = 0; i < MAX_NBR_STREAMS; i++) { - ReleasePatchedStream(&m_PatchedStreams[i]); - } + m_uiNbrStreams = 0; + m_pVertexShaderInfo = nullptr; } size_t GetVerticesInBuffer(DWORD dwOffset, DWORD dwVertexCount, PWORD pIndexData, DWORD dwIndexBase) @@ -198,7 +113,7 @@ size_t GetVerticesInBuffer(DWORD dwOffset, DWORD dwVertexCount, PWORD pIndexData // If we are drawing from an offset, we know that the vertex count must have offset vertices // before the first drawn vertices dwVertexCount += dwOffset; - if (pIndexData == nullptr) { + if (pIndexData == xbnullptr) { return dwVertexCount; } @@ -218,7 +133,7 @@ int CountActiveD3DStreams() { int lastStreamIndex = 0; for (int i = 0; i < 16; i++) { - if (g_D3DStreams[i] != nullptr) { + if (g_D3DStreams[i] != xbnullptr) { lastStreamIndex = i + 1; } } @@ -231,7 +146,7 @@ XTL::CxbxVertexShaderInfo *GetCxbxVertexShaderInfo(DWORD Handle); // forward UINT XTL::CxbxVertexBufferConverter::GetNbrStreams(CxbxDrawContext *pDrawContext) { // Draw..Up always have one stream - if (pDrawContext->pXboxVertexStreamZeroData != nullptr) { + if (pDrawContext->pXboxVertexStreamZeroData != xbnullptr) { return 1; } @@ -274,13 +189,47 @@ inline FLOAT NormShortToFloat(const SHORT value) inline FLOAT ByteToFloat(const BYTE value) { return ((FLOAT)value) / 255.0f; -} - +} + +XTL::CxbxPatchedStream& XTL::CxbxVertexBufferConverter::GetPatchedStream(uint64_t key) +{ + // First, attempt to fetch an existing patched stream + auto it = m_PatchedStreams.find(key); + if (it != m_PatchedStreams.end()) { + m_PatchedStreamUsageList.splice(m_PatchedStreamUsageList.begin(), m_PatchedStreamUsageList, it->second); + return *it->second; + } + + // We didn't find an existing patched stream, so we must insert one and get a reference to it + m_PatchedStreamUsageList.push_front({}); + CxbxPatchedStream& stream = m_PatchedStreamUsageList.front(); + + // Insert a reference iterator into the fast lookup map + m_PatchedStreams[key] = m_PatchedStreamUsageList.begin(); + + // If the cache has exceeded it's upper bound, discard the oldest entries in the cache + if (m_PatchedStreams.size() > (m_MaxCacheSize + m_CacheElasticity)) { + while (m_PatchedStreams.size() > m_MaxCacheSize) { + m_PatchedStreams.erase(m_PatchedStreamUsageList.back().uiVertexDataHash); + m_PatchedStreamUsageList.pop_back(); + } + } + + return stream; +} + +void XTL::CxbxVertexBufferConverter::PrintStats() +{ + printf("Vertex Buffer Cache Status: \n"); + printf("- Cache Size: %d\n", m_PatchedStreams.size()); + printf("- Hits: %d\n", m_TotalCacheHits); + printf("- Misses: %d\n", m_TotalCacheMisses); +} + void XTL::CxbxVertexBufferConverter::ConvertStream ( CxbxDrawContext *pDrawContext, - UINT uiStream, - DWORD StartIndex + UINT uiStream ) { extern XTL::D3DCAPS g_D3DCaps; @@ -337,12 +286,12 @@ void XTL::CxbxVertexBufferConverter::ConvertStream bool bNeedRHWReset = bVshHandleIsFVF && ((XboxFVF & D3DFVF_POSITION_MASK) == D3DFVF_XYZRHW); bool bNeedStreamCopy = bNeedTextureNormalization || bNeedVertexPatching || bNeedRHWReset; - uint8_t *pXboxVertexData; - UINT uiXboxVertexStride; - UINT uiVertexCount; - UINT uiHostVertexStride; - DWORD dwHostVertexDataSize; - uint8_t *pHostVertexData; + uint8_t *pXboxVertexData = xbnullptr; + UINT uiXboxVertexStride = 0; + UINT uiVertexCount = 0; + UINT uiHostVertexStride = 0; + DWORD dwHostVertexDataSize = 0; + uint8_t *pHostVertexData = nullptr; IDirect3DVertexBuffer *pNewHostVertexBuffer = nullptr; if (pDrawContext->pXboxVertexStreamZeroData != xbnullptr) { @@ -356,17 +305,7 @@ void XTL::CxbxVertexBufferConverter::ConvertStream uiVertexCount = pDrawContext->VerticesInBuffer; uiHostVertexStride = (bNeedVertexPatching) ? pVertexShaderStreamInfo->HostVertexStride : uiXboxVertexStride; dwHostVertexDataSize = uiVertexCount * uiHostVertexStride; - if (bNeedStreamCopy) { - pHostVertexData = (uint8_t*)malloc(dwHostVertexDataSize); - if (pHostVertexData == nullptr) { - CxbxKrnlCleanup("Couldn't allocate the new stream zero buffer"); - } - } - else { - pHostVertexData = pXboxVertexData; - } - } - else { + } else { XTL::X_D3DVertexBuffer *pXboxVertexBuffer = g_D3DStreams[uiStream]; pXboxVertexData = (uint8_t*)GetDataFromXboxResource(pXboxVertexBuffer); if (pXboxVertexData == NULL) { @@ -392,19 +331,99 @@ void XTL::CxbxVertexBufferConverter::ConvertStream uiHostVertexStride = (bNeedVertexPatching) ? pVertexShaderStreamInfo->HostVertexStride : uiXboxVertexStride; dwHostVertexDataSize = uiVertexCount * uiHostVertexStride; - GetCachedVertexBufferObject(pXboxVertexBuffer->Data, dwHostVertexDataSize, &pNewHostVertexBuffer); - - if (FAILED(pNewHostVertexBuffer->Lock(0, 0, (D3DLockData **)&pHostVertexData, D3DLOCK_DISCARD))) { - CxbxKrnlCleanup("Couldn't lock the new buffer"); - } // Copy stream for patching and caching. bNeedStreamCopy = true; } + + // FAST PATH: If this draw is a zerostream based draw, and does not require patching, we can use it directly + // No need to hash or patch at all in this case! + if (pDrawContext->pXboxVertexStreamZeroData != xbnullptr && !bNeedStreamCopy) { + pHostVertexData = pXboxVertexData; + + CxbxPatchedStream stream; + stream.isValid = true; + stream.XboxPrimitiveType = pDrawContext->XboxPrimitiveType; + stream.uiCachedHostVertexStride = uiHostVertexStride; + stream.bCacheIsStreamZeroDrawUP = true; + stream.pCachedHostVertexStreamZeroData = pHostVertexData; + stream.Activate(pDrawContext, uiStream); + return; + } + + // Now we have enough information to hash the existing resource and find it in our cache! + DWORD xboxVertexDataSize = uiVertexCount * uiXboxVertexStride; + uint64_t vertexDataHash = ComputeHash(pXboxVertexData, xboxVertexDataSize); + uint64_t pVertexShaderSteamInfoHash = 0; + + if (pVertexShaderStreamInfo != nullptr) { + pVertexShaderSteamInfoHash = ComputeHash(pVertexShaderStreamInfo, sizeof(CxbxVertexShaderStreamInfo)); + } + + // Lookup implicity inserts a new entry if not exists, so this always works + CxbxPatchedStream& patchedStream = GetPatchedStream(vertexDataHash); + + // We check a few fields of the patched stream to protect against hash collisions (rare) + // but also to protect against games using the exact same vertex data for different vertex formats (Test Case: Burnout) + if (patchedStream.isValid && // Check that we found a cached stream + patchedStream.uiVertexStreamInformationHash == pVertexShaderSteamInfoHash && // Check that the vertex conversion is valid + patchedStream.uiCachedHostVertexStride == patchedStream.uiCachedHostVertexStride && // Make sure the host stride didn't change + patchedStream.uiCachedXboxVertexStride == uiXboxVertexStride && // Make sure the Xbox Stride didn't change + patchedStream.uiCachedXboxVertexDataSize == xboxVertexDataSize ) { // Make sure the Xbox Data Size also didn't change + m_TotalCacheHits++; + patchedStream.Activate(pDrawContext, uiStream); + return; + } + + m_TotalCacheMisses++; + + // If execution reaches here, the cached vertex buffer was not valid and we must reconvert the data + if (patchedStream.isValid) { + pHostVertexData = (uint8_t*)patchedStream.pCachedHostVertexStreamZeroData; + pNewHostVertexBuffer = patchedStream.pCachedHostVertexBuffer; + + // Free the existing buffers + if (pHostVertexData != nullptr) { + free(pHostVertexData); + pHostVertexData = nullptr; + } else if (pNewHostVertexBuffer != nullptr) { + pNewHostVertexBuffer->Release(); + pNewHostVertexBuffer = nullptr; + } + } + + // Allocate new buffers + if (pDrawContext->pXboxVertexStreamZeroData != xbnullptr) { + pHostVertexData = (uint8_t*)malloc(dwHostVertexDataSize); + + if (pHostVertexData == nullptr) { + CxbxKrnlCleanup("Couldn't allocate the new stream zero buffer"); + } + } else { + HRESULT hRet = g_pD3DDevice->CreateVertexBuffer( + dwHostVertexDataSize, + D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC, + 0, + XTL::D3DPOOL_DEFAULT, + &pNewHostVertexBuffer, + nullptr + ); + + if (FAILED(hRet)) { + CxbxKrnlCleanup("Failed to create vertex buffer"); + } + } + + // If we need to lock a host vertex buffer, do so now + if (pHostVertexData == nullptr && pNewHostVertexBuffer != nullptr) { + if (FAILED(pNewHostVertexBuffer->Lock(0, 0, (D3DLockData **)&pHostVertexData, D3DLOCK_DISCARD))) { + CxbxKrnlCleanup("Couldn't lock vertex buffer"); + } + } if (bNeedVertexPatching) { // assert(bNeedStreamCopy || "bNeedVertexPatching implies bNeedStreamCopy (but copies via conversions"); - for (uint32_t uiVertex = StartIndex; uiVertex < uiVertexCount; uiVertex++) { + for (uint32_t uiVertex = 0; uiVertex < uiVertexCount; uiVertex++) { uint8_t *pXboxVertexAsByte = &pXboxVertexData[uiVertex * uiXboxVertexStride]; uint8_t *pHostVertexAsByte = &pHostVertexData[uiVertex * uiHostVertexStride]; for (UINT uiElement = 0; uiElement < pVertexShaderStreamInfo->NumberOfVertexElements; uiElement++) { @@ -659,7 +678,7 @@ void XTL::CxbxVertexBufferConverter::ConvertStream // the uiTextureCoordinatesByteOffsetInVertex on host will match Xbox } - for (uint32_t uiVertex = StartIndex; uiVertex < uiVertexCount; uiVertex++) { + for (uint32_t uiVertex = 0; uiVertex < uiVertexCount; uiVertex++) { FLOAT *pVertexDataAsFloat = (FLOAT*)(&pHostVertexData[uiVertex * uiHostVertexStride]); // Handle pre-transformed vertices (which bypass the vertex shader pipeline) @@ -715,38 +734,34 @@ void XTL::CxbxVertexBufferConverter::ConvertStream } } } + + patchedStream.isValid = true; + patchedStream.XboxPrimitiveType = pDrawContext->XboxPrimitiveType; + patchedStream.pCachedXboxVertexData = pXboxVertexData; + patchedStream.uiCachedXboxVertexDataSize = xboxVertexDataSize; + patchedStream.uiVertexDataHash = vertexDataHash; + patchedStream.uiVertexStreamInformationHash = pVertexShaderSteamInfoHash; + patchedStream.uiCachedXboxVertexStride = uiXboxVertexStride; + patchedStream.uiCachedHostVertexStride = uiHostVertexStride; + patchedStream.bCacheIsStreamZeroDrawUP = (pDrawContext->pXboxVertexStreamZeroData != NULL); + if (patchedStream.bCacheIsStreamZeroDrawUP) { + patchedStream.pCachedHostVertexStreamZeroData = pHostVertexData; + patchedStream.bCachedHostVertexStreamZeroDataIsAllocated = bNeedStreamCopy; + } else { + // assert(pNewHostVertexBuffer != nullptr); + pNewHostVertexBuffer->Unlock(); + patchedStream.pCachedHostVertexBuffer = pNewHostVertexBuffer; + } - CxbxPatchedStream *pPatchedStream = &m_PatchedStreams[uiStream]; - -#if 0 // new - pPatchedStream->pCachedXboxVertexData = pXboxVertexData; // TODO : For hashing & caching purposes -#endif - pPatchedStream->uiCachedXboxVertexStride = uiXboxVertexStride; -#if 0 // new - pPatchedStream->uiCachedXboxVertexDataSize = uiVertexCount * uiXboxVertexStride; // TODO : For hashing & caching purposes -#endif - pPatchedStream->uiCachedHostVertexStride = uiHostVertexStride; - pPatchedStream->bCacheIsStreamZeroDrawUP = (pDrawContext->pXboxVertexStreamZeroData != NULL); - if (pPatchedStream->bCacheIsStreamZeroDrawUP) { - pPatchedStream->pCachedHostVertexStreamZeroData = pHostVertexData; - pPatchedStream->bCachedHostVertexStreamZeroDataIsAllocated = bNeedStreamCopy; - } - else { - // assert(pNewHostVertexBuffer != nullptr); - - pNewHostVertexBuffer->Unlock(); - pPatchedStream->pCachedHostVertexBuffer = pNewHostVertexBuffer; - } - - ActivatePatchedStream(pDrawContext, uiStream, pPatchedStream, - /*Release=*/!bNeedStreamCopy); // Release when it won't get cached + patchedStream.Activate(pDrawContext, uiStream); } -void XTL::CxbxVertexBufferConverter::Apply(CxbxDrawContext *pDrawContext, DWORD StartIndex) +void XTL::CxbxVertexBufferConverter::Apply(CxbxDrawContext *pDrawContext) { if ((pDrawContext->XboxPrimitiveType < X_D3DPT_POINTLIST) || (pDrawContext->XboxPrimitiveType > X_D3DPT_POLYGON)) CxbxKrnlCleanup("Unknown primitive type: 0x%.02X\n", pDrawContext->XboxPrimitiveType); + m_pVertexShaderInfo = nullptr; if (VshHandleIsVertexShader(pDrawContext->hVertexShader)) { m_pVertexShaderInfo = &(GetCxbxVertexShader(pDrawContext->hVertexShader)->VertexShaderInfo); } @@ -766,11 +781,7 @@ void XTL::CxbxVertexBufferConverter::Apply(CxbxDrawContext *pDrawContext, DWORD } for(UINT uiStream = 0; uiStream < m_uiNbrStreams; uiStream++) { - // TODO: Check for cached vertex buffer, and use it if possible - - ConvertStream(pDrawContext, uiStream, StartIndex); - - // TODO: Cache Vertex Buffer Data + ConvertStream(pDrawContext, uiStream); } if (pDrawContext->XboxPrimitiveType == X_D3DPT_QUADSTRIP) { diff --git a/src/core/hle/D3D8/XbVertexBuffer.h b/src/core/hle/D3D8/XbVertexBuffer.h index 3ea447271..fa7ad14b1 100644 --- a/src/core/hle/D3D8/XbVertexBuffer.h +++ b/src/core/hle/D3D8/XbVertexBuffer.h @@ -24,8 +24,9 @@ // ****************************************************************** #ifndef XBVERTEXBUFFER_H #define XBVERTEXBUFFER_H + +#include "Cxbx.h" -#include "Cxbx.h" //#include // Conflict with io.h #define MAX_NBR_STREAMS 16 @@ -50,31 +51,44 @@ typedef struct _CxbxDrawContext } CxbxDrawContext; -typedef struct _CxbxPatchedStream -{ - UINT uiCachedXboxVertexStride; - UINT uiCachedHostVertexStride; - bool bCacheIsStreamZeroDrawUP; - void *pCachedHostVertexStreamZeroData; - bool bCachedHostVertexStreamZeroDataIsAllocated; - XTL::IDirect3DVertexBuffer *pCachedHostVertexBuffer; -} CxbxPatchedStream; - +class CxbxPatchedStream +{ +public: + CxbxPatchedStream(); + ~CxbxPatchedStream(); + void Activate(XTL::CxbxDrawContext *pDrawContext, UINT uiStream) const; + bool isValid = false; + XTL::X_D3DPRIMITIVETYPE XboxPrimitiveType = XTL::X_D3DPT_NONE; + PVOID pCachedXboxVertexData = xbnullptr; + UINT uiCachedXboxVertexDataSize = 0; + uint64_t uiVertexDataHash = 0; + uint64_t uiVertexStreamInformationHash = 0; + UINT uiCachedXboxVertexStride = 0; + UINT uiCachedHostVertexStride = 0; + bool bCacheIsStreamZeroDrawUP = false; + void *pCachedHostVertexStreamZeroData = nullptr; + bool bCachedHostVertexStreamZeroDataIsAllocated = false; + XTL::IDirect3DVertexBuffer *pCachedHostVertexBuffer = nullptr; +}; + class CxbxVertexBufferConverter { public: CxbxVertexBufferConverter(); - ~CxbxVertexBufferConverter(); - - void Apply(CxbxDrawContext *pPatchDesc, DWORD StartIndex = 0); + void Apply(CxbxDrawContext *pPatchDesc); + void PrintStats(); private: + UINT m_uiNbrStreams; + + // Stack tracking + ULONG m_TotalCacheHits = 0; + ULONG m_TotalCacheMisses = 0; - UINT m_uiNbrStreams; - CxbxPatchedStream m_PatchedStreams[MAX_NBR_STREAMS]; - - PVOID m_pNewVertexStreamZeroData; - - bool m_bAllocatedStreamZeroData; + UINT m_MaxCacheSize = 2000; // Maximum number of entries in the cache + UINT m_CacheElasticity = 200; // Cache is allowed to grow this much more than maximum before being purged to maximum + std::unordered_map::iterator> m_PatchedStreams; // Stores references to patched streams for fast lookup + std::list m_PatchedStreamUsageList; // Linked list of vertex streams, least recently used is last in the list + CxbxPatchedStream& GetPatchedStream(uint64_t); // Fetches (or inserts) a patched stream associated with the given key XTL::CxbxVertexShaderInfo *m_pVertexShaderInfo; @@ -82,7 +96,7 @@ class CxbxVertexBufferConverter UINT GetNbrStreams(CxbxDrawContext *pPatchDesc); // Patches the types of the stream - void ConvertStream(CxbxDrawContext *pPatchDesc, UINT uiStream, DWORD StartIndex); + void ConvertStream(CxbxDrawContext *pPatchDesc, UINT uiStream); }; // inline vertex buffer emulation diff --git a/src/core/hle/Intercept.cpp b/src/core/hle/Intercept.cpp index 5e2d08500..eea7ede83 100644 --- a/src/core/hle/Intercept.cpp +++ b/src/core/hle/Intercept.cpp @@ -45,7 +45,7 @@ #include "..\..\import\XbSymbolDatabase\XbSymbolDatabase.h" #include "Intercept.hpp" #include "Patches.hpp" -#include "common\util\xxhash32.h" +#include "common\util\hasher.h" #include #include #include @@ -385,7 +385,7 @@ void EmuHLEIntercept(Xbe::Header *pXbeHeader) } // Hash the loaded XBE's header, use it as a filename - uint32_t uiHash = XXHash32::hash((void*)&CxbxKrnl_Xbe->m_Header, sizeof(Xbe::Header), 0); + uint64_t uiHash = ComputeHash((void*)&CxbxKrnl_Xbe->m_Header, sizeof(Xbe::Header)); std::stringstream sstream; char tAsciiTitle[40] = "Unknown"; std::setlocale(LC_ALL, "English"); diff --git a/src/core/kernel/support/EmuXTL.h b/src/core/kernel/support/EmuXTL.h index 380c89fa7..9c9e5a9f0 100644 --- a/src/core/kernel/support/EmuXTL.h +++ b/src/core/kernel/support/EmuXTL.h @@ -26,6 +26,7 @@ #define EMUXTL_H #include // Needed for EmuDSound.h file, must be outside of XTL namespace. +#include namespace XTL { diff --git a/src/gui/WndMain.cpp b/src/gui/WndMain.cpp index fdace4af2..56f0a1ba6 100644 --- a/src/gui/WndMain.cpp +++ b/src/gui/WndMain.cpp @@ -59,7 +59,7 @@ #include #include #include // for _O_TEXT -#include "common\util\xxhash32.h" // for XXHash32::hash +#include "common\util\hasher.h" #define XBOX_LED_FLASH_PERIOD 176 // if you know a more accurate value, put it here @@ -1063,7 +1063,7 @@ LRESULT CALLBACK WndMain::WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lP std::string cacheDir = g_Settings->GetDataLocation() + "\\SymbolCache\\"; // Hash the loaded XBE's header, use it as a filename - uint32_t uiHash = XXHash32::hash((void*)&m_Xbe->m_Header, sizeof(Xbe::Header), 0); + uint64_t uiHash = ComputeHash((void*)&m_Xbe->m_Header, sizeof(Xbe::Header)); std::stringstream sstream; std::string szTitleName(m_Xbe->m_szAsciiTitle); m_Xbe->PurgeBadChar(szTitleName);