Implement Meiyan Hash (Much faster)
Tests do not pass due to new alignement constraint.
This commit is contained in:
1 parent
29b3dd2b75
commit
c64c875969
1 file changed
+64
-19
@@ -28,6 +28,8 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
/***************************************************************/
|
/***************************************************************/
|
||||||
/************************ Compiler Macro ***********************/
|
/************************ Compiler Macro ***********************/
|
||||||
@@ -581,10 +583,19 @@
|
|||||||
#define M_TYPE_FROM_FIELD(type, ptr, field_type, field) \
|
#define M_TYPE_FROM_FIELD(type, ptr, field_type, field) \
|
||||||
((type *)(void*)( (char *)M_ASSIGN_CAST(field_type*, (ptr)) - offsetof(type, field) ))
|
((type *)(void*)( (char *)M_ASSIGN_CAST(field_type*, (ptr)) - offsetof(type, field) ))
|
||||||
|
|
||||||
|
|
||||||
/************************************************************/
|
/************************************************************/
|
||||||
/********************* HASH selection ***********************/
|
/********************* HASH selection ***********************/
|
||||||
/************************************************************/
|
/************************************************************/
|
||||||
|
|
||||||
|
/* User code shall overwrite this macro by a random seed (of type size_t)
|
||||||
|
so that the hash are not easily predictible by an attacker.
|
||||||
|
See https://events.ccc.de/congress/2011/Fahrplan/attachments/2007_28C3_Effective_DoS_on_web_application_platforms.pdf
|
||||||
|
*/
|
||||||
|
#ifndef M_HASH_SEED
|
||||||
|
# define M_HASH_SEED 0
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(M_USE_DJB_HASH)
|
#if defined(M_USE_DJB_HASH)
|
||||||
#define M_HASH_INIT 5381UL
|
#define M_HASH_INIT 5381UL
|
||||||
#define M_HASH_CALC(h1,h2) (((h1) * 33UL) + (h2))
|
#define M_HASH_CALC(h1,h2) (((h1) * 33UL) + (h2))
|
||||||
@@ -611,9 +622,52 @@
|
|||||||
#define M_HASH_CALC(h1,h2) (((h1) * 31421) + (h2) + 6927)
|
#define M_HASH_CALC(h1,h2) (((h1) * 31421) + (h2) + 6927)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define M_HASH_DECL(hash) size_t hash = M_HASH_INIT
|
#define M_HASH_DECL(hash) size_t hash = M_HASH_INIT ^ M_HASH_SEED
|
||||||
#define M_HASH_UP(hash,h) hash = M_HASH_CALC(hash, (h))
|
#define M_HASH_UP(hash,h) hash = M_HASH_CALC(hash, (h))
|
||||||
|
|
||||||
|
/* Safe, efficient, and portable Rotate:
|
||||||
|
It should be recognized by any compiler and generate a single roll instruction */
|
||||||
|
static inline uint32_t m_core_rotl32a (uint32_t x, uint32_t n)
|
||||||
|
{
|
||||||
|
assert (n > 0 && n<32);
|
||||||
|
return (x<<n) | (x>>(32-n));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Implement Meiyan Hash
|
||||||
|
See https://www.strchr.com/hash_functions & http://www.sanmayce.com/Fastest_Hash/
|
||||||
|
NOTE: A lot of cast. Not really type nor alignement safe.
|
||||||
|
NOTE: Can be reduced to very few instructions if constant size argument.
|
||||||
|
*/
|
||||||
|
static inline uint32_t
|
||||||
|
m_core_hash (const void *str, size_t wrdlen)
|
||||||
|
{
|
||||||
|
const uint32_t prime = 709607U;
|
||||||
|
uint32_t hash32 = 2166136261U ^ M_HASH_SEED;
|
||||||
|
const uint8_t *p = str;
|
||||||
|
assert ( ( (uintptr_t)p & (sizeof(uint32_t)-1) ) == 0);
|
||||||
|
while (wrdlen >= 2*sizeof(uint32_t)) {
|
||||||
|
const uint32_t *pui = (const uint32_t *) (uintptr_t) p;
|
||||||
|
hash32 = (hash32 ^ (m_core_rotl32a(pui[0],5) ^ pui[1])) * prime;
|
||||||
|
wrdlen -= 2*sizeof(uint32_t);
|
||||||
|
p += 2*sizeof(uint32_t);
|
||||||
|
}
|
||||||
|
// Cases: 0,1,2,3,4,5,6,7
|
||||||
|
if (wrdlen & sizeof(uint32_t)) {
|
||||||
|
const uint16_t *pui = (const uint16_t *) (uintptr_t) p;
|
||||||
|
hash32 = (hash32 ^ pui[0]) * prime;
|
||||||
|
hash32 = (hash32 ^ pui[1]) * prime;
|
||||||
|
p += 2* sizeof(uint16_t);
|
||||||
|
}
|
||||||
|
if (wrdlen & sizeof(uint16_t)) {
|
||||||
|
const uint16_t *pui = (const uint16_t *) (uintptr_t) p;
|
||||||
|
hash32 = (hash32 ^ pui[0]) * prime;
|
||||||
|
p += sizeof(uint16_t);
|
||||||
|
}
|
||||||
|
if (wrdlen & 1)
|
||||||
|
hash32 = (hash32 ^ *p) * prime;
|
||||||
|
return hash32 ^ (hash32 >> 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/************************************************************/
|
/************************************************************/
|
||||||
/******************** METHODS handling **********************/
|
/******************** METHODS handling **********************/
|
||||||
@@ -675,6 +729,7 @@
|
|||||||
M_RET_ARG2 (M_MAP2B(M_C, M_C3(M_, method, _), __VA_ARGS__), method_default,)
|
M_RET_ARG2 (M_MAP2B(M_C, M_C3(M_, method, _), __VA_ARGS__), method_default,)
|
||||||
|
|
||||||
/* Get the given method */
|
/* Get the given method */
|
||||||
|
// TODO: MOVE & INIT_MOVE shall not a default potentially non-working implementation.
|
||||||
#define M_GET_INIT(...) M_GET_METHOD(INIT, M_INIT_DEFAULT, __VA_ARGS__)
|
#define M_GET_INIT(...) M_GET_METHOD(INIT, M_INIT_DEFAULT, __VA_ARGS__)
|
||||||
#define M_GET_INIT_SET(...) M_GET_METHOD(INIT_SET, M_SET_DEFAULT, __VA_ARGS__)
|
#define M_GET_INIT_SET(...) M_GET_METHOD(INIT_SET, M_SET_DEFAULT, __VA_ARGS__)
|
||||||
#define M_GET_INIT_MOVE(...) M_GET_METHOD(INIT_MOVE, M_MOVE_DEFAULT, __VA_ARGS__)
|
#define M_GET_INIT_MOVE(...) M_GET_METHOD(INIT_MOVE, M_MOVE_DEFAULT, __VA_ARGS__)
|
||||||
@@ -720,14 +775,9 @@
|
|||||||
#define M_GET_EXT_ALGO(...) M_GET_METHOD(EXT_ALGO, M_NO_EXT_ALGO, __VA_ARGS__)
|
#define M_GET_EXT_ALGO(...) M_GET_METHOD(EXT_ALGO, M_NO_EXT_ALGO, __VA_ARGS__)
|
||||||
#define M_GET_EXPECTED_SIZE(...) M_GET_METHOD(EXPECTED_SIZE, 8, __VA_ARGS__)
|
#define M_GET_EXPECTED_SIZE(...) M_GET_METHOD(EXPECTED_SIZE, 8, __VA_ARGS__)
|
||||||
|
|
||||||
/* Define the default method.
|
/* Define the default method. */
|
||||||
NOTE: MEMSET_DEFAULT & MEMCPY_DEFAULT are NOT compatible with the '[1]' tricks
|
|
||||||
if the variable is defined as a parameter of a function. */
|
|
||||||
#define M_INIT_DEFAULT(a) ((a) = 0)
|
#define M_INIT_DEFAULT(a) ((a) = 0)
|
||||||
#define M_MEMSET_DEFAULT(a) (memset(&(a), 0, sizeof (a)))
|
|
||||||
#define M_SET_DEFAULT(a,b) ((a) = (b))
|
#define M_SET_DEFAULT(a,b) ((a) = (b))
|
||||||
#define M_MOVE_DEFAULT(a,b) (M_MEMCPY_DEFAULT(a, b), M_MEMSET_DEFAULT(b))
|
|
||||||
#define M_MEMCPY_DEFAULT(a,b) (memcpy(&(a), &(b), sizeof (a)))
|
|
||||||
#define M_CLEAR_DEFAULT(a) (void)a
|
#define M_CLEAR_DEFAULT(a) (void)a
|
||||||
#define M_NEW_DEFAULT(a) M_MEMORY_ALLOC(a)
|
#define M_NEW_DEFAULT(a) M_MEMORY_ALLOC(a)
|
||||||
#define M_DEL_DEFAULT(a) M_MEMORY_FREE(a)
|
#define M_DEL_DEFAULT(a) M_MEMORY_FREE(a)
|
||||||
@@ -741,20 +791,15 @@
|
|||||||
#define M_DIV_DEFAULT(a,b,c) ((a) = (b) / (c))
|
#define M_DIV_DEFAULT(a,b,c) ((a) = (b) / (c))
|
||||||
#define M_NO_EXT_ALGO(n,co,to)
|
#define M_NO_EXT_ALGO(n,co,to)
|
||||||
|
|
||||||
/* Default hash: perform a hash of the memory pattern of the object */
|
/* NOTE: Theses operators are NOT compatible with the '[1]' tricks
|
||||||
static inline size_t
|
if the variable is defined as a parameter of a function
|
||||||
m_core_hash(const void *ptr, size_t s)
|
(sizeof (a) is not portable). */
|
||||||
{
|
#define M_MOVE_DEFAULT(a,b) (M_MEMCPY_DEFAULT(a, b), M_MEMSET_DEFAULT(b))
|
||||||
const unsigned char *p = (const unsigned char *) ptr;
|
#define M_MEMCPY_DEFAULT(a,b) (memcpy(&(a), &(b), sizeof (a)))
|
||||||
M_HASH_DECL(hash);
|
#define M_MEMSET_DEFAULT(a) (memset(&(a), 0, sizeof (a)))
|
||||||
for(size_t i = 0 ; i < s; i++) {
|
|
||||||
unsigned int c = p[i];
|
|
||||||
M_HASH_UP(hash, c);
|
|
||||||
}
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
#define M_HASH_DEFAULT(a) m_core_hash((const void*) &(a), sizeof (a))
|
#define M_HASH_DEFAULT(a) m_core_hash((const void*) &(a), sizeof (a))
|
||||||
|
|
||||||
|
/* Default oplist for C standard types (int & float) */
|
||||||
#define M_DEFAULT_OPLIST \
|
#define M_DEFAULT_OPLIST \
|
||||||
(INIT(M_INIT_DEFAULT), INIT_SET(M_SET_DEFAULT), SET(M_SET_DEFAULT), \
|
(INIT(M_INIT_DEFAULT), INIT_SET(M_SET_DEFAULT), SET(M_SET_DEFAULT), \
|
||||||
CLEAR(M_CLEAR_DEFAULT), EQUAL(M_EQUAL_DEFAULT), CMP(M_CMP_DEFAULT), \
|
CLEAR(M_CLEAR_DEFAULT), EQUAL(M_EQUAL_DEFAULT), CMP(M_CMP_DEFAULT), \
|
||||||
|
|||||||
Reference in new issue
Block a user