Skip to content

Instantly share code, notes, and snippets.

@Boostibot
Created June 27, 2024 17:03
Show Gist options
  • Save Boostibot/6d8e62b49110df13391290d36db9c243 to your computer and use it in GitHub Desktop.
Save Boostibot/6d8e62b49110df13391290d36db9c243 to your computer and use it in GitHub Desktop.
Small implementation of xxhash64
#include <stdint.h>
#include <assert.h>
#include <string.h>
#define XXHASH64_PRIME_1 0x9E3779B185EBCA87ULL
#define XXHASH64_PRIME_2 0xC2B2AE3D27D4EB4FULL
#define XXHASH64_PRIME_3 0x165667B19E3779F9ULL
#define XXHASH64_PRIME_4 0x85EBCA77C2B2AE63ULL
#define XXHASH64_PRIME_5 0x27D4EB2F165667C5ULL
static inline uint64_t _xxhash64_rotate_left(uint64_t x, uint8_t bits)
{
return (x << bits) | (x >> (64 - bits));
}
static inline uint64_t _xxhash64_process_single(uint64_t previous, uint64_t input)
{
return _xxhash64_rotate_left(previous + input * XXHASH64_PRIME_2, 31) * XXHASH64_PRIME_1;
}
static inline uint64_t xxhash64(const void* input, uint64_t length, uint64_t seed)
{
uint32_t endian_check = 0x33221100;
assert(*(uint8_t*) (void*) &endian_check == 0 && "Big endian machine detected! Please change this algorithm to suite your machine!");
assert((input == NULL) == (length == 0) && length >= 0);
uint8_t* data = (uint8_t*) (void*) input;
uint8_t* end = data + length;
//Bulk computation
uint64_t hash = seed + XXHASH64_PRIME_5;
if (length >= 32)
{
uint64_t state[4] = {0};
uint64_t block[4] = {0};
state[0] = seed + XXHASH64_PRIME_1 + XXHASH64_PRIME_2;
state[1] = seed + XXHASH64_PRIME_2;
state[2] = seed;
state[3] = seed - XXHASH64_PRIME_1;
for(; data < end - 31; data += 32)
{
memcpy(block, data, 32);
state[0] = _xxhash64_process_single(state[0], block[0]);
state[1] = _xxhash64_process_single(state[1], block[1]);
state[2] = _xxhash64_process_single(state[2], block[2]);
state[3] = _xxhash64_process_single(state[3], block[3]);
}
hash = _xxhash64_rotate_left(state[0], 1)
+ _xxhash64_rotate_left(state[1], 7)
+ _xxhash64_rotate_left(state[2], 12)
+ _xxhash64_rotate_left(state[3], 18);
hash = (hash ^ _xxhash64_process_single(0, state[0])) * XXHASH64_PRIME_1 + XXHASH64_PRIME_4;
hash = (hash ^ _xxhash64_process_single(0, state[1])) * XXHASH64_PRIME_1 + XXHASH64_PRIME_4;
hash = (hash ^ _xxhash64_process_single(0, state[2])) * XXHASH64_PRIME_1 + XXHASH64_PRIME_4;
hash = (hash ^ _xxhash64_process_single(0, state[3])) * XXHASH64_PRIME_1 + XXHASH64_PRIME_4;
}
hash += length;
//Consume last <32 Bytes
for (; data + 8 <= end; data += 8)
{
uint64_t read = 0; memcpy(&read, data, sizeof read);
hash = _xxhash64_rotate_left(hash ^ _xxhash64_process_single(0, read), 27) * XXHASH64_PRIME_1 + XXHASH64_PRIME_4;
}
if (data + 4 <= end)
{
uint32_t read = 0; memcpy(&read, data, sizeof read);
hash = _xxhash64_rotate_left(hash ^ read * XXHASH64_PRIME_1, 23) * XXHASH64_PRIME_2 + XXHASH64_PRIME_3;
data += 4;
}
while (data < end)
hash = _xxhash64_rotate_left(hash ^ (*data++) * XXHASH64_PRIME_5, 11) * XXHASH64_PRIME_1;
// Avalanche
hash ^= hash >> 33;
hash *= XXHASH64_PRIME_2;
hash ^= hash >> 29;
hash *= XXHASH64_PRIME_3;
hash ^= hash >> 32;
return hash;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment