#ifndef T1HA0_DISABLED
#include "t1ha_bits.h"
static __maybe_unused __always_inline uint32_t tail32_le_aligned(const void *v,
size_t tail) {
const uint8_t *const p = (const uint8_t *)v;
#if T1HA_USE_FAST_ONESHOT_READ && !defined(__SANITIZE_ADDRESS__)
const unsigned shift = ((4 - tail) & 3) << 3;
return fetch32_le_aligned(p) & ((~UINT32_C(0)) >> shift);
#else
uint32_t r = 0;
switch (tail & 3) {
default:
unreachable();
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
case 0:
return fetch32_le_aligned(p);
case 3:
r = (uint32_t)p[2] << 16;
case 2:
return r + fetch16_le_aligned(p);
case 1:
return p[0];
#else
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
}
#endif
}
static __maybe_unused __always_inline uint32_t
tail32_le_unaligned(const void *v, size_t tail) {
const uint8_t *p = (const uint8_t *)v;
#ifdef can_read_underside
const unsigned offset = (4 - tail) & 3;
const unsigned shift = offset << 3;
if (likely(can_read_underside(p, 4))) {
p -= offset;
return fetch32_le_unaligned(p) >> shift;
}
return fetch32_le_unaligned(p) & ((~UINT32_C(0)) >> shift);
#else
uint32_t r = 0;
switch (tail & 3) {
default:
unreachable();
#if T1HA_SYS_UNALIGNED_ACCESS == T1HA_UNALIGNED_ACCESS__EFFICIENT && \
__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
case 0:
return fetch32_le_unaligned(p);
case 3:
r = (uint32_t)p[2] << 16;
case 2:
return r + fetch16_le_unaligned(p);
case 1:
return p[0];
#else
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
}
#endif
}
static __maybe_unused __always_inline uint32_t tail32_be_aligned(const void *v,
size_t tail) {
const uint8_t *const p = (const uint8_t *)v;
#if T1HA_USE_FAST_ONESHOT_READ && !defined(__SANITIZE_ADDRESS__)
const unsigned shift = ((4 - tail) & 3) << 3;
return fetch32_be_aligned(p) >> shift;
#else
switch (tail & 3) {
default:
unreachable();
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
case 1:
return p[0];
case 2:
return fetch16_be_aligned(p);
case 3:
return fetch16_be_aligned(p) << 8 | p[2];
case 0:
return fetch32_be_aligned(p);
#else
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
}
#endif
}
static __maybe_unused __always_inline uint32_t
tail32_be_unaligned(const void *v, size_t tail) {
const uint8_t *p = (const uint8_t *)v;
#ifdef can_read_underside
const unsigned offset = (4 - tail) & 3;
const unsigned shift = offset << 3;
if (likely(can_read_underside(p, 4))) {
p -= offset;
return fetch32_be_unaligned(p) & ((~UINT32_C(0)) >> shift);
}
return fetch32_be_unaligned(p) >> shift;
#else
switch (tail & 3) {
default:
unreachable();
#if T1HA_SYS_UNALIGNED_ACCESS == T1HA_UNALIGNED_ACCESS__EFFICIENT && \
__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
case 1:
return p[0];
case 2:
return fetch16_be_unaligned(p);
case 3:
return fetch16_be_unaligned(p) << 8 | p[2];
case 0:
return fetch32_be_unaligned(p);
#else
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
}
#endif
}
#ifndef rot32
static __maybe_unused __always_inline uint32_t rot32(uint32_t v, unsigned s) {
return (v >> s) | (v << (32 - s));
}
#endif
static __always_inline void mixup32(uint32_t *a, uint32_t *b, uint32_t v,
uint32_t prime) {
uint64_t l = mul_32x32_64(*b + v, prime);
*a ^= (uint32_t)l;
*b += (uint32_t)(l >> 32);
}
static __always_inline uint64_t final32(uint32_t a, uint32_t b) {
uint64_t l = (b ^ rot32(a, 13)) | (uint64_t)a << 32;
l *= prime_0;
l ^= l >> 41;
l *= prime_4;
l ^= l >> 47;
l *= prime_6;
return l;
}
static const uint32_t prime32_0 = UINT32_C(0x92D78269);
static const uint32_t prime32_1 = UINT32_C(0xCA9B4735);
static const uint32_t prime32_2 = UINT32_C(0xA4ABA1C3);
static const uint32_t prime32_3 = UINT32_C(0xF6499843);
static const uint32_t prime32_4 = UINT32_C(0x86F0FD61);
static const uint32_t prime32_5 = UINT32_C(0xCA2DA6FB);
static const uint32_t prime32_6 = UINT32_C(0xC4BB3575);
#define T1HA0_BODY(ENDIANNES, ALIGNESS) \
const uint32_t *v = (const uint32_t *)data; \
if (unlikely(len > 16)) { \
uint32_t c = ~a; \
uint32_t d = rot32(b, 5); \
const uint32_t *detent = \
(const uint32_t *)((const uint8_t *)data + len - 15); \
do { \
const uint32_t w0 = fetch32_##ENDIANNES##_##ALIGNESS(v + 0); \
const uint32_t w1 = fetch32_##ENDIANNES##_##ALIGNESS(v + 1); \
const uint32_t w2 = fetch32_##ENDIANNES##_##ALIGNESS(v + 2); \
const uint32_t w3 = fetch32_##ENDIANNES##_##ALIGNESS(v + 3); \
v += 4; \
prefetch(v); \
\
const uint32_t d13 = w1 + rot32(w3 + d, 17); \
const uint32_t c02 = w0 ^ rot32(w2 + c, 11); \
d ^= rot32(a + w0, 3); \
c ^= rot32(b + w1, 7); \
b = prime32_1 * (c02 + w3); \
a = prime32_0 * (d13 ^ w2); \
} while (likely(v < detent)); \
\
c += a; \
d += b; \
a ^= prime32_6 * (rot32(c, 16) + d); \
b ^= prime32_5 * (c + rot32(d, 16)); \
\
len &= 15; \
} \
\
switch (len) { \
default: \
mixup32(&a, &b, fetch32_##ENDIANNES##_##ALIGNESS(v++), prime32_4); \
\
case 12: \
case 11: \
case 10: \
case 9: \
mixup32(&b, &a, fetch32_##ENDIANNES##_##ALIGNESS(v++), prime32_3); \
\
case 8: \
case 7: \
case 6: \
case 5: \
mixup32(&a, &b, fetch32_##ENDIANNES##_##ALIGNESS(v++), prime32_2); \
\
case 4: \
case 3: \
case 2: \
case 1: \
mixup32(&b, &a, tail32_##ENDIANNES##_##ALIGNESS(v, len), prime32_1); \
\
case 0: \
return final32(a, b); \
}
uint64_t t1ha0_32le(const void *data, size_t len, uint64_t seed) {
uint32_t a = rot32((uint32_t)len, 17) + (uint32_t)seed;
uint32_t b = (uint32_t)len ^ (uint32_t)(seed >> 32);
#if T1HA_SYS_UNALIGNED_ACCESS == T1HA_UNALIGNED_ACCESS__EFFICIENT
T1HA0_BODY(le, unaligned);
#else
const bool misaligned = (((uintptr_t)data) & (ALIGNMENT_32 - 1)) != 0;
if (misaligned) {
T1HA0_BODY(le, unaligned);
} else {
T1HA0_BODY(le, aligned);
}
#endif
}
uint64_t t1ha0_32be(const void *data, size_t len, uint64_t seed) {
uint32_t a = rot32((uint32_t)len, 17) + (uint32_t)seed;
uint32_t b = (uint32_t)len ^ (uint32_t)(seed >> 32);
#if T1HA_SYS_UNALIGNED_ACCESS == T1HA_UNALIGNED_ACCESS__EFFICIENT
T1HA0_BODY(be, unaligned);
#else
const bool misaligned = (((uintptr_t)data) & (ALIGNMENT_32 - 1)) != 0;
if (misaligned) {
T1HA0_BODY(be, unaligned);
} else {
T1HA0_BODY(be, aligned);
}
#endif
}
#if T1HA0_RUNTIME_SELECT
#if T1HA0_AESNI_AVAILABLE && defined(__ia32__)
static __cold uint64_t x86_cpu_features(void) {
uint32_t features = 0;
uint32_t extended = 0;
#ifdef __GNUC__
uint32_t eax, ebx, ecx, edx;
const unsigned cpuid_max = __get_cpuid_max(0, NULL);
if (cpuid_max >= 1) {
__cpuid_count(1, 0, eax, ebx, features, edx);
if (cpuid_max >= 7)
__cpuid_count(7, 0, eax, extended, ecx, edx);
}
#elif defined(_MSC_VER)
int info[4];
__cpuid(info, 0);
const unsigned cpuid_max = info[0];
if (cpuid_max >= 1) {
__cpuidex(info, 1, 0);
features = info[2];
if (cpuid_max >= 7) {
__cpuidex(info, 7, 0);
extended = info[1];
}
}
#endif
return features | (uint64_t)extended << 32;
}
#endif
__cold t1ha0_function_t t1ha0_resolve(void) {
#if T1HA0_AESNI_AVAILABLE && defined(__ia32__)
uint64_t features = x86_cpu_features();
if (features & UINT32_C(0x02000000) ) {
if ((features & UINT32_C(0x1A000000)) ==
UINT32_C(0x1A000000) )
return ((features >> 32) & 32) ? t1ha0_ia32aes_avx2 : t1ha0_ia32aes_avx;
return t1ha0_ia32aes_noavx;
}
#endif
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#if (UINTPTR_MAX > 0xffffFFFFul || ULONG_MAX > 0xffffFFFFul) && \
(!defined(T1HA1_DISABLED) || !defined(T1HA2_DISABLED))
#ifndef T1HA1_DISABLED
return t1ha1_be;
#else
return t1ha2_atonce;
#endif
#else
return t1ha0_32be;
#endif
#else
#if (UINTPTR_MAX > 0xffffFFFFul || ULONG_MAX > 0xffffFFFFul) && \
(!defined(T1HA1_DISABLED) || !defined(T1HA2_DISABLED))
#ifndef T1HA1_DISABLED
return t1ha1_le;
#else
return t1ha2_atonce;
#endif
#else
return t1ha0_32le;
#endif
#endif
}
#if T1HA_USE_INDIRECT_FUNCTIONS
#if __has_attribute(ifunc)
uint64_t t1ha0(const void *data, size_t len, uint64_t seed)
__attribute__((ifunc("t1ha0_resolve")));
#else
__asm("\t.globl\tt1ha0\n\t.type\tt1ha0, "
"%gnu_indirect_function\n\t.set\tt1ha0,t1ha0_resolve");
#endif
#elif __GNUC_PREREQ(4, 0) || __has_attribute(constructor)
uint64_t (*t1ha0_funcptr)(const void *, size_t, uint64_t);
static __cold void __attribute__((constructor)) t1ha0_init(void) {
t1ha0_funcptr = t1ha0_resolve();
}
#else
static __cold uint64_t t1ha0_proxy(const void *data, size_t len,
uint64_t seed) {
t1ha0_funcptr = t1ha0_resolve();
return t1ha0_funcptr(data, len, seed);
}
uint64_t (*t1ha0_funcptr)(const void *, size_t, uint64_t) = t1ha0_proxy;
#endif
#endif
#endif