#include <string.h>
#include "hash.h"
#include "util.h"
struct bucket
{
hash_key key;
hash_data data;
struct bucket *next;
};
#define scan_bucket(b, var) for (var = b; var; var = var->next)
struct Hash_table
{
region r;
hash_fn hash;
keyeq_fn cmp;
int size;
int elts;
bool internal_rgn;
bucket *table;
};
static void rehash(hash_table ht) deletes;
hash_table make_hash_table(region r, int size, hash_fn hash,
keyeq_fn cmp, bool internal_rgn)
{
hash_table result;
assert(size > 0);
result = ralloc(r, struct Hash_table);
if (internal_rgn)
result->r = newregion();
else
result->r = r;
result->internal_rgn = internal_rgn;
result->hash = hash;
result->cmp = cmp;
result->size = size;
result->elts = 0;
result->table = rarrayalloc(result->r, size, bucket);
return result;
}
static int string_hash(char *str)
{
char *c;
int h;
c = str;
h = 0;
if (!c)
return 0;
while (*c)
h = 33*h + 720 + *c++;
return h;
}
static bool string_eq(char *s1, char *s2)
{
return !strcmp(s1, s2);
}
hash_table make_string_hash_table(region rhash, int size, bool internal_rgn)
{
return make_hash_table(rhash, size, (hash_fn) string_hash,
(keyeq_fn) string_eq,internal_rgn);
}
void hash_table_reset(hash_table ht) deletes
{
int i;
if (ht->internal_rgn)
{
deleteregion(ht->r);
ht->r = newregion();
}
ht->elts = 0;
for (i = 0; i < ht->size; i++)
ht->table[i] = NULL;
}
void hash_table_delete(hash_table ht) deletes
{
if (ht->internal_rgn)
deleteregion(ht->r);
}
int hash_table_size(hash_table ht)
{
return ht->elts;
}
static inline bucket *find_bucket(hash_table ht, hash_key k)
{
int hash;
hash = ht->hash(k);
if (hash < 0)
hash = -1*hash;
return &ht->table[hash % ht->size];
}
bool hash_table_lookup(hash_table ht, hash_key k, hash_data *d)
{
bucket cur;
cur = *find_bucket(ht, k);
while (cur)
{
if (ht->cmp(k, cur->key))
{
if (d)
*d = cur->data;
return TRUE;
}
cur = cur->next;
}
return FALSE;
}
bool hash_table_insert(hash_table ht, hash_key k, hash_data d) deletes
{
bucket *cur;
if (ht->elts > ht->size*15)
rehash(ht);
cur = find_bucket(ht, k);
while (*cur)
{
if (ht->cmp(k, (*cur)->key))
{
(*cur)->data = d;
return FALSE;
}
cur = &(*cur)->next;
}
*cur = ralloc(ht->r, struct bucket);
(*cur)->key = k;
(*cur)->data = d;
(*cur)->next = NULL;
ht->elts++;
return TRUE;
}
bool hash_table_remove(hash_table ht, hash_key k)
{
bucket *cur;
bucket *prev = NULL;
cur = find_bucket(ht, k);
while (*cur)
{
if (ht->cmp(k, (*cur)->key))
{
if (!*prev)
(*prev)->next = (*cur)->next;
else
*cur = NULL;
ht->elts--;
return TRUE;
}
prev = cur;
cur = &(*cur)->next;
}
return FALSE;
}
hash_table hash_table_copy(region r, hash_table ht)
{
int i;
hash_table result;
bucket cur, newbucket, *prev;
result = make_hash_table(r, ht->size, ht->hash, ht->cmp,ht->internal_rgn);
result->elts = ht->elts;
for (i = 0; i < ht->size; i++)
{
prev = &result->table[i];
scan_bucket(ht->table[i], cur)
{
newbucket = ralloc(result->r, struct bucket);
newbucket->key = cur->key;
newbucket->data = cur->data;
newbucket->next = NULL;
assert(!*prev);
*prev = newbucket;
prev = &newbucket->next;
}
}
return result;
}
static void rehash(hash_table ht) deletes
{
int old_table_size, i;
bucket *old_table, cur;
region old_region;
#ifdef DEBUG
printf("Rehash table size=%d, elts=%d\n", ht->size, ht->elts);
#endif
old_table_size = ht->size;
old_table = ht->table;
old_region = ht->r;
if (ht->internal_rgn)
ht->r = newregion();
ht->size = ht->size*2;
ht->elts = 0;
ht->table = rarrayalloc(ht->r, ht->size, bucket);
for (i = 0; i < old_table_size; i++)
scan_bucket(old_table[i], cur)
insist(hash_table_insert(ht, cur->key, cur->data));
if (ht->internal_rgn)
deleteregion(old_region);
}
void hash_table_scan(hash_table ht, hash_table_scanner *hts)
{
hts->ht = ht;
hts->i = 0;
hts->cur = hts->ht->table[0];
}
bool hash_table_next(hash_table_scanner *hts, hash_key *k, hash_data *d)
{
while (hts->cur == NULL)
{
hts->i++;
if (hts->i < hts->ht->size)
hts->cur = hts->ht->table[hts->i];
else
break;
}
if (hts->i == hts->ht->size)
{
return FALSE;
}
else
{
if (k)
*k = hts->cur->key;
if (d)
*d = hts->cur->data;
hts->cur = hts->cur->next;
}
return TRUE;
}
void hash_table_apply(hash_table ht, hash_apply_fn f, void *arg)
{
int i;
bucket cur;
for (i = 0; i < ht->size; i++)
scan_bucket(ht->table[i], cur)
f(cur->key, cur->data, arg);
}
hash_table hash_table_map(hash_table ht, hash_map_fn f, void *arg)
{
int i;
hash_table result;
bucket cur, newbucket, *prev;
result = make_hash_table(ht->r, ht->size, ht->hash, ht->cmp,ht->internal_rgn);
result->elts = ht->elts;
for (i = 0; i < ht->size; i++)
{
prev = &result->table[i];
scan_bucket(ht->table[i], cur)
{
newbucket = ralloc(ht->r, struct bucket);
newbucket->key = cur->key;
newbucket->data = f(cur->key, cur->data, arg);
newbucket->next = NULL;
assert(!*prev);
*prev = newbucket;
prev = &newbucket->next;
}
}
return result;
}
static keycmp_fn cur_cmp = NULL;
static int entry_cmp(const void *a, const void *b)
{
struct sorted_entry *ae = (struct sorted_entry *) a;
struct sorted_entry *be = (struct sorted_entry *) b;
return cur_cmp(ae->k, be->k);
}
void hash_table_scan_sorted(hash_table ht, keycmp_fn f,
hash_table_scanner_sorted *htss)
{
hash_table_scanner hts;
int i;
htss->r = newregion();
htss->size = hash_table_size(ht);
htss->entries = rarrayalloc(htss->r, htss->size, struct sorted_entry);
htss->i = 0;
hash_table_scan(ht, &hts);
i = 0;
while (hash_table_next(&hts, &htss->entries[i].k,
&htss->entries[i].d))
i++;
assert(i == htss->size);
cur_cmp = f;
qsort(htss->entries, htss->size, sizeof(struct sorted_entry), entry_cmp);
cur_cmp = NULL;
}
bool hash_table_next_sorted(hash_table_scanner_sorted *htss, hash_key *k,
hash_data *d) deletes
{
if (htss->i < htss->size)
{
*k = htss->entries[htss->i].k;
*d = htss->entries[htss->i].d;
htss->i++;
return TRUE;
}
else
{
deleteregion(htss->r);
htss->r = NULL;
return FALSE;
}
}