Author | Jamozed <[email protected]> |
Date | 2021-12-08 07:00:24 |
Commit | 62177455a95ed26f0b89410a23f4721918e9e882 |
Parent | f1897e7f9865ddd994bf88c9a71f6f01134f7f3c |
map: Add currently broken hashmap
Currently this generic hashmap implementation appears to be broken, for whatever reason the memory allocation is completely insane and must be allocated x8 to not result in a sefault. I am surely missing something, but for the life of me I cannot find what. Additionally, the map_remove function is not yet implemented.
Diffstat
M | CMakeLists.txt | | | 2 | ++ |
A | src/map.c | | | 140 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | src/map.h | | | 53 | +++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | src/test/test_map.c | | | 44 | ++++++++++++++++++++++++++++++++++++++++++++ |
4 files changed, 239 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 039feb3..ad026ee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,6 +20,7 @@ ADD_EXECUTABLE(test_crypt ${PROJECT_SOURCE_DIR}/src/test/test_crypt.c) ADD_EXECUTABLE(test_endian ${PROJECT_SOURCE_DIR}/src/test/test_endian.c) ADD_EXECUTABLE(test_error ${PROJECT_SOURCE_DIR}/src/test/test_error.c) ADD_EXECUTABLE(test_fnv ${PROJECT_SOURCE_DIR}/src/test/test_fnv.c) +ADD_EXECUTABLE(test_map ${PROJECT_SOURCE_DIR}/src/test/test_map.c) ADD_EXECUTABLE(test_optget ${PROJECT_SOURCE_DIR}/src/test/test_optget.c) ADD_EXECUTABLE(test_rc2 ${PROJECT_SOURCE_DIR}/src/test/test_rc2.c) ADD_EXECUTABLE(test_strtou ${PROJECT_SOURCE_DIR}/src/test/test_strtou.c) @@ -31,6 +32,7 @@ ADD_TEST(NAME test_crypt COMMAND test_crypt) ADD_TEST(NAME test_endian COMMAND test_endian) ADD_TEST(NAME test_error COMMAND test_error) ADD_TEST(NAME test_fnv COMMAND test_fnv) +ADD_TEST(NAME test_map COMMAND test_map) ADD_TEST(NAME test_optget COMMAND test_optget) ADD_TEST(NAME test_rc2 COMMAND test_rc2) ADD_TEST(NAME test_strtou COMMAND test_strtou) diff --git a/src/map.c b/src/map.c new file mode 100644 index 0000000..3fc9bb5 --- /dev/null +++ b/src/map.c @@ -0,0 +1,140 @@ +// util/map.c, version 0.0.0 +// Map utility source file from libutil +// Copyright (C) 2021, Jakob Wakeling +// All rights reserved. + +/* +OMKOV Permissive Licence, version 1.0 + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal with +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimers. +* Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimers in the documentation and/or + other materials provided with the distribution. +* Neither the names of the copyright holders, nor the names of its contributors + may be used to endorse or promote products derived from this Software without + specific prior written permission. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT +HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE. +*/ + +#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, this needs a 'p' pointer in the entry struct */ +} + +/* 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/map.h b/src/map.h new file mode 100644 index 0000000..59c37d5 --- /dev/null +++ b/src/map.h @@ -0,0 +1,53 @@ +// util/map.h, version 0.0.0 +// Map utility header file from libutil +// Copyright (C) 2021, Jakob Wakeling +// All rights reserved. + +/* +OMKOV Permissive Licence, version 1.0 + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal with +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimers. +* Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimers in the documentation and/or + other materials provided with the distribution. +* Neither the names of the copyright holders, nor the names of its contributors + may be used to endorse or promote products derived from this Software without + specific prior written permission. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT +HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE. +*/ + +#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, *p, *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 diff --git a/src/test/test_map.c b/src/test/test_map.c new file mode 100644 index 0000000..adbbb45 --- /dev/null +++ b/src/test/test_map.c @@ -0,0 +1,44 @@ +// test_map.c +// Map unit test for libutil +// Copyright (C) 2021, Jakob Wakeling +// All rights reserved. + +/* +OMKOV Public Domain Licence, version 1.0 + +Permission is hereby granted to deal with this software and its associated +documentation files without restriction. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT +HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE. +*/ + +#include "../map.h" +#include "../util.h" +#include "unit.h" + +#include <stdio.h> +#include <string.h> + +int main(void) { + { + map m = map_init(sizeof (void *), NULL); + + map_insert(&m, "key0", "value0"); + // map_insert(&m, "key1", "value1"); + + // ASSERT("T000 INSERT AND LOOKUP STRING 0", + // strcmp(map_lookup(&m, "key0"), "value0") == 0); + // ASSERT("T001 INSERT AND LOOKUP STRING 1", + // strcmp(map_lookup(&m, "key1"), "value1") == 0); + + map_free(&m); + } + + // printf("%d of %d tests passed\n", testspassed, testsrun); + return testsfailed; +}