website

Website contents
git clone git://git.reagancfischer.dev/website.git
Log | Files | Refs

hash_table.c (4753B)


      1 /* hash_table.c */
      2 #include <stdlib.h>
      3 #include <string.h>
      4 #include <stdio.h>
      5 #include "hash_table.h"
      6 
      7 /* Hash Table Data Structure */
      8 struct hash_table {
      9     struct hash_table_entry **entries;
     10     int size;
     11     hash_table_cmp_fn cmp;
     12     hash_table_hash_fn hash;
     13     hash_table_dtor dtor;
     14 };
     15 
     16 /* Hash Table Entry Data Structure */
     17 struct hash_table_entry {
     18     void *key;
     19     void *value;
     20     struct hash_table_entry *next;
     21 };
     22 
     23 
     24 hash_table_t *hash_table_create(int size, hash_table_cmp_fn cmp, hash_table_hash_fn hash, hash_table_dtor dtor) {
     25     /* Allocate and Initialize Hash Table */
     26     hash_table_t *table = malloc(sizeof(struct hash_table));
     27     if (!table) {
     28         fprintf(stderr, "Error: Out of memory, could not allocate hash table\n");
     29         exit(EXIT_FAILURE);
     30     }
     31     table->entries = calloc(size, sizeof(struct hash_table_entry *));
     32     if (!table->entries) {
     33         fprintf(stderr, "Error: Out of memory, could not allocate hash table entries\n");
     34         free(table);
     35         exit(EXIT_FAILURE);
     36     }
     37     table->size = size;
     38     table->cmp = cmp;
     39     table->hash = hash;
     40     table->dtor = dtor;
     41 
     42     return table;
     43 }
     44 
     45 void hash_table_destroy(hash_table_t *table) {
     46     if (!table) return;
     47     /* Destroy Entries */
     48     for (int i = 0; i < table->size; i++) {
     49         struct hash_table_entry *entry = table->entries[i];
     50         while (entry) {
     51             struct hash_table_entry *next = entry->next;
     52             if (table->dtor) {
     53                 table->dtor(entry->key, 1);
     54                 table->dtor(entry->value, 0);
     55             }
     56             free(entry);
     57             entry = next;
     58         }
     59     }
     60 
     61     free(table->entries);
     62     free(table);
     63 }
     64 
     65 void *hash_table_get(const hash_table_t *table, const void *key) {
     66     if (!table || !key) return NULL;
     67     /* Get Entry By Hash */
     68     unsigned int hash = table->hash(key) % table->size;
     69     struct hash_table_entry *entry = table->entries[hash];
     70 
     71     /* Loop Through Entries and Return Value if Match */
     72     while (entry) {
     73         if (table->cmp(entry->key, key) == 0) {
     74             return entry->value;
     75         }
     76         entry = entry->next;
     77     }
     78 
     79     return NULL;
     80 }
     81 
     82 void hash_table_put(hash_table_t *table, void *key, void *value) {
     83     if (!table || !key) return;
     84     /* Get Entry By Hash */
     85     unsigned int hash = table->hash(key) % table->size;
     86     struct hash_table_entry *entry = table->entries[hash];
     87 
     88     /* Loop Through Entries and Replace Value if Key Matches */
     89     while (entry) {
     90         if (table->cmp(entry->key, key) == 0) {
     91             if (table->dtor) table->dtor(entry->value, 0);
     92             entry->value = value;
     93             return;
     94         }
     95         entry = entry->next;
     96     }
     97 
     98     /* Allocate New Entry if No Match */
     99     struct hash_table_entry *new_entry = malloc(sizeof(struct hash_table_entry));
    100     if (!new_entry) {
    101         fprintf(stderr, "Error: Out of memory, could not allocate hash table entry\n");
    102         exit(EXIT_FAILURE);
    103     }
    104     new_entry->key = key;
    105     new_entry->value = value;
    106     new_entry->next = table->entries[hash];
    107     table->entries[hash] = new_entry;
    108 
    109 }
    110 
    111 void hash_table_remove(hash_table_t *table, const void *key) {
    112     if (!table || !key) return;
    113     /* Get Entry By Hash */
    114     unsigned int hash = table->hash(key) % table->size;
    115     struct hash_table_entry *entry = table->entries[hash];
    116 
    117     /* Loop Through Entries and Remove Entry if Key Matches */
    118     struct hash_table_entry *prev = NULL;
    119     while (entry) {
    120         if (table->cmp(entry->key, key) == 0) {
    121             if (prev)
    122                 prev->next = entry->next;
    123             else
    124                 table->entries[hash] = entry->next;
    125             
    126             if (table->dtor) {
    127                 table->dtor(entry->key, 1);
    128                 table->dtor(entry->value, 0);
    129             }
    130             free(entry);
    131             return;
    132         }
    133         prev = entry;
    134         entry = entry->next;
    135     }
    136 
    137 }
    138 
    139 #ifdef TEST_HASH_TABLE
    140 #include <assert.h>
    141 
    142 static int string_cmp(const void *key1, const void *key2) {
    143     return strcmp((const char *)key1, (const char *)key2);
    144 }
    145 
    146 static unsigned int string_hash(const void *key) {
    147     unsigned int hash = 5381;
    148     const unsigned char *str = key;
    149     int c;
    150 
    151     while ((c = *str++))
    152     {
    153         hash = ((hash << 5) + hash) + c;
    154     }
    155 
    156     return hash;
    157 }
    158 
    159 int main(void) {
    160     hash_table_t *table = hash_table_create(16, string_cmp, string_hash, NULL);
    161     assert(table != NULL);
    162 
    163     hash_table_put(table, "foo", "bar");
    164     hash_table_put(table, "foo", "baz");
    165     assert(strcmp((const char *)hash_table_get(table, "foo"), "baz") == 0);
    166 
    167     hash_table_remove(table, "foo");
    168     assert(hash_table_get(table, "foo") == NULL);
    169 
    170     hash_table_destroy(table);
    171     printf("All tests passed!\n");
    172     return 0;
    173 }
    174 #endif
    175