X-Git-Url: https://git.xonotic.org/?p=xonotic%2Fgmqcc.git;a=blobdiff_plain;f=stat.c;h=287ec0bf583721cd4e2a980d385ded02180963bd;hp=f4b996937ed9e4ddcaaad58042a67ea7e62ab76e;hb=a982d4e524e6cdfa1ba675df97a5d9e9f3d4c3ce;hpb=90a016c6e08d11f67b127100d9e8bf1b8ccbc3ef diff --git a/stat.c b/stat.c index f4b9969..287ec0b 100644 --- a/stat.c +++ b/stat.c @@ -24,7 +24,6 @@ #include #include -#include #include "gmqcc.h" @@ -72,8 +71,8 @@ static stat_mem_block_t *stat_mem_block_root = NULL; */ static stat_size_table_t stat_size_new(void) { return (stat_size_table_t)memset( - mem_a(sizeof(stat_size_entry_t) * ST_SIZE), - 0, ST_SIZE * sizeof(stat_size_entry_t) + mem_a(sizeof(stat_size_entry_t*) * ST_SIZE), + 0, ST_SIZE * sizeof(stat_size_entry_t*) ); } @@ -271,6 +270,104 @@ typedef struct hash_node_t { struct hash_node_t *next; /* next node (linked list) */ } hash_node_t; +/* + * This is a patched version of the Murmur2 hashing function to use + * a proper pre-mix and post-mix setup. Infact this is Murmur3 for + * the most part just reinvented. + * + * Murmur 2 contains an inner loop such as: + * while (l >= 4) { + * u32 k = *(u32*)d; + * k *= m; + * k ^= k >> r; + * k *= m; + * + * h *= m; + * h ^= k; + * d += 4; + * l -= 4; + * } + * + * The two u32s that form the key are the same value x (pulled from data) + * this premix stage will perform the same results for both values. Unrolled + * this produces just: + * x *= m; + * x ^= x >> r; + * x *= m; + * + * h *= m; + * h ^= x; + * h *= m; + * h ^= x; + * + * This appears to be fine, except what happens when m == 1? well x + * cancels out entierly, leaving just: + * x ^= x >> r; + * h ^= x; + * h ^= x; + * + * So all keys hash to the same value, but how often does m == 1? + * well, it turns out testing x for all possible values yeilds only + * 172,013,942 unique results instead of 2^32. So nearly ~4.6 bits + * are cancelled out on average! + * + * This means we have a 14.5% (rounded) chance of colliding more, which + * results in another bucket/chain for the hashtable. + * + * We fix it buy upgrading the pre and post mix ssystems to align with murmur + * hash 3. + */ +#if 1 +#define GMQCC_ROTL32(X, R) (((X) << (R)) | ((X) >> (32 - (R)))) +GMQCC_INLINE size_t util_hthash(hash_table_t *ht, const char *key) { + const unsigned char *data = (const unsigned char *)key; + const size_t len = strlen(key); + const size_t block = len / 4; + const uint32_t mask1 = 0xCC9E2D51; + const uint32_t mask2 = 0x1B873593; + const uint32_t *blocks = (const uint32_t*)(data + block * 4); + const unsigned char *tail = (const unsigned char *)(data + block * 4); + + size_t i; + uint32_t k; + uint32_t h = 0x1EF0 ^ len; + + for (i = -block; i; i++) { + k = blocks[i]; + k *= mask1; + k = GMQCC_ROTL32(k, 15); + k *= mask2; + h ^= k; + h = GMQCC_ROTL32(h, 13); + h = h * 5 + 0xE6546B64; + } + + k = 0; + switch (len & 3) { + case 3: + k ^= tail[2] << 16; + case 2: + k ^= tail[1] << 8; + case 1: + k ^= tail[0]; + k *= mask1; + k = GMQCC_ROTL32(k, 15); + k *= mask2; + h ^= k; + } + + h ^= len; + h ^= h >> 16; + h *= 0x85EBCA6B; + h ^= h >> 13; + h *= 0xC2B2AE35; + h ^= h >> 16; + + return (size_t) (h % ht->size); +} +#undef GMQCC_ROTL32 +#else +/* We keep the old for reference */ GMQCC_INLINE size_t util_hthash(hash_table_t *ht, const char *key) { const uint32_t mix = 0x5BD1E995; const uint32_t rot = 24; @@ -305,6 +402,7 @@ GMQCC_INLINE size_t util_hthash(hash_table_t *ht, const char *key) { return (size_t) (hash % ht->size); } +#endif static hash_node_t *_util_htnewpair(const char *key, void *value) { hash_node_t *node; @@ -514,7 +612,7 @@ static void stat_dump_mem_contents(stat_mem_block_t *memory, uint16_t cols) { con_out("%c", (j >= memory->size) ? ' ' - : (isprint(((unsigned char*)(memory + 1))[j])) + : (util_isprint(((unsigned char*)(memory + 1))[j])) ? 0xFF & ((unsigned char*)(memory + 1)) [j] : '.' );