ctstats/hashtable.c

129 lines
2.7 KiB
C

#include <stdlib.h>
#include <inttypes.h>
#include <pthread.h>
#include "hashtable.h"
/* see linux/include/linux/jhash.h for details */
#define JHASH_GOLDEN_RATIO 0x9e3779b9
#define __jhash_mix(a, b, c) \
{ \
a -= b; a -= c; a ^= (c>>13); \
b -= c; b -= a; b ^= (a<<8); \
c -= a; c -= b; c ^= (b>>13); \
a -= b; a -= c; a ^= (c>>12); \
b -= c; b -= a; b ^= (a<<16); \
c -= a; c -= b; c ^= (b>>5); \
a -= b; a -= c; a ^= (c>>3); \
b -= c; b -= a; b ^= (a<<10); \
c -= a; c -= b; c ^= (b>>15); \
}
struct hash_table * create_hash(uint32_t buckets)
{
struct hash_table *table;
uint32_t i;
table = malloc(sizeof(struct hash_table) +
(sizeof(struct hash_entry *) * buckets));
if (table == NULL)
return NULL;
for (i = 0; i < buckets; i++)
table->bucket[i] = NULL;
table->buckets = buckets;
table->hash_rnd = time(NULL);
pthread_mutex_init(&table->mutex, NULL);
return table;
}
void purge_hash(struct hash_table *table,
void (*callback)(const struct hash_entry *entry, void *privdata),
void *privdata)
{
uint32_t i;
pthread_mutex_lock(&table->mutex);
for (i = 0; i < table->buckets; i++) {
struct hash_entry *entry = table->bucket[i];
while (entry != NULL) {
struct hash_entry *tmp = entry;
if (callback != NULL)
callback(entry, privdata);
entry = entry->next;
free(tmp);
}
table->bucket[i] = NULL;
}
table->hash_rnd = time(NULL);
pthread_mutex_unlock(&table->mutex);
}
void destroy_hash(struct hash_table *table)
{
purge_hash(table, NULL, NULL);
pthread_mutex_destroy(&table->mutex);
free(table);
}
static uint32_t calc_hashkey(const struct hash_entry *entry, uint32_t initval)
{
uint32_t a = entry->src_ip;
uint32_t b = (entry->flags << 8) | entry->protonum;
uint32_t c = entry->dst_port;
a += JHASH_GOLDEN_RATIO;
b += JHASH_GOLDEN_RATIO;
c += initval;
__jhash_mix(a, b, c);
return c;
}
static int cmp_entry(const struct hash_entry *a, const struct hash_entry *b)
{
return (a->src_ip ^ b->src_ip) |
(a->protonum ^ b->protonum) |
(a->dst_port ^ b->dst_port) |
(a->flags ^ b->flags);
}
void hash_add(struct hash_table *table, struct hash_entry *entry)
{
uint32_t key = calc_hashkey(entry, table->hash_rnd) % table->buckets;
pthread_mutex_lock(&table->mutex);
struct hash_entry *search = table->bucket[key];
/* search collision-list */
while (search != NULL) {
if (cmp_entry(entry, search) == 0) {
/* add values */
search->src_bytes += entry->src_bytes;
search->dst_bytes += entry->dst_bytes;
search->count++;
pthread_mutex_unlock(&table->mutex);
/* free entry */
free(entry);
return;
}
search = search->next;
}
/* insert new bucket */
entry->count = 1;
entry->next = table->bucket[key];
table->bucket[key] = entry;
pthread_mutex_unlock(&table->mutex);
}