ox

The Ox programming language, compiler and tools (WIP)
Log | Files | Refs | README | LICENSE

hmap.c (4112B)


      1 #include <stdio.h>
      2 #include <string.h>
      3 
      4 #include "hmap.h"
      5 
      6 #define INITIAL_BUCKETS 8
      7 #define LOAD_FACTOR 0.75
      8 
      9 static void hmap_grow(HashMap* map);
     10 
     11 // Simple string hash function (djb2)
     12 static unsigned long
     13 hash(const char* str)
     14 {
     15 	unsigned long h = 5381;
     16 	unsigned char c;
     17 	while ((c = (unsigned char)*str++))
     18 		h = ((h << 5) + h) + c;
     19 	return h;
     20 }
     21 
     22 HashMap*
     23 hmap_create(size_t value_size)
     24 {
     25 	HashMap* map = calloc(1, sizeof(HashMap));
     26 	if (map == NULL) { fprintf(stderr, "hmap_create: map: could not alloc\n"); }
     27 	map->bucket_count = INITIAL_BUCKETS;
     28 	map->size = 0;
     29 	map->value_size = value_size;
     30 	map->buckets = calloc(map->bucket_count, sizeof(HashNode*));
     31 	if (map->buckets == NULL) {
     32 		fprintf(stderr, "hmap_create: bucket: could not alloc\n");
     33 		exit(1);
     34 	}
     35 	return map;
     36 }
     37 
     38 void
     39 hmap_put(HashMap* map, const char* key, const void* value)
     40 {
     41 	if ((float)(map->size + 1) / map->bucket_count > LOAD_FACTOR) { hmap_grow(map); }
     42 	unsigned long h = hash(key) % map->bucket_count;
     43 	HashNode* node = map->buckets[h];
     44 	while (node) {
     45 		if (strcmp(node->key, key) == 0) {
     46 			memcpy(node->value, value, map->value_size);
     47 			return;
     48 		}
     49 		node = node->next;
     50 	}
     51 	HashNode* new_node = calloc(1, sizeof(HashNode));
     52 	if (new_node == NULL) {
     53 		fprintf(stderr, "hmap_put: new_node: could not alloc\n");
     54 		exit(1);
     55 	}
     56 	new_node->key = strdup(key);
     57 	new_node->value = calloc(1, map->value_size);
     58 	if (new_node == NULL) {
     59 		fprintf(stderr, "hmap_put: new_node->value: could not alloc\n");
     60 		exit(1);
     61 	}
     62 	memcpy(new_node->value, value, map->value_size);
     63 	new_node->next = map->buckets[h];
     64 	map->buckets[h] = new_node;
     65 	map->size++;
     66 }
     67 
     68 bool
     69 hmap_get(HashMap* map, const char* key, void* out)
     70 {
     71 	unsigned long h = hash(key) % map->bucket_count;
     72 	HashNode* node = map->buckets[h];
     73 	while (node) {
     74 		if (strcmp(node->key, key) == 0) {
     75 			memcpy(out, node->value, map->value_size);
     76 			return true;
     77 		}
     78 		node = node->next;
     79 	}
     80 	return false;
     81 }
     82 
     83 bool
     84 hmap_remove(HashMap* map, const char* key)
     85 {
     86 	unsigned long h = hash(key) % map->bucket_count;
     87 	HashNode* node = map->buckets[h];
     88 	HashNode* prev = NULL;
     89 	while (node) {
     90 		if (strcmp(node->key, key) == 0) {
     91 			if (prev) {
     92 				prev->next = node->next;
     93 			} else {
     94 				map->buckets[h] = node->next;
     95 			}
     96 			free(node->key);
     97 			free(node->value);
     98 			free(node);
     99 			map->size--;
    100 			return true;
    101 		}
    102 		prev = node;
    103 		node = node->next;
    104 	}
    105 	return false;
    106 }
    107 
    108 static void
    109 hmap_grow(HashMap* map)
    110 {
    111 	size_t new_bucket_count = map->bucket_count * 2;
    112 	HashNode** new_buckets = calloc(new_bucket_count, sizeof(HashNode*));
    113 	if (new_buckets == NULL) {
    114 		fprintf(stderr, "hmap_grow: could not alloc\n");
    115 		exit(1);
    116 	}
    117 	for (size_t i = 0; i < map->bucket_count; i++) {
    118 		HashNode* node = map->buckets[i];
    119 		while (node) {
    120 			HashNode* next = node->next;
    121 			unsigned long h = hash(node->key) % new_bucket_count;
    122 			node->next = new_buckets[h];
    123 			new_buckets[h] = node;
    124 			node = next;
    125 		}
    126 	}
    127 	free(map->buckets);
    128 	map->buckets = new_buckets;
    129 	map->bucket_count = new_bucket_count;
    130 }
    131 
    132 void
    133 hmap_free(HashMap* map)
    134 {
    135 	for (size_t i = 0; i < map->bucket_count; i++) {
    136 		HashNode* node = map->buckets[i];
    137 		while (node) {
    138 			HashNode* next = node->next;
    139 			free(node->key);
    140 			free(node->value);
    141 			free(node);
    142 			node = next;
    143 		}
    144 	}
    145 	free(map->buckets);
    146 	free(map);
    147 }
    148 
    149 // Example usage for struct T
    150 // struct T {
    151 //     int id;
    152 //     char name[32];
    153 // };
    154 
    155 // int main() {
    156 //     HashMap* map = hmap_create(sizeof(struct T));
    157 //     struct T t1 = {1, "Alice"};
    158 //     struct T t2 = {2, "Bob"};
    159 //     struct T t3 = {3, "Carol"};
    160 
    161 //     hmap_put(map, "alice", &t1);
    162 //     hmap_put(map, "bob", &t2);
    163 //     hmap_put(map, "carol", &t3);
    164 
    165 //     struct T out;
    166 //     if (hmap_get(map, "bob", &out)) {
    167 //         printf("bob: id=%d, name=%s\n", out.id, out.name);
    168 //     }
    169 //     if (hmap_get(map, "alice", &out)) {
    170 //         printf("alice: id=%d, name=%s\n", out.id, out.name);
    171 //     }
    172 //     if (hmap_get(map, "dave", &out)) {
    173 //         printf("dave: id=%d, name=%s\n", out.id, out.name);
    174 //     } else {
    175 //         printf("dave not found\n");
    176 //     }
    177 //     hmap_free(map);
    178 //     return 0;
    179 // }