/* * SPDX-License-Identifier: GPL-3.0-or-later * * VaptVupt — XXH64 checksum (simplified, standalone) * Based on xxHash by Yann Collet. Public domain. */ #include "vaptvupt.h" #include "vv_platform.h" /* VV_NO_SANITIZE_INTEGER */ #include #define XXH_PRIME64_1 0x9E3779B185EBCA87ULL #define XXH_PRIME64_2 0xC2B2AE3D27D4EB4FULL #define XXH_PRIME64_3 0x165667B19E3779F9ULL #define XXH_PRIME64_4 0x85EBCA77C2B2AE63ULL #define XXH_PRIME64_5 0x27D4EB2F165667C5ULL /* Sprint 117: VV_NO_SANITIZE_INTEGER is provided by include/vv_platform.h. * xxh64 relies on intentional unsigned modular arithmetic (overflow * and shift-out-of-range bits in the round mixer); the annotation * silences -fsanitize=integer false positives in hardened builds. */ /* Sprint 117: prefer the compiler builtin which lowers to a single * rotate instruction and does NOT trigger UBSan's shift-base check. * Falls back to the manual shift expression on older compilers, * still annotated with no_sanitize for hardened builds. */ #if defined(__clang__) && __has_builtin(__builtin_rotateleft64) # define XXH_ROTL64(x, r) __builtin_rotateleft64((x), (r)) #elif defined(__GNUC__) && (__GNUC__ >= 7) /* GCC 7+ recognizes the manual idiom as a rotate */ # define XXH_ROTL64(x, r) (((x) << (r)) | ((x) >> (64 - (r)))) #else # define XXH_ROTL64(x, r) (((x) << (r)) | ((x) >> (64 - (r)))) #endif #if defined(__clang__) __attribute__((noinline)) #endif static VV_NO_SANITIZE_INTEGER uint64_t xxh_rotl64(uint64_t x, int r) { return XXH_ROTL64(x, r); } static inline VV_NO_SANITIZE_INTEGER uint64_t xxh_round(uint64_t acc, uint64_t input) { acc += input * XXH_PRIME64_2; acc = xxh_rotl64(acc, 31); acc *= XXH_PRIME64_1; return acc; } static inline VV_NO_SANITIZE_INTEGER uint64_t xxh_merge_round(uint64_t acc, uint64_t val) { val = xxh_round(0, val); acc ^= val; acc = acc * XXH_PRIME64_1 + XXH_PRIME64_4; return acc; } VV_NO_SANITIZE_INTEGER uint64_t vv_xxh64(const void *data, size_t len, uint64_t seed) { const uint8_t *p = (const uint8_t *)data; const uint8_t *end = p + len; uint64_t h64; if (len >= 32) { uint64_t v1 = seed + XXH_PRIME64_1 + XXH_PRIME64_2; uint64_t v2 = seed + XXH_PRIME64_2; uint64_t v3 = seed + 0; uint64_t v4 = seed - XXH_PRIME64_1; do { uint64_t k; memcpy(&k, p, 8); v1 = xxh_round(v1, k); p += 8; memcpy(&k, p, 8); v2 = xxh_round(v2, k); p += 8; memcpy(&k, p, 8); v3 = xxh_round(v3, k); p += 8; memcpy(&k, p, 8); v4 = xxh_round(v4, k); p += 8; } while (p <= end - 32); h64 = xxh_rotl64(v1, 1) + xxh_rotl64(v2, 7) + xxh_rotl64(v3, 12) + xxh_rotl64(v4, 18); h64 = xxh_merge_round(h64, v1); h64 = xxh_merge_round(h64, v2); h64 = xxh_merge_round(h64, v3); h64 = xxh_merge_round(h64, v4); } else { h64 = seed + XXH_PRIME64_5; } h64 += (uint64_t)len; while (p + 8 <= end) { uint64_t k; memcpy(&k, p, 8); k *= XXH_PRIME64_2; k = xxh_rotl64(k, 31); k *= XXH_PRIME64_1; h64 ^= k; h64 = xxh_rotl64(h64, 27) * XXH_PRIME64_1 + XXH_PRIME64_4; p += 8; } while (p + 4 <= end) { uint32_t k; memcpy(&k, p, 4); h64 ^= (uint64_t)k * XXH_PRIME64_1; h64 = xxh_rotl64(h64, 23) * XXH_PRIME64_2 + XXH_PRIME64_3; p += 4; } while (p < end) { h64 ^= (*p) * XXH_PRIME64_5; h64 = xxh_rotl64(h64, 11) * XXH_PRIME64_1; p++; } h64 ^= h64 >> 33; h64 *= XXH_PRIME64_2; h64 ^= h64 >> 29; h64 *= XXH_PRIME64_3; h64 ^= h64 >> 32; return h64; } /* ═══════════════════════════════════════════════════════════════ * STREAMING XXH64 * ═══════════════════════════════════════════════════════════════ */ void vv_xxh64_init(vv_xxh64_state_t *s, uint64_t seed) { s->v1 = seed + XXH_PRIME64_1 + XXH_PRIME64_2; s->v2 = seed + XXH_PRIME64_2; s->v3 = seed + 0; s->v4 = seed - XXH_PRIME64_1; s->total_len = 0; s->buf_len = 0; s->seed = seed; } VV_NO_SANITIZE_INTEGER void vv_xxh64_update(vv_xxh64_state_t *s, const void *data, size_t len) { const uint8_t *p = (const uint8_t *)data; const uint8_t *end = p + len; s->total_len += (uint64_t)len; /* Fill buffer if partial data pending */ if (s->buf_len > 0) { size_t fill = 32 - s->buf_len; if (fill > len) fill = len; memcpy(s->buf + s->buf_len, p, fill); s->buf_len += fill; p += fill; if (s->buf_len < 32) return; /* still partial */ /* Process the full 32 bytes */ uint64_t k; memcpy(&k, s->buf + 0, 8); s->v1 = xxh_round(s->v1, k); memcpy(&k, s->buf + 8, 8); s->v2 = xxh_round(s->v2, k); memcpy(&k, s->buf + 16, 8); s->v3 = xxh_round(s->v3, k); memcpy(&k, s->buf + 24, 8); s->v4 = xxh_round(s->v4, k); s->buf_len = 0; } /* Process full 32-byte chunks */ while (p + 32 <= end) { uint64_t k; memcpy(&k, p + 0, 8); s->v1 = xxh_round(s->v1, k); memcpy(&k, p + 8, 8); s->v2 = xxh_round(s->v2, k); memcpy(&k, p + 16, 8); s->v3 = xxh_round(s->v3, k); memcpy(&k, p + 24, 8); s->v4 = xxh_round(s->v4, k); p += 32; } /* Buffer trailing bytes */ if (p < end) { size_t rem = (size_t)(end - p); memcpy(s->buf + s->buf_len, p, rem); s->buf_len += rem; } } VV_NO_SANITIZE_INTEGER uint64_t vv_xxh64_finalize(const vv_xxh64_state_t *s) { uint64_t h64; if (s->total_len >= 32) { h64 = xxh_rotl64(s->v1, 1) + xxh_rotl64(s->v2, 7) + xxh_rotl64(s->v3, 12) + xxh_rotl64(s->v4, 18); h64 = xxh_merge_round(h64, s->v1); h64 = xxh_merge_round(h64, s->v2); h64 = xxh_merge_round(h64, s->v3); h64 = xxh_merge_round(h64, s->v4); } else { h64 = s->seed + XXH_PRIME64_5; } h64 += s->total_len; const uint8_t *p = s->buf; const uint8_t *end = p + s->buf_len; while (p + 8 <= end) { uint64_t k; memcpy(&k, p, 8); k *= XXH_PRIME64_2; k = xxh_rotl64(k, 31); k *= XXH_PRIME64_1; h64 ^= k; h64 = xxh_rotl64(h64, 27) * XXH_PRIME64_1 + XXH_PRIME64_4; p += 8; } while (p + 4 <= end) { uint32_t k; memcpy(&k, p, 4); h64 ^= (uint64_t)k * XXH_PRIME64_1; h64 = xxh_rotl64(h64, 23) * XXH_PRIME64_2 + XXH_PRIME64_3; p += 4; } while (p < end) { h64 ^= (*p) * XXH_PRIME64_5; h64 = xxh_rotl64(h64, 11) * XXH_PRIME64_1; p++; } h64 ^= h64 >> 33; h64 *= XXH_PRIME64_2; h64 ^= h64 >> 29; h64 *= XXH_PRIME64_3; h64 ^= h64 >> 32; return h64; }