jrk

my own c stdlib to keep myself sane
git clone git://git.jakekoroman.com/jrk
Log | Files | Refs

commit 65df062e8ad1eacf7ebb1b957aa533eee335c59e
parent 38eadc144b00bec2bbcc4b94af9f8871db3e1ff3
Author: Jake Koroman <jake@jakekoroman.com>
Date:   Fri,  2 Jan 2026 18:17:48 -0500

add basic hashmap implementation.

Diffstat:
Mjrk.h | 117++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 116 insertions(+), 1 deletion(-)

diff --git a/jrk.h b/jrk.h @@ -4,9 +4,13 @@ * that i like. * * TODO: + * - add jrk_StringView compare function. + * + * - remove strnlen/strncmp from hm impl. should probably be using + * jrk_StringView's + * * - look at at the jrk_sv_chop_delim_loop stuff. there must * must be a better way of handling things. - * */ #include <stdbool.h> @@ -275,6 +279,107 @@ bool jrk_sb_write_entire_file(jrk_StringBuilder *, char *); bool jrk_sb_fd_read_all(jrk_StringBuilder*, i32); bool jrk_sb_read_entire_file(jrk_StringBuilder*, char*); + +// http://www.isthe.com/chongo/tech/comp/fnv/index.html#FNV-param +// for 64 bit hash +#define FNV1A_64_OFFSET_BASIS 0xCBF29CE484222325 +#define FNV1A_64_PRIME 0x100000001B3 +uint64_t jrk_fnv1a_64(uint8_t *data, uint64_t size); + +#define JRK_HM_MAX_KEY_SIZE 16 +#define jrk_hm_prototype(type) \ + typedef struct { \ + char key[JRK_HM_MAX_KEY_SIZE]; \ + type val; \ + } jrk_HashMapItem_##type; \ + \ + typedef struct { \ + jrk_HashMapItem_##type *items; \ + uint64_t capacity; \ + } jrk_HashMap_##type; \ + \ + bool jrk_hm_##type##_init(jrk_HashMap_##type *map, jrk_HashMapItem_##type *items, uint64_t items_sz); \ + bool jrk_hm_##type##_push(jrk_HashMap_##type *map, char *key, type val); \ + bool jrk_hm_##type##_get(jrk_HashMap_##type *map, char *key, type *outval); + +#define jrk_hm_impl(type) \ + bool \ + jrk_hm_##type##_init(jrk_HashMap_##type *map, jrk_HashMapItem_##type *items, uint64_t items_cap) \ + { \ + if (!map) { \ + fprintf(stderr, "error: failed to init hashmap: map pointer is NULL\n"); \ + return false; \ + } \ + if (!items) { \ + fprintf(stderr, "error: failed to init hashmap: buf pointer is NULL\n"); \ + return false; \ + } \ + map->items = items; \ + map->capacity = items_cap; \ + return true; \ + } \ + \ + bool \ + jrk_hm_##type##_push(jrk_HashMap_##type *map, char *key, type val) \ + { \ + if (!key) { \ + fprintf(stderr, "error: failed to push key into hashmap: key pointer is NULL\n"); \ + return false; \ + } \ + if (!map) { \ + fprintf(stderr, "error: failed to push key '%s' into hashmap: map pointer is NULL\n", key); \ + return false; \ + } \ + uint64_t idx = jrk_fnv1a_64((uint8_t *) key, strnlen(key, JRK_HM_MAX_KEY_SIZE)) % map->capacity; \ + jrk_HashMapItem_##type *item = &map->items[idx]; \ + if (item->key[0] != 0 && 0 != strncmp(item->key, key, JRK_HM_MAX_KEY_SIZE)) \ + printf("%s collision with %s\n", key, item->key); \ + for (; item < &map->items[map->capacity] && item->key[0] != 0 && 0 != strncmp(item->key, key, JRK_HM_MAX_KEY_SIZE); ++item); \ + if (item == &map->items[map->capacity]) { \ + for (item = &map->items[0]; item < &map->items[idx] && item->key[0] != 0; ++item); \ + if (item == &map->items[idx]) { \ + fprintf(stderr, "error: no space found for key '%s': hashmap is full\n", key); \ + return false; \ + } \ + } \ + memcpy(item->key, key, strlen(key)); \ + item->val = val; \ + return true; \ + } \ + \ + bool \ + jrk_hm_##type##_get(jrk_HashMap_##type *map, char *key, type *outval) \ + { \ + if (!key) { \ + fprintf(stderr, "error: failed to get key from hashmap: key pointer is NULL\n"); \ + return false; \ + } \ + if (!map) { \ + fprintf(stderr, "error: failed to get key '%s' from hashmap: map pointer is NULL\n", key); \ + return false; \ + } \ + if (!outval) { \ + fprintf(stderr, "error: failed to get key '%s' from hashmap: outval pointer is NULL\n", key); \ + return false; \ + } \ + uint64_t idx = jrk_fnv1a_64((uint8_t *) key, strnlen(key, JRK_HM_MAX_KEY_SIZE)) % map->capacity; \ + jrk_HashMapItem_##type *item = &map->items[idx]; \ + if (0 == strncmp(item->key, key, JRK_HM_MAX_KEY_SIZE)) { \ + *outval = item->val; \ + return true; \ + } \ + for (; item < &map->items[map->capacity] && 0 != strncmp(item->key, key, JRK_HM_MAX_KEY_SIZE); ++item) \ + if (item->key[0] == 0) \ + return false; \ + if (item == &map->items[map->capacity]) { \ + for (item = &map->items[0]; item < &map->items[idx] && 0 != strncmp(item->key, key, JRK_HM_MAX_KEY_SIZE); ++item); \ + if (item == &map->items[idx]) \ + return false; \ + } \ + *outval = item->val; \ + return true; \ + } + #ifdef JRK_IMPLEMENTATION #include <ctype.h> #include <errno.h> @@ -689,6 +794,16 @@ jrk_sv_chop_delim(jrk_StringView *sv, char delim) return result; } +uint64_t jrk_fnv1a_64(uint8_t *data, uint64_t size) +{ + uint64_t result = FNV1A_64_OFFSET_BASIS; + for (uint64_t i = 0; i < size; ++i) { + result ^= data[i]; + result *= FNV1A_64_PRIME; + } + return result; +} + i32 jrk_rand_num(i32 upbound) {