ox

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

hmap.c (4112B)



#include <stdio.h>
#include <string.h>

#include "hmap.h"

#define INITIAL_BUCKETS 8
#define LOAD_FACTOR 0.75

static void hmap_grow(HashMap* map);

// Simple string hash function (djb2)
static unsigned long
hash(const char* str)
{
	unsigned long h = 5381;
	unsigned char c;
	while ((c = (unsigned char)*str++))
		h = ((h << 5) + h) + c;
	return h;
}

HashMap*
hmap_create(size_t value_size)
{
	HashMap* map = calloc(1, sizeof(HashMap));
	if (map == NULL) { fprintf(stderr, "hmap_create: map: could not alloc\n"); }
	map->bucket_count = INITIAL_BUCKETS;
	map->size = 0;
	map->value_size = value_size;
	map->buckets = calloc(map->bucket_count, sizeof(HashNode*));
	if (map->buckets == NULL) {
		fprintf(stderr, "hmap_create: bucket: could not alloc\n");
		exit(1);
	}
	return map;
}

void
hmap_put(HashMap* map, const char* key, const void* value)
{
	if ((float)(map->size + 1) / map->bucket_count > LOAD_FACTOR) { hmap_grow(map); }
	unsigned long h = hash(key) % map->bucket_count;
	HashNode* node = map->buckets[h];
	while (node) {
		if (strcmp(node->key, key) == 0) {
			memcpy(node->value, value, map->value_size);
			return;
		}
		node = node->next;
	}
	HashNode* new_node = calloc(1, sizeof(HashNode));
	if (new_node == NULL) {
		fprintf(stderr, "hmap_put: new_node: could not alloc\n");
		exit(1);
	}
	new_node->key = strdup(key);
	new_node->value = calloc(1, map->value_size);
	if (new_node == NULL) {
		fprintf(stderr, "hmap_put: new_node->value: could not alloc\n");
		exit(1);
	}
	memcpy(new_node->value, value, map->value_size);
	new_node->next = map->buckets[h];
	map->buckets[h] = new_node;
	map->size++;
}

bool
hmap_get(HashMap* map, const char* key, void* out)
{
	unsigned long h = hash(key) % map->bucket_count;
	HashNode* node = map->buckets[h];
	while (node) {
		if (strcmp(node->key, key) == 0) {
			memcpy(out, node->value, map->value_size);
			return true;
		}
		node = node->next;
	}
	return false;
}

bool
hmap_remove(HashMap* map, const char* key)
{
	unsigned long h = hash(key) % map->bucket_count;
	HashNode* node = map->buckets[h];
	HashNode* prev = NULL;
	while (node) {
		if (strcmp(node->key, key) == 0) {
			if (prev) {
				prev->next = node->next;
			} else {
				map->buckets[h] = node->next;
			}
			free(node->key);
			free(node->value);
			free(node);
			map->size--;
			return true;
		}
		prev = node;
		node = node->next;
	}
	return false;
}

static void
hmap_grow(HashMap* map)
{
	size_t new_bucket_count = map->bucket_count * 2;
	HashNode** new_buckets = calloc(new_bucket_count, sizeof(HashNode*));
	if (new_buckets == NULL) {
		fprintf(stderr, "hmap_grow: could not alloc\n");
		exit(1);
	}
	for (size_t i = 0; i < map->bucket_count; i++) {
		HashNode* node = map->buckets[i];
		while (node) {
			HashNode* next = node->next;
			unsigned long h = hash(node->key) % new_bucket_count;
			node->next = new_buckets[h];
			new_buckets[h] = node;
			node = next;
		}
	}
	free(map->buckets);
	map->buckets = new_buckets;
	map->bucket_count = new_bucket_count;
}

void
hmap_free(HashMap* map)
{
	for (size_t i = 0; i < map->bucket_count; i++) {
		HashNode* node = map->buckets[i];
		while (node) {
			HashNode* next = node->next;
			free(node->key);
			free(node->value);
			free(node);
			node = next;
		}
	}
	free(map->buckets);
	free(map);
}

// Example usage for struct T
// struct T {
//     int id;
//     char name[32];
// };

// int main() {
//     HashMap* map = hmap_create(sizeof(struct T));
//     struct T t1 = {1, "Alice"};
//     struct T t2 = {2, "Bob"};
//     struct T t3 = {3, "Carol"};

//     hmap_put(map, "alice", &t1);
//     hmap_put(map, "bob", &t2);
//     hmap_put(map, "carol", &t3);

//     struct T out;
//     if (hmap_get(map, "bob", &out)) {
//         printf("bob: id=%d, name=%s\n", out.id, out.name);
//     }
//     if (hmap_get(map, "alice", &out)) {
//         printf("alice: id=%d, name=%s\n", out.id, out.name);
//     }
//     if (hmap_get(map, "dave", &out)) {
//         printf("dave: id=%d, name=%s\n", out.id, out.name);
//     } else {
//         printf("dave not found\n");
//     }
//     hmap_free(map);
//     return 0;
// }