I've been working on a simple hashmap in C. Here is hashmap.h:
#ifndef __HASHMAP_H__
#define __HASHMAP_H__
#include <stdlib.h>
#include <stdbool.h>
typedef size_t (*HashFunction)(void*);
typedef bool (*CompareFunction)(void*, void*);
struct Pair {
size_t hash_id;
void* key;
void* value;
struct Pair* next;
};
struct HashMap {
struct Pair** buckets;
size_t num_buckets;
HashFunction hfunc;
CompareFunction cfunc;
};
struct HashMap* new_hashmap(HashFunction, CompareFunction);
struct HashMap* new_hashmap_c(HashFunction, CompareFunction, size_t);
void free_hashmap(struct HashMap*);
void insert_hashmap(struct HashMap*, void*, void*);
void* get_hashmap(struct HashMap*, void*);
void remove_hashmap(struct HashMap*, void*);
#endif // __HASHMAP_H__
And here is hashmap.c:
#include <assert.h>
#include "hashmap.h"
#define DEFAULT_BUCKETS 750000
struct HashMap* new_hashmap(HashFunction hfunc, CompareFunction cfunc) {
return new_hashmap_c(hfunc, cfunc, DEFAULT_BUCKETS);
}
struct HashMap* new_hashmap_c(HashFunction hfunc, CompareFunction cfunc, size_t buckets) {
struct HashMap* hmap;
hmap = malloc(sizeof(*hmap));
assert(hmap);
hmap->buckets = malloc(buckets * sizeof(*hmap->buckets));
hmap->num_buckets = buckets;
hmap->hfunc = hfunc;
hmap->cfunc = cfunc;
assert(hmap->buckets);
for (size_t i = 0; i < buckets; ++i) {
hmap->buckets[i] = NULL;
}
return hmap;
}
void free_hashmap(struct HashMap* hmap) {
free(hmap->buckets);
free(hmap);
hmap = NULL;
}
void insert_hashmap(struct HashMap* hmap, void* key, void* value) {
size_t hashed_key = hmap->hfunc(key);
struct Pair* prev = NULL;
struct Pair* entry = hmap->buckets[hashed_key];
while (entry != NULL) {
if (hmap->cfunc(entry->key, key)) {
prev = entry;
break;
}
entry = entry->next;
}
if (entry == NULL) {
entry = malloc(sizeof(struct Pair));
entry->hash_id = hashed_key;
entry->key = key;
entry->value = value;
entry->next = NULL;
if (prev == NULL) {
hmap->buckets[hashed_key] = entry;
} else {
prev->next = entry;
}
} else {
entry->value = value;
}
}
void* get_hashmap(struct HashMap* hmap, void* key) {
size_t hashed_key = hmap->hfunc(key);
struct Pair* entry = hmap->buckets[hashed_key];
while (entry != NULL) {
if (hmap->cfunc(entry->key, key)) return entry->value;
entry = entry->next;
}
return NULL;
}
void remove_hashmap(struct HashMap* hmap, void* key) {
size_t hashed_key = hmap->hfunc(key);
struct Pair* prev = NULL;
struct Pair* entry = hmap->buckets[hashed_key];
while (entry != NULL) {
if (hmap->cfunc(entry->key, key)) {
prev = entry;
break;
}
entry = entry->next;
}
if (entry == NULL) return;
if (prev == NULL) {
hmap->buckets[hashed_key] = entry->next;
} else {
prev->next = entry->next;
}
free(entry);
hmap->buckets[hashed_key] = NULL;
}
And here is my test code:
#include <stdio.h>
#include <string.h>
#include "hashmap.h"
size_t hash(void* key) {
size_t hash = 0;
for (size_t i = 0; i < strlen(key); i++) {
hash = 31 * hash + *((char*) (key + i));
}
return hash;
}
bool compare(void* key1, void* key2) {
return *((int*) key1) == *((int*)key2);
}
int main() {
struct HashMap* my_hmap = new_hashmap(hash, compare);
int k = 10;
int v = 101;
int v2 = 102;
insert_hashmap(my_hmap, &k, &v);
printf("initial value: %d\n", *((int*)get_hashmap(my_hmap, &k)));
insert_hashmap(my_hmap, &k, &v2);
printf("value after changing: %d\n", *((int*)get_hashmap(my_hmap, &k)));
remove_hashmap(my_hmap, &k);
printf("pointer to deleted value: %p\n", get_hashmap(my_hmap, &k));
free_hashmap(my_hmap);
printf("done!");
}
Any tips on performance, coding style, etc. would be nice. Thanks!
DEFAULT_BUCKETS). In your test code, why is a map that stores integers using a string based hash computation? \$\endgroup\$749993closest prime to750000this will help you avoid collisions. \$\endgroup\$