Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 18 additions & 56 deletions map.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,51 +6,14 @@

#include "map.h"
#include <stdlib.h>
#include <string.h>
//#include <stdio.h>

#define HASHMAP_DEFAULT_CAPACITY 20
#define HASHMAP_MAX_LOAD 0.75f
#define HASHMAP_RESIZE_FACTOR 2

struct bucket
int hashmap_init(hashmap *m)
{
// `next` must be the first struct element.
// changing the order will break multiple functions
struct bucket* next;

// key, key size, key hash, and associated value
const void* key;
size_t ksize;
uint32_t hash;
uintptr_t value;
};

struct hashmap
{
struct bucket* buckets;
int capacity;
int count;

#ifdef __HASHMAP_REMOVABLE
// "tombstones" are empty buckets from removing elements
int tombstone_count;
#endif

// a linked list of all valid entries, in order
struct bucket* first;
// lets us know where to add the next element
struct bucket* last;
};

hashmap* hashmap_create(void)
{
hashmap* m = malloc(sizeof(hashmap));
if (m == NULL)
{
return NULL;
}

m->capacity = HASHMAP_DEFAULT_CAPACITY;
m->count = 0;

Expand All @@ -65,6 +28,18 @@ hashmap* hashmap_create(void)
// m->first will be treated as the "next" pointer in an imaginary bucket.
// when the first item is added, m->first will be set to the correct address.
m->last = (struct bucket*)&m->first;
return 0;
}

hashmap* hashmap_create(void)
{
hashmap* m = malloc(sizeof(hashmap));
if (m == NULL)
{
return NULL;
}
hashmap_init(m);

return m;
}

Expand All @@ -74,6 +49,11 @@ void hashmap_free(hashmap* m)
free(m);
}

void hashmap_deinit(hashmap *m)
{
free(m->buckets);
}

// puts an old bucket into a resized hashmap
static struct bucket* resize_entry(hashmap* m, struct bucket* old_entry)
{
Expand Down Expand Up @@ -357,24 +337,6 @@ int hashmap_size(hashmap* m)
#endif
}

void hashmap_iterate(hashmap* m, hashmap_callback c, void* user_ptr)
{
// loop through the linked list of valid entries
// this way we can skip over empty buckets
struct bucket* current = m->first;

while (current != NULL)
{
#ifdef __HASHMAP_REMOVABLE
// "tombstone" check
if (current->key != NULL)
#endif
c((void*)current->key, current->ksize, current->value, user_ptr);

current = current->next;
}
}

/*void bucket_dump(hashmap* m)
{
for (int i = 0; i < m->capacity; i++)
Expand Down
78 changes: 77 additions & 1 deletion map.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,37 @@
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <string.h>

struct bucket
{
// `next` must be the first struct element.
// changing the order will break multiple functions
struct bucket* next;

// key, key size, key hash, and associated value
const void* key;
size_t ksize;
uint32_t hash;
uintptr_t value;
};

struct hashmap
{
struct bucket* buckets;
int capacity;
int count;

#ifdef __HASHMAP_REMOVABLE
// "tombstones" are empty buckets from removing elements
int tombstone_count;
#endif

// a linked list of all valid entries, in order
struct bucket* first;
// lets us know where to add the next element
struct bucket* last;
};

// hashmaps can associate keys with pointer values or integral types.
typedef struct hashmap hashmap;
Expand All @@ -28,11 +59,18 @@ typedef void (*hashmap_callback)(void *key, size_t ksize, uintptr_t value, void

hashmap* hashmap_create(void);

// if you alloc hashmap object yourself or have one on the stack
// you can use this function to initialize it.
int hashmap_init(hashmap *m);

// only frees the hashmap object and buckets.
// does not call free on each element's `key` or `value`.
// to free data associated with an element, call `hashmap_iterate`.
void hashmap_free(hashmap* map);

// only frees the buckets
void hashmap_deinit(hashmap *m);

// does not make a copy of `key`.
// you must copy it yourself if you want to guarantee its lifetime,
// or if you intend to call `hashmap_key_free`.
Expand Down Expand Up @@ -65,11 +103,49 @@ int hashmap_size(hashmap* map);
// iterate over the map, calling `c` on every element.
// goes through elements in the order they were added.
// the element's key, key size, value, and `usr` will be passed to `c`.
void hashmap_iterate(hashmap* map, hashmap_callback c, void* usr);
inline static void hashmap_iterate(hashmap* m, hashmap_callback c, void* user_ptr)
{
// loop through the linked list of valid entries
// this way we can skip over empty buckets
struct bucket* current = m->first;

while (current != NULL)
{
#ifdef __HASHMAP_REMOVABLE
// "tombstone" check
if (current->key != NULL)
#endif
c((void*)current->key, current->ksize, current->value, user_ptr);

current = current->next;
}
}

// dumps bucket info for debugging.
// allows you to see how many collisions you are getting.
// `0` is an empty bucket, `1` is occupied, and `x` is removed.
//void bucket_dump(hashmap *m);

#ifdef __HASHMAP_REMOVABLE
#define __HASHMAP_REMOVABLE_CHECK if (cur->key == NULL) continue
#else
#define __HASHMAP_REMOVABLE_CHECK do {} while(0)
#endif

#define hashmap_foreach(a_map, a_key, a_ksize, a_value, a_code) \
{\
for (struct bucket *cur = (a_map)->first; cur != NULL; cur = cur->next) {\
__HASHMAP_REMOVABLE_CHECK; \
const void *(a_key) = cur->key; \
size_t (a_ksize) = cur->ksize; \
uintptr_t (a_value) = cur->value; \
(void) (a_key); \
(void) (a_ksize); \
(void) (a_value); \
a_code \
} \
}



#endif // map_h