clientlist: implement hash table (512 buckets) for speedy lookups

This commit is contained in:
Heikki Hannikainen 2012-10-11 23:51:20 +03:00
parent 745c092044
commit 36e3c02b0b
1 changed files with 69 additions and 14 deletions

View File

@ -27,20 +27,36 @@
#include "clientlist.h"
#include "hmalloc.h"
#include "rwlock.h"
#include "keyhash.h"
#include "hlog.h"
//#define CLIENTLIST_DEBUG
#ifdef CLIENTLIST_DEBUG
#define DLOG(fmt, ...) \
do { hlog(LOG_DEBUG, fmt, __VA_ARGS__); } while (0)
#else
#define DLOG(fmt, ...)
#endif
struct clientlist_t {
struct clientlist_t *next;
struct clientlist_t **prevp;
char username[16]; /* The callsign */
int username_len;
int validated; /* Valid passcode given? */
int fd; /* File descriptor, can be used by another
thread to shut down a socket */
void *client_id; /* DO NOT REFERENCE - just used for an ID */
uint32_t hash; /* hash value */
};
struct clientlist_t *clientlist = NULL;
/* hash buckets - serious overkill, but great for 40k clients */
#define CLIENTLIST_BUCKETS 512
struct clientlist_t *clientlist[CLIENTLIST_BUCKETS];
rwlock_t clientlist_lock = RWL_INITIALIZER;
/*
@ -48,13 +64,25 @@ rwlock_t clientlist_lock = RWL_INITIALIZER;
* before calling!
*/
static struct clientlist_t *clientlist_find_id(void *id)
static struct clientlist_t *clientlist_find_id(char *username, int len, void *id)
{
struct clientlist_t *cl;
uint32_t hash, idx;
int i;
for (cl = clientlist; cl; cl = cl->next)
if (cl->client_id == id)
hash = keyhash(username, len, 0);
idx = hash;
// "CLIENT_HEARD_BUCKETS" is 512..
idx ^= (idx >> 16);
idx ^= (idx >> 8);
i = idx % CLIENTLIST_BUCKETS;
DLOG("clientlist_find_id '%.*s' id %p bucket %d", len, username, id, i);
for (cl = clientlist[i]; cl; cl = cl->next)
if (cl->client_id == id) {
DLOG("clientlist_find_id '%.*s' found %p", len, username, cl);
return cl;
}
return NULL;
}
@ -68,10 +96,21 @@ static struct clientlist_t *clientlist_find_id(void *id)
static int check_if_validated_client(char *username, int len)
{
struct clientlist_t *cl;
uint32_t hash, idx;
int i;
for (cl = clientlist; cl; cl = cl->next) {
if (memcmp(username, cl->username, len) == 0
&& strlen(cl->username) == len && cl->validated) {
hash = keyhash(username, len, 0);
idx = hash;
// "CLIENT_HEARD_BUCKETS" is 512..
idx ^= (idx >> 16);
idx ^= (idx >> 8);
i = idx % CLIENTLIST_BUCKETS;
DLOG("check_if_validated_client '%.*s': bucket %d", len, username, i);
for (cl = clientlist[i]; cl; cl = cl->next) {
if (cl->hash == hash && memcmp(username, cl->username, len) == 0
&& cl->username_len == len && cl->validated) {
DLOG("check_if_validated_client '%.*s' found validated, fd %d", cl->username_len, cl->username, cl->fd);
return cl->fd;
}
}
@ -106,25 +145,38 @@ int clientlist_add(struct client_t *c)
{
struct clientlist_t *cl;
int old_fd;
uint32_t hash, idx;
int i;
hash = keyhash(c->username, c->username_len, 0);
idx = hash;
// "CLIENT_HEARD_BUCKETS" is 512..
idx ^= (idx >> 16);
idx ^= (idx >> 8);
i = idx % CLIENTLIST_BUCKETS;
DLOG("clientlist_add '%s': bucket %d", c->username, i);
/* allocate and fill in */
cl = hmalloc(sizeof(*cl));
strncpy(cl->username, c->username, sizeof(cl->username));
cl->username[sizeof(cl->username)-1] = 0;
cl->username_len = c->username_len;
cl->validated = c->validated;
cl->fd = c->fd;
cl->hash = hash;
/* get lock for list and insert */
rwl_wrlock(&clientlist_lock);
old_fd = check_if_validated_client(c->username, strlen(c->username));
old_fd = check_if_validated_client(c->username, c->username_len);
if (clientlist)
clientlist->prevp = &cl->next;
cl->next = clientlist;
cl->prevp = &clientlist;
if (clientlist[i])
clientlist[i]->prevp = &cl->next;
cl->next = clientlist[i];
cl->prevp = &clientlist[i];
cl->client_id = (void *)c;
clientlist = cl;
clientlist[i] = cl;
rwl_wrunlock(&clientlist_lock);
return old_fd;
@ -138,11 +190,14 @@ void clientlist_remove(struct client_t *c)
{
struct clientlist_t *cl;
DLOG("clientlist_remove '%s'", c->username);
/* get lock for list, find, and remove */
rwl_wrlock(&clientlist_lock);
cl = clientlist_find_id((void *)c);
cl = clientlist_find_id(c->username, c->username_len, (void *)c);
if (cl) {
DLOG("clientlist_remove found '%s'", c->username);
if (cl->next)
cl->next->prevp = cl->prevp;
*cl->prevp = cl->next;