mirror of
https://github.com/amadvance/snapraid.git
synced 2026-10-25 18:40:19 -04:00
190 lines
5.5 KiB
C
190 lines
5.5 KiB
C
// SPDX-License-Identifier: BSD-2-Clause AND MIT
|
|
// Copyright (C) 2010 Andrea Mazzoleni
|
|
|
|
#include "tommyhash.h"
|
|
|
|
/******************************************************************************/
|
|
/* hash */
|
|
|
|
#include <string.h> /* for memcpy */
|
|
|
|
/**
|
|
* Swap endianness.
|
|
* They are needed only if BigEndian.
|
|
*/
|
|
#if defined(__GNUC__)
|
|
#define tommy_imp_hash_swap32(x) __builtin_bswap32(x)
|
|
#else
|
|
tommy_inline tommy_uint32_t tommy_imp_hash_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_imp_hash_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_imp_hash_swap32(v);
|
|
#endif
|
|
return v;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/* a5hash */
|
|
|
|
/**
|
|
* a5hash32 and a5hash (64-bit) by Aleksey Vaneev, from https://github.com/avaneev/a5hash.
|
|
* Adapted from version 5.25 with little-endian word reads.
|
|
* The 32-bit variant additionally mixes the expanded seed asymmetrically into
|
|
* h[2] and h[3] before processing the input and produces different hash values.
|
|
* Copyright (c) 2025 Aleksey Vaneev. Released under the MIT license, see LICENSES/MIT.
|
|
*/
|
|
|
|
tommy_inline void tommy_mul(tommy_uint32_t* lo, tommy_uint32_t* hi)
|
|
{
|
|
/* both operands are 32-bit; the full product supplies two state words */
|
|
tommy_uint64_t v = (tommy_uint64_t)*lo * *hi;
|
|
*lo = (tommy_uint32_t)v;
|
|
*hi = v >> 32;
|
|
}
|
|
|
|
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 val01 = 0x55555555;
|
|
tommy_uint32_t val10 = 0xaaaaaaaa;
|
|
tommy_uint32_t h[4] = { 0x243f6a88, 0x85a308d3, 0xfb0bd3ea, 0x0f58fd47 };
|
|
tommy_uint32_t a, b;
|
|
tommy_size_t len = key_len;
|
|
|
|
h[0] ^= key_len;
|
|
h[1] ^= key_len;
|
|
#if TOMMY_SIZE_BIT == 64
|
|
h[2] = (key_len >> 32) ^ 0x452821e6;
|
|
h[3] = (key_len >> 32) ^ 0x38d01377;
|
|
tommy_mul(&h[2], &h[3]);
|
|
#endif
|
|
h[0] ^= init_val & val01;
|
|
h[1] ^= init_val & val10;
|
|
tommy_mul(&h[0], &h[1]);
|
|
|
|
/*
|
|
* Mix the expanded seed asymmetrically into h[2] and h[3] so that short keys
|
|
* skipping the main loop still depend on the seed, and neither h[0] nor h[1]
|
|
* cancels out when folding h[0] ^= h[2] and h[1] ^= h[3] at the end.
|
|
*/
|
|
h[2] ^= h[0] ^ h[1];
|
|
h[3] ^= h[0];
|
|
|
|
if (len <= 16) {
|
|
if (len >= 4) {
|
|
const unsigned char* tail = key + len - 4;
|
|
a = tommy_imp_hash_le_uint32_read(key);
|
|
b = tommy_imp_hash_le_uint32_read(tail);
|
|
if (len >= 9) {
|
|
tommy_size_t offset = (len / 8) * 4;
|
|
h[2] += tommy_imp_hash_le_uint32_read(key + offset);
|
|
h[3] += tommy_imp_hash_le_uint32_read(tail - offset);
|
|
tommy_mul(&h[2], &h[3]);
|
|
}
|
|
} else {
|
|
a = 0;
|
|
b = 0;
|
|
if (len != 0) {
|
|
a = key[0];
|
|
if (len >= 2)
|
|
a |= key[1] << 8;
|
|
if (len == 3)
|
|
a |= key[2] << 16;
|
|
}
|
|
}
|
|
} else {
|
|
val01 ^= h[0];
|
|
val10 ^= h[1];
|
|
do {
|
|
tommy_uint32_t s1 = h[0];
|
|
tommy_uint32_t s4 = h[3];
|
|
h[0] += tommy_imp_hash_le_uint32_read(key);
|
|
h[1] += tommy_imp_hash_le_uint32_read(key + 4);
|
|
tommy_mul(&h[0], &h[1]);
|
|
h[2] += tommy_imp_hash_le_uint32_read(key + 8);
|
|
h[3] += tommy_imp_hash_le_uint32_read(key + 12);
|
|
tommy_mul(&h[2], &h[3]);
|
|
h[0] += val01;
|
|
h[1] += s4;
|
|
h[2] += s1;
|
|
h[3] += val10;
|
|
key += 16;
|
|
len -= 16;
|
|
} while (len > 16);
|
|
|
|
/* the last words may overlap the previous block, but stay within the input */
|
|
const unsigned char* tail = key + len;
|
|
a = tommy_imp_hash_le_uint32_read(tail - 8);
|
|
b = tommy_imp_hash_le_uint32_read(tail - 4);
|
|
if (len >= 9) {
|
|
h[2] += tommy_imp_hash_le_uint32_read(tail - 16);
|
|
h[3] += tommy_imp_hash_le_uint32_read(tail - 12);
|
|
tommy_mul(&h[2], &h[3]);
|
|
}
|
|
}
|
|
|
|
h[0] ^= h[2];
|
|
h[1] ^= h[3];
|
|
h[0] += a;
|
|
h[1] += b;
|
|
tommy_mul(&h[0], &h[1]);
|
|
h[0] ^= val01;
|
|
tommy_mul(&h[0], &h[1]);
|
|
return h[0] ^ h[1];
|
|
}
|
|
|
|
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_uint64_t val01 = 0x5555555555555555ULL;
|
|
tommy_uint64_t val10 = 0xaaaaaaaaaaaaaaaaULL;
|
|
tommy_uint64_t h[2] = { (0x452821e638d01377ULL ^ key_len) ^ (init_val & val10), (0x243f6a8885a308d3ULL ^ key_len) ^ (init_val & val01) };
|
|
tommy_size_t len = key_len;
|
|
|
|
tommy_mum(&h[0], &h[1]);
|
|
if (len > 16) {
|
|
val01 ^= h[0];
|
|
val10 ^= h[1];
|
|
do {
|
|
/* each 64-bit operand puts the first little-endian word in its high half */
|
|
h[0] ^= (tommy_uint64_t)tommy_imp_hash_le_uint32_read(key) << 32 ^ tommy_imp_hash_le_uint32_read(key + 4);
|
|
h[1] ^= (tommy_uint64_t)tommy_imp_hash_le_uint32_read(key + 8) << 32 ^ tommy_imp_hash_le_uint32_read(key + 12);
|
|
tommy_mum(&h[0], &h[1]);
|
|
h[0] += val01;
|
|
h[1] += val10;
|
|
key += 16;
|
|
len -= 16;
|
|
} while (len > 16);
|
|
}
|
|
|
|
if (len >= 4) {
|
|
const unsigned char* tail = key + len - 4;
|
|
tommy_size_t offset = (len / 8) * 4;
|
|
h[0] ^= (tommy_uint64_t)tommy_imp_hash_le_uint32_read(key) << 32 | tommy_imp_hash_le_uint32_read(tail);
|
|
h[1] ^= (tommy_uint64_t)tommy_imp_hash_le_uint32_read(key + offset) << 32 | tommy_imp_hash_le_uint32_read(tail - offset);
|
|
} else if (len != 0) {
|
|
h[0] ^= key[0];
|
|
if (len >= 2)
|
|
h[0] ^= key[1] << 8;
|
|
if (len == 3)
|
|
h[0] ^= key[2] << 16;
|
|
}
|
|
|
|
tommy_mum(&h[0], &h[1]);
|
|
h[0] ^= val01;
|
|
tommy_mum(&h[0], &h[1]);
|
|
return h[0] ^ h[1];
|
|
}
|
|
|