// SPDX-License-Identifier: BSD-2-Clause // Copyright (C) 2010 Andrea Mazzoleni #include "tommyhash.h" /******************************************************************************/ /* hash */ #include /* for memcpy */ /** * Swap endianness. * They are needed only if BigEndian. */ #if defined(__GNUC__) #define tommy_swap32(x) __builtin_bswap32(x) #else tommy_inline tommy_uint32_t tommy_swap32(tommy_uint32_t v) { return ((v & 0xFF000000) >> 24) | ((v & 0x00FF0000) >> 8) | ((v & 0x0000FF00) << 8) | ((v & 0x000000FF) << 24); } #endif tommy_inline tommy_uint32_t tommy_le_uint32_read(const void* ptr) { tommy_uint32_t v; memcpy(&v, ptr, sizeof(v)); #if defined(WORDS_BIGENDIAN) || defined(__BIG_ENDIAN__) || \ (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) v = tommy_swap32(v); #endif return v; } #define tommy_rot(x, k) \ (((x) << (k)) | ((x) >> (32 - (k)))) #define tommy_mix(a, b, c) \ do { \ a -= c; a ^= tommy_rot(c, 4); c += b; \ b -= a; b ^= tommy_rot(a, 6); a += c; \ c -= b; c ^= tommy_rot(b, 8); b += a; \ a -= c; a ^= tommy_rot(c, 16); c += b; \ b -= a; b ^= tommy_rot(a, 19); a += c; \ c -= b; c ^= tommy_rot(b, 4); b += a; \ } while (0) #define tommy_final(a, b, c) \ do { \ c ^= b; c -= tommy_rot(b, 14); \ a ^= c; a -= tommy_rot(c, 11); \ b ^= a; b -= tommy_rot(a, 25); \ c ^= b; c -= tommy_rot(b, 16); \ a ^= c; a -= tommy_rot(c, 4); \ b ^= a; b -= tommy_rot(a, 14); \ c ^= b; c -= tommy_rot(b, 24); \ } while (0) TOMMY_API tommy_uint32_t tommy_hash_u32(tommy_uint32_t init_val, const void* void_key, tommy_size_t key_len) { const unsigned char* key = tommy_cast(const unsigned char*, void_key); tommy_uint32_t a, b, c; a = b = c = 0xdeadbeef + ((tommy_uint32_t)key_len) + init_val; while (key_len > 12) { a += tommy_le_uint32_read(key + 0); b += tommy_le_uint32_read(key + 4); c += tommy_le_uint32_read(key + 8); tommy_mix(a, b, c); key_len -= 12; key += 12; } switch (key_len) { case 0 : return c; /* used only when called with a zero length */ case 12 : c += tommy_le_uint32_read(key + 8); b += tommy_le_uint32_read(key + 4); a += tommy_le_uint32_read(key + 0); break; case 11 : c += ((tommy_uint32_t)key[10]) << 16; /* fallthrough */ case 10 : c += ((tommy_uint32_t)key[9]) << 8; /* fallthrough */ case 9 : c += key[8]; /* fallthrough */ case 8 : b += tommy_le_uint32_read(key + 4); a += tommy_le_uint32_read(key + 0); break; case 7 : b += ((tommy_uint32_t)key[6]) << 16; /* fallthrough */ case 6 : b += ((tommy_uint32_t)key[5]) << 8; /* fallthrough */ case 5 : b += key[4]; /* fallthrough */ case 4 : a += tommy_le_uint32_read(key + 0); break; case 3 : a += ((tommy_uint32_t)key[2]) << 16; /* fallthrough */ case 2 : a += ((tommy_uint32_t)key[1]) << 8; /* fallthrough */ case 1 : a += key[0]; /* fallthrough */ } tommy_final(a, b, c); return c; } TOMMY_API tommy_uint64_t tommy_hash_u64(tommy_uint64_t init_val, const void* void_key, tommy_size_t key_len) { const unsigned char* key = tommy_cast(const unsigned char*, void_key); tommy_uint32_t a, b, c; a = b = c = 0xdeadbeef + ((tommy_uint32_t)key_len) + (init_val & 0xffffffff); c += init_val >> 32; while (key_len > 12) { a += tommy_le_uint32_read(key + 0); b += tommy_le_uint32_read(key + 4); c += tommy_le_uint32_read(key + 8); tommy_mix(a, b, c); key_len -= 12; key += 12; } switch (key_len) { case 0 : return c + ((tommy_uint64_t)b << 32); /* used only when called with a zero length */ case 12 : c += tommy_le_uint32_read(key + 8); b += tommy_le_uint32_read(key + 4); a += tommy_le_uint32_read(key + 0); break; case 11 : c += ((tommy_uint32_t)key[10]) << 16; /* fallthrough */ case 10 : c += ((tommy_uint32_t)key[9]) << 8; /* fallthrough */ case 9 : c += key[8]; /* fallthrough */ case 8 : b += tommy_le_uint32_read(key + 4); a += tommy_le_uint32_read(key + 0); break; case 7 : b += ((tommy_uint32_t)key[6]) << 16; /* fallthrough */ case 6 : b += ((tommy_uint32_t)key[5]) << 8; /* fallthrough */ case 5 : b += key[4]; /* fallthrough */ case 4 : a += tommy_le_uint32_read(key + 0); break; case 3 : a += ((tommy_uint32_t)key[2]) << 16; /* fallthrough */ case 2 : a += ((tommy_uint32_t)key[1]) << 8; /* fallthrough */ case 1 : a += key[0]; /* fallthrough */ } tommy_final(a, b, c); return c + ((tommy_uint64_t)b << 32); } TOMMY_API tommy_uint32_t tommy_strhash_u32(tommy_uint32_t init_val, const void* void_key) { const unsigned char* key = tommy_cast(const unsigned char*, void_key); tommy_uint32_t a, b, c; tommy_uint32_t m[3] = { 0xff, 0xff00, 0xff0000 }; a = b = c = 0xdeadbeef + init_val; /* this is different than original lookup3 and the result won't match */ while (1) { tommy_uint32_t v = tommy_le_uint32_read(key); if (tommy_haszero_u32(v)) { if (v & m[0]) { a += v & m[0]; if (v & m[1]) { a += v & m[1]; if (v & m[2]) a += v & m[2]; } } break; } a += v; v = tommy_le_uint32_read(key + 4); if (tommy_haszero_u32(v)) { if (v & m[0]) { b += v & m[0]; if (v & m[1]) { b += v & m[1]; if (v & m[2]) b += v & m[2]; } } break; } b += v; v = tommy_le_uint32_read(key + 8); if (tommy_haszero_u32(v)) { if (v & m[0]) { c += v & m[0]; if (v & m[1]) { c += v & m[1]; if (v & m[2]) c += v & m[2]; } } break; } c += v; tommy_mix(a, b, c); key += 12; } /* for lengths that are multipliers of 12 we already have called mix */ /* this is different than the original lookup3 and the result won't match */ tommy_final(a, b, c); return c; }