hammer/src/datastructures.c

434 lines
12 KiB
C
Raw Normal View History

#include "internal.h"
#include "hammer.h"
#include "allocator.h"
2014-05-12 09:45:22 +02:00
#include "parsers/parser_internal.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
// {{{ counted arrays
2012-05-26 14:06:52 +02:00
HCountedArray *h_carray_new_sized(HArena * arena, size_t size) {
HCountedArray *ret = h_arena_malloc(arena, sizeof(HCountedArray));
if (size == 0)
size = 1;
ret->used = 0;
ret->capacity = size;
ret->arena = arena;
2012-05-26 14:06:52 +02:00
ret->elements = h_arena_malloc(arena, sizeof(void*) * size);
return ret;
}
2012-05-26 14:06:52 +02:00
HCountedArray *h_carray_new(HArena * arena) {
return h_carray_new_sized(arena, 4);
}
2012-05-26 14:06:52 +02:00
void h_carray_append(HCountedArray *array, void* item) {
if (array->used >= array->capacity) {
2012-05-26 14:06:52 +02:00
HParsedToken **elements = h_arena_malloc(array->arena, (array->capacity *= 2) * sizeof(HCountedArray*));
for (size_t i = 0; i < array->used; i++)
elements[i] = array->elements[i];
for (size_t i = array->used; i < array->capacity; i++)
elements[i] = 0;
array->elements = elements;
}
array->elements[array->used++] = item;
}
2012-10-08 17:11:47 +02:00
// HSlist
HSlist* h_slist_new(HArena *arena) {
HSlist *ret = h_arena_malloc(arena, sizeof(HSlist));
ret->head = NULL;
ret->arena = arena;
return ret;
}
HSlist* h_slist_copy(HSlist *slist) {
HSlist *ret = h_slist_new(slist->arena);
HSlistNode *head = slist->head;
HSlistNode *tail;
if (head != NULL) {
h_slist_push(ret, head->elem);
tail = ret->head;
head = head->next;
}
while (head != NULL) {
// append head item to tail in a new node
HSlistNode *node = h_arena_malloc(slist->arena, sizeof(HSlistNode));
node->elem = head->elem;
node->next = NULL;
tail = tail->next = node;
head = head->next;
}
return ret;
}
2013-06-20 11:05:57 +02:00
// like h_slist_pop, but does not deallocate the head node
void* h_slist_drop(HSlist *slist) {
HSlistNode *head = slist->head;
if (!head)
return NULL;
void* ret = head->elem;
slist->head = head->next;
return ret;
}
2012-10-08 17:11:47 +02:00
void* h_slist_pop(HSlist *slist) {
HSlistNode *head = slist->head;
if (!head)
return NULL;
void* ret = head->elem;
slist->head = head->next;
h_arena_free(slist->arena, head);
return ret;
}
void h_slist_push(HSlist *slist, void* item) {
HSlistNode *hnode = h_arena_malloc(slist->arena, sizeof(HSlistNode));
hnode->elem = item;
hnode->next = slist->head;
// write memory barrier here.
slist->head = hnode;
}
bool h_slist_find(HSlist *slist, const void* item) {
assert (item != NULL);
HSlistNode *head = slist->head;
while (head != NULL) {
if (head->elem == item)
return true;
head = head->next;
}
return false;
}
HSlist* h_slist_remove_all(HSlist *slist, const void* item) {
assert (item != NULL);
HSlistNode *node = slist->head;
HSlistNode *prev = NULL;
while (node != NULL) {
if (node->elem == item) {
HSlistNode *next = node->next;
if (prev)
prev->next = next;
else
slist->head = next;
// FIXME free the removed node! this leaks.
node = next;
}
else {
prev = node;
node = prev->next;
}
}
return slist;
}
2012-10-08 17:11:47 +02:00
void h_slist_free(HSlist *slist) {
while (slist->head != NULL)
h_slist_pop(slist);
h_arena_free(slist->arena, slist);
}
HHashTable* h_hashtable_new(HArena *arena, HEqualFunc equalFunc, HHashFunc hashFunc) {
2012-10-08 22:06:33 +02:00
HHashTable *ht = h_arena_malloc(arena, sizeof(HHashTable));
2012-10-08 19:20:36 +02:00
ht->hashFunc = hashFunc;
ht->equalFunc = equalFunc;
ht->capacity = 64; // to start; should be tuned later...
ht->used = 0;
2012-10-08 22:06:33 +02:00
ht->arena = arena;
2012-10-08 19:20:36 +02:00
ht->contents = h_arena_malloc(arena, sizeof(HHashTableEntry) * ht->capacity);
2012-10-08 22:06:33 +02:00
for (size_t i = 0; i < ht->capacity; i++) {
ht->contents[i].key = NULL;
ht->contents[i].value = NULL;
ht->contents[i].next = NULL;
ht->contents[i].hashval = 0;
}
//memset(ht->contents, 0, sizeof(HHashTableEntry) * ht->capacity);
2012-10-08 19:20:36 +02:00
return ht;
}
2013-05-08 17:01:23 +02:00
void* h_hashtable_get(const HHashTable* ht, const void* key) {
2012-10-08 19:20:36 +02:00
HHashValue hashval = ht->hashFunc(key);
#ifdef CONSISTENCY_CHECK
assert((ht->capacity & (ht->capacity - 1)) == 0); // capacity is a power of 2
#endif
2012-10-08 22:06:33 +02:00
HHashTableEntry *hte = NULL;
for (hte = &ht->contents[hashval & (ht->capacity - 1)];
2012-10-08 19:20:36 +02:00
hte != NULL;
hte = hte->next) {
2014-04-20 21:00:50 +02:00
if (hte->key == NULL) {
continue;
2014-04-20 21:00:50 +02:00
}
if (hte->hashval != hashval) {
2012-10-08 19:20:36 +02:00
continue;
2014-04-20 21:00:50 +02:00
}
if (ht->equalFunc(key, hte->key)) {
2012-10-08 19:20:36 +02:00
return hte->value;
2014-04-20 21:00:50 +02:00
}
2012-10-08 19:20:36 +02:00
}
return NULL;
}
2014-04-20 21:00:50 +02:00
void h_hashtable_put_raw(HHashTable* ht, HHashTableEntry* new_entry);
void h_hashtable_ensure_capacity(HHashTable* ht, size_t n) {
bool do_resize = false;
size_t old_capacity = ht->capacity;
while (n * 1.3 > ht->capacity) {
ht->capacity *= 2;
do_resize = true;
}
if (!do_resize)
return;
HHashTableEntry *old_contents = ht->contents;
HHashTableEntry *new_contents = h_arena_malloc(ht->arena, sizeof(HHashTableEntry) * ht->capacity);
ht->contents = new_contents;
ht->used = 0;
memset(new_contents, 0, sizeof(HHashTableEntry) * ht->capacity);
for (size_t i = 0; i < old_capacity; ++i)
for (HHashTableEntry *entry = &old_contents[i];
entry;
entry = entry->next)
if (entry->key)
h_hashtable_put_raw(ht, entry);
//h_arena_free(ht->arena, old_contents);
}
2013-04-30 17:44:54 +02:00
void h_hashtable_put(HHashTable* ht, const void* key, void* value) {
2012-10-08 19:20:36 +02:00
// # Start with a rebalancing
2014-04-20 21:00:50 +02:00
h_hashtable_ensure_capacity(ht, ht->used + 1);
2012-10-08 19:20:36 +02:00
HHashValue hashval = ht->hashFunc(key);
2014-04-20 21:00:50 +02:00
HHashTableEntry entry = {
.key = key,
.value = value,
.hashval = hashval
};
h_hashtable_put_raw(ht, &entry);
}
void h_hashtable_put_raw(HHashTable* ht, HHashTableEntry *new_entry) {
2012-10-08 19:20:36 +02:00
#ifdef CONSISTENCY_CHECK
assert((ht->capacity & (ht->capacity - 1)) == 0); // capacity is a power of 2
#endif
2014-04-20 21:00:50 +02:00
HHashTableEntry *hte = &ht->contents[new_entry->hashval & (ht->capacity - 1)];
2012-10-08 19:20:36 +02:00
if (hte->key != NULL) {
for(;;) {
// check each link, stay on last if not found
2014-04-20 21:00:50 +02:00
if (hte->hashval == new_entry->hashval && ht->equalFunc(new_entry->key, hte->key))
2012-10-08 19:20:36 +02:00
goto insert_here;
if (hte->next == NULL)
break;
hte = hte->next;
}
2012-10-08 19:20:36 +02:00
// Add a new link...
assert (hte->next == NULL);
hte->next = h_arena_malloc(ht->arena, sizeof(HHashTableEntry));
hte = hte->next;
hte->next = NULL;
2012-10-08 22:06:33 +02:00
ht->used++;
} else
ht->used++;
2012-10-08 19:20:36 +02:00
insert_here:
2014-04-20 21:00:50 +02:00
hte->key = new_entry->key;
hte->value = new_entry->value;
hte->hashval = new_entry->hashval;
2012-10-08 19:20:36 +02:00
}
void h_hashtable_update(HHashTable *dst, const HHashTable *src) {
2013-04-30 17:49:07 +02:00
size_t i;
HHashTableEntry *hte;
for(i=0; i < src->capacity; i++) {
for(hte = &src->contents[i]; hte; hte = hte->next) {
if(hte->key == NULL)
continue;
2013-04-30 17:49:07 +02:00
h_hashtable_put(dst, hte->key, hte->value);
}
2013-04-30 17:49:07 +02:00
}
}
void h_hashtable_merge(void *(*combine)(void *v1, const void *v2),
2013-05-22 21:57:46 +02:00
HHashTable *dst, const HHashTable *src) {
size_t i;
HHashTableEntry *hte;
for(i=0; i < src->capacity; i++) {
for(hte = &src->contents[i]; hte; hte = hte->next) {
if(hte->key == NULL)
continue;
void *dstvalue = h_hashtable_get(dst, hte->key);
void *srcvalue = hte->value;
h_hashtable_put(dst, hte->key, combine(dstvalue, srcvalue));
2013-05-22 21:57:46 +02:00
}
}
}
2013-05-08 17:01:23 +02:00
int h_hashtable_present(const HHashTable* ht, const void* key) {
2012-10-08 19:20:36 +02:00
HHashValue hashval = ht->hashFunc(key);
#ifdef CONSISTENCY_CHECK
assert((ht->capacity & (ht->capacity - 1)) == 0); // capacity is a power of 2
#endif
for (HHashTableEntry *hte = &ht->contents[hashval & (ht->capacity - 1)];
hte != NULL;
hte = hte->next) {
if (hte->hashval != hashval)
continue;
if (ht->equalFunc(key, hte->key))
return true;
}
return false;
}
2013-06-05 15:12:48 +02:00
2013-04-30 17:44:54 +02:00
void h_hashtable_del(HHashTable* ht, const void* key) {
2012-10-08 19:20:36 +02:00
HHashValue hashval = ht->hashFunc(key);
#ifdef CONSISTENCY_CHECK
assert((ht->capacity & (ht->capacity - 1)) == 0); // capacity is a power of 2
#endif
for (HHashTableEntry *hte = &ht->contents[hashval & (ht->capacity - 1)];
hte != NULL;
hte = hte->next) {
if (hte->hashval != hashval)
continue;
if (ht->equalFunc(key, hte->key)) {
// FIXME: Leaks keys and values.
HHashTableEntry* hten = hte->next;
if (hten != NULL) {
*hte = *hten;
h_arena_free(ht->arena, hten);
} else {
hte->key = hte->value = NULL;
hte->hashval = 0;
}
return;
}
}
}
2013-06-05 15:12:48 +02:00
2012-10-08 19:20:36 +02:00
void h_hashtable_free(HHashTable* ht) {
for (size_t i = 0; i < ht->capacity; i++) {
2012-10-08 19:20:36 +02:00
HHashTableEntry *hten, *hte = &ht->contents[i];
// FIXME: Free key and value
hte = hte->next;
while (hte != NULL) {
// FIXME: leaks keys and values.
hten = hte->next;
h_arena_free(ht->arena, hte);
hte = hten;
}
}
h_arena_free(ht->arena, ht->contents);
}
2012-10-08 19:21:56 +02:00
2013-06-05 15:12:48 +02:00
// helper for hte_equal
static bool hte_same_length(HHashTableEntry *xs, HHashTableEntry *ys) {
2013-06-07 13:46:16 +02:00
while(xs && ys) {
xs=xs->next;
ys=ys->next;
2013-06-05 15:12:48 +02:00
// skip NULL keys (= element not present)
2013-06-07 13:46:16 +02:00
while(xs && xs->key == NULL) xs=xs->next;
while(ys && ys->key == NULL) ys=ys->next;
2013-06-05 15:12:48 +02:00
}
return (xs == ys); // both NULL
}
// helper for hte_equal: are all elements of xs present in ys?
static bool hte_subset(HEqualFunc eq, HHashTableEntry *xs, HHashTableEntry *ys)
{
for(; xs; xs=xs->next) {
if(xs->key == NULL) continue; // element not present
HHashTableEntry *hte;
for(hte=ys; hte; hte=hte->next) {
if(hte->key == xs->key) break; // assume an element is equal to itself
if(hte->hashval != xs->hashval) continue; // shortcut
if(eq(hte->key, xs->key)) break;
}
if(hte == NULL) return false; // element not found
}
return true; // all found
}
// compare two lists of HHashTableEntries
static inline bool hte_equal(HEqualFunc eq, HHashTableEntry *xs, HHashTableEntry *ys) {
return (hte_same_length(xs, ys) && hte_subset(eq, xs, ys));
}
/* Set equality of HHashSets.
* Obviously, 'a' and 'b' must use the same equality function.
* Not strictly necessary, but we also assume the same hash function.
*/
bool h_hashset_equal(const HHashSet *a, const HHashSet *b) {
if(a->capacity == b->capacity) {
// iterate over the buckets in parallel
for(size_t i=0; i < a->capacity; i++) {
if(!hte_equal(a->equalFunc, &a->contents[i], &b->contents[i]))
return false;
}
} else {
assert_message(0, "h_hashset_equal called on sets of different capacity");
// TODO implement general case
}
return true;
}
bool h_eq_ptr(const void *p, const void *q) {
return (p==q);
}
HHashValue h_hash_ptr(const void *p) {
// XXX just djbhash it? it does make the benchmark ~7% slower.
//return h_djbhash((const uint8_t *)&p, sizeof(void *));
return (uintptr_t)p >> 4;
}
uint32_t h_djbhash(const uint8_t *buf, size_t len) {
uint32_t hash = 5381;
while (len--) {
hash = hash * 33 + *buf++;
}
return hash;
}
2013-07-10 21:32:05 +02:00
2014-05-12 09:45:22 +02:00
void h_symbol_put(HParseState *state, const char* key, void *value) {
if (!state->symbol_table) {
state->symbol_table = h_slist_new(state->arena);
h_slist_push(state->symbol_table, h_hashtable_new(state->arena,
h_eq_ptr,
h_hash_ptr));
}
HHashTable *head = h_slist_top(state->symbol_table);
assert(!h_hashtable_present(head, key));
h_hashtable_put(head, key, value);
}
void* h_symbol_get(HParseState *state, const char* key) {
if (state->symbol_table) {
HHashTable *head = h_slist_top(state->symbol_table);
if (head) {
return h_hashtable_get(head, key);
}
}
return NULL;
}
HSArray *h_sarray_new(HAllocator *mm__, size_t size) {
HSArray *ret = h_new(HSArray, 1);
ret->capacity = size;
ret->used = 0;
ret->nodes = h_new(HSArrayNode, size); // Does not actually need to be initialized.
ret->mm__ = mm__;
// TODO: Add the valgrind hooks to mark this initialized.
return ret;
}
void h_sarray_free(HSArray *arr) {
HAllocator *mm__ = arr->mm__;
h_free(arr->nodes);
h_free(arr);
}