mirror of
https://github.com/netwide-assembler/nasm.git
synced 2024-12-21 09:19:31 +08:00
166c247f36
struct hash_table, a fixed-sized structure, is now allocated by the caller. This lets us integrate it into the Context structure, thus avoiding an additional dynamically allocated object for no good reason. Add some minor code collapsing: make it more obvious that all that differs is a pointer value, rather than relying on the compiler to do tail merging.
188 lines
4.3 KiB
C
188 lines
4.3 KiB
C
/*
|
|
* hashtbl.c
|
|
*
|
|
* Efficient dictionary hash table class.
|
|
*/
|
|
|
|
#include "compiler.h"
|
|
|
|
#include <inttypes.h>
|
|
#include <string.h>
|
|
#include "nasm.h"
|
|
#include "hashtbl.h"
|
|
|
|
#define HASH_MAX_LOAD 2 /* Higher = more memory-efficient, slower */
|
|
|
|
static struct hash_tbl_node *alloc_table(size_t newsize)
|
|
{
|
|
size_t bytes = newsize*sizeof(struct hash_tbl_node);
|
|
struct hash_tbl_node *newtbl = nasm_zalloc(bytes);
|
|
|
|
return newtbl;
|
|
}
|
|
|
|
void hash_init(struct hash_table *head, size_t size)
|
|
{
|
|
head->table = alloc_table(size);
|
|
head->load = 0;
|
|
head->size = size;
|
|
head->max_load = size*(HASH_MAX_LOAD-1)/HASH_MAX_LOAD;
|
|
}
|
|
|
|
/*
|
|
* Find an entry in a hash table.
|
|
*
|
|
* On failure, if "insert" is non-NULL, store data in that structure
|
|
* which can be used to insert that node using hash_add().
|
|
*
|
|
* WARNING: this data is only valid until the very next call of
|
|
* hash_add(); it cannot be "saved" to a later date.
|
|
*
|
|
* On success, return a pointer to the "data" element of the hash
|
|
* structure.
|
|
*/
|
|
void **hash_find(struct hash_table *head, const char *key,
|
|
struct hash_insert *insert)
|
|
{
|
|
struct hash_tbl_node *np;
|
|
uint64_t hash = crc64(CRC64_INIT, key);
|
|
struct hash_tbl_node *tbl = head->table;
|
|
size_t mask = head->size-1;
|
|
size_t pos = hash & mask;
|
|
size_t inc = ((hash >> 32) & mask) | 1; /* Always odd */
|
|
|
|
while ((np = &tbl[pos])->key) {
|
|
if (hash == np->hash && !strcmp(key, np->key))
|
|
return &np->data;
|
|
pos = (pos+inc) & mask;
|
|
}
|
|
|
|
/* Not found. Store info for insert if requested. */
|
|
if (insert) {
|
|
insert->head = head;
|
|
insert->hash = hash;
|
|
insert->where = np;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Same as hash_find, but for case-insensitive hashing.
|
|
*/
|
|
void **hash_findi(struct hash_table *head, const char *key,
|
|
struct hash_insert *insert)
|
|
{
|
|
struct hash_tbl_node *np;
|
|
uint64_t hash = crc64i(CRC64_INIT, key);
|
|
struct hash_tbl_node *tbl = head->table;
|
|
size_t mask = head->size-1;
|
|
size_t pos = hash & mask;
|
|
size_t inc = ((hash >> 32) & mask) | 1; /* Always odd */
|
|
|
|
while ((np = &tbl[pos])->key) {
|
|
if (hash == np->hash && !nasm_stricmp(key, np->key))
|
|
return &np->data;
|
|
pos = (pos+inc) & mask;
|
|
}
|
|
|
|
/* Not found. Store info for insert if requested. */
|
|
if (insert) {
|
|
insert->head = head;
|
|
insert->hash = hash;
|
|
insert->where = np;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Insert node. Return a pointer to the "data" element of the newly
|
|
* created hash node.
|
|
*/
|
|
void **hash_add(struct hash_insert *insert, const char *key, void *data)
|
|
{
|
|
struct hash_table *head = insert->head;
|
|
struct hash_tbl_node *np = insert->where;
|
|
|
|
/* Insert node. We can always do this, even if we need to
|
|
rebalance immediately after. */
|
|
np->hash = insert->hash;
|
|
np->key = key;
|
|
np->data = data;
|
|
|
|
if (++head->load > head->max_load) {
|
|
/* Need to expand the table */
|
|
size_t newsize = head->size << 1;
|
|
struct hash_tbl_node *newtbl = alloc_table(newsize);
|
|
size_t mask = newsize-1;
|
|
|
|
if (head->table) {
|
|
struct hash_tbl_node *op, *xp;
|
|
size_t i;
|
|
|
|
/* Rebalance all the entries */
|
|
for (i = 0, op = head->table; i < head->size; i++, op++) {
|
|
if (op->key) {
|
|
size_t pos = op->hash & mask;
|
|
size_t inc = ((op->hash >> 32) & mask) | 1;
|
|
|
|
while ((xp = &newtbl[pos])->key)
|
|
pos = (pos+inc) & mask;
|
|
|
|
*xp = *op;
|
|
if (op == np)
|
|
np = xp;
|
|
}
|
|
}
|
|
nasm_free(head->table);
|
|
}
|
|
|
|
head->table = newtbl;
|
|
head->size = newsize;
|
|
head->max_load = newsize*(HASH_MAX_LOAD-1)/HASH_MAX_LOAD;
|
|
}
|
|
|
|
return &np->data;
|
|
}
|
|
|
|
/*
|
|
* Iterate over all members of a hash set. For the first call,
|
|
* iterator should be initialized to NULL. Returns the data pointer,
|
|
* or NULL on failure.
|
|
*/
|
|
void *hash_iterate(const struct hash_table *head,
|
|
struct hash_tbl_node **iterator,
|
|
const char **key)
|
|
{
|
|
struct hash_tbl_node *np = *iterator;
|
|
struct hash_tbl_node *ep = head->table + head->size;
|
|
|
|
if (!np)
|
|
np = head->table;
|
|
|
|
while (np < ep) {
|
|
if (np->key) {
|
|
*iterator = np+1;
|
|
if (key)
|
|
*key = np->key;
|
|
return np->data;
|
|
}
|
|
np++;
|
|
}
|
|
|
|
*iterator = NULL;
|
|
if (key)
|
|
*key = NULL;
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Free the hash itself. Doesn't free the data elements; use
|
|
* hash_iterate() to do that first, if needed.
|
|
*/
|
|
void hash_free(struct hash_table *head)
|
|
{
|
|
void *p = head->table;
|
|
head->table = NULL;
|
|
nasm_free(p);
|
|
}
|