diff --git a/src/assembler/hash_table.c b/src/assembler/hash_table.c index c8e57e7..614f526 100644 --- a/src/assembler/hash_table.c +++ b/src/assembler/hash_table.c @@ -36,8 +36,21 @@ static inline size_t hash_key(const HashTable *table, const char *key) /* Create and return a new HashTable. + + These HashTables are designed to be generic, a sort of poor-man's C++ + template. They can store any kind of node data as long as they are structs + with a char* field (for the node key), and a pointer to its own type (to + implement separate chaining). + + key_offset is the (byte) offset of the key field, and next_offset is the + offset of the self-pointer. The callback function is called on a node when + it is removed from the table. Typically, it should free() the node's + members and then free() the node itself. + + The hash_table_NEW macro can be used to call this function more easily. */ -HashTable* hash_table_new(size_t key_offset, size_t next_offset) +HashTable* hash_table_new( + size_t key_offset, size_t next_offset, HashFreeCallback callback) { HashTable *table; if (!(table = malloc(sizeof(HashTable)))) @@ -49,17 +62,14 @@ HashTable* hash_table_new(size_t key_offset, size_t next_offset) table->buckets = INITIAL_BUCKETS; table->key_offset = key_offset; table->next_offset = next_offset; + table->free = callback; return table; } /* Deallocate a HashTable. This function does nothing if the table is NULL. - - The given callback function is called on each node as it is removed from - the table. Typically, it will free() the node's members and then free() - the node itself. */ -void hash_table_free(HashTable *table, HashFreeCallback callback) +void hash_table_free(HashTable *table) { if (!table) return; @@ -68,7 +78,7 @@ void hash_table_free(HashTable *table, HashFreeCallback callback) HashNode *node = table->nodes[bucket]; while (node) { HashNode *temp = NEXT_NODE(table, node); - callback(node); + table->free(node); node = temp; } } @@ -94,7 +104,10 @@ const HashNode* hash_table_find(const HashTable *table, const char *key) /* Insert a node into the table. - This doesn't check for duplicate keys, so you must do that beforehand. + This doesn't prevent inserting duplicate keys. If a duplicate key is + inserted, the table acts like a stack; the new one will shadow the old one, + and hash_table_remove() will remove the most-recently inserted key to + reveal the second-most recent. */ void hash_table_insert(HashTable *table, HashNode *node) { @@ -102,3 +115,29 @@ void hash_table_insert(HashTable *table, HashNode *node) NEXT_NODE(table, node) = table->nodes[index]; table->nodes[index] = node; } + +/* + (Try to) remove a node with the given key from the table. + + Return true if the node was removed, or false if it was not found. +*/ +bool hash_table_remove(HashTable *table, const char *key) +{ + size_t index = hash_key(table, key); + HashNode *node = table->nodes[index], *prev = NULL, *next; + + while (node) { + next = NEXT_NODE(table, node); + if (!strcmp(key, NODE_KEY(table, node))) { + if (prev) + NEXT_NODE(table, prev) = next; + else + table->nodes[index] = next; + table->free(node); + return true; + } + prev = node; + node = next; + } + return false; +} diff --git a/src/assembler/hash_table.h b/src/assembler/hash_table.h index 306c373..2b70560 100644 --- a/src/assembler/hash_table.h +++ b/src/assembler/hash_table.h @@ -3,27 +3,31 @@ #pragma once +#include #include -#define hash_table_NEW(node, key, next) \ - hash_table_new(offsetof(node, key), offsetof(node, next)) +#define hash_table_NEW(node, key, next, callback) \ + hash_table_new(offsetof(node, key), offsetof(node, next), \ + (HashFreeCallback) callback) /* Structs */ typedef struct HashNode HashNode; +typedef void (*HashFreeCallback)(HashNode*); + typedef struct { HashNode **nodes; size_t buckets; size_t key_offset; size_t next_offset; + HashFreeCallback free; } HashTable; -typedef void (*HashFreeCallback)(HashNode*); - /* Functions */ -HashTable* hash_table_new(size_t, size_t); -void hash_table_free(HashTable*, HashFreeCallback); +HashTable* hash_table_new(size_t, size_t, HashFreeCallback); +void hash_table_free(HashTable*); const HashNode* hash_table_find(const HashTable*, const char*); void hash_table_insert(HashTable*, HashNode*); +bool hash_table_remove(HashTable*, const char*); diff --git a/src/assembler/state.c b/src/assembler/state.c index adb449a..f0477d7 100644 --- a/src/assembler/state.c +++ b/src/assembler/state.c @@ -41,11 +41,29 @@ void state_free(AssemblerState *state) } /* + Callback function for freeing an ASMSymbol. +*/ +static void free_asm_symbol(ASMSymbol *node) +{ + free(node->symbol); + free(node); +} + +/* Initialize an ASMSymbolTable and place it in *symtable_ptr. */ void asm_symtable_init(ASMSymbolTable **symtable_ptr) { - *symtable_ptr = hash_table_NEW(ASMSymbol, symbol, next); + *symtable_ptr = hash_table_NEW(ASMSymbol, symbol, next, free_asm_symbol); +} + +/* + Callback function for freeing an ASMDefine. +*/ +static void free_asm_define(ASMDefine *node) +{ + free(node->name); + free(node); } /* @@ -53,7 +71,7 @@ void asm_symtable_init(ASMSymbolTable **symtable_ptr) */ ASMDefineTable* asm_deftable_new() { - return hash_table_NEW(ASMDefine, name, next); + return hash_table_NEW(ASMDefine, name, next, free_asm_define); } /* @@ -110,37 +128,20 @@ void asm_data_free(ASMData *data) } /* - Callback function for freeing an ASMSymbol. -*/ -static void free_asm_symbol(HashNode *node) -{ - free(((ASMSymbol*) node)->symbol); - free(node); -} - -/* Deallocate an ASMSymbolTable. */ void asm_symtable_free(ASMSymbolTable *symtable) { - hash_table_free(symtable, free_asm_symbol); + hash_table_free(symtable); } -/* - Callback function for freeing an ASMDefine. -*/ -static void free_asm_define(HashNode *node) -{ - free(((ASMDefine*) node)->name); - free(node); -} /* Deallocate an ASMDefineTable. */ void asm_deftable_free(ASMDefineTable *deftable) { - hash_table_free(deftable, free_asm_define); + hash_table_free(deftable); } /*