femtolisp/llt/ptrhash.c

196 lines
4.5 KiB
C

/*
pointer hash table
optimized for storing info about particular values
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <limits.h>
#include "dtypes.h"
#include "ptrhash.h"
#include "hashing.h"
#define ptrhash_size(h) ((h)->size/2)
ptrhash_t *ptrhash_new(ptrhash_t *h, size_t size)
{
size = nextipow2(size);
size *= 2; // 2 pointers per key/value pair
size *= 2; // aim for 50% occupancy
h->size = size;
h->table = (void**)malloc(size*sizeof(void*));
if (h->table == NULL) return NULL;
size_t i;
for(i=0; i < size; i++)
h->table[i] = PH_NOTFOUND;
return h;
}
void ptrhash_free(ptrhash_t *h)
{
free(h->table);
}
// empty and reduce size
void ptrhash_reset(ptrhash_t *h, size_t sz)
{
if (h->size > sz*4) {
size_t newsz = sz*4;
void **newtab = (void**)realloc(h->table, newsz*sizeof(void*));
if (newtab == NULL)
return;
h->size = newsz;
h->table = newtab;
}
size_t i, hsz=h->size;
for(i=0; i < hsz; i++)
h->table[i] = PH_NOTFOUND;
}
// compute empirical max-probe for a given size
#define ph_max_probe(size) ((size)>>5)
static void **ptrhash_lookup_bp(ptrhash_t *h, void *key)
{
uint_t hv;
size_t i, orig, index, iter;
size_t newsz, sz = ptrhash_size(h);
size_t maxprobe = ph_max_probe(sz);
void **tab = h->table;
void **ol;
hv = inthash((uptrint_t)key);
retry_bp:
iter = 0;
index = (index_t)(hv & (sz-1)) * 2;
sz *= 2;
orig = index;
do {
if (tab[index+1] == PH_NOTFOUND) {
tab[index] = key;
return &tab[index+1];
}
if (key == tab[index])
return &tab[index+1];
index = (index+2) & (sz-1);
iter++;
if (iter > maxprobe)
break;
} while (index != orig);
// table full
// quadruple size, rehash, retry the insert
// it's important to grow the table really fast; otherwise we waste
// lots of time rehashing all the keys over and over.
sz = h->size;
ol = h->table;
if (sz >= (1<<19))
newsz = sz<<1;
else
newsz = sz<<2;
//printf("trying to allocate %d words.\n", newsz); fflush(stdout);
tab = (void**)malloc(newsz*sizeof(void*));
if (tab == NULL)
return NULL;
for(i=0; i < newsz; i++)
tab[i] = PH_NOTFOUND;
h->table = tab;
h->size = newsz;
for(i=0; i < sz; i+=2) {
if (ol[i] != PH_NOTFOUND && ol[i+1] != PH_NOTFOUND) {
(*ptrhash_lookup_bp(h, ol[i])) = ol[i+1];
/*
// this condition is not really possible
if (bp == NULL) {
free(h->table);
h->table = ol;
h->size = sz;
// another thing we could do in this situation
// is newsz<<=1 and go back to the malloc, retrying with
// a bigger buffer on this level of recursion.
return NULL;
}
*/
}
}
free(ol);
sz = ptrhash_size(h);
maxprobe = ph_max_probe(sz);
goto retry_bp;
return NULL;
}
void ptrhash_put(ptrhash_t *h, void *key, void *val)
{
void **bp = ptrhash_lookup_bp(h, key);
*bp = val;
}
void **ptrhash_bp(ptrhash_t *h, void *key)
{
return ptrhash_lookup_bp(h, key);
}
// returns bp if key is in hash, otherwise NULL
static void **ptrhash_peek_bp(ptrhash_t *h, void *key)
{
size_t sz = ptrhash_size(h);
size_t maxprobe = ph_max_probe(sz);
void **tab = h->table;
size_t index = (index_t)(inthash((uptrint_t)key) & (sz-1)) * 2;
sz *= 2;
size_t orig = index;
size_t iter = 0;
do {
if (tab[index] == PH_NOTFOUND)
return NULL;
if (key == tab[index] && tab[index+1] != PH_NOTFOUND)
return &tab[index+1];
index = (index+2) & (sz-1);
iter++;
if (iter > maxprobe)
break;
} while (index != orig);
return NULL;
}
void *ptrhash_get(ptrhash_t *h, void *key)
{
void **bp = ptrhash_peek_bp(h, key);
if (bp == NULL)
return PH_NOTFOUND;
return *bp;
}
int ptrhash_has(ptrhash_t *h, void *key)
{
return (ptrhash_get(h,key) != PH_NOTFOUND);
}
void ptrhash_remove(ptrhash_t *h, void *key)
{
void **bp = ptrhash_peek_bp(h, key);
if (bp != NULL)
*bp = PH_NOTFOUND;
}
void ptrhash_adjoin(ptrhash_t *h, void *key, void *val)
{
void **bp = ptrhash_lookup_bp(h, key);
if (*bp == PH_NOTFOUND)
*bp = val;
}