From: David Given Date: Thu, 15 Dec 2016 23:32:22 +0000 (+0100) Subject: Add hashtable and work-in-progress bigraph modules. X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=fe88a6830e979a39679d473a6f46f299b8b88328;p=ack.git Add hashtable and work-in-progress bigraph modules. --- diff --git a/modules/src/data/bigraph.c b/modules/src/data/bigraph.c new file mode 100644 index 000000000..d172a7461 --- /dev/null +++ b/modules/src/data/bigraph.c @@ -0,0 +1,228 @@ +#include +#include +#include +#include +#include "bigraph.h" + +struct edgenode +{ + struct edgenode* next; + struct vertex* this; + struct vertex* other; +}; + +struct vertex +{ + void* data; + struct edgenode* connections; + int degree; +}; + +static uint32_t edge_hash_function(void* key) +{ + struct edgenode* en = key; + /* This will always return the same value, even if the two endpoints are swapped. */ + return standard_pointer_hash_function(en->this) ^ standard_pointer_hash_function(en->other); +} + +static bool edge_comparison_function(void* key1, void* key2) +{ + struct edgenode* en1 = key1; + struct edgenode* en2 = key2; + + return ((en1->this == en2->this) && (en1->other == en2->other)) + || ((en1->this == en2->other) && (en1->other == en2->this)); +} + +static void lazy_init(struct graph* g) +{ + g->edges.hashfunction = edge_hash_function; + g->edges.cmpfunction = edge_comparison_function; +} + +void graph_reset(struct graph* g) +{ + lazy_init(g); + + for (;;) + { + struct vertex* vertex = hashtable_pop(&g->vertices); + if (!vertex) + return; + + while (vertex->connections) + { + struct edgenode* next = vertex->connections->next; + free(vertex->connections); + vertex->connections = next; + } + + free(vertex); + } +} + +bool graph_contains_vertex(struct graph* g, void* data) +{ + lazy_init(g); + + return hashtable_contains(&g->vertices, data); +} + +static struct vertex* find_or_add_vertex(struct graph* g, void* data) +{ + struct vertex* vertex; + + lazy_init(g); + + vertex = hashtable_get(&g->vertices, data); + if (!vertex) + { + vertex = calloc(1, sizeof(struct vertex)); + vertex->data = data; + hashtable_put(&g->vertices, data, vertex); + } + + return vertex; +} + +static struct edgenode** find_edgep(struct vertex* v1, struct vertex* v2) +{ + struct edgenode** ep = &v1->connections; + + while (*ep) + { + if ((*ep)->other == v2) + return ep; + ep = &(*ep)->next; + } + + return ep; +} + +static struct edgenode* add_edge(struct vertex* v1, struct vertex* v2) +{ + struct edgenode** ep = find_edgep(v1, v2); + + if (!*ep) + { + *ep = calloc(1, sizeof(struct edgenode)); + (*ep)->this = v1; + (*ep)->other = v2; + v1->degree++; + } + + return *ep; +} + +void graph_add_edge(struct graph* g, void* data1, void* data2) +{ + struct vertex* v1 = find_or_add_vertex(g, data1); + struct vertex* v2 = find_or_add_vertex(g, data2); + struct edgenode* e; + + add_edge(v1, v2); + e = add_edge(v2, v1); + + hashtable_put(&g->edges, e, e); +} + +static struct edgenode* remove_edge(struct vertex* v1, struct vertex* v2) +{ + struct edgenode** ep = find_edgep(v1, v2); + + if (*ep) + { + struct edgenode* old = *ep; + *ep = (*ep)->next; + v1->degree--; + return old; + } + + return NULL; +} + +void graph_remove_edge(struct graph* g, void* data1, void* data2) +{ + struct vertex* v1 = find_or_add_vertex(g, data1); + struct vertex* v2 = find_or_add_vertex(g, data2); + struct edgenode* e1 = remove_edge(v1, v2); + struct edgenode* e2 = remove_edge(v2, v1); + + assert(!e1 == !e2); + + if (e1) + { + /* e1 is a template; the actual object in the hashtable might actually + * be e2 (as they compare the same). So, remove from the hashtable + * before freeing anything. */ + hashtable_remove(&g->edges, e1); + free(e1); + } + if (e2) + free(e2); +} + +void graph_add_vertex(struct graph* g, void* data) +{ + find_or_add_vertex(g, data); +} + +void graph_remove_vertex(struct graph* g, void* data) +{ + struct vertex* vertex; + + lazy_init(g); + vertex = hashtable_get(&g->vertices, data); + if (!vertex) + return; + + while (vertex->connections) + { + struct edgenode* next = vertex->connections->next; + struct edgenode* e = remove_edge(vertex->connections->other, vertex); + hashtable_remove(&g->edges, vertex->connections); + free(e); + free(vertex->connections); + vertex->connections = next; + } + + hashtable_remove(&g->vertices, data); +} + +int graph_get_vertex_degree(struct graph* g, void* data) +{ + struct vertex* vertex; + + lazy_init(g); + vertex = hashtable_get(&g->vertices, data); + assert(vertex); + return vertex->degree; +} + +void* graph_next_vertex(struct graph* g, struct vertex_iterator* it) +{ + struct vertex* vertex = hashtable_next(&g->vertices, &it->hit); + if (vertex) + { + it->data = vertex->data; + return vertex->data; + } + else + return NULL; +} + +bool graph_next_edge(struct graph* g, struct edge_iterator* it) +{ + struct edgenode* e = hashtable_next(&g->edges, &it->hit); + if (e) + { + it->left = e->this->data; + it->right = e->other->data; + return true; + } + else + { + it->left = it->right = NULL; + return false; + } +} diff --git a/modules/src/data/bigraph.h b/modules/src/data/bigraph.h new file mode 100644 index 000000000..ff6f420a9 --- /dev/null +++ b/modules/src/data/bigraph.h @@ -0,0 +1,45 @@ +#ifndef BIGRAPH_H +#define BIGRAPH_H + +#include "hashtable.h" + +/* A bidirectional graph with node addition and removal capabilities. */ + +struct graph +{ + struct hashtable vertices; + struct hashtable edges; +}; + +struct vertex_iterator +{ + /* Public */ + void* data; + + /* Private */ + struct hashtable_iterator hit; +}; + +struct edge_iterator +{ + /* Public */ + void* left; + void* right; + + /* Private */ + struct hashtable_iterator hit; +}; + +extern void graph_reset(struct graph* g); + +extern bool graph_contains_vertex(struct graph* g, void* data); +extern void graph_add_vertex(struct graph* g, void* data); +extern void graph_remove_vertex(struct graph* g, void* data); +extern void graph_add_edge(struct graph* g, void* data1, void* data2); +extern void graph_remove_edge(struct graph* g, void* data1, void* data2); +extern int graph_get_vertex_degree(struct graph* g, void* data); + +extern void* graph_next_vertex(struct graph* g, struct vertex_iterator* it); +extern bool graph_next_edge(struct graph* g, struct edge_iterator* it); + +#endif diff --git a/modules/src/data/hashtable.c b/modules/src/data/hashtable.c new file mode 100644 index 000000000..ebaae3b08 --- /dev/null +++ b/modules/src/data/hashtable.c @@ -0,0 +1,158 @@ +#include +#include +#include +#include "hashtable.h" + +struct hashnode +{ + struct hashnode* next; + void* key; + void* data; +}; + +uint32_t standard_pointer_hash_function(void* key) +{ + /* Basile-Starynkevitch pointer hash */ + uintptr_t ptr = (uintptr_t) key; + return (uint32_t) ((13 * ptr) ^ (ptr >> 15)); +} + +bool standard_pointer_comparison_function(void* key1, void* key2) +{ + return (key1 == key2); +} + +static void lazy_init(struct hashtable* ht) +{ + if (!ht->num_buckets) + ht->num_buckets = 37; + + if (!ht->hashfunction) + ht->hashfunction = standard_pointer_hash_function; + + if (!ht->cmpfunction) + ht->cmpfunction = standard_pointer_comparison_function; + + if (!ht->buckets) + ht->buckets = calloc(ht->num_buckets, sizeof(struct hashnode*)); +} + +void hashtable_reset(struct hashtable* ht) +{ + while (ht->size) + hashtable_pop(ht); + + free(ht->buckets); + ht->buckets = NULL; +} + +static struct hashnode** findnodep(struct hashtable* ht, void* key) +{ + uint32_t hash; + struct hashnode** hnp; + + lazy_init(ht); + hash = ht->hashfunction(key) % ht->num_buckets; + hnp = &ht->buckets[hash]; + + while (*hnp && !ht->cmpfunction((*hnp)->key, key)) + hnp = &(*hnp)->next; + + return hnp; +} + +void* hashtable_put(struct hashtable* ht, void* key, void* data) +{ + void* olddata; + struct hashnode** hnp = findnodep(ht, key); + if (!*hnp) + { + *hnp = calloc(1, sizeof(struct hashnode)); + ht->size++; + } + + olddata = (*hnp)->data; + (*hnp)->key = key; + (*hnp)->data = data; + return olddata; +} + +void* hashtable_get(struct hashtable* ht, void* key) +{ + struct hashnode** hnp = findnodep(ht, key); + if (*hnp) + return (*hnp)->data; + return NULL; +} + +bool hashtable_contains(struct hashtable* ht, void* key) +{ + return *findnodep(ht, key); +} + +bool hashtable_remove(struct hashtable* ht, void* key) +{ + struct hashnode** hnp = findnodep(ht, key); + if (*hnp) + { + struct hashnode* hn = *hnp; + *hnp = hn->next; + free(hn); + ht->size--; + return true; + } + + return false; +} + +void* hashtable_pop(struct hashtable* ht) +{ + int i; + + if (ht->size == 0) + return NULL; + + lazy_init(ht); + for (i=0; inum_buckets; i++) + { + struct hashnode** hnp = &ht->buckets[i]; + if (*hnp) + { + struct hashnode* hn = *hnp; + void* data = hn->data; + *hnp = hn->next; + free(hn); + ht->size--; + return data; + } + } + + return NULL; +} + +void* hashtable_next(struct hashtable* ht, struct hashtable_iterator* it) +{ + while (!it->node) + { + if (it->bucket == ht->num_buckets) + { + it->data = NULL; + it->bucket = 0; + it->node = NULL; + return NULL; + } + + it->node = ht->buckets[it->bucket]; + if (it->node) + break; + + it->bucket++; + } + + it->data = it->node->data; + it->node = it->node->next; + if (!it->node) + it->bucket++; + + return it->data; +} diff --git a/modules/src/data/hashtable.h b/modules/src/data/hashtable.h new file mode 100644 index 000000000..dac58c099 --- /dev/null +++ b/modules/src/data/hashtable.h @@ -0,0 +1,40 @@ +#ifndef HASHTABLE_H +#define HASHTABLE_H + +/* A simple, autoresizing hash table. */ + +typedef uint32_t hashfunction_t(void* key); +typedef bool cmpfunction_t(void* key1, void* key2); + +extern uint32_t standard_pointer_hash_function(void* key); +extern bool standard_pointer_comparison_function(void* key1, void* key2); + +struct hashtable +{ + unsigned int num_buckets; + hashfunction_t* hashfunction; + cmpfunction_t* cmpfunction; + struct hashnode** buckets; + int size; +}; + +struct hashtable_iterator +{ + /* Public */ + void* data; + + /* Private */ + int bucket; + struct hashnode* node; +}; + +extern void hashtable_reset(struct hashtable* ht); + +extern void* hashtable_put(struct hashtable* ht, void* key, void* data); +extern void* hashtable_get(struct hashtable* ht, void* key); +extern bool hashtable_contains(struct hashtable* ht, void* key); +extern bool hashtable_remove(struct hashtable* ht, void* key); +extern void* hashtable_pop(struct hashtable* ht); +extern void* hashtable_next(struct hashtable* ht, struct hashtable_iterator* it); + +#endif