Author | Jakob Wakeling <[email protected]> |
Date | 2021-12-08 06:46:24 |
Commit | 0955f0975240413d08f5e09eea918e3a40219f3a |
Parent | 954d27a5abbf25a73a0df3b21660239ae9a1c9d2 |
util: Add experimental generic hashmap
Diffstat
D | src/map.c | | | 62 | -------------------------------------------------------------- |
D | src/map.h | | | 28 | ---------------------------- |
A | src/util/map.c | | | 115 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | src/util/map.h | | | 28 | ++++++++++++++++++++++++++++ |
4 files changed, 143 insertions, 90 deletions
diff --git a/src/map.c b/src/map.c deleted file mode 100644 index fa2e39e..0000000 --- a/src/map.c +++ /dev/null @@ -1,62 +0,0 @@ -// map.c -// Map source file for G -// Copyright (C) 2021, Jakob Wakeling -// All rights reserved. - - - -#include "map.h" -#include "util/error.h" -#include "util/fnv.h" -#include "util/util.h" - -#include <stdlib.h> -#include <string.h> - -/* Grow a map. */ -static inline void map_grow(map *m) { - /* TODO */ -} - -/* Re-hash a map. */ -static inline void map_hash(map *m) { - /* TODO */ -} - -/* Initialise a map. */ -void map_init(map *m) { - m->al = 0; m->ac = 1024; - m->a = calloc(m->ac, sizeof (ent *)); -} - -/* Free a map. */ -void map_free(map *m) { - /* TODO */ -} - -/* Insert an element into a map. */ -void map_insert(map *m, char *k, sym v) { - if ((f64)m->al / m->ac >= 0.75) { /* TODO */ warn("map_insert growth"); } - - u64 i = fnv1a64(k, strlen(k)) % m->ac; - - register ent *e = calloc(1, sizeof (*e)); /* TODO check memory allocation */ - e->k = strdup(k); e->v = v; e->n = m->a[i]; m->a[i] = e; -} - -/* Lookup an element in a map. */ -sym *map_lookup(map *m, char *k) { - u64 i = fnv1a64(k, strlen(k)) % m->ac; - - for (register ent *e = m->a[i]; e; e = e->n) { - if (!strcmp(k, e->k)) { return &e->v; } - } - - return NULL; -} - -/* Pop an element from a map. */ -void map_pop(map *m, char *k) { - u64 i = fnv1a64(k, strlen(k)) % m->ac; - m->a[i] = m->a[i]->n; /* TODO deallocate memory */ -} diff --git a/src/map.h b/src/map.h deleted file mode 100644 index 1cdbe77..0000000 --- a/src/map.h +++ /dev/null @@ -1,28 +0,0 @@ -// map.h -// Map header file for G -// Copyright (C) 2021, Jakob Wakeling -// All rights reserved. - - - -#ifndef G_MAP_H_ISL5XLWM -#define G_MAP_H_ISL5XLWM - -#include "util/util.h" -#include "type.h" - -typedef enum { SK_VOID, } sym_k; /* Symbol Kind */ -typedef enum { SF_VOID = BIT(0), } sym_f; /* Symbol Flag */ - -typedef struct sym { sym_k k; sym_f f; type t; } sym; -typedef struct ent { char *k; sym v; struct ent *n, *c; } ent; -typedef struct { ent **a, *c; UINT al, ac; } map; - -extern void map_init(map *m); -extern void map_free(map *m); - -extern void map_insert(map *m, char *k, sym s); -extern sym *map_lookup(map *m, char *k); -extern void map_pop(map *m, char *k); - -#endif // G_MAP_H_ISL5XLWM diff --git a/src/util/map.c b/src/util/map.c new file mode 100644 index 0000000..901346a --- /dev/null +++ b/src/util/map.c @@ -0,0 +1,115 @@ +// util/map.c, version 0.0.0 +// Map utility source file from libutil +// Copyright (C) 2021, Jakob Wakeling +// All rights reserved. + + + +#include "alloc.h" +#include "fnv.h" +#include "map.h" +#include "util.h" + +#include <malloc.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define LOAD_FACTOR 0.75 + +static void map_resize(map *m); + +/* Initialise a map. */ +map map_init(UINT el, void (*free)(void *)) { + return (map){ NULL, NULL, 0, 0, el, free }; +} + +/* Unititialise a map. */ +void map_free(map *m) { + for (map_ent *e = m->n, *n; e; e = n) { + n = e->n; if (m->free) { m->free(e->v); } free(e); + } + + free(m->a); +} + +/* Insert a pointer into a map. */ +void *map_insert(map *m, const char *k, void *v) { + if (m->ac == 0 || m->al >= ((f64)m->ac * LOAD_FACTOR)) { map_resize(m); } + UINT index = fnv1a64(k, strlen(k)) % m->ac; void *old = map_remove(m, k); + + /* Allocate and define the entry */ + map_ent *e = xcalloc(1, sizeof (*e)); + // map_debug(m); + e->k = (char *)k; e->v = v; e->n = m->n; m->n = e; + + /* Insert the entry at begining of the buckets chain */ + // printf("m->a: %p\n", m->a); + // printf("%zu\n", malloc_usable_size(m->a + (index * m->el))); + memcpy(&e->c, m->a + (index * m->el), m->el); + memcpy(m->a + (index * m->el), &e, m->el); m->al += 1; + + return old; +} + +/* Lookup a pointer from a map. */ +void *map_lookup(map *m, const char *k) { + UINT index = fnv1a64(k, strlen(k)) % m->ac; + + map_ent *e; memcpy(&e, m->a + (index * m->el), m->el); + for (; e; e = e->c) { if (!strcmp(e->k, k)) { return e->v; } } + + return NULL; +} + +/* Remove a pointer from the top of a map. */ +void *map_remove(map *m, const char *k) { + return NULL; /* TODO */ +} + +/* Print a basic representation of the map to stdout. */ +void map_print(map *m) { + for (map_ent *e = m->n; e; e = e->n) { + printf("%s -> %s\n", e->k, (char *)e->v); + } +} + +/* Print a debug representation of the map to stdout. */ +/* FIXME for some reason printf leaves junk in my calloc (m->a[33]) */ +void map_debug(map *m) { + for (UINT i = 0; i != m->ac; i += 1) { + map_ent *e; memcpy(&e, m->a + (i * m->el), m->el); + + // printf("%zu: ", i); + for (; e; e = e->c) { printf("%s -> %s, ", e->k, (char *)e->v); } + // printf("\n"); + } printf("\n"); +} + +/* Double the number of buckets in a map. */ +static void map_resize(map *m) { + if (m->ac == 0) { m->ac = 256; } else { m->ac *= 2; } + + /* If the map is empty, simply resize it without rehashing */ + if (m->al == 0) { + // printf("%zu * %zu = %zu; %zu\n", m->ac, m->el, m->ac * m->el, m->ac * 8 * m->el); fflush(stdout); + free(m->a); m->a = xcalloc(m->ac * 8, m->el); + // printf("m->a: %p\n", m->a); + // printf("%p: %zu\n", m->a, malloc_usable_size(m->a)); + // printf("%p: %zu\n", m->a + 1696, malloc_usable_size(m->a + 1696)); + // printf("%zu\n", ((m->a + 1696) - m->a)); + fflush(stdout); + } + /* Otherwise rehash every element into a new resized map */ + else { + // printf("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"); fflush(stdout); + + map old = *m; + + m->a = xcalloc(m->ac * 8, m->el); m->n = NULL; m->al = 0; + for (map_ent *e = old.n; e; e = e->n) { map_insert(m, e->k, e->v); } + + map_free(&old); + } +} diff --git a/src/util/map.h b/src/util/map.h new file mode 100644 index 0000000..d4b4401 --- /dev/null +++ b/src/util/map.h @@ -0,0 +1,28 @@ +// util/map.h, version 0.0.0 +// Map utility header file from libutil +// Copyright (C) 2021, Jakob Wakeling +// All rights reserved. + + + +#ifndef UTIL_SYMBOL_H_87BJYLNZ +#define UTIL_SYMBOL_H_87BJYLNZ + +#include "util.h" + +#define MAP_INSERT(m, k, v) map_insert(m, k, (void *)(UINT)(e)) + +typedef struct map_ent { char *k; void *v; struct map_ent *c, *n; } map_ent; +typedef struct { map_ent **a, *n; UINT al, ac, el; void (*free)(void *); } map; + +extern map map_init(UINT el, void (*free)(void *)); +extern void map_free(map *m); + +extern void *map_insert(map *m, const char *k, void *v); +extern void *map_lookup(map *m, const char *k); +extern void *map_remove(map *m, const char *k); + +extern void map_print(map *m); +extern void map_debug(map *m); + +#endif // UTIL_SYMBOL_H_87BJYLNZ