/*
 *  Copyright (c) 2016 Positive Technologies, https://www.ptsecurity.com,
 *  Fast Positive Hash.
 *
 *  Portions Copyright (c) 2010-2016 Leonid Yuriev <leo@yuriev.ru>,
 *  The 1Hippeus project (t1h).
 *
 *  This software is provided 'as-is', without any express or implied
 *  warranty. In no event will the authors be held liable for any damages
 *  arising from the use of this software.
 *
 *  Permission is granted to anyone to use this software for any purpose,
 *  including commercial applications, and to alter it and redistribute it
 *  freely, subject to the following restrictions:
 *
 *  1. The origin of this software must not be misrepresented; you must not
 *     claim that you wrote the original software. If you use this software
 *     in a product, an acknowledgement in the product documentation would be
 *     appreciated but is not required.
 *  2. Altered source versions must be plainly marked as such, and must not be
 *     misrepresented as being the original software.
 *  3. This notice may not be removed or altered from any source distribution.
 */

/*
 * t1ha = { Fast Positive Hash}
 * by [Positive Technologies](https://www.ptsecurity.ru)
 *
 * Briefly, it is a 64-bit Hash Function:
 *  1. Created for 64-bit little-endian platforms, in predominantly for x86_64,
 *     but without penalties could runs on any 64-bit CPU.
 *  2. In most cases up to 15% faster than City64, xxHash, mum-hash, metro-hash
 *     and all others which are not use specific hardware tricks.
 *  3. Not suitable for cryptography.
 *
 * ACKNOWLEDGEMENT:
 * The t1ha was originally developed by Leonid Yuriev
 * for The 1Hippeus project - zerocopy messaging in the spirit of Sparta!
 */

#ifndef T1HA_INCLUDED
#define T1HA_INCLUDED
#include "config.h"
#include <string.h>

#ifdef BYTE_ORDER
#ifndef __ORDER_LITTLE_ENDIAN__
#define __ORDER_LITTLE_ENDIAN__ LITTLE_ENDIAN
#endif
#ifndef __ORDER_BIG_ENDIAN__
#define __ORDER_BIG_ENDIAN__ BIG_ENDIAN
#endif
#ifndef __BYTE_ORDER__
#define __BYTE_ORDER__ BYTE_ORDER
#endif
#else
#if !defined(__BYTE_ORDER__) || !defined(__ORDER_LITTLE_ENDIAN__) ||           \
		!defined(__ORDER_BIG_ENDIAN__)
#define __ORDER_LITTLE_ENDIAN__ 1234
#define __ORDER_BIG_ENDIAN__ 4321
#if defined(__LITTLE_ENDIAN__) || defined(__ARMEL__) ||                        \
		defined(__THUMBEL__) || defined(__AARCH64EL__) || defined(__MIPSEL__) ||   \
		defined(_MIPSEL) || defined(__MIPSEL) || defined(__i386) ||                \
		defined(__x86_64) || defined(_M_IX86) || defined(_M_X64) ||                \
		defined(i386) || defined(_X86_) || defined(__i386__) || defined(_X86_64_)
#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__
#elif defined(__BIG_ENDIAN__) || defined(__ARMEB__) || defined(__THUMBEB__) || \
		defined(__AARCH64EB__) || defined(__MIPSEB__) || defined(_MIPSEB) ||       \
		defined(__MIPSEB)
#define __BYTE_ORDER__ __ORDER_BIG_ENDIAN__
#else
#error __BYTE_ORDER__ should be defined.
#endif
#endif
#endif
#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__ &&                               \
		__BYTE_ORDER__ != __ORDER_BIG_ENDIAN__
#error Unsupported byte order.
#endif

#if !defined(UNALIGNED_OK)
#if defined(__i386) || defined(__x86_64) || defined(_M_IX86) ||                \
		defined(_M_X64) || defined(i386) || defined(_X86_) || defined(__i386__) || \
		defined(_X86_64_)
#define UNALIGNED_OK 1
#else
#define UNALIGNED_OK 0
#endif
#endif

#if defined(__GNUC__) && (__GNUC__ > 3)

#if defined(__i386) || defined(__x86_64)
#include <x86intrin.h>
#endif
#define likely(cond) __builtin_expect(!!(cond), 1)
#define unlikely(cond) __builtin_expect(!!(cond), 0)
#define unreachable() __builtin_unreachable()
#define bswap64(v) __builtin_bswap64(v)
#define bswap32(v) __builtin_bswap32(v)
#define bswap16(v) __builtin_bswap16(v)

#elif defined(_MSC_VER)

#include <intrin.h>
#include <stdlib.h>
#define likely(cond) (cond)
#define unlikely(cond) (cond)
#define unreachable() __assume(0)
#define bswap64(v) _byteswap_uint64(v)
#define bswap32(v) _byteswap_ulong(v)
#define bswap16(v) _byteswap_ushort(v)
#define rot64(v, s) _rotr64(v, s)
#define rot32(v, s) _rotr(v, s)

#if defined(_M_ARM64) || defined(_M_X64)
#pragma intrinsic(_umul128)
#define mul_64x64_128(a, b, ph) _umul128(a, b, ph)
#pragma intrinsic(__umulh)
#define mul_64x64_high(a, b) __umulh(a, b)
#endif

#if defined(_M_IX86)
#pragma intrinsic(__emulu)
#define mul_32x32_64(a, b) __emulu(a, b)
#elif defined(_M_ARM)
#define mul_32x32_64(a, b) _arm_umull(a, b)
#endif

#else /* Compiler */

#define likely(cond) (cond)
#define unlikely(cond) (cond)
#define unreachable()                                                          \
		do                                                                           \
		for (;;)                                                                   \
		;                                                                        \
		while (0)
#endif /* Compiler */

#ifndef bswap64
static __inline uint64_t bswap64(uint64_t v) {
	return v << 56 | v >> 56 | ((v << 40) & 0x00ff000000000000ull) |
			((v << 24) & 0x0000ff0000000000ull) |
			((v << 8) & 0x000000ff00000000ull) |
			((v >> 8) & 0x00000000ff000000ull) |
			((v >> 24) & 0x0000000000ff0000ull) |
			((v >> 40) & 0x000000000000ff00ull);
}
#endif /* bswap64 */

#ifndef bswap32
static __inline uint32_t bswap32(uint32_t v) {
	return v << 24 | v >> 24 | ((v << 8) & 0x00ff0000) | ((v >> 8) & 0x0000ff00);
}
#endif /* bswap32 */

#ifndef bswap16
static __inline uint16_t bswap16(uint16_t v) { return v << 8 | v >> 8; }
#endif /* bswap16 */

#ifndef rot64
static __inline uint64_t rot64(uint64_t v, unsigned s) {
	return (v >> s) | (v << (64 - s));
}
#endif /* rot64 */

#ifndef rot32
static __inline uint32_t rot32(uint32_t v, unsigned s) {
	return (v >> s) | (v << (32 - s));
}
#endif /* rot32 */

#ifndef mul_32x32_64
static __inline uint64_t mul_32x32_64(uint32_t a, uint32_t b) {
	return a * (uint64_t)b;
}
#endif /* mul_32x32_64 */

/***************************************************************************/

static __inline uint64_t fetch64(const void *v) {
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
	return *(const uint64_t *)v;
#else
	return bswap64(*(const uint64_t *)v);
#endif
}

static __inline uint64_t fetch32(const void *v) {
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
	return *(const uint32_t *)v;
#else
	return bswap32(*(const uint32_t *)v);
#endif
}

static __inline uint64_t fetch16(const void *v) {
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
	return *(const uint16_t *)v;
#else
	return bswap16(*(const uint16_t *)v);
#endif
}

static __inline uint64_t fetch_tail(const void *v, size_t tail) {
	const uint8_t *_ = (const uint8_t *)v;
	switch (tail & 7) {
	case 1:
		return _[0];
	case 2:
		return fetch16(_);
	case 3:
		return fetch16(_) | (_[2] << 16);
	case 4:
		return fetch32(_);
	case 5:
		return fetch32(_) | ((uint64_t)_[4] << 32);
	case 6:
		return fetch32(_) | (fetch16(_ + 4) << 32);
	case 7:
		return fetch32(_) | (fetch16(_ + 4) << 32) | ((uint64_t)_[6] << 48);
	case 0:
		return fetch64(_);
	default:
		unreachable();
	}
}

/* xor-mul-xor mixer */
static __inline uint64_t mix(uint64_t v, uint64_t p) {
	static const unsigned s0 = 41;
	v *= p;
	return v ^ rot64(v, s0);
}

static __inline unsigned add_with_carry(uint64_t *sum, uint64_t addend) {
	*sum += addend;
	return *sum < addend;
}

/* xor high and low parts of full 128-bit product */
static __inline uint64_t mux64(uint64_t v, uint64_t p) {
#ifdef __SIZEOF_INT128__
	__uint128_t r = (__uint128_t)v * (__uint128_t)p;
	/* modern GCC could nicely optimize this */
	return r ^ (r >> 64);
#elif defined(_INTEGRAL_MAX_BITS) && _INTEGRAL_MAX_BITS >= 128
	__uint128 r = (__uint128)v * (__uint128)p;
	return r ^ (r >> 64);
#elif defined(mul_64x64_128)
	uint64_t l, h;
	l = mul_64x64_128(v, p, &h);
	return l ^ h;
#elif defined(mul_64x64_high)
	uint64_t l, h;
	l = v * p;
	h = mul_64x64_high(v, p);
	return l ^ h;
#else
	/* performs 64x64 to 128 bit multiplication */
	uint64_t ll = mul_32x32_64((uint32_t)v, (uint32_t)p);
	uint64_t lh = mul_32x32_64(v >> 32, (uint32_t)p);
	uint64_t hl = mul_32x32_64(p >> 32, (uint32_t)v);
	uint64_t hh =
			mul_32x32_64(v >> 32, p >> 32) + (lh >> 32) + (hl >> 32) +
			/* Few simplification are possible here for 32-bit architectures,
			 * but thus we would lost compatibility with the original 64-bit
			 * version.  Think is very bad idea, because then 32-bit t1ha will
			 * still (relatively) very slowly and well yet not compatible. */
			add_with_carry(&ll, lh << 32) + add_with_carry(&ll, hl << 32);
	return hh ^ ll;
#endif
}

static uint64_t
t1ha(const void *data, size_t len, uint64_t seed)
{
	/* 'magic' primes */
	static const uint64_t p0 = 17048867929148541611ull;
	static const uint64_t p1 = 9386433910765580089ull;
	static const uint64_t p2 = 15343884574428479051ull;
	static const uint64_t p3 = 13662985319504319857ull;
	static const uint64_t p4 = 11242949449147999147ull;
	static const uint64_t p5 = 13862205317416547141ull;
	static const uint64_t p6 = 14653293970879851569ull;
	/* rotations */
	static const unsigned s0 = 41;
	static const unsigned s1 = 17;
	static const unsigned s2 = 31;

	uint64_t a = seed;
	uint64_t b = len;

	const int need_align = (((uintptr_t)data) & 7) != 0 && !UNALIGNED_OK;
	uint64_t align[4];

	if (unlikely(len > 32)) {
		uint64_t c = rot64(len, s1) + seed;
		uint64_t d = len ^ rot64(seed, s1);
		const void *detent = (const uint8_t *)data + len - 31;
		do {
			const uint64_t *v = (const uint64_t *)data;
			if (unlikely(need_align))
				v = (const uint64_t *)memcpy(&align, v, 32);

			uint64_t w0 = fetch64(v + 0);
			uint64_t w1 = fetch64(v + 1);
			uint64_t w2 = fetch64(v + 2);
			uint64_t w3 = fetch64(v + 3);

			uint64_t d02 = w0 ^ rot64(w2 + d, s1);
			uint64_t c13 = w1 ^ rot64(w3 + c, s1);
			c += a ^ rot64(w0, s0);
			d -= b ^ rot64(w1, s2);
			a ^= p1 * (d02 + w3);
			b ^= p0 * (c13 + w2);
			data = (const uint64_t *)data + 4;
		} while (likely(data < detent));

		a ^= p6 * (rot64(c, s1) + d);
		b ^= p5 * (c + rot64(d, s1));
		len &= 31;
	}

	const uint64_t *v = (const uint64_t *)data;
	if (unlikely(need_align) && len > 1)
		v = (const uint64_t *)memcpy(&align, v, len);

	switch (len) {
	default:
		b += mux64(fetch64(v++), p4);
	case 24:
	case 23:
	case 22:
	case 21:
	case 20:
	case 19:
	case 18:
	case 17:
		a += mux64(fetch64(v++), p3);
	case 16:
	case 15:
	case 14:
	case 13:
	case 12:
	case 11:
	case 10:
	case 9:
		b += mux64(fetch64(v++), p2);
	case 8:
	case 7:
	case 6:
	case 5:
	case 4:
	case 3:
	case 2:
	case 1:
		a += mux64(fetch_tail(v, len), p1);
	case 0:
		return mux64(rot64(a + b, s1), p4) + mix(a ^ b, p0);
	}
}

static __inline uint32_t tail32_le(const void *v, size_t tail) {
	const uint8_t *p = (const uint8_t *)v;
	uint32_t r = 0;
	switch (tail & 3) {
#if UNALIGNED_OK && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
	/* For most CPUs this code is better when not needed
	 * copying for alignment or byte reordering. */
	case 0:
		return fetch32(p);
	case 3:
		r = (uint32_t)p[2] << 16;
	case 2:
		return r + fetch16(p);
	case 1:
		return p[0];
#else
	/* For most CPUs this code is better than a
	 * copying for alignment and/or byte reordering. */
	case 0:
		r += p[3];
		r <<= 8;
	case 3:
		r += p[2];
		r <<= 8;
	case 2:
		r += p[1];
		r <<= 8;
	case 1:
		return r + p[0];
#endif
	}
	unreachable();
}

static __inline uint32_t tail32_be(const void *v, size_t tail) {
	const uint8_t *p = (const uint8_t *)v;
	switch (tail & 3) {
#if UNALIGNED_OK && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
	/* For most CPUs this code is better when not needed
	 * copying for alignment or byte reordering. */
	case 1:
		return p[0];
	case 2:
		return fetch16_be(p);
	case 3:
		return fetch16_be(p) << 8 | p[2];
	case 0:
		return fetch32_be(p);
#else
	/* For most CPUs this code is better than a
	 * copying for alignment and/or byte reordering. */
	case 1:
		return p[0];
	case 2:
		return p[1] | (uint32_t)p[0] << 8;
	case 3:
		return p[2] | (uint32_t)p[1] << 8 | (uint32_t)p[0] << 16;
	case 0:
		return p[3] | (uint32_t)p[2] << 8 | (uint32_t)p[1] << 16 |
				(uint32_t)p[0] << 24;
#endif
	}
	unreachable();
}

static __inline uint64_t remix32(uint32_t a, uint32_t b) {
	static const uint64_t p0 = 17048867929148541611ull;
	a ^= rot32(b, 13);
	uint64_t l = a | (uint64_t)b << 32;
	l *= p0;
	l ^= l >> 41;
	return l;
}

static __inline void mixup32(uint32_t *a, uint32_t *b, uint32_t v, uint32_t p) {
	uint64_t l = mul_32x32_64(*b + v, p);
	*a ^= (uint32_t)l;
	*b += (uint32_t)(l >> 32);
}

static uint64_t t1ha32(const void *data, size_t len, uint64_t seed) {
	/* 32-bit 'magic' primes */
	static const uint32_t q0 = 0x92D78269;
	static const uint32_t q1 = 0xCA9B4735;
	static const uint32_t q2 = 0xA4ABA1C3;
	static const uint32_t q3 = 0xF6499843;
	static const uint32_t q4 = 0x86F0FD61;
	static const uint32_t q5 = 0xCA2DA6FB;
	static const uint32_t q6 = 0xC4BB3575;
	/* rotations */
	static const unsigned s1 = 17;

	uint32_t a = rot32((uint32_t)len, s1) + (uint32_t)seed;
	uint32_t b = (uint32_t)len ^ (uint32_t)(seed >> 32);

	const int need_align = (((uintptr_t)data) & 3) != 0 && !UNALIGNED_OK;
	uint32_t align[4];

	if (unlikely(len > 16)) {
		uint32_t c = ~a;
		uint32_t d = rot32(b, 5);
		const void *detent = (const uint8_t *)data + len - 15;
		do {
			const uint32_t *v = (const uint32_t *)data;
			if (unlikely(need_align))
				v = (const uint32_t *)memcpy(&align, v, 16);

			uint32_t w0 = fetch32(v + 0);
			uint32_t w1 = fetch32(v + 1);
			uint32_t w2 = fetch32(v + 2);
			uint32_t w3 = fetch32(v + 3);

			uint32_t c02 = w0 ^ rot32(w2 + c, 11);
			uint32_t d13 = w1 + rot32(w3 + d, s1);
			c ^= rot32(b + w1, 7);
			d ^= rot32(a + w0, 3);
			b = q1 * (c02 + w3);
			a = q0 * (d13 ^ w2);

			data = (const uint32_t *)data + 4;
		} while (likely(data < detent));

		c += a;
		d += b;
		a ^= q6 * (rot32(c, 16) + d);
		b ^= q5 * (c + rot32(d, 16));

		len &= 15;
	}

	const uint8_t *v = (const uint8_t *)data;
	if (unlikely(need_align) && len > 4)
		v = (const uint8_t *)memcpy(&align, v, len);

	switch (len) {
	default:
		mixup32(&a, &b, fetch32(v), q4);
		v += 4;
	case 12:
	case 11:
	case 10:
	case 9:
		mixup32(&b, &a, fetch32(v), q3);
		v += 4;
	case 8:
	case 7:
	case 6:
	case 5:
		mixup32(&a, &b, fetch32(v), q2);
		v += 4;
	case 4:
	case 3:
	case 2:
	case 1:
		mixup32(&b, &a, tail32_le(v, len), q1);
	case 0:
		return remix32(a, b);
	}
}

#endif
