Newer
Older
vvd / src / utils / hash.c
/**
 * XXHash32 implementation
 */

#include <stdint.h>
#include <string.h>
#include <assert.h>
#include "utils/hash.h"

static const uint32_t PRIME32_1 = 2654435761U;
static const uint32_t PRIME32_2 = 2246822519U;
static const uint32_t PRIME32_3 = 3266489917U;
static const uint32_t PRIME32_4 = 668265263U;
static const uint32_t PRIME32_5 = 374761393U;

// Internal: rotate 32-bit value (v) with offset (o)
uint32_t hash_rot(uint32_t v, int o) {
	return (v << o) | (v >> (32 - o));
}

//
// hash_string
//

uint32_t hash_string(const char *data) {
	return hash_compute_s(data, strlen(data), 0);
}

//
// hash_compute
//

uint32_t hash_compute(const char *data, uint32_t length) {
	return hash_compute_s(data, length, 0);
}

//
// hash_compute_s
//

uint32_t hash_compute_s(const char *data, uint32_t length, uint32_t seed) {
	union {
		uint32_t *u32;
		uint8_t  *u8;
		const char *c8;
	} p;
	uint32_t hash;
	const char *end = data + length;	// End of data pointer

	p.c8 = data;

	//
	// Main loop
	//

	if (length >= 16) {
		const char *limit = end - 16;

		uint32_t v1 = seed + PRIME32_1 + PRIME32_2;
		uint32_t v2 = seed + PRIME32_2;
		uint32_t v3 = seed + 0;
		uint32_t v4 = seed - PRIME32_1;

		// Main loop proccesses 4*4 Bytes of information per iteration
		do {
			v1 += p.u32[0] * PRIME32_2;
			v1 =  hash_rot(v1, 13);
			v1 *= PRIME32_1;

			v2 += p.u32[1] * PRIME32_2;
			v2 =  hash_rot(v2, 13);
			v2 *= PRIME32_1;

			v3 += p.u32[2] * PRIME32_2;
			v3 =  hash_rot(v3, 13);
			v3 *= PRIME32_1;

			v4 += p.u32[3] * PRIME32_2;
			v4 =  hash_rot(v4, 13);
			v4 *= PRIME32_1;

			p.u32 += 4;
		} while (p.c8 <= limit);

		hash =	hash_rot(v1, 1)  + hash_rot(v2, 7) +
			hash_rot(v3, 12) + hash_rot(v4, 18);
	} else {
		hash = seed + PRIME32_5;
	}

	hash += length;

	//
	// Finalization
	//

	// Per 4 Bytes
	while (p.c8 <= end - 4) {
		hash += *p.u32 * PRIME32_3;
		hash =  hash_rot(hash, 17) * PRIME32_4;
		++p.u32;
	}

	// Per Byte
	while (p.c8 < end) {
		hash += *p.u8 * PRIME32_5;
		hash =  hash_rot(hash, 11) * PRIME32_1;
		++p.u8;
	}

	//
	// Avalanche
	//

	hash ^= hash >> 15;
	hash *= PRIME32_2;
	hash ^= hash >> 13;
	hash *= PRIME32_3;
	hash ^= hash >> 16;

	return hash;
}