From f6bc58a3d76f6a6e6f13302e7a9946e7cf912372 Mon Sep 17 00:00:00 2001 From: Leonardo Vencovsky Date: Tue, 17 Jan 2023 15:41:21 +0100 Subject: [PATCH] More refactoring --- .github/workflows/test_suit.yml | 4 +- .gitignore | 3 + .travis.yml | 24 - Makefile | 19 + benchmarks/benchmark.c | 266 ++++ benchmarks/comparisons/hashtable/Makefile | 18 + benchmarks/comparisons/hashtable/hashmap.c | 55 + .../comparisons/hashtable/hashmultimap.c | 55 + benchmarks/comparisons/hashtable/murmurhash.h | 58 + .../comparisons/hashtable/unorderedmap.cpp | 34 + benchmarks/util/twister.c | 102 ++ src/cmc/cmc/deque/header.h | 17 + src/cmc/cmc/deque/struct.h | 16 - src/cmc/cmc/hashbidimap/code.h | 845 +++++++++++ src/cmc/cmc/hashbidimap/ext/code.h | 393 +++++ src/cmc/cmc/hashbidimap/ext/header.h | 86 ++ src/cmc/cmc/hashbidimap/ext/struct.h | 51 + src/cmc/cmc/hashbidimap/header.h | 91 ++ src/cmc/cmc/hashbidimap/struct.h | 69 + src/cmc/cmc/hashmap/code.h | 636 ++++++++ src/cmc/cmc/hashmap/ext/code.h | 438 ++++++ src/cmc/cmc/hashmap/ext/header.h | 82 + src/cmc/cmc/hashmap/ext/struct.h | 51 + src/cmc/cmc/hashmap/header.h | 90 ++ src/cmc/cmc/hashmap/struct.h | 60 + src/cmc/cmc/hashmultimap/code.h | 870 +++++++++++ src/cmc/cmc/hashmultimap/ext/code.h | 397 +++++ src/cmc/cmc/hashmultimap/ext/header.h | 75 + src/cmc/cmc/hashmultimap/ext/struct.h | 53 + src/cmc/cmc/hashmultimap/header.h | 93 ++ src/cmc/cmc/hashmultimap/struct.h | 59 + src/cmc/cmc/list/ext/struct.h | 3 - src/cmc/cmc/list/header.h | 17 + src/cmc/cmc/list/struct.h | 16 - src/cmc/cmc/queue/code.h | 324 ++++ src/cmc/cmc/queue/ext/code.h | 354 +++++ src/cmc/cmc/queue/ext/header.h | 80 + src/cmc/cmc/queue/ext/struct.h | 47 + src/cmc/cmc/queue/header.h | 66 + src/cmc/cmc/queue/struct.h | 45 + src/cmc/cmc/stack/header.h | 17 + src/cmc/cmc/stack/struct.h | 16 - src/cmc/cor/undef.h | 10 + src/cmc/deque.h | 4 +- src/cmc/hashbidimap.h | 85 ++ src/cmc/hashmap.h | 78 + src/cmc/hashmultimap.h | 103 ++ src/cmc/list.h | 5 +- src/cmc/queue.h | 99 ++ src/cmc/stack.h | 6 +- tests/Makefile | 14 +- tests/main.c | 18 +- tests/unt_deque.h | 1 + tests/unt_hashbidimap.h | 818 ++++++++++ tests/unt_hashmap.h | 1333 +++++++++++++++++ tests/unt_hashmultimap.h | 1031 +++++++++++++ tests/unt_list.h | 1 + tests/unt_queue.h | 958 ++++++++++++ tests/unt_stack.h | 1 + 59 files changed, 10562 insertions(+), 98 deletions(-) delete mode 100644 .travis.yml create mode 100644 Makefile create mode 100644 benchmarks/benchmark.c create mode 100644 benchmarks/comparisons/hashtable/Makefile create mode 100644 benchmarks/comparisons/hashtable/hashmap.c create mode 100644 benchmarks/comparisons/hashtable/hashmultimap.c create mode 100644 benchmarks/comparisons/hashtable/murmurhash.h create mode 100644 benchmarks/comparisons/hashtable/unorderedmap.cpp create mode 100644 benchmarks/util/twister.c create mode 100644 src/cmc/cmc/hashbidimap/code.h create mode 100644 src/cmc/cmc/hashbidimap/ext/code.h create mode 100644 src/cmc/cmc/hashbidimap/ext/header.h create mode 100644 src/cmc/cmc/hashbidimap/ext/struct.h create mode 100644 src/cmc/cmc/hashbidimap/header.h create mode 100644 src/cmc/cmc/hashbidimap/struct.h create mode 100644 src/cmc/cmc/hashmap/code.h create mode 100644 src/cmc/cmc/hashmap/ext/code.h create mode 100644 src/cmc/cmc/hashmap/ext/header.h create mode 100644 src/cmc/cmc/hashmap/ext/struct.h create mode 100644 src/cmc/cmc/hashmap/header.h create mode 100644 src/cmc/cmc/hashmap/struct.h create mode 100644 src/cmc/cmc/hashmultimap/code.h create mode 100644 src/cmc/cmc/hashmultimap/ext/code.h create mode 100644 src/cmc/cmc/hashmultimap/ext/header.h create mode 100644 src/cmc/cmc/hashmultimap/ext/struct.h create mode 100644 src/cmc/cmc/hashmultimap/header.h create mode 100644 src/cmc/cmc/hashmultimap/struct.h create mode 100644 src/cmc/cmc/queue/code.h create mode 100644 src/cmc/cmc/queue/ext/code.h create mode 100644 src/cmc/cmc/queue/ext/header.h create mode 100644 src/cmc/cmc/queue/ext/struct.h create mode 100644 src/cmc/cmc/queue/header.h create mode 100644 src/cmc/cmc/queue/struct.h create mode 100644 src/cmc/hashbidimap.h create mode 100644 src/cmc/hashmap.h create mode 100644 src/cmc/hashmultimap.h create mode 100644 src/cmc/queue.h create mode 100644 tests/unt_hashbidimap.h create mode 100644 tests/unt_hashmap.h create mode 100644 tests/unt_hashmultimap.h create mode 100644 tests/unt_queue.h diff --git a/.github/workflows/test_suit.yml b/.github/workflows/test_suit.yml index af5f983..9be60d5 100644 --- a/.github/workflows/test_suit.yml +++ b/.github/workflows/test_suit.yml @@ -17,9 +17,9 @@ jobs: make --version \ valgrind --version \ - name: Test Suite - run: cd tests && make + run: make tests - name: Upload Coverage uses: codecov/codecov-action@v3 with: - directory: ./tests/coverage + directory: ./tests verbose: true diff --git a/.gitignore b/.gitignore index d755066..4f1b4d1 100644 --- a/.gitignore +++ b/.gitignore @@ -59,3 +59,6 @@ experimental/ # Test / Code Coverage build folder build + +tests/coverage +tests/out diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 6f0bd4f..0000000 --- a/.travis.yml +++ /dev/null @@ -1,24 +0,0 @@ -dist: xenial -os: linux -language: c -compiler: gcc -addons: - apt: - sources: - - deadsnakes - - ubuntu-toolchain-r-test - packages: - - gcc-9 - - cmake - - python3.8 -script: - - sudo rm -f /usr/bin/gcc /usr/bin/gcov /usr/bin/python - - sudo ln -s /usr/bin/gcc-9 /usr/bin/gcc - - sudo ln -s /usr/bin/gcov-9 /usr/bin/gcov - - sudo ln -s /usr/bin/python3.8 /usr/bin/python - - gcc --version - - gcov --version - - python --version - - python autogen.py -e -b -cv -r -nf -nt -after_success: - - bash <(curl -s https://codecov.io/bash) diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..78c10d2 --- /dev/null +++ b/Makefile @@ -0,0 +1,19 @@ + +help: + @echo Welcome to the C Macro Collections Library. Targets: + @echo + @echo - tests: run tests + @echo - format: formats the entire project + +tests: + make -C ./tests + +tests_dev: + make -C ./tests dev + +format: + - find ./ -name *.h -type f | xargs clang-format --style=file --verbose -i + +bench: + gcc ./benchmarks/benchmark.c -o ./benchmarks/benchmark.exe -I ./src -O3 + ./benchmarks/benchmark.exe diff --git a/benchmarks/benchmark.c b/benchmarks/benchmark.c new file mode 100644 index 0000000..e2884c4 --- /dev/null +++ b/benchmarks/benchmark.c @@ -0,0 +1,266 @@ +/** + * benchmark.c + * + * Creation Date: 13/04/2019 + * + * Authors: + * Leonardo Vencovsky (https://github.com/LeoVen) + * + * Benchmarks: + * - Input + * - Search + * - Iteration + * - Output + */ +#include +#include +#include +#include +#include + +#include "util/twister.c" + +#include "cmc/utl/foreach.h" +#include "cmc/utl/futils.h" +#include "cmc/utl/timer.h" + +#define NTOTAL UINT64_C(10000000) +#define NMIN UINT64_C(100) + +// Percentage % macro +#define PC(x) x * 100.0 + +static int intcmp(int a, int b) +{ + return a - b; +} + +static size_t inthash(int t) +{ + size_t a = t; + a += ~(a << 15); + a ^= (a >> 10); + a += (a << 3); + a ^= (a >> 6); + a += ~(a << 11); + a ^= (a >> 16); + return a; +} + +size_t randsize(size_t min, size_t max, mt_state_ptr st) +{ + return (size_t)floor(twist_real(st) * ((double)max - (double)min + 1.0) + (double)min); +} + +void shuffle(int *array, mt_state_ptr st) +{ + for (size_t i = 0; i < NTOTAL; i++) + { + size_t j = randsize(0, NTOTAL - 1, st); + int tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + } +} + +#define K int32_t +#define V int32_t +#define PFX d +#define SNAME deque +#define CMC_EXT_DEQUE_ITER +#include "cmc/deque.h" + +// CMC_COLLECTION_GENERATE(DEQUE, d, deque, /* K */, int) +// CMC_COLLECTION_GENERATE(HASHMAP, hm, hmap, int, int) +// CMC_COLLECTION_GENERATE(HASHSET, hs, hset, /* K */, int) +// CMC_COLLECTION_GENERATE(HEAP, h, heap, /* K */, int) +// CMC_COLLECTION_GENERATE(LINKEDLIST, ll, linked, /* K */, int) +// CMC_COLLECTION_GENERATE(LIST, l, list, /* K */, int) +// CMC_COLLECTION_GENERATE(QUEUE, q, queue, /* K */, int) +// CMC_COLLECTION_GENERATE(STACK, s, stack, /* K */, int) +// CMC_COLLECTION_GENERATE(TREEMAP, tm, tmap, int, int) +// CMC_COLLECTION_GENERATE(TREESET, ts, tset, /* K */, int) + +// CMC_COLLECTION_GENERATE(INTERVALHEAP, ih, iheap, /* k */, int) +// CMC_COLLECTION_GENERATE(MULTIMAP, mm, mmap, int, int) +// CMC_COLLECTION_GENERATE(MULTISET, ms, mset, /* K */, int) + +#define BENCHMARK(NAME, PFX, sname, initfunc, insertbody, removebody, searchbody) \ + void NAME##_io_benchmark(int *array, int *sarray, size_t s) \ + { \ + printf("+------------------------------------------------------------ %10s\n", #NAME); \ + printf("+------------------------------------------------------------ \n"); \ + struct cmc_timer timer, total; \ + int r, k, v; \ + size_t j; \ +\ + struct sname *coll = initfunc; \ +\ + cmc_timer_start(total); \ + cmc_timer_start(timer); \ +\ + for (size_t i = 0; i < NTOTAL; i++) \ + { \ + insertbody; \ + } \ +\ + cmc_timer_stop(timer); \ +\ + size_t s1 = PFX##_count(coll); \ +\ + printf(" INPUT %10s TOOK %8.0lf milliseconds for %8" PRIuMAX " elements\n", #NAME, timer.result, s1); \ +\ + double input_time = timer.result; \ +\ + cmc_timer_start(timer); \ +\ + for (size_t i = 0; i < s; i++) \ + { \ + searchbody; \ + } \ +\ + cmc_timer_stop(timer); \ +\ + printf(" SEARCH %10s TOOK %8.0lf milliseconds for %8" PRIuMAX " elements\n", #NAME, timer.result, s); \ +\ + double search_time = timer.result; \ +\ + cmc_timer_start(timer); \ +\ + CMC_FOREACH (PFX, sname, iter, coll) \ + { \ + } \ +\ + cmc_timer_stop(timer); \ +\ + printf("ITERATION %10s TOOK %8.0lf milliseconds for %8" PRIuMAX " elements\n", #NAME, timer.result, s1); \ +\ + double iter_time = timer.result; \ +\ + cmc_timer_start(timer); \ +\ + for (size_t i = 0; i < NTOTAL; i++) \ + { \ + removebody; \ + } \ +\ + cmc_timer_stop(timer); \ +\ + size_t s2 = PFX##_count(coll); \ +\ + printf(" OUTPUT %10s TOOK %8.0lf milliseconds for %8" PRIuMAX " elements\n", #NAME, timer.result, s1); \ +\ + double output_time = timer.result; \ +\ + cmc_timer_stop(total); \ +\ + PFX##_free(coll); \ +\ + double adj_search_time = (total.result - search_time) + (search_time * (double)NTOTAL) / (double)s; \ + double no_search = input_time + iter_time + output_time; \ +\ + printf("+------------------------------------------------------------ \n"); \ + printf("TOTAL TIME : %.0lf milliseconds\n", total.result); \ + printf("ADJUSTED TIME : %.0lf milliseconds\n", adj_search_time); \ +\ + printf("+------------------------------------------------------------ \n"); \ + printf(" INPUT : %5.2lf%% (%5.2f%%)\n", PC(input_time / total.result), PC(input_time / no_search)); \ + printf(" SEARCH : %5.2lf%%\n", PC(search_time / total.result)); \ + printf("ITERATION : %5.2lf%% (%5.2f%%)\n", PC(iter_time / total.result), PC(iter_time / no_search)); \ + printf(" OUTPUT : %5.2lf%% (%5.2f%%)\n", PC(output_time / total.result), PC(output_time / no_search)); \ + printf("+------------------------------------------------------------ \n"); \ +\ + assert(s1 == NTOTAL && s2 == 0); \ +\ + printf("+------------------------------------------------------------ %10s\n\n", #NAME); \ + } + +BENCHMARK(DEQUE, d, deque, d_new(NTOTAL, &(struct deque_fval){ .cmp = cmc_i32_cmp }), d_push_back(coll, array[i]), + d_pop_back(coll), d_contains(coll, sarray[i])) +// BENCHMARK(HASHMAP, hm, hmap, hm_new(NTOTAL, 0.6, intcmp, inthash), hm_insert(coll, array[i], array[i]), +// hm_remove(coll, array[i], &r), hm_contains(coll, sarray[i])) +// BENCHMARK(HASHSET, hs, hset, hs_new(NTOTAL, 0.6, intcmp, inthash), hs_insert(coll, array[i]), hs_remove(coll, +// array[i]), +// hs_contains(coll, sarray[i])) +// BENCHMARK(HEAP, h, heap, h_new(NTOTAL, cmc_min_heap, intcmp), h_insert(coll, array[i]), h_remove(coll, &r), +// h_contains(coll, sarray[i])) +// BENCHMARK(LINKEDLIST, ll, linked, ll_new(), ll_push_back(coll, array[i]), ll_pop_back(coll), +// ll_contains(coll, sarray[i], intcmp)) +// BENCHMARK(LIST, l, list, l_new(NTOTAL), l_push_back(coll, array[i]), l_pop_back(coll), +// l_contains(coll, sarray[i], intcmp)) +// BENCHMARK(QUEUE, q, queue, q_new(NTOTAL), q_enqueue(coll, array[i]), q_dequeue(coll), +// q_contains(coll, sarray[i], intcmp)) +// BENCHMARK(STACK, s, stack, s_new(NTOTAL), s_push(coll, array[i]), s_pop(coll), s_contains(coll, sarray[i], intcmp)) +// BENCHMARK(TREEMAP, tm, tmap, tm_new(intcmp), tm_insert(coll, array[i], array[i]), tm_remove(coll, array[i], &r), +// tm_contains(coll, sarray[i])) +// BENCHMARK(TREESET, ts, tset, ts_new(intcmp), ts_insert(coll, array[i]), ts_remove(coll, array[i]), +// ts_contains(coll, sarray[i])) + +// BENCHMARK(INTERVALHEAP, ih, iheap, ih_new(NTOTAL, intcmp), ih_insert(coll, array[i]), ih_remove_max(coll, &r), +// ih_contains(coll, sarray[i])) +// BENCHMARK(MULTIMAP, mm, mmap, mm_new(NTOTAL, 0.8, intcmp, inthash), mm_insert(coll, array[i], array[i]), +// mm_remove(coll, array[i], &r), mm_contains(coll, sarray[i])) +// BENCHMARK(MULTISET, ms, mset, ms_new(NTOTAL, 0.6, intcmp, inthash), ms_insert(coll, array[i]), +// ms_remove(coll, array[i]), ms_contains(coll, sarray[i])) + +int main(void) +{ + // Using the Mersenne Twister to generate random numbers. This seed + // guarantees that everytime this program runs it will generate the same + // random numbers. The random numbers influence on the shuffling of the + // array. + mt_state st; + twist_init(&st, UINT64_C(12604951237)); + + int *array = malloc(NTOTAL * sizeof(int)); + int *sarray = malloc(NTOTAL * sizeof(int)); + + if (!array) + { + printf("Allocation failed\n"); + return 1; + } + + for (int i = -5000000; i < 5000000; i++) + array[i + 5000000] = i; + + shuffle(array, &st); + + memcpy(sarray, array, NTOTAL * sizeof(int)); + + shuffle(sarray, &st); + + printf("\nNote: Adjusted Time refers to if the container had done a search over all %" PRIuMAX " elements.\n\n", + NTOTAL); + + // Linear containers have to search for NMIN elements and non-linear (hash + // and tree) have to search for all elements. + struct cmc_timer timer; + cmc_timer_start(timer); + + DEQUE_io_benchmark(array, sarray, NMIN); + // HASHMAP_io_benchmark(array, sarray, NTOTAL); + // HASHSET_io_benchmark(array, sarray, NTOTAL); + // HEAP_io_benchmark(array, sarray, NMIN); + // LINKEDLIST_io_benchmark(array, sarray, NMIN); + // LIST_io_benchmark(array, sarray, NMIN); + // QUEUE_io_benchmark(array, sarray, NMIN); + // STACK_io_benchmark(array, sarray, NMIN); + // TREEMAP_io_benchmark(array, sarray, NTOTAL); + // TREESET_io_benchmark(array, sarray, NTOTAL); + + // INTERVALHEAP_io_benchmark(array, array, NMIN); + // MULTIMAP_io_benchmark(array, array, NTOTAL); + // MULTISET_io_benchmark(array, array, NTOTAL); + + cmc_timer_stop(timer); + + printf("+-----------------------------------------------------------+ \n"); + printf("| Total Benchmark Running Time : %12.0lf seconds | \n", timer.result / 1000); + printf("+-----------------------------------------------------------+ \n"); + + free(array); + free(sarray); + + return 0; +} diff --git a/benchmarks/comparisons/hashtable/Makefile b/benchmarks/comparisons/hashtable/Makefile new file mode 100644 index 0000000..7fbe732 --- /dev/null +++ b/benchmarks/comparisons/hashtable/Makefile @@ -0,0 +1,18 @@ +INCLUDE=-I ../../../src +CFLAGS=-O3 -Wall -DCOUNT=20000000 -DCMC_NO_CALLBACKS + +# Install hyperfine +# https://github.com/sharkdp/hyperfine + +run: build + hyperfine --warmup 2 --runs 5 './hashmap.exe' + hyperfine --warmup 2 --runs 5 './hashmultimap.exe' + hyperfine --warmup 2 --runs 5 './unorderedmap.exe' + +build: + gcc hashmap.c -o hashmap.exe $(INCLUDE) $(CFLAGS) + gcc hashmultimap.c -o hashmultimap.exe $(INCLUDE) $(CFLAGS) + g++ unorderedmap.cpp $(INCLUDE) -o unorderedmap.exe $(CFLAGS) + +clean: + rm *.exe diff --git a/benchmarks/comparisons/hashtable/hashmap.c b/benchmarks/comparisons/hashtable/hashmap.c new file mode 100644 index 0000000..2476f74 --- /dev/null +++ b/benchmarks/comparisons/hashtable/hashmap.c @@ -0,0 +1,55 @@ +#ifndef COUNT +#error "Please define COUNT" +#endif + +#include "murmurhash.h" + +#include "../../util/twister.c" + +#define K uint64_t +#define V uint64_t +#define PFX hm +#define SNAME hashmap +#include "cmc/hashmap.h" + +int cmp(uint64_t a, uint64_t b) +{ + return (a > b) - (a < b); +} + +// size_t hash(uint64_t a) +// { +// size_t x = (size_t)a; +// x = ((x >> 16) ^ x) * 0x45d9f3b; +// x = ((x >> 16) ^ x) * 0x45d9f3b; +// x = (x >> 16) ^ x; +// return x; +// } + +size_t hash(uint64_t k) +{ + static size_t seed = 211106232532969; + return MurMurHash(&k, sizeof(k), seed); +} + +struct hashmap_fkey *fkey = &(struct hashmap_fkey){ .cmp = cmp, .hash = hash, NULL }; + +struct hashmap_fval *fval = &(struct hashmap_fval){ .cmp = cmp, .hash = hash, NULL }; + +int main(void) +{ + mt_state tw; + twist_init(&tw, 50331653); + + struct hashmap *map = hm_new(COUNT, 0.6, fkey, fval); + + for (uint64_t i = 0; i < COUNT; i++) + { + uint64_t v = twist_uint64(&tw); + hm_insert(map, v, v); + } + + hm_free(map); + + return 0; +} diff --git a/benchmarks/comparisons/hashtable/hashmultimap.c b/benchmarks/comparisons/hashtable/hashmultimap.c new file mode 100644 index 0000000..52df48b --- /dev/null +++ b/benchmarks/comparisons/hashtable/hashmultimap.c @@ -0,0 +1,55 @@ +#ifndef COUNT +#error "Please define COUNT" +#endif + +#include "murmurhash.h" + +#include "../../util/twister.c" + +#define K uint64_t +#define V uint64_t +#define PFX hmm +#define SNAME hashmultimap +#include "cmc/hashmultimap.h" + +int cmp(uint64_t a, uint64_t b) +{ + return (a > b) - (a < b); +} + +// size_t hash(uint64_t a) +// { +// size_t x = (size_t)a; +// x = ((x >> 16) ^ x) * 0x45d9f3b; +// x = ((x >> 16) ^ x) * 0x45d9f3b; +// x = (x >> 16) ^ x; +// return x; +// } + +size_t hash(uint64_t k) +{ + static size_t seed = 211106232532969; + return MurMurHash(&k, sizeof(k), seed); +} + +struct hashmultimap_fkey *fkey = &(struct hashmultimap_fkey){ .cmp = cmp, .hash = hash, NULL }; + +struct hashmultimap_fval *fval = &(struct hashmultimap_fval){ .cmp = cmp, .hash = hash, NULL }; + +int main(void) +{ + mt_state tw; + twist_init(&tw, 50331653); + + struct hashmultimap *map = hmm_new(COUNT, 0.6, fkey, fval); + + for (uint64_t i = 0; i < COUNT; i++) + { + uint64_t v = twist_uint64(&tw); + hmm_insert(map, v, v); + } + + hmm_free(map); + + return 0; +} diff --git a/benchmarks/comparisons/hashtable/murmurhash.h b/benchmarks/comparisons/hashtable/murmurhash.h new file mode 100644 index 0000000..a334dd9 --- /dev/null +++ b/benchmarks/comparisons/hashtable/murmurhash.h @@ -0,0 +1,58 @@ +// https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/hash_bytes.cc + +#ifndef MURMURHASH +#define MURMURHASH + +#include + +inline size_t unaligned_load(const char *p) +{ + size_t result; + memcpy(&result, p, sizeof(result)); + return result; +} + +inline size_t load_bytes(const char *p, int n) +{ + size_t result = 0; + --n; + do + { + result = (result << 8) + (unsigned char)(p[n]); + } while (--n >= 0); + return result; +} + +inline size_t shift_mix(size_t v) +{ + return v ^ (v >> 47); +} + +size_t MurMurHash(const void *ptr, size_t len, size_t seed) +{ + static const size_t mul = (((size_t)0xc6a4a793UL) << 32UL) + (size_t)0x5bd1e995UL; + const char *const buf = (const char *)(ptr); + + // Remove the bytes not divisible by the sizeof(size_t). This + // allows the main loop to process the data as 64-bit integers. + const size_t len_aligned = len & ~(size_t)0x7; + const char *const end = buf + len_aligned; + size_t hash = seed ^ (len * mul); + for (const char *p = buf; p != end; p += 8) + { + const size_t data = shift_mix(unaligned_load(p) * mul) * mul; + hash ^= data; + hash *= mul; + } + if ((len & 0x7) != 0) + { + const size_t data = load_bytes(end, len & 0x7); + hash ^= data; + hash *= mul; + } + hash = shift_mix(hash) * mul; + hash = shift_mix(hash); + return hash; +} + +#endif /* MURMURHASH */ diff --git a/benchmarks/comparisons/hashtable/unorderedmap.cpp b/benchmarks/comparisons/hashtable/unorderedmap.cpp new file mode 100644 index 0000000..63db801 --- /dev/null +++ b/benchmarks/comparisons/hashtable/unorderedmap.cpp @@ -0,0 +1,34 @@ +#ifndef COUNT +#error "Please define COUNT" +#endif + +#include "murmurhash.h" + +#include + +#include "../../util/twister.c" + +struct IntHasher +{ + std::size_t operator()(const uint64_t &k) const + { + static size_t seed = 211106232532969; + return MurMurHash(&k, sizeof(k), seed); + } +}; + +int main(void) +{ + mt_state tw; + twist_init(&tw, 50331653); + + std::unordered_map map(COUNT); + + for (uint64_t i = 0; i < COUNT; i++) + { + uint64_t v = twist_uint64(&tw); + map.insert({ v, v }); + } + + return 0; +} diff --git a/benchmarks/util/twister.c b/benchmarks/util/twister.c new file mode 100644 index 0000000..bd9b8a3 --- /dev/null +++ b/benchmarks/util/twister.c @@ -0,0 +1,102 @@ +/** + * twister.c + * + * Creation Date: 09/05/2019 + * + * Authors: + * Leonardo Vencovsky (https://github.com/LeoVen) + * + * Mersenne Twister PRNG utility + * + */ +#include +#include +#include + +#define NN 312 +#define MM 156 +#define MATRIX_A UINT64_C(0xB5026F5AA96619E9) +#define UM UINT64_C(0xFFFFFFFF80000000) // Most significant 33 bits +#define LM UINT64_C(0x7FFFFFFF) // Least significant 31 bits + +// State vector +typedef struct mt_state_s +{ + uint64_t mt[NN]; + int mti; +} mt_state, *mt_state_ptr; + +// Initializes mt[NN] with a seed +void twist_init(mt_state_ptr st, uint64_t seed); + +// Generates a random uint64_t in [0, 2^64-1] interval +uint64_t twist_uint64(mt_state_ptr st); + +// Generates a double in [0, 1) interval +double twist_real(mt_state_ptr st); + +// +// Implementation +// + +// Initialize a state mt[NN] with a seed +void twist_init(mt_state_ptr st, uint64_t seed) +{ + st->mti = NN + 1; + + st->mt[0] = seed; + + for (st->mti = 1; st->mti < NN; st->mti++) + { + st->mt[st->mti] = (UINT64_C(6364136223846793005) * (st->mt[st->mti - 1] ^ (st->mt[st->mti - 1] >> 62)) + st->mti); + } +} + +// Generates a random uint64_t in [0, 2^64-1] interval +uint64_t twist_uint64(mt_state_ptr st) +{ + int i; + uint64_t x; + static uint64_t mag01[2] = {UINT64_C(0), MATRIX_A}; + + if (st->mti >= NN) + { + // Generate NN words at one time if twist_init() has not been called + // A default initial seed is used + if (st->mti == NN + 1) + twist_init(st, UINT64_C(5489)); + + for (i = 0; i < NN - MM; i++) + { + x = (st->mt[i] & UM) | (st->mt[i + 1] & LM); + st->mt[i] = st->mt[i + MM] ^ (x >> 1) ^ mag01[(int)(x & UINT64_C(1))]; + } + + for (; i < NN - 1; i++) + { + x = (st->mt[i] & UM) | (st->mt[i + 1] & LM); + st->mt[i] = st->mt[i + (MM - NN)] ^ (x >> 1) ^ mag01[(int)(x & UINT64_C(1))]; + } + + x = (st->mt[NN - 1] & UM) | (st->mt[0] & LM); + + st->mt[NN - 1] = st->mt[MM - 1] ^ (x >> 1) ^ mag01[(int)(x & UINT64_C(1))]; + + st->mti = 0; + } + + x = st->mt[st->mti++]; + + x ^= (x >> 29) & UINT64_C(0x5555555555555555); + x ^= (x << 17) & UINT64_C(0x71D67FFFEDA60000); + x ^= (x << 37) & UINT64_C(0xFFF7EEE000000000); + x ^= (x >> 43); + + return x; +} + +// Generates a double in [0, 1) interval +double twist_real(mt_state_ptr st) +{ + return (twist_uint64(st) >> 11) * (1.0 / 9007199254740992.0); +} diff --git a/src/cmc/cmc/deque/header.h b/src/cmc/cmc/deque/header.h index 54576fb..8cc168e 100644 --- a/src/cmc/cmc/deque/header.h +++ b/src/cmc/cmc/deque/header.h @@ -22,6 +22,23 @@ * SOFTWARE. */ +/* Value function table */ +struct CMC_DEF_FVAL(SNAME) +{ + /* Comparator function */ + CMC_DEF_FTAB_CMP(V); + /* Copy function */ + CMC_DEF_FTAB_CPY(V); + /* To string function */ + CMC_DEF_FTAB_STR(V); + /* Free from memory function */ + CMC_DEF_FTAB_FREE(V); + /* Hash function */ + CMC_DEF_FTAB_HASH(V); + /* Priority function */ + CMC_DEF_FTAB_PRI(V); +}; + /* Collection Allocation and Deallocation */ struct SNAME *CMC_(PFX, _new)(size_t capacity, struct CMC_DEF_FVAL(SNAME) * f_val); struct SNAME *CMC_(PFX, _new_custom)(size_t capacity, struct CMC_DEF_FVAL(SNAME) * f_val, diff --git a/src/cmc/cmc/deque/struct.h b/src/cmc/cmc/deque/struct.h index 69ae077..3515a5d 100644 --- a/src/cmc/cmc/deque/struct.h +++ b/src/cmc/cmc/deque/struct.h @@ -43,19 +43,3 @@ struct SNAME /* Custom callback functions */ CMC_CALLBACKS_DECL; }; - -struct CMC_DEF_FVAL(SNAME) -{ - /* Comparator function */ - CMC_DEF_FTAB_CMP(V); - /* Copy function */ - CMC_DEF_FTAB_CPY(V); - /* To string function */ - CMC_DEF_FTAB_STR(V); - /* Free from memory function */ - CMC_DEF_FTAB_FREE(V); - /* Hash function */ - CMC_DEF_FTAB_HASH(V); - /* Priority function */ - CMC_DEF_FTAB_PRI(V); -}; diff --git a/src/cmc/cmc/hashbidimap/code.h b/src/cmc/cmc/hashbidimap/code.h new file mode 100644 index 0000000..9e7d90b --- /dev/null +++ b/src/cmc/cmc/hashbidimap/code.h @@ -0,0 +1,845 @@ +/** + * Copyright (c) 2019 Leonardo Vencovsky + * + * This file is part of the C Macro Collections Libray. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in 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: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * 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 + * AUTHORS OR COPYRIGHT HOLDERS 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 IN THE + * SOFTWARE. + */ + +/* Implementation Detail Functions */ +static struct CMC_DEF_ENTRY(SNAME) * CMC_(PFX, _impl_new_entry)(struct SNAME *_map_, K key, V value); +static struct CMC_DEF_ENTRY(SNAME) * *CMC_(PFX, _impl_get_entry_by_key)(struct SNAME *_map_, K key); +static struct CMC_DEF_ENTRY(SNAME) * *CMC_(PFX, _impl_get_entry_by_val)(struct SNAME *_map_, V val); +static struct CMC_DEF_ENTRY(SNAME) * + *CMC_(PFX, _impl_add_entry_to_key)(struct SNAME *_map_, struct CMC_DEF_ENTRY(SNAME) * entry); +static struct CMC_DEF_ENTRY(SNAME) * + *CMC_(PFX, _impl_add_entry_to_val)(struct SNAME *_map_, struct CMC_DEF_ENTRY(SNAME) * entry); +static size_t CMC_(PFX, _impl_calculate_size)(size_t required); + +struct SNAME *CMC_(PFX, _new)(size_t capacity, double load, struct CMC_DEF_FKEY(SNAME) * f_key, + struct CMC_DEF_FVAL(SNAME) * f_val) +{ + return CMC_(PFX, _new_custom)(capacity, load, f_key, f_val, NULL, NULL); +} + +struct SNAME *CMC_(PFX, _new_custom)(size_t capacity, double load, struct CMC_DEF_FKEY(SNAME) * f_key, + struct CMC_DEF_FVAL(SNAME) * f_val, struct CMC_ALLOC_NODE_NAME *alloc, + struct CMC_CALLBACKS_NAME *callbacks) +{ + CMC_CALLBACKS_MAYBE_UNUSED(callbacks); + + if (capacity == 0 || load <= 0 || load >= 1) + return NULL; + + /* Prevent integer overflow */ + if (capacity >= UINTMAX_MAX * load) + return NULL; + + if (!f_key || !f_val) + return NULL; + + size_t real_capacity = CMC_(PFX, _impl_calculate_size)(capacity / load); + + if (!alloc) + alloc = &cmc_alloc_node_default; + + struct SNAME *_map_ = alloc->malloc(sizeof(struct SNAME)); + + if (!_map_) + return NULL; + + _map_->buffer = alloc->calloc(real_capacity, sizeof(struct CMC_DEF_ENTRY(SNAME) *[2])); + + if (!_map_->buffer) + { + alloc->free(_map_->buffer); + alloc->free(_map_); + return NULL; + } + + _map_->count = 0; + _map_->capacity = real_capacity; + _map_->load = load; + _map_->flag = CMC_FLAG_OK; + _map_->f_key = f_key; + _map_->f_val = f_val; + _map_->flag = CMC_FLAG_OK; + _map_->alloc = alloc; + CMC_CALLBACKS_ASSIGN(_map_, callbacks); + + return _map_; +} + +void CMC_(PFX, _clear)(struct SNAME *_map_) +{ + for (size_t i = 0; i < _map_->capacity; i++) + { + struct CMC_DEF_ENTRY(SNAME) *entry = _map_->buffer[i][0]; + + if (entry && entry != CMC_ENTRY_DELETED) + { + if (_map_->f_key->free) + _map_->f_key->free(entry->key); + if (_map_->f_val->free) + _map_->f_val->free(entry->value); + + _map_->alloc->free(entry); + } + + _map_->buffer[i][0] = NULL; + _map_->buffer[i][1] = NULL; + } + + _map_->count = 0; + _map_->flag = CMC_FLAG_OK; +} + +void CMC_(PFX, _free)(struct SNAME *_map_) +{ + CMC_(PFX, _clear)(_map_); + + _map_->alloc->free(_map_->buffer); + _map_->alloc->free(_map_); +} + +void CMC_(PFX, _customize)(struct SNAME *_map_, struct CMC_ALLOC_NODE_NAME *alloc, struct CMC_CALLBACKS_NAME *callbacks) +{ + CMC_CALLBACKS_MAYBE_UNUSED(callbacks); + + if (!alloc) + _map_->alloc = &cmc_alloc_node_default; + else + _map_->alloc = alloc; + + CMC_CALLBACKS_ASSIGN(_map_, callbacks); + + _map_->flag = CMC_FLAG_OK; +} + +bool CMC_(PFX, _insert)(struct SNAME *_map_, K key, V value) +{ + if (CMC_(PFX, _full)(_map_)) + { + if (!CMC_(PFX, _resize)(_map_, _map_->capacity + 1)) + return false; + } + + if (CMC_(PFX, _impl_get_entry_by_key)(_map_, key) != NULL || + CMC_(PFX, _impl_get_entry_by_val)(_map_, value) != NULL) + { + _map_->flag = CMC_FLAG_DUPLICATE; + return false; + } + + struct CMC_DEF_ENTRY(SNAME) *entry = CMC_(PFX, _impl_new_entry)(_map_, key, value); + + struct CMC_DEF_ENTRY(SNAME) **key_entry = CMC_(PFX, _impl_add_entry_to_key)(_map_, entry); + struct CMC_DEF_ENTRY(SNAME) **val_entry = CMC_(PFX, _impl_add_entry_to_val)(_map_, entry); + + if (!key_entry || !val_entry) + { + if (key_entry) + *key_entry = CMC_ENTRY_DELETED; + if (val_entry) + *val_entry = CMC_ENTRY_DELETED; + + _map_->alloc->free(entry); + + _map_->flag = CMC_FLAG_ERROR; + + return false; + } + + _map_->count++; + _map_->flag = CMC_FLAG_OK; + + CMC_CALLBACKS_CALL(_map_, create); + + return true; +} + +bool CMC_(PFX, _update_key)(struct SNAME *_map_, V val, K new_key) +{ + if (CMC_(PFX, _empty)(_map_)) + { + _map_->flag = CMC_FLAG_EMPTY; + return false; + } + + struct CMC_DEF_ENTRY(SNAME) * *key_entry, **val_entry; + + val_entry = CMC_(PFX, _impl_get_entry_by_val)(_map_, val); + + if (!val_entry) + { + _map_->flag = CMC_FLAG_NOT_FOUND; + return false; + } + + /* The mapping val -> new_key is already true */ + if (_map_->f_key->cmp(new_key, (*val_entry)->key) == 0) + goto success; + + if (CMC_(PFX, _impl_get_entry_by_key)(_map_, new_key) != NULL) + { + _map_->flag = CMC_FLAG_DUPLICATE; + return false; + } + + key_entry = (*val_entry)->ref[0]; + + if (!key_entry || *key_entry != *val_entry) + { + /* Should never happen */ + _map_->flag = CMC_FLAG_ERROR; + return false; + } + + /* Remove entry from key buffer and add it again with new key */ + struct CMC_DEF_ENTRY(SNAME) *to_add = *key_entry; + K tmp_key = to_add->key; + to_add->key = new_key; + + *key_entry = CMC_ENTRY_DELETED; + + if (!CMC_(PFX, _impl_add_entry_to_key)(_map_, to_add)) + { + /* Revert changes */ + to_add->key = tmp_key; + *key_entry = to_add; + + _map_->flag = CMC_FLAG_ERROR; + return false; + } + +success: + + _map_->flag = CMC_FLAG_OK; + + CMC_CALLBACKS_CALL(_map_, update); + + return true; +} + +bool CMC_(PFX, _update_val)(struct SNAME *_map_, K key, V new_val) +{ + if (CMC_(PFX, _empty)(_map_)) + { + _map_->flag = CMC_FLAG_EMPTY; + return false; + } + + struct CMC_DEF_ENTRY(SNAME) * *key_entry, **val_entry; + + key_entry = CMC_(PFX, _impl_get_entry_by_key)(_map_, key); + + if (!key_entry) + { + _map_->flag = CMC_FLAG_NOT_FOUND; + return false; + } + + /* The mapping key -> new_val is already true */ + if (_map_->f_val->cmp(new_val, (*key_entry)->value) == 0) + goto success; + + if (CMC_(PFX, _impl_get_entry_by_val)(_map_, new_val) != NULL) + { + _map_->flag = CMC_FLAG_DUPLICATE; + return false; + } + + val_entry = (*key_entry)->ref[1]; + + if (!val_entry || *val_entry != *key_entry) + { + /* Should never happen */ + _map_->flag = CMC_FLAG_ERROR; + return false; + } + + /* Remove entry from value buffer and add it again with new value */ + struct CMC_DEF_ENTRY(SNAME) *to_add = *val_entry; + V tmp_val = to_add->value; + to_add->value = new_val; + + *val_entry = CMC_ENTRY_DELETED; + + if (!CMC_(PFX, _impl_add_entry_to_val)(_map_, to_add)) + { + /* Revert changes */ + to_add->value = tmp_val; + *val_entry = to_add; + + _map_->flag = CMC_FLAG_ERROR; + + return false; + } + +success: + + _map_->flag = CMC_FLAG_OK; + + CMC_CALLBACKS_CALL(_map_, update); + + return true; +} + +bool CMC_(PFX, _remove_by_key)(struct SNAME *_map_, K key, K *out_key, V *out_val) +{ + if (CMC_(PFX, _empty)(_map_)) + { + _map_->flag = CMC_FLAG_EMPTY; + return false; + } + + struct CMC_DEF_ENTRY(SNAME) * *key_entry, **val_entry; + + key_entry = CMC_(PFX, _impl_get_entry_by_key)(_map_, key); + + if (!key_entry) + { + _map_->flag = CMC_FLAG_NOT_FOUND; + return false; + } + + val_entry = (*key_entry)->ref[1]; + + if (!val_entry || *val_entry != *key_entry) + { + /* Should never happen */ + _map_->flag = CMC_FLAG_ERROR; + return false; + } + + if (out_key) + *out_key = (*key_entry)->key; + if (out_val) + *out_val = (*key_entry)->value; + + _map_->alloc->free(*key_entry); + + *key_entry = CMC_ENTRY_DELETED; + *val_entry = CMC_ENTRY_DELETED; + + _map_->count--; + _map_->flag = CMC_FLAG_OK; + + CMC_CALLBACKS_CALL(_map_, delete); + + return true; +} + +bool CMC_(PFX, _remove_by_val)(struct SNAME *_map_, V val, K *out_key, V *out_val) +{ + if (CMC_(PFX, _empty)(_map_)) + { + _map_->flag = CMC_FLAG_EMPTY; + return false; + } + + struct CMC_DEF_ENTRY(SNAME) * *key_entry, **val_entry; + + val_entry = CMC_(PFX, _impl_get_entry_by_val)(_map_, val); + + if (!val_entry) + { + _map_->flag = CMC_FLAG_NOT_FOUND; + return false; + } + + key_entry = (*val_entry)->ref[0]; + + if (!key_entry || *key_entry != *val_entry) + { + /* Should never happen */ + _map_->flag = CMC_FLAG_ERROR; + return false; + } + + if (out_key) + *out_key = (*val_entry)->key; + if (out_val) + *out_val = (*val_entry)->value; + + _map_->alloc->free(*val_entry); + + *key_entry = CMC_ENTRY_DELETED; + *val_entry = CMC_ENTRY_DELETED; + + _map_->count--; + _map_->flag = CMC_FLAG_OK; + + CMC_CALLBACKS_CALL(_map_, delete); + + return true; +} + +K CMC_(PFX, _get_key)(struct SNAME *_map_, V val) +{ + struct CMC_DEF_ENTRY(SNAME) **entry = CMC_(PFX, _impl_get_entry_by_val)(_map_, val); + + if (!entry) + { + _map_->flag = CMC_FLAG_NOT_FOUND; + return (K){ 0 }; + } + + _map_->flag = CMC_FLAG_OK; + + CMC_CALLBACKS_CALL(_map_, read); + + return (*entry)->key; +} + +V CMC_(PFX, _get_val)(struct SNAME *_map_, K key) +{ + struct CMC_DEF_ENTRY(SNAME) **entry = CMC_(PFX, _impl_get_entry_by_key)(_map_, key); + + if (!entry) + { + _map_->flag = CMC_FLAG_NOT_FOUND; + return (V){ 0 }; + } + + _map_->flag = CMC_FLAG_OK; + + CMC_CALLBACKS_CALL(_map_, read); + + return (*entry)->value; +} + +bool CMC_(PFX, _contains_key)(struct SNAME *_map_, K key) +{ + _map_->flag = CMC_FLAG_OK; + + bool result = CMC_(PFX, _impl_get_entry_by_key)(_map_, key) != NULL; + + CMC_CALLBACKS_CALL(_map_, read); + + return result; +} + +bool CMC_(PFX, _contains_val)(struct SNAME *_map_, V val) +{ + _map_->flag = CMC_FLAG_OK; + + bool result = CMC_(PFX, _impl_get_entry_by_val)(_map_, val) != NULL; + + CMC_CALLBACKS_CALL(_map_, read); + + return result; +} + +bool CMC_(PFX, _empty)(struct SNAME *_map_) +{ + return _map_->count == 0; +} + +bool CMC_(PFX, _full)(struct SNAME *_map_) +{ + return (double)_map_->capacity * _map_->load <= (double)_map_->count; +} + +size_t CMC_(PFX, _count)(struct SNAME *_map_) +{ + return _map_->count; +} + +size_t CMC_(PFX, _capacity)(struct SNAME *_map_) +{ + return _map_->capacity; +} + +double CMC_(PFX, _load)(struct SNAME *_map_) +{ + return _map_->load; +} + +int CMC_(PFX, _flag)(struct SNAME *_map_) +{ + return _map_->flag; +} + +bool CMC_(PFX, _resize)(struct SNAME *_map_, size_t capacity) +{ + _map_->flag = CMC_FLAG_OK; + + if (_map_->capacity == capacity) + goto success; + + if (_map_->capacity > capacity / _map_->load) + goto success; + + /* Prevent integer overflow */ + if (capacity >= UINTMAX_MAX * _map_->load) + { + _map_->flag = CMC_FLAG_ERROR; + return false; + } + + /* Calculate required capacity based on the prime numbers */ + size_t new_cap = CMC_(PFX, _impl_calculate_size)(capacity); + + /* Not possible to shrink with current available prime numbers */ + if (new_cap < _map_->count / _map_->load) + { + _map_->flag = CMC_FLAG_INVALID; + return false; + } + + /* No callbacks since _new_map_ is just a temporary hashtable */ + struct SNAME *_new_map_ = + CMC_(PFX, _new_custom)(capacity, _map_->load, _map_->f_key, _map_->f_val, _map_->alloc, NULL); + + if (!_new_map_) + { + _map_->flag = CMC_FLAG_ALLOC; + return false; + } + + for (size_t i = 0; i < _map_->capacity; i++) + { + struct CMC_DEF_ENTRY(SNAME) *scan = _map_->buffer[i][0]; + + if (scan && scan != CMC_ENTRY_DELETED) + { + struct CMC_DEF_ENTRY(SNAME) **e1 = CMC_(PFX, _impl_add_entry_to_key)(_new_map_, scan); + struct CMC_DEF_ENTRY(SNAME) **e2 = CMC_(PFX, _impl_add_entry_to_val)(_new_map_, scan); + + if (!e1 || !e2) + { + /* Prevent the map from freeing the data */ + _new_map_->f_key = &(struct CMC_DEF_FKEY(SNAME)){ NULL }; + _new_map_->f_val = &(struct CMC_DEF_FVAL(SNAME)){ NULL }; + + _map_->alloc->free(_new_map_->buffer); + _map_->alloc->free(_new_map_); + + _map_->flag = CMC_FLAG_ERROR; + + return false; + } + + _new_map_->count++; + } + } + + if (_map_->count != _new_map_->count) + { + /* Prevent the map from freeing the data */ + _new_map_->f_key = &(struct CMC_DEF_FKEY(SNAME)){ NULL }; + _new_map_->f_val = &(struct CMC_DEF_FVAL(SNAME)){ NULL }; + + _map_->alloc->free(_new_map_->buffer); + _map_->alloc->free(_new_map_); + + _map_->flag = CMC_FLAG_ERROR; + + return false; + } + + struct CMC_DEF_ENTRY(SNAME) * (*tmp_buff)[2] = _map_->buffer; + + _map_->buffer = _new_map_->buffer; + _map_->capacity = _new_map_->capacity; + + _map_->alloc->free(tmp_buff); + _map_->alloc->free(_new_map_); + +success: + + CMC_CALLBACKS_CALL(_map_, resize); + + return true; +} + +struct SNAME *CMC_(PFX, _copy_of)(struct SNAME *_map_) +{ + /* TODO this function can be optimized */ + struct SNAME *result = CMC_(PFX, _new_custom)(_map_->capacity * _map_->load, _map_->load, _map_->f_key, + _map_->f_val, _map_->alloc, NULL); + + if (!result) + { + _map_->flag = CMC_FLAG_ERROR; + return NULL; + } + + CMC_CALLBACKS_ASSIGN(result, _map_->callbacks); + + for (size_t i = 0; i < _map_->capacity; i++) + { + struct CMC_DEF_ENTRY(SNAME) *scan = _map_->buffer[i][0]; + + if (scan && scan != CMC_ENTRY_DELETED) + { + K tmp_key; + V tmp_val; + + if (_map_->f_key->cpy) + tmp_key = _map_->f_key->cpy(scan->key); + else + tmp_key = scan->key; + + if (_map_->f_val->cpy) + tmp_val = _map_->f_val->cpy(scan->value); + else + tmp_val = scan->value; + + /* TODO treat the possible errors */ + struct CMC_DEF_ENTRY(SNAME) *entry = CMC_(PFX, _impl_new_entry)(result, tmp_key, tmp_val); + + CMC_(PFX, _impl_add_entry_to_key)(result, entry); + CMC_(PFX, _impl_add_entry_to_val)(result, entry); + + result->count++; + } + } + + _map_->flag = CMC_FLAG_OK; + + return result; +} + +bool CMC_(PFX, _equals)(struct SNAME *_map1_, struct SNAME *_map2_) +{ + _map1_->flag = CMC_FLAG_OK; + _map2_->flag = CMC_FLAG_OK; + + if (_map1_->count != _map2_->count) + return false; + + struct SNAME *_mapA_; + struct SNAME *_mapB_; + + if (_map1_->capacity < _map2_->capacity) + { + _mapA_ = _map1_; + _mapB_ = _map2_; + } + else + { + _mapA_ = _map2_; + _mapB_ = _map1_; + } + + for (size_t i = 0; i < _mapA_->capacity; i++) + { + struct CMC_DEF_ENTRY(SNAME) *scan = _mapA_->buffer[i][0]; + + if (scan && scan != CMC_ENTRY_DELETED) + { + struct CMC_DEF_ENTRY(SNAME) **entry_B = CMC_(PFX, _impl_get_entry_by_key)(_mapB_, scan->key); + + if (!entry_B) + return false; + + if (_mapA_->f_val->cmp((*entry_B)->value, scan->value) != 0) + return false; + } + } + + return true; +} + +static struct CMC_DEF_ENTRY(SNAME) * CMC_(PFX, _impl_new_entry)(struct SNAME *_map_, K key, V value) +{ + struct CMC_DEF_ENTRY(SNAME) *entry = _map_->alloc->malloc(sizeof(struct CMC_DEF_ENTRY(SNAME))); + + if (!entry) + return NULL; + + entry->key = key; + entry->value = value; + entry->dist[0] = 0; + entry->dist[1] = 0; + entry->ref[0] = NULL; + entry->ref[1] = NULL; + + return entry; +} + +static struct CMC_DEF_ENTRY(SNAME) * *CMC_(PFX, _impl_get_entry_by_key)(struct SNAME *_map_, K key) +{ + size_t hash = _map_->f_key->hash(key); + size_t pos = hash % _map_->capacity; + + struct CMC_DEF_ENTRY(SNAME) *target = _map_->buffer[pos][0]; + + while (target != NULL) + { + if (target != CMC_ENTRY_DELETED && _map_->f_key->cmp(target->key, key) == 0) + return &(_map_->buffer[pos % _map_->capacity][0]); + + pos++; + target = _map_->buffer[pos % _map_->capacity][0]; + } + + return NULL; +} + +static struct CMC_DEF_ENTRY(SNAME) * *CMC_(PFX, _impl_get_entry_by_val)(struct SNAME *_map_, V val) +{ + size_t hash = _map_->f_val->hash(val); + size_t pos = hash % _map_->capacity; + + struct CMC_DEF_ENTRY(SNAME) *target = _map_->buffer[pos][1]; + + while (target != NULL) + { + if (target != CMC_ENTRY_DELETED && _map_->f_val->cmp(target->value, val) == 0) + return &(_map_->buffer[pos % _map_->capacity][1]); + + pos++; + target = _map_->buffer[pos % _map_->capacity][1]; + } + + return NULL; +} + +static struct CMC_DEF_ENTRY(SNAME) * + *CMC_(PFX, _impl_add_entry_to_key)(struct SNAME *_map_, struct CMC_DEF_ENTRY(SNAME) * entry) +{ + struct CMC_DEF_ENTRY(SNAME) **to_return = NULL; + + size_t hash = _map_->f_key->hash(entry->key); + size_t original_pos = hash % _map_->capacity; + size_t pos = original_pos; + + struct CMC_DEF_ENTRY(SNAME) **scan = &(_map_->buffer[hash % _map_->capacity][0]); + + if (*scan == NULL) + { + *scan = entry; + + entry->ref[0] = scan; + + return scan; + } + else + { + while (true) + { + pos++; + scan = &(_map_->buffer[pos % _map_->capacity][0]); + + if (*scan == NULL || *scan == CMC_ENTRY_DELETED) + { + if (!to_return) + to_return = scan; + + *scan = entry; + entry->ref[0] = scan; + entry->dist[0] = pos - original_pos; + + return to_return; + } + else if ((*scan)->dist[0] < pos - original_pos) + { + if (!to_return) + to_return = scan; + + size_t tmp_dist = (*scan)->dist[0]; + entry->dist[0] = pos - original_pos; + original_pos = pos - tmp_dist; + + entry->ref[0] = scan; + + struct CMC_DEF_ENTRY(SNAME) *_tmp_ = *scan; + *scan = entry; + entry = _tmp_; + } + } + } + + return NULL; +} + +static struct CMC_DEF_ENTRY(SNAME) * + *CMC_(PFX, _impl_add_entry_to_val)(struct SNAME *_map_, struct CMC_DEF_ENTRY(SNAME) * entry) +{ + struct CMC_DEF_ENTRY(SNAME) **to_return = NULL; + + size_t hash = _map_->f_val->hash(entry->value); + size_t original_pos = hash % _map_->capacity; + size_t pos = original_pos; + + struct CMC_DEF_ENTRY(SNAME) **scan = &(_map_->buffer[hash % _map_->capacity][1]); + + if (*scan == NULL) + { + *scan = entry; + + entry->ref[1] = scan; + + return scan; + } + else + { + while (true) + { + pos++; + scan = &(_map_->buffer[pos % _map_->capacity][1]); + + if (*scan == NULL || *scan == CMC_ENTRY_DELETED) + { + if (!to_return) + to_return = scan; + + *scan = entry; + entry->ref[1] = scan; + entry->dist[1] = pos - original_pos; + + return to_return; + } + else if ((*scan)->dist[1] < pos - original_pos) + { + if (!to_return) + to_return = scan; + + size_t tmp_dist = (*scan)->dist[1]; + entry->dist[1] = pos - original_pos; + original_pos = pos - tmp_dist; + + entry->ref[1] = scan; + + struct CMC_DEF_ENTRY(SNAME) *_tmp_ = *scan; + *scan = entry; + entry = _tmp_; + } + } + } + + return NULL; +} + +static size_t CMC_(PFX, _impl_calculate_size)(size_t required) +{ + const size_t count = sizeof(cmc_hashtable_primes) / sizeof(cmc_hashtable_primes[0]); + + if (cmc_hashtable_primes[count - 1] < required) + return required; + + size_t i = 0; + while (cmc_hashtable_primes[i] < required) + i++; + + return cmc_hashtable_primes[i]; +} diff --git a/src/cmc/cmc/hashbidimap/ext/code.h b/src/cmc/cmc/hashbidimap/ext/code.h new file mode 100644 index 0000000..9ae2654 --- /dev/null +++ b/src/cmc/cmc/hashbidimap/ext/code.h @@ -0,0 +1,393 @@ +/** + * Copyright (c) 2019 Leonardo Vencovsky + * + * This file is part of the C Macro Collections Libray. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in 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: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * 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 + * AUTHORS OR COPYRIGHT HOLDERS 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 IN THE + * SOFTWARE. + */ + +/** + * INIT + * + * The part 'INIT' gives a new way of initializing a collection. The collection + * struct is not heap allocated, only its internal structure (nodes, buffers). + */ +#ifdef CMC_EXT_HASHBIDIMAP_INIT + +struct SNAME CMC_(PFX, _init)(size_t capacity, double load, struct CMC_DEF_FKEY(SNAME) * f_key, + struct CMC_DEF_FVAL(SNAME) * f_val) +{ + return CMC_(PFX, _init_custom)(capacity, load, f_key, f_val, NULL, NULL); +} + +struct SNAME CMC_(PFX, _init_custom)(size_t capacity, double load, struct CMC_DEF_FKEY(SNAME) * f_key, + struct CMC_DEF_FVAL(SNAME) * f_val, struct CMC_ALLOC_NODE_NAME *alloc, + struct CMC_CALLBACKS_NAME *callbacks) +{ + CMC_CALLBACKS_MAYBE_UNUSED(callbacks); + + struct SNAME _map_ = { 0 }; + + if (capacity == 0 || load <= 0 || load >= 1) + return _map_; + + /* Prevent integer overflow */ + if (capacity >= UINTMAX_MAX * load) + return _map_; + + if (!f_key || !f_val) + return _map_; + + size_t real_capacity = CMC_(PFX, _impl_calculate_size)(capacity / load); + + if (!alloc) + alloc = &cmc_alloc_node_default; + + _map_.buffer = alloc->calloc(real_capacity, sizeof(struct CMC_DEF_ENTRY(SNAME) *[2])); + + if (!_map_.buffer) + return _map_; + + _map_.count = 0; + _map_.capacity = real_capacity; + _map_.load = load; + _map_.flag = CMC_FLAG_OK; + _map_.f_key = f_key; + _map_.f_val = f_val; + _map_.flag = CMC_FLAG_OK; + _map_.alloc = alloc; + CMC_CALLBACKS_ASSIGN(&_map_, callbacks); + + return _map_; +} + +void CMC_(PFX, _release)(struct SNAME _map_) +{ + CMC_(PFX, _clear)(&_map_); + + _map_.alloc->free(_map_.buffer); +} + +#endif /* CMC_EXT_HASHBIDIMAP_INIT */ + +/** + * ITER + * + * Hashbidimap bi-directional iterator. + */ +#ifdef CMC_EXT_HASHBIDIMAP_ITER + +struct CMC_DEF_ITER(SNAME) * CMC_(PFX, _iter_new)(struct SNAME *target) +{ + struct CMC_DEF_ITER(SNAME) *iter = target->alloc->malloc(sizeof(struct CMC_DEF_ITER(SNAME))); + + if (!iter) + return NULL; + + CMC_(PFX, _iter_init)(iter, target); + + return iter; +} + +void CMC_(PFX, _iter_free)(struct CMC_DEF_ITER(SNAME) * iter) +{ + iter->target->alloc->free(iter); +} + +void CMC_(PFX, _iter_init)(struct CMC_DEF_ITER(SNAME) * iter, struct SNAME *target) +{ + memset(iter, 0, sizeof(struct CMC_DEF_ITER(SNAME))); + + iter->target = target; + iter->start = true; + iter->end = CMC_(PFX, _empty)(target); + + if (!CMC_(PFX, _empty)(target)) + { + for (size_t i = 0; i < target->capacity; i++) + { + struct CMC_DEF_ENTRY(SNAME) *entry = target->buffer[i][0]; + + if (entry && entry != CMC_ENTRY_DELETED) + { + iter->first = i; + break; + } + } + + iter->cursor = iter->first; + + for (size_t i = target->capacity; i > 0; i--) + { + struct CMC_DEF_ENTRY(SNAME) *entry = target->buffer[i - 1][0]; + + if (entry && entry != CMC_ENTRY_DELETED) + { + iter->last = i - 1; + break; + } + } + } +} + +bool CMC_(PFX, _iter_start)(struct CMC_DEF_ITER(SNAME) * iter) +{ + return CMC_(PFX, _empty)(iter->target) || iter->start; +} + +bool CMC_(PFX, _iter_end)(struct CMC_DEF_ITER(SNAME) * iter) +{ + return CMC_(PFX, _empty)(iter->target) || iter->end; +} + +void CMC_(PFX, _iter_to_start)(struct CMC_DEF_ITER(SNAME) * iter) +{ + if (!CMC_(PFX, _empty)(iter->target)) + { + iter->cursor = iter->first; + iter->index = 0; + iter->start = true; + iter->end = false; + } +} + +void CMC_(PFX, _iter_to_end)(struct CMC_DEF_ITER(SNAME) * iter) +{ + if (!CMC_(PFX, _empty)(iter->target)) + { + iter->cursor = iter->last; + iter->index = iter->target->count - 1; + iter->start = false; + iter->end = true; + } +} + +bool CMC_(PFX, _iter_next)(struct CMC_DEF_ITER(SNAME) * iter) +{ + if (iter->end) + return false; + + if (iter->index + 1 == iter->target->count) + { + iter->end = true; + return false; + } + + iter->start = CMC_(PFX, _empty)(iter->target); + + struct CMC_DEF_ENTRY(SNAME) *scan = iter->target->buffer[iter->cursor][0]; + + iter->index++; + + while (1) + { + iter->cursor++; + scan = iter->target->buffer[iter->cursor][0]; + + if (scan != NULL && scan != CMC_ENTRY_DELETED) + break; + } + + return true; +} + +bool CMC_(PFX, _iter_prev)(struct CMC_DEF_ITER(SNAME) * iter) +{ + if (iter->start) + return false; + + if (iter->index == 0) + { + iter->start = true; + return false; + } + + iter->end = CMC_(PFX, _empty)(iter->target); + + struct CMC_DEF_ENTRY(SNAME) *scan = iter->target->buffer[iter->cursor][0]; + + iter->index--; + + while (1) + { + iter->cursor--; + scan = iter->target->buffer[iter->cursor][0]; + + if (scan != NULL && scan != CMC_ENTRY_DELETED) + break; + } + + return true; +} + +/* Returns true only if the iterator moved */ +bool CMC_(PFX, _iter_advance)(struct CMC_DEF_ITER(SNAME) * iter, size_t steps) +{ + if (iter->end) + return false; + + if (iter->index + 1 == iter->target->count) + { + iter->end = true; + return false; + } + + if (steps == 0 || iter->index + steps >= iter->target->count) + return false; + + for (size_t i = 0; i < steps; i++) + CMC_(PFX, _iter_next)(iter); + + return true; +} + +/* Returns true only if the iterator moved */ +bool CMC_(PFX, _iter_rewind)(struct CMC_DEF_ITER(SNAME) * iter, size_t steps) +{ + if (iter->start) + return false; + + if (iter->index == 0) + { + iter->start = true; + return false; + } + + if (steps == 0 || iter->index < steps) + return false; + + for (size_t i = 0; i < steps; i++) + CMC_(PFX, _iter_prev)(iter); + + return true; +} + +/* Returns true only if the iterator was able to be positioned at */ +/* the given index */ +bool CMC_(PFX, _iter_go_to)(struct CMC_DEF_ITER(SNAME) * iter, size_t index) +{ + if (index >= iter->target->count) + return false; + + if (iter->index > index) + return CMC_(PFX, _iter_rewind)(iter, iter->index - index); + else if (iter->index < index) + return CMC_(PFX, _iter_advance)(iter, index - iter->index); + + return true; +} + +K CMC_(PFX, _iter_key)(struct CMC_DEF_ITER(SNAME) * iter) +{ + if (CMC_(PFX, _empty)(iter->target)) + { + iter->target->flag = CMC_FLAG_EMPTY; + return (K){ 0 }; + } + + return iter->target->buffer[iter->cursor][0]->key; +} + +V CMC_(PFX, _iter_value)(struct CMC_DEF_ITER(SNAME) * iter) +{ + if (CMC_(PFX, _empty)(iter->target)) + { + iter->target->flag = CMC_FLAG_EMPTY; + return (V){ 0 }; + } + + return iter->target->buffer[iter->cursor][0]->value; +} + +size_t CMC_(PFX, _iter_index)(struct CMC_DEF_ITER(SNAME) * iter) +{ + return iter->index; +} + +#endif /* CMC_EXT_HASHBIDIMAP_ITER */ + +/** + * STR + * + * Print helper functions. + */ +#ifdef CMC_EXT_HASHBIDIMAP_STR + +bool CMC_(PFX, _to_string)(struct SNAME *_map_, FILE *fptr) +{ + struct SNAME *m_ = _map_; + + return 0 <= fprintf(fptr, + "struct %s<%s, %s> " + "at %p { " + "buffer:%p, " + "capacity:%" PRIuMAX ", " + "count:%" PRIuMAX ", " + "load:%lf, " + "flag:%d, " + "f_key:%p, " + "f_val:%p, " + "alloc:%p, " + "callbacks:%p }", + CMC_TO_STRING(SNAME), CMC_TO_STRING(K), CMC_TO_STRING(V), m_, m_->buffer, m_->capacity, + m_->count, m_->load, m_->flag, m_->f_key, m_->f_val, m_->alloc, CMC_CALLBACKS_GET(m_)); +} + +bool CMC_(PFX, _print)(struct SNAME *_map_, FILE *fptr, const char *start, const char *separator, const char *end, + const char *key_val_sep) +{ + fprintf(fptr, "%s", start); + + size_t last = 0; + for (size_t i = _map_->capacity; i > 0; i--) + { + struct CMC_DEF_ENTRY(SNAME) *entry = _map_->buffer[i - 1][0]; + + if (entry && entry != CMC_ENTRY_DELETED) + { + last = i - 1; + break; + } + } + + for (size_t i = 0; i < _map_->capacity; i++) + { + struct CMC_DEF_ENTRY(SNAME) *entry = _map_->buffer[i][0]; + + if (entry && entry != CMC_ENTRY_DELETED) + { + if (!_map_->f_key->str(fptr, entry->key)) + return false; + + fprintf(fptr, "%s", key_val_sep); + + if (!_map_->f_val->str(fptr, entry->value)) + return false; + + if (i + 1 < last) + fprintf(fptr, "%s", separator); + } + } + + fprintf(fptr, "%s", end); + + return true; +} + +#endif /* CMC_EXT_HASHBIDIMAP_STR */ diff --git a/src/cmc/cmc/hashbidimap/ext/header.h b/src/cmc/cmc/hashbidimap/ext/header.h new file mode 100644 index 0000000..f924208 --- /dev/null +++ b/src/cmc/cmc/hashbidimap/ext/header.h @@ -0,0 +1,86 @@ +/** + * Copyright (c) 2019 Leonardo Vencovsky + * + * This file is part of the C Macro Collections Libray. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in 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: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * 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 + * AUTHORS OR COPYRIGHT HOLDERS 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 IN THE + * SOFTWARE. + */ + +/** + * INIT + * + * The part 'INIT' gives a new way of initializing a collection. The collection + * struct is not heap allocated, only its internal structure (nodes, buffers). + */ +#ifdef CMC_EXT_HASHBIDIMAP_INIT + +struct SNAME CMC_(PFX, _init)(size_t capacity, double load, struct CMC_DEF_FKEY(SNAME) * f_key, + struct CMC_DEF_FVAL(SNAME) * f_val); +struct SNAME CMC_(PFX, _init_custom)(size_t capacity, double load, struct CMC_DEF_FKEY(SNAME) * f_key, + struct CMC_DEF_FVAL(SNAME) * f_val, struct CMC_ALLOC_NODE_NAME *alloc, + struct CMC_CALLBACKS_NAME *callbacks); +void CMC_(PFX, _release)(struct SNAME _map_); + +#endif /* CMC_EXT_HASHBIDIMAP_INIT */ + +/** + * ITER + * + * Hashbidimap bi-directional iterator. + */ +#ifdef CMC_EXT_HASHBIDIMAP_ITER + +/* Iterator Functions */ +/* Iterator Allocation and Deallocation */ +struct CMC_DEF_ITER(SNAME) * CMC_(PFX, _iter_new)(struct SNAME *target); +void CMC_(PFX, _iter_free)(struct CMC_DEF_ITER(SNAME) * iter); +/* Iterator Initialization */ +void CMC_(PFX, _iter_init)(struct CMC_DEF_ITER(SNAME) * iter, struct SNAME *target); +/* Iterator State */ +bool CMC_(PFX, _iter_start)(struct CMC_DEF_ITER(SNAME) * iter); +bool CMC_(PFX, _iter_end)(struct CMC_DEF_ITER(SNAME) * iter); +/* Iterator Movement */ +void CMC_(PFX, _iter_to_start)(struct CMC_DEF_ITER(SNAME) * iter); +void CMC_(PFX, _iter_to_end)(struct CMC_DEF_ITER(SNAME) * iter); +bool CMC_(PFX, _iter_next)(struct CMC_DEF_ITER(SNAME) * iter); +bool CMC_(PFX, _iter_prev)(struct CMC_DEF_ITER(SNAME) * iter); +bool CMC_(PFX, _iter_advance)(struct CMC_DEF_ITER(SNAME) * iter, size_t steps); +bool CMC_(PFX, _iter_rewind)(struct CMC_DEF_ITER(SNAME) * iter, size_t steps); +bool CMC_(PFX, _iter_go_to)(struct CMC_DEF_ITER(SNAME) * iter, size_t index); +/* Iterator Access */ +K CMC_(PFX, _iter_key)(struct CMC_DEF_ITER(SNAME) * iter); +V CMC_(PFX, _iter_value)(struct CMC_DEF_ITER(SNAME) * iter); +size_t CMC_(PFX, _iter_index)(struct CMC_DEF_ITER(SNAME) * iter); + +#endif /* CMC_EXT_HASHBIDIMAP_ITER */ + +/** + * STR + * + * Print helper functions. + */ +#ifdef CMC_EXT_HASHBIDIMAP_STR + +/* Debug prints the struct to fptr */ +bool CMC_(PFX, _to_string)(struct SNAME *_map_, FILE *fptr); +/* Prints each item in the hashbidimap as an array */ +bool CMC_(PFX, _print)(struct SNAME *_map_, FILE *fptr, const char *start, const char *separator, const char *end, + const char *key_val_sep); + +#endif /* CMC_EXT_HASHBIDIMAP_STR */ diff --git a/src/cmc/cmc/hashbidimap/ext/struct.h b/src/cmc/cmc/hashbidimap/ext/struct.h new file mode 100644 index 0000000..557347e --- /dev/null +++ b/src/cmc/cmc/hashbidimap/ext/struct.h @@ -0,0 +1,51 @@ +/** + * Copyright (c) 2019 Leonardo Vencovsky + * + * This file is part of the C Macro Collections Libray. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in 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: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * 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 + * AUTHORS OR COPYRIGHT HOLDERS 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 IN THE + * SOFTWARE. + */ + +/** + * ITER + * + * Hashbidimap bi-directional iterator. + */ +#ifdef CMC_EXT_HASHBIDIMAP_ITER + +/* HashBidiMap Iterator */ +struct CMC_DEF_ITER(SNAME) +{ + /* Target hashbidimap */ + struct SNAME *target; + /* Cursor's position (index) */ + size_t cursor; + /* Keeps track of relative index to the iteration of elements */ + size_t index; + /* The index of the first element */ + size_t first; + /* The index of the last element */ + size_t last; + /* If the iterator has reached the start of the iteration */ + bool start; + /* If the iterator has reached the end of the iteration */ + bool end; +}; + +#endif /* CMC_EXT_HASHBIDIMAP_ITER */ diff --git a/src/cmc/cmc/hashbidimap/header.h b/src/cmc/cmc/hashbidimap/header.h new file mode 100644 index 0000000..db01c02 --- /dev/null +++ b/src/cmc/cmc/hashbidimap/header.h @@ -0,0 +1,91 @@ +/** + * Copyright (c) 2019 Leonardo Vencovsky + * + * This file is part of the C Macro Collections Libray. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in 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: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * 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 + * AUTHORS OR COPYRIGHT HOLDERS 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 IN THE + * SOFTWARE. + */ + +/* Key struct function table */ +struct CMC_DEF_FKEY(SNAME) +{ + /* Comparator function */ + CMC_DEF_FTAB_CMP(K); + /* Copy function */ + CMC_DEF_FTAB_CPY(K); + /* To string function */ + CMC_DEF_FTAB_STR(K); + /* Free from memory function */ + CMC_DEF_FTAB_FREE(K); + /* Hash function */ + CMC_DEF_FTAB_HASH(K); + /* Priority function */ + CMC_DEF_FTAB_PRI(K); +}; + +/* Value struct function table */ +struct CMC_DEF_FVAL(SNAME) +{ + /* Comparator function */ + CMC_DEF_FTAB_CMP(V); + /* Copy function */ + CMC_DEF_FTAB_CPY(V); + /* To string function */ + CMC_DEF_FTAB_STR(V); + /* Free from memory function */ + CMC_DEF_FTAB_FREE(V); + /* Hash function */ + CMC_DEF_FTAB_HASH(V); + /* Priority function */ + CMC_DEF_FTAB_PRI(V); +}; + +/* Collection Allocation and Deallocation */ +struct SNAME *CMC_(PFX, _new)(size_t capacity, double load, struct CMC_DEF_FKEY(SNAME) * f_key, + struct CMC_DEF_FVAL(SNAME) * f_val); +struct SNAME *CMC_(PFX, _new_custom)(size_t capacity, double load, struct CMC_DEF_FKEY(SNAME) * f_key, + struct CMC_DEF_FVAL(SNAME) * f_val, struct CMC_ALLOC_NODE_NAME *alloc, + struct CMC_CALLBACKS_NAME *callbacks); +void CMC_(PFX, _clear)(struct SNAME *_map_); +void CMC_(PFX, _free)(struct SNAME *_map_); +/* Customization of Allocation and Callbacks */ +void CMC_(PFX, _customize)(struct SNAME *_map_, struct CMC_ALLOC_NODE_NAME *alloc, + struct CMC_CALLBACKS_NAME *callbacks); +/* Collection Input and Output */ +bool CMC_(PFX, _insert)(struct SNAME *_map_, K key, V value); +bool CMC_(PFX, _update_key)(struct SNAME *_map_, V val, K new_key); +bool CMC_(PFX, _update_val)(struct SNAME *_map_, K key, V new_val); +bool CMC_(PFX, _remove_by_key)(struct SNAME *_map_, K key, K *out_key, V *out_val); +bool CMC_(PFX, _remove_by_val)(struct SNAME *_map_, V val, K *out_key, V *out_val); +/* Element Access */ +K CMC_(PFX, _get_key)(struct SNAME *_map_, V val); +V CMC_(PFX, _get_val)(struct SNAME *_map_, K key); +/* Collection State */ +bool CMC_(PFX, _contains_key)(struct SNAME *_map_, K key); +bool CMC_(PFX, _contains_val)(struct SNAME *_map_, V val); +bool CMC_(PFX, _empty)(struct SNAME *_map_); +bool CMC_(PFX, _full)(struct SNAME *_map_); +size_t CMC_(PFX, _count)(struct SNAME *_map_); +size_t CMC_(PFX, _capacity)(struct SNAME *_map_); +double CMC_(PFX, _load)(struct SNAME *_map_); +int CMC_(PFX, _flag)(struct SNAME *_map_); +/* Collection Utility */ +bool CMC_(PFX, _resize)(struct SNAME *_map_, size_t capacity); +struct SNAME *CMC_(PFX, _copy_of)(struct SNAME *_map_); +bool CMC_(PFX, _equals)(struct SNAME *_map1_, struct SNAME *_map2_); diff --git a/src/cmc/cmc/hashbidimap/struct.h b/src/cmc/cmc/hashbidimap/struct.h new file mode 100644 index 0000000..da38526 --- /dev/null +++ b/src/cmc/cmc/hashbidimap/struct.h @@ -0,0 +1,69 @@ +/** + * Copyright (c) 2019 Leonardo Vencovsky + * + * This file is part of the C Macro Collections Libray. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in 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: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * 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 + * AUTHORS OR COPYRIGHT HOLDERS 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 IN THE + * SOFTWARE. + */ + +struct SNAME +{ + /* Array 0 is K -> V and array 1 is V -> K */ + struct CMC_DEF_ENTRY(SNAME) * (*buffer)[2]; + /* Current arrays capacity */ + size_t capacity; + /* Current amount of keys */ + size_t count; + /* Load factor in range (0.0, 1.0) */ + double load; + /* Flags indicating errors or success */ + int flag; + /* Key function table */ + struct CMC_DEF_FKEY(SNAME) * f_key; + /* Value function table */ + struct CMC_DEF_FVAL(SNAME) * f_val; + /* Custom allocation functions */ + struct CMC_ALLOC_NODE_NAME *alloc; + /* Custom callback functions */ + CMC_CALLBACKS_DECL; + /* Methods */ + /* Returns an iterator to the start of the hashbidimap */ + struct CMC_DEF_ITER(SNAME) (*it_start)(struct SNAME *); + /* Returns an iterator to the end of the hashbidimap */ + struct CMC_DEF_ITER(SNAME) (*it_end)(struct SNAME *); +}; + +/* HashBidiMap Entry */ +struct CMC_DEF_ENTRY(SNAME) +{ + /* Entry Key */ + K key; + /* Entry Value */ + V value; + /* The distance of this node to its original position */ + /* dist[0] is relative to K -> V */ + /* dist[1] is relative to V -> K */ + size_t dist[2]; + /* References to this node in the hashbidimap buffer. Used to */ + /* prevent searching for this node twice for update() and */ + /* remove(). Increases memory overhead but reduces execution time */ + /* ref[0] is relative to K -> V */ + /* ref[1] is relative to V -> K */ + struct CMC_DEF_ENTRY(SNAME) * *ref[2]; +}; diff --git a/src/cmc/cmc/hashmap/code.h b/src/cmc/cmc/hashmap/code.h new file mode 100644 index 0000000..515bff0 --- /dev/null +++ b/src/cmc/cmc/hashmap/code.h @@ -0,0 +1,636 @@ +/** + * Copyright (c) 2019 Leonardo Vencovsky + * + * This file is part of the C Macro Collections Libray. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in 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: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * 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 + * AUTHORS OR COPYRIGHT HOLDERS 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 IN THE + * SOFTWARE. + */ + +/* Implementation Detail Functions */ +static struct CMC_DEF_ENTRY(SNAME) * CMC_(PFX, _impl_get_entry)(struct SNAME *_map_, K key); +static size_t CMC_(PFX, _impl_calculate_size)(size_t required); + +struct SNAME *CMC_(PFX, _new)(size_t capacity, double load, struct CMC_DEF_FKEY(SNAME) * f_key, + struct CMC_DEF_FVAL(SNAME) * f_val) +{ + return CMC_(PFX, _new_custom)(capacity, load, f_key, f_val, NULL, NULL); +} + +struct SNAME *CMC_(PFX, _new_custom)(size_t capacity, double load, struct CMC_DEF_FKEY(SNAME) * f_key, + struct CMC_DEF_FVAL(SNAME) * f_val, struct CMC_ALLOC_NODE_NAME *alloc, + struct CMC_CALLBACKS_NAME *callbacks) +{ + CMC_CALLBACKS_MAYBE_UNUSED(callbacks); + + if (capacity == 0 || load <= 0 || load >= 1) + return NULL; + + /* Prevent integer overflow */ + if (capacity >= UINTMAX_MAX * load) + return NULL; + + if (!f_key || !f_val) + return NULL; + + size_t real_capacity = CMC_(PFX, _impl_calculate_size)(capacity / load); + + if (!alloc) + alloc = &cmc_alloc_node_default; + + struct SNAME *_map_ = alloc->malloc(sizeof(struct SNAME)); + + if (!_map_) + return NULL; + + _map_->buffer = alloc->calloc(real_capacity, sizeof(struct CMC_DEF_ENTRY(SNAME))); + + if (!_map_->buffer) + { + alloc->free(_map_); + return NULL; + } + + _map_->count = 0; + _map_->capacity = real_capacity; + _map_->load = load; + _map_->flag = CMC_FLAG_OK; + _map_->f_key = f_key; + _map_->f_val = f_val; + _map_->alloc = alloc; + CMC_CALLBACKS_ASSIGN(_map_, callbacks); + + return _map_; +} + +void CMC_(PFX, _clear)(struct SNAME *_map_) +{ + if (_map_->f_key->free || _map_->f_val->free) + { + for (size_t i = 0; i < _map_->capacity; i++) + { + struct CMC_DEF_ENTRY(SNAME) *entry = &(_map_->buffer[i]); + + if (entry->state == CMC_ES_FILLED) + { + if (_map_->f_key->free) + _map_->f_key->free(entry->key); + if (_map_->f_val->free) + _map_->f_val->free(entry->value); + } + } + } + + memset(_map_->buffer, 0, sizeof(struct CMC_DEF_ENTRY(SNAME)) * _map_->capacity); + + _map_->count = 0; + _map_->flag = CMC_FLAG_OK; +} + +void CMC_(PFX, _free)(struct SNAME *_map_) +{ + if (_map_->f_key->free || _map_->f_val->free) + { + for (size_t i = 0; i < _map_->capacity; i++) + { + struct CMC_DEF_ENTRY(SNAME) *entry = &(_map_->buffer[i]); + + if (entry->state == CMC_ES_FILLED) + { + if (_map_->f_key->free) + _map_->f_key->free(entry->key); + if (_map_->f_val->free) + _map_->f_val->free(entry->value); + } + } + } + + _map_->alloc->free(_map_->buffer); + _map_->alloc->free(_map_); +} + +void CMC_(PFX, _customize)(struct SNAME *_map_, struct CMC_ALLOC_NODE_NAME *alloc, struct CMC_CALLBACKS_NAME *callbacks) +{ + CMC_CALLBACKS_MAYBE_UNUSED(callbacks); + + if (!alloc) + _map_->alloc = &cmc_alloc_node_default; + else + _map_->alloc = alloc; + + CMC_CALLBACKS_ASSIGN(_map_, callbacks); + + _map_->flag = CMC_FLAG_OK; +} + +bool CMC_(PFX, _insert)(struct SNAME *_map_, K key, V value) +{ + if (CMC_(PFX, _full)(_map_)) + { + if (!CMC_(PFX, _resize)(_map_, _map_->capacity + 1)) + return false; + } + + if (CMC_(PFX, _impl_get_entry)(_map_, key) != NULL) + { + _map_->flag = CMC_FLAG_DUPLICATE; + return false; + } + + size_t hash = _map_->f_key->hash(key); + size_t original_pos = hash % _map_->capacity; + size_t pos = original_pos; + + struct CMC_DEF_ENTRY(SNAME) *target = &(_map_->buffer[pos]); + + if (target->state == CMC_ES_EMPTY || target->state == CMC_ES_DELETED) + { + target->key = key; + target->value = value; + target->dist = 0; + target->state = CMC_ES_FILLED; + } + else + { + while (true) + { + pos++; + target = &(_map_->buffer[pos % _map_->capacity]); + + if (target->state == CMC_ES_EMPTY || target->state == CMC_ES_DELETED) + { + target->key = key; + target->value = value; + target->dist = pos - original_pos; + target->state = CMC_ES_FILLED; + + break; + } + else if (target->dist < pos - original_pos) + { + K tmp_k = target->key; + V tmp_v = target->value; + size_t tmp_dist = target->dist; + + target->key = key; + target->value = value; + target->dist = pos - original_pos; + + key = tmp_k; + value = tmp_v; + original_pos = pos - tmp_dist; + } + } + } + + _map_->count++; + _map_->flag = CMC_FLAG_OK; + + CMC_CALLBACKS_CALL(_map_, create); + + return true; +} + +bool CMC_(PFX, _update)(struct SNAME *_map_, K key, V new_value, V *old_value) +{ + if (CMC_(PFX, _empty)(_map_)) + { + _map_->flag = CMC_FLAG_EMPTY; + return false; + } + + struct CMC_DEF_ENTRY(SNAME) *entry = CMC_(PFX, _impl_get_entry)(_map_, key); + + if (!entry) + { + _map_->flag = CMC_FLAG_NOT_FOUND; + return false; + } + + if (old_value) + *old_value = entry->value; + + entry->value = new_value; + + _map_->flag = CMC_FLAG_OK; + + CMC_CALLBACKS_CALL(_map_, update); + + return true; +} + +bool CMC_(PFX, _remove)(struct SNAME *_map_, K key, V *out_value) +{ + if (CMC_(PFX, _empty)(_map_)) + { + _map_->flag = CMC_FLAG_EMPTY; + return false; + } + + struct CMC_DEF_ENTRY(SNAME) *result = CMC_(PFX, _impl_get_entry)(_map_, key); + + if (result == NULL) + { + _map_->flag = CMC_FLAG_NOT_FOUND; + return false; + } + + if (out_value) + *out_value = result->value; + + result->key = (K){ 0 }; + result->value = (V){ 0 }; + result->dist = 0; + result->state = CMC_ES_DELETED; + + _map_->count--; + _map_->flag = CMC_FLAG_OK; + + CMC_CALLBACKS_CALL(_map_, delete); + + return true; +} + +bool CMC_(PFX, _max)(struct SNAME *_map_, K *key, V *value) +{ + if (CMC_(PFX, _empty)(_map_)) + { + _map_->flag = CMC_FLAG_EMPTY; + return false; + } + + bool first = true; + K max_key = (K){ 0 }; + V max_val = (V){ 0 }; + + for (size_t i = 0; i < _map_->capacity; i++) + { + if (_map_->buffer[i].state == CMC_ES_FILLED) + { + if (first) + { + max_key = _map_->buffer[i].key; + max_val = _map_->buffer[i].value; + first = false; + } + else if (_map_->f_key->cmp(_map_->buffer[i].key, max_key) > 0) + { + max_key = _map_->buffer[i].key; + max_val = _map_->buffer[i].value; + } + } + } + + if (key) + *key = max_key; + if (value) + *value = max_val; + + _map_->flag = CMC_FLAG_OK; + + CMC_CALLBACKS_CALL(_map_, read); + + return true; +} + +bool CMC_(PFX, _min)(struct SNAME *_map_, K *key, V *value) +{ + if (CMC_(PFX, _empty)(_map_)) + { + _map_->flag = CMC_FLAG_EMPTY; + return false; + } + + bool first = true; + K min_key = (K){ 0 }; + V min_val = (V){ 0 }; + + for (size_t i = 0; i < _map_->capacity; i++) + { + if (_map_->buffer[i].state == CMC_ES_FILLED) + { + if (first) + { + min_key = _map_->buffer[i].key; + min_val = _map_->buffer[i].value; + first = false; + } + else if (_map_->f_key->cmp(_map_->buffer[i].key, min_key) < 0) + { + min_key = _map_->buffer[i].key; + min_val = _map_->buffer[i].value; + } + } + } + + if (key) + *key = min_key; + if (value) + *value = min_val; + + _map_->flag = CMC_FLAG_OK; + + CMC_CALLBACKS_CALL(_map_, read); + + return true; +} + +V CMC_(PFX, _get)(struct SNAME *_map_, K key) +{ + if (CMC_(PFX, _empty)(_map_)) + { + _map_->flag = CMC_FLAG_EMPTY; + return (V){ 0 }; + } + + struct CMC_DEF_ENTRY(SNAME) *entry = CMC_(PFX, _impl_get_entry)(_map_, key); + + if (!entry) + { + _map_->flag = CMC_FLAG_NOT_FOUND; + return (V){ 0 }; + } + + _map_->flag = CMC_FLAG_OK; + + CMC_CALLBACKS_CALL(_map_, read); + + return entry->value; +} + +V *CMC_(PFX, _get_ref)(struct SNAME *_map_, K key) +{ + if (CMC_(PFX, _empty)(_map_)) + { + _map_->flag = CMC_FLAG_EMPTY; + return NULL; + } + + struct CMC_DEF_ENTRY(SNAME) *entry = CMC_(PFX, _impl_get_entry)(_map_, key); + + if (!entry) + { + _map_->flag = CMC_FLAG_NOT_FOUND; + return NULL; + } + + _map_->flag = CMC_FLAG_OK; + + CMC_CALLBACKS_CALL(_map_, read); + + return &(entry->value); +} + +bool CMC_(PFX, _contains)(struct SNAME *_map_, K key) +{ + _map_->flag = CMC_FLAG_OK; + + bool result = CMC_(PFX, _impl_get_entry)(_map_, key) != NULL; + + CMC_CALLBACKS_CALL(_map_, read); + + return result; +} + +bool CMC_(PFX, _empty)(struct SNAME *_map_) +{ + return _map_->count == 0; +} + +bool CMC_(PFX, _full)(struct SNAME *_map_) +{ + return (double)_map_->capacity * _map_->load <= (double)_map_->count; +} + +size_t CMC_(PFX, _count)(struct SNAME *_map_) +{ + return _map_->count; +} + +size_t CMC_(PFX, _capacity)(struct SNAME *_map_) +{ + return _map_->capacity; +} + +double CMC_(PFX, _load)(struct SNAME *_map_) +{ + return _map_->load; +} + +int CMC_(PFX, _flag)(struct SNAME *_map_) +{ + return _map_->flag; +} + +bool CMC_(PFX, _resize)(struct SNAME *_map_, size_t capacity) +{ + _map_->flag = CMC_FLAG_OK; + + if (_map_->capacity == capacity) + goto success; + + if (_map_->capacity > capacity / _map_->load) + goto success; + + /* Prevent integer overflow */ + if (capacity >= UINTMAX_MAX * _map_->load) + { + _map_->flag = CMC_FLAG_ERROR; + return false; + } + + /* Calculate required capacity based on the prime numbers */ + size_t theoretical_size = CMC_(PFX, _impl_calculate_size)(capacity); + + /* Not possible to shrink with current available prime numbers */ + if (theoretical_size < _map_->count / _map_->load) + { + _map_->flag = CMC_FLAG_INVALID; + return false; + } + + /* No callbacks since _new_map_ is just a temporary hashtable */ + struct SNAME *_new_map_ = + CMC_(PFX, _new_custom)(capacity, _map_->load, _map_->f_key, _map_->f_val, _map_->alloc, NULL); + + if (!_new_map_) + { + _map_->flag = CMC_FLAG_ALLOC; + return false; + } + + for (size_t i = 0; i < _map_->capacity; i++) + { + if (_map_->buffer[i].state == CMC_ES_FILLED) + { + K key = _map_->buffer[i].key; + V value = _map_->buffer[i].value; + + /* TODO check this for possible errors */ + CMC_(PFX, _insert)(_new_map_, key, value); + } + } + + /* Unlikely */ + if (_map_->count != _new_map_->count) + { + CMC_(PFX, _free)(_new_map_); + + _map_->flag = CMC_FLAG_ERROR; + return false; + } + + struct CMC_DEF_ENTRY(SNAME) *tmp_b = _map_->buffer; + _map_->buffer = _new_map_->buffer; + _new_map_->buffer = tmp_b; + + size_t tmp_c = _map_->capacity; + _map_->capacity = _new_map_->capacity; + _new_map_->capacity = tmp_c; + + /* Prevent the map from freeing the data */ + _new_map_->f_key = &(struct CMC_DEF_FKEY(SNAME)){ NULL }; + _new_map_->f_val = &(struct CMC_DEF_FVAL(SNAME)){ NULL }; + + CMC_(PFX, _free)(_new_map_); + +success: + + CMC_CALLBACKS_CALL(_map_, resize); + + return true; +} + +struct SNAME *CMC_(PFX, _copy_of)(struct SNAME *_map_) +{ + struct SNAME *result = CMC_(PFX, _new_custom)(_map_->capacity * _map_->load, _map_->load, _map_->f_key, + _map_->f_val, _map_->alloc, NULL); + + if (!result) + { + _map_->flag = CMC_FLAG_ERROR; + return NULL; + } + + CMC_CALLBACKS_ASSIGN(result, _map_->callbacks); + + if (_map_->f_key->cpy || _map_->f_val->cpy) + { + for (size_t i = 0; i < _map_->capacity; i++) + { + struct CMC_DEF_ENTRY(SNAME) *scan = &(_map_->buffer[i]); + + if (scan->state != CMC_ES_EMPTY) + { + struct CMC_DEF_ENTRY(SNAME) *target = &(result->buffer[i]); + + if (scan->state == CMC_ES_DELETED) + target->state = CMC_ES_DELETED; + else + { + target->state = scan->state; + target->dist = scan->dist; + + if (_map_->f_key->cpy) + target->key = _map_->f_key->cpy(scan->key); + else + target->key = scan->key; + + if (_map_->f_val->cpy) + target->value = _map_->f_val->cpy(scan->value); + else + target->value = scan->value; + } + } + } + } + else + memcpy(result->buffer, _map_->buffer, sizeof(struct CMC_DEF_ENTRY(SNAME)) * _map_->capacity); + + result->count = _map_->count; + + _map_->flag = CMC_FLAG_OK; + + return result; +} + +bool CMC_(PFX, _equals)(struct SNAME *_map1_, struct SNAME *_map2_) +{ + _map1_->flag = CMC_FLAG_OK; + _map2_->flag = CMC_FLAG_OK; + + if (_map1_->count != _map2_->count) + return false; + + /* Optimize loop using the smallest hashtable */ + struct SNAME *_map_a_; + struct SNAME *_map_b_; + + _map_a_ = _map1_->capacity < _map2_->capacity ? _map1_ : _map2_; + _map_b_ = _map_a_ == _map1_ ? _map2_ : _map1_; + + for (size_t i = 0; i < _map_a_->capacity; i++) + { + if (_map_a_->buffer[i].state == CMC_ES_FILLED) + { + struct CMC_DEF_ENTRY(SNAME) *entry_a = &(_map_a_->buffer[i]); + struct CMC_DEF_ENTRY(SNAME) *entry_b = CMC_(PFX, _impl_get_entry)(_map_b_, entry_a->key); + + if (!entry_b) + return false; + + if (_map_a_->f_val->cmp(entry_a->value, entry_b->value) != 0) + return false; + } + } + + return true; +} + +static struct CMC_DEF_ENTRY(SNAME) * CMC_(PFX, _impl_get_entry)(struct SNAME *_map_, K key) +{ + size_t hash = _map_->f_key->hash(key); + size_t pos = hash % _map_->capacity; + + struct CMC_DEF_ENTRY(SNAME) *target = &(_map_->buffer[pos]); + + while (target->state == CMC_ES_FILLED || target->state == CMC_ES_DELETED) + { + if (target->state != CMC_ES_DELETED && _map_->f_key->cmp(target->key, key) == 0) + return target; + + pos++; + target = &(_map_->buffer[pos % _map_->capacity]); + } + + return NULL; +} + +static size_t CMC_(PFX, _impl_calculate_size)(size_t required) +{ + const size_t count = sizeof(cmc_hashtable_primes) / sizeof(cmc_hashtable_primes[0]); + + if (cmc_hashtable_primes[count - 1] < required) + return required; + + size_t i = 0; + while (cmc_hashtable_primes[i] < required) + i++; + + return cmc_hashtable_primes[i]; +} diff --git a/src/cmc/cmc/hashmap/ext/code.h b/src/cmc/cmc/hashmap/ext/code.h new file mode 100644 index 0000000..c3f7efb --- /dev/null +++ b/src/cmc/cmc/hashmap/ext/code.h @@ -0,0 +1,438 @@ +/** + * Copyright (c) 2019 Leonardo Vencovsky + * + * This file is part of the C Macro Collections Libray. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in 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: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * 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 + * AUTHORS OR COPYRIGHT HOLDERS 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 IN THE + * SOFTWARE. + */ + +/** + * INIT + * + * The part 'INIT' gives a new way of initializing a collection. The collection + * struct is not heap allocated, only its internal structure (nodes, buffers). + */ +#ifdef CMC_EXT_HASHMAP_INIT + +struct SNAME CMC_(PFX, _init)(size_t capacity, double load, struct CMC_DEF_FKEY(SNAME) * f_key, + struct CMC_DEF_FVAL(SNAME) * f_val) +{ + return CMC_(PFX, _init_custom)(capacity, load, f_key, f_val, NULL, NULL); +} + +struct SNAME CMC_(PFX, _init_custom)(size_t capacity, double load, struct CMC_DEF_FKEY(SNAME) * f_key, + struct CMC_DEF_FVAL(SNAME) * f_val, struct CMC_ALLOC_NODE_NAME *alloc, + struct CMC_CALLBACKS_NAME *callbacks) +{ + CMC_CALLBACKS_MAYBE_UNUSED(callbacks); + + struct SNAME _map_ = { 0 }; + + if (capacity == 0 || load <= 0 || load >= 1) + return _map_; + + /* Prevent integer overflow */ + if (capacity >= UINTMAX_MAX * load) + return _map_; + + if (!f_key || !f_val) + return _map_; + + size_t real_capacity = CMC_(PFX, _impl_calculate_size)(capacity / load); + + if (!alloc) + alloc = &cmc_alloc_node_default; + + _map_.buffer = alloc->calloc(real_capacity, sizeof(struct CMC_DEF_ENTRY(SNAME))); + + if (!_map_.buffer) + return _map_; + + _map_.count = 0; + _map_.capacity = real_capacity; + _map_.load = load; + _map_.flag = CMC_FLAG_OK; + _map_.f_key = f_key; + _map_.f_val = f_val; + _map_.alloc = alloc; + CMC_CALLBACKS_ASSIGN(&_map_, callbacks); + + return _map_; +} + +void CMC_(PFX, _release)(struct SNAME _map_) +{ + if (_map_.f_key->free || _map_.f_val->free) + { + for (size_t i = 0; i < _map_.capacity; i++) + { + struct CMC_DEF_ENTRY(SNAME) *entry = &(_map_.buffer[i]); + + if (entry->state == CMC_ES_FILLED) + { + if (_map_.f_key->free) + _map_.f_key->free(entry->key); + if (_map_.f_val->free) + _map_.f_val->free(entry->value); + } + } + } + + _map_.alloc->free(_map_.buffer); +} + +#endif /* CMC_EXT_HASHMAP_INIT */ + +/** + * ITER + * + * Hashmap bi-directional iterator. + */ +#ifdef CMC_EXT_HASHMAP_ITER + +struct CMC_DEF_ITER(SNAME) CMC_(PFX, _iter_start)(struct SNAME *target) +{ + struct CMC_DEF_ITER(SNAME) iter; + + iter.target = target; + iter.cursor = 0; + iter.index = 0; + iter.first = 0; + iter.last = 0; + iter.start = true; + iter.end = CMC_(PFX, _empty)(target); + + if (!CMC_(PFX, _empty)(target)) + { + for (size_t i = 0; i < target->capacity; i++) + { + if (target->buffer[i].state == CMC_ES_FILLED) + { + iter.first = i; + break; + } + } + + iter.cursor = iter.first; + + for (size_t i = target->capacity; i > 0; i--) + { + if (target->buffer[i - 1].state == CMC_ES_FILLED) + { + iter.last = i - 1; + break; + } + } + } + + return iter; +} + +struct CMC_DEF_ITER(SNAME) CMC_(PFX, _iter_end)(struct SNAME *target) +{ + struct CMC_DEF_ITER(SNAME) iter; + + iter.target = target; + iter.cursor = 0; + iter.index = 0; + iter.first = 0; + iter.last = 0; + iter.start = CMC_(PFX, _empty)(target); + iter.end = true; + + if (!CMC_(PFX, _empty)(target)) + { + for (size_t i = 0; i < target->capacity; i++) + { + if (target->buffer[i].state == CMC_ES_FILLED) + { + iter.first = i; + break; + } + } + + for (size_t i = target->capacity; i > 0; i--) + { + if (target->buffer[i - 1].state == CMC_ES_FILLED) + { + iter.last = i - 1; + break; + } + } + + iter.cursor = iter.last; + iter.index = target->count - 1; + } + + return iter; +} + +bool CMC_(PFX, _iter_at_start)(struct CMC_DEF_ITER(SNAME) * iter) +{ + return CMC_(PFX, _empty)(iter->target) || iter->start; +} + +bool CMC_(PFX, _iter_at_end)(struct CMC_DEF_ITER(SNAME) * iter) +{ + return CMC_(PFX, _empty)(iter->target) || iter->end; +} + +bool CMC_(PFX, _iter_to_start)(struct CMC_DEF_ITER(SNAME) * iter) +{ + if (!CMC_(PFX, _empty)(iter->target)) + { + iter->cursor = iter->first; + iter->index = 0; + iter->start = true; + iter->end = CMC_(PFX, _empty)(iter->target); + + return true; + } + + return false; +} + +bool CMC_(PFX, _iter_to_end)(struct CMC_DEF_ITER(SNAME) * iter) +{ + if (!CMC_(PFX, _empty)(iter->target)) + { + iter->cursor = iter->last; + iter->index = iter->target->count - 1; + iter->start = CMC_(PFX, _empty)(iter->target); + iter->end = true; + + return true; + } + + return false; +} + +bool CMC_(PFX, _iter_next)(struct CMC_DEF_ITER(SNAME) * iter) +{ + if (iter->end) + return false; + + if (iter->index + 1 == iter->target->count) + { + iter->end = true; + return false; + } + + iter->start = CMC_(PFX, _empty)(iter->target); + + struct CMC_DEF_ENTRY(SNAME) *scan = &(iter->target->buffer[iter->cursor]); + + iter->index++; + + while (1) + { + iter->cursor++; + scan = &(iter->target->buffer[iter->cursor]); + + if (scan->state == CMC_ES_FILLED) + break; + } + + return true; +} + +bool CMC_(PFX, _iter_prev)(struct CMC_DEF_ITER(SNAME) * iter) +{ + if (iter->start) + return false; + + if (iter->index == 0) + { + iter->start = true; + return false; + } + + iter->end = CMC_(PFX, _empty)(iter->target); + + struct CMC_DEF_ENTRY(SNAME) *scan = &(iter->target->buffer[iter->cursor]); + + iter->index--; + + while (1) + { + iter->cursor--; + scan = &(iter->target->buffer[iter->cursor]); + + if (scan->state == CMC_ES_FILLED) + break; + } + + return true; +} + +/* Returns true only if the iterator moved */ +bool CMC_(PFX, _iter_advance)(struct CMC_DEF_ITER(SNAME) * iter, size_t steps) +{ + if (iter->end) + return false; + + if (iter->index + 1 == iter->target->count) + { + iter->end = true; + return false; + } + + if (steps == 0 || iter->index + steps >= iter->target->count) + return false; + + for (size_t i = 0; i < steps; i++) + CMC_(PFX, _iter_next)(iter); + + return true; +} + +/* Returns true only if the iterator moved */ +bool CMC_(PFX, _iter_rewind)(struct CMC_DEF_ITER(SNAME) * iter, size_t steps) +{ + if (iter->start) + return false; + + if (iter->index == 0) + { + iter->start = true; + return false; + } + + if (steps == 0 || iter->index < steps) + return false; + + for (size_t i = 0; i < steps; i++) + CMC_(PFX, _iter_prev)(iter); + + return true; +} + +/* Returns true only if the iterator was able to be positioned at the */ +/* given index */ +bool CMC_(PFX, _iter_go_to)(struct CMC_DEF_ITER(SNAME) * iter, size_t index) +{ + if (index >= iter->target->count) + return false; + + if (iter->index > index) + return CMC_(PFX, _iter_rewind)(iter, iter->index - index); + else if (iter->index < index) + return CMC_(PFX, _iter_advance)(iter, index - iter->index); + + return true; +} + +K CMC_(PFX, _iter_key)(struct CMC_DEF_ITER(SNAME) * iter) +{ + if (CMC_(PFX, _empty)(iter->target)) + return (K){ 0 }; + + return iter->target->buffer[iter->cursor].key; +} + +V CMC_(PFX, _iter_value)(struct CMC_DEF_ITER(SNAME) * iter) +{ + if (CMC_(PFX, _empty)(iter->target)) + return (V){ 0 }; + + return iter->target->buffer[iter->cursor].value; +} + +V *CMC_(PFX, _iter_rvalue)(struct CMC_DEF_ITER(SNAME) * iter) +{ + if (CMC_(PFX, _empty)(iter->target)) + return NULL; + + return &(iter->target->buffer[iter->cursor].value); +} + +size_t CMC_(PFX, _iter_index)(struct CMC_DEF_ITER(SNAME) * iter) +{ + return iter->index; +} + +#endif /* CMC_EXT_HASHMAP_ITER */ + +/** + * STR + * + * Print helper functions. + */ +#ifdef CMC_EXT_HASHMAP_STR + +bool CMC_(PFX, _to_string)(struct SNAME *_map_, FILE *fptr) +{ + struct SNAME *m_ = _map_; + + return 0 <= fprintf(fptr, + "struct %s<%s, %s> " + "at %p { " + "buffer:%p, " + "capacity:%" PRIuMAX ", " + "count:%" PRIuMAX ", " + "load:%lf, " + "flag:%d, " + "f_key:%p, " + "f_val:%p, " + "alloc:%p, " + "callbacks:%p }", + CMC_TO_STRING(SNAME), CMC_TO_STRING(K), CMC_TO_STRING(V), m_, m_->buffer, m_->capacity, + m_->count, m_->load, m_->flag, m_->f_key, m_->f_val, m_->alloc, CMC_CALLBACKS_GET(m_)); +} + +bool CMC_(PFX, _print)(struct SNAME *_map_, FILE *fptr, const char *start, const char *separator, const char *end, + const char *key_val_sep) +{ + fprintf(fptr, "%s", start); + + size_t last = 0; + for (size_t i = _map_->capacity; i > 0; i--) + { + if ((_map_->buffer[i - 1]).state == CMC_ES_FILLED) + { + last = i - 1; + break; + } + } + + for (size_t i = 0; i < _map_->capacity; i++) + { + struct CMC_DEF_ENTRY(SNAME) *entry = &(_map_->buffer[i]); + + if (entry->state == CMC_ES_FILLED) + { + if (!_map_->f_key->str(fptr, entry->key)) + return false; + + fprintf(fptr, "%s", key_val_sep); + + if (!_map_->f_val->str(fptr, entry->value)) + return false; + + if (i + 1 < last) + fprintf(fptr, "%s", separator); + } + } + + fprintf(fptr, "%s", end); + + return true; +} + +#endif /* CMC_EXT_HASHMAP_STR */ diff --git a/src/cmc/cmc/hashmap/ext/header.h b/src/cmc/cmc/hashmap/ext/header.h new file mode 100644 index 0000000..8f4d34c --- /dev/null +++ b/src/cmc/cmc/hashmap/ext/header.h @@ -0,0 +1,82 @@ +/** + * Copyright (c) 2019 Leonardo Vencovsky + * + * This file is part of the C Macro Collections Libray. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in 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: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * 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 + * AUTHORS OR COPYRIGHT HOLDERS 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 IN THE + * SOFTWARE. + */ + +/** + * INIT + * + * The part 'INIT' gives a new way of initializing a collection. The collection + * struct is not heap allocated, only its internal structure (nodes, buffers). + */ +#ifdef CMC_EXT_HASHMAP_INIT + +struct SNAME CMC_(PFX, _init)(size_t capacity, double load, struct CMC_DEF_FKEY(SNAME) * f_key, + struct CMC_DEF_FVAL(SNAME) * f_val); +struct SNAME CMC_(PFX, _init_custom)(size_t capacity, double load, struct CMC_DEF_FKEY(SNAME) * f_key, + struct CMC_DEF_FVAL(SNAME) * f_val, struct CMC_ALLOC_NODE_NAME *alloc, + struct CMC_CALLBACKS_NAME *callbacks); +void CMC_(PFX, _release)(struct SNAME _map_); + +#endif /* CMC_EXT_HASHMAP_INIT */ + +/** + * ITER + * + * Hashmap bi-directional iterator. + */ +#ifdef CMC_EXT_HASHMAP_ITER + +/* Iterator Initialization */ +struct CMC_DEF_ITER(SNAME) CMC_(PFX, _iter_start)(struct SNAME *target); +struct CMC_DEF_ITER(SNAME) CMC_(PFX, _iter_end)(struct SNAME *target); +/* Iterator State */ +bool CMC_(PFX, _iter_at_start)(struct CMC_DEF_ITER(SNAME) * iter); +bool CMC_(PFX, _iter_at_end)(struct CMC_DEF_ITER(SNAME) * iter); +/* Iterator Movement */ +bool CMC_(PFX, _iter_to_start)(struct CMC_DEF_ITER(SNAME) * iter); +bool CMC_(PFX, _iter_to_end)(struct CMC_DEF_ITER(SNAME) * iter); +bool CMC_(PFX, _iter_next)(struct CMC_DEF_ITER(SNAME) * iter); +bool CMC_(PFX, _iter_prev)(struct CMC_DEF_ITER(SNAME) * iter); +bool CMC_(PFX, _iter_advance)(struct CMC_DEF_ITER(SNAME) * iter, size_t steps); +bool CMC_(PFX, _iter_rewind)(struct CMC_DEF_ITER(SNAME) * iter, size_t steps); +bool CMC_(PFX, _iter_go_to)(struct CMC_DEF_ITER(SNAME) * iter, size_t index); +/* Iterator Access */ +K CMC_(PFX, _iter_key)(struct CMC_DEF_ITER(SNAME) * iter); +V CMC_(PFX, _iter_value)(struct CMC_DEF_ITER(SNAME) * iter); +V *CMC_(PFX, _iter_rvalue)(struct CMC_DEF_ITER(SNAME) * iter); +size_t CMC_(PFX, _iter_index)(struct CMC_DEF_ITER(SNAME) * iter); + +#endif /* CMC_EXT_HASHMAP_ITER */ + +/** + * STR + * + * Print helper functions. + */ +#ifdef CMC_EXT_HASHMAP_STR + +bool CMC_(PFX, _to_string)(struct SNAME *_map_, FILE *fptr); +bool CMC_(PFX, _print)(struct SNAME *_map_, FILE *fptr, const char *start, const char *separator, const char *end, + const char *key_val_sep); + +#endif /* CMC_EXT_HASHMAP_STR */ diff --git a/src/cmc/cmc/hashmap/ext/struct.h b/src/cmc/cmc/hashmap/ext/struct.h new file mode 100644 index 0000000..d55996b --- /dev/null +++ b/src/cmc/cmc/hashmap/ext/struct.h @@ -0,0 +1,51 @@ +/** + * Copyright (c) 2019 Leonardo Vencovsky + * + * This file is part of the C Macro Collections Libray. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in 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: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * 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 + * AUTHORS OR COPYRIGHT HOLDERS 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 IN THE + * SOFTWARE. + */ + +/** + * ITER + * + * List bi-directional iterator. + */ +#ifdef CMC_EXT_HASHMAP_ITER + +/* HashMap Iterator */ +struct CMC_DEF_ITER(SNAME) +{ + /* Target hashmap */ + struct SNAME *target; + /* Cursor's position (index) */ + size_t cursor; + /* Keeps track of relative index to the iteration of elements */ + size_t index; + /* The index of the first element */ + size_t first; + /* The index of the last element */ + size_t last; + /* If the iterator has reached the start of the iteration */ + bool start; + /* If the iterator has reached the end of the iteration */ + bool end; +}; + +#endif /* CMC_EXT_HASHMAP_ITER */ diff --git a/src/cmc/cmc/hashmap/header.h b/src/cmc/cmc/hashmap/header.h new file mode 100644 index 0000000..c69e79c --- /dev/null +++ b/src/cmc/cmc/hashmap/header.h @@ -0,0 +1,90 @@ +/** + * Copyright (c) 2019 Leonardo Vencovsky + * + * This file is part of the C Macro Collections Libray. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in 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: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * 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 + * AUTHORS OR COPYRIGHT HOLDERS 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 IN THE + * SOFTWARE. + */ + +/* Key struct function table */ +struct CMC_DEF_FKEY(SNAME) +{ + /* Comparator function */ + CMC_DEF_FTAB_CMP(K); + /* Copy function */ + CMC_DEF_FTAB_CPY(K); + /* To string function */ + CMC_DEF_FTAB_STR(K); + /* Free from memory function */ + CMC_DEF_FTAB_FREE(K); + /* Hash function */ + CMC_DEF_FTAB_HASH(K); + /* Priority function */ + CMC_DEF_FTAB_PRI(K); +}; + +/* Value struct function table */ +struct CMC_DEF_FVAL(SNAME) +{ + /* Comparator function */ + CMC_DEF_FTAB_CMP(V); + /* Copy function */ + CMC_DEF_FTAB_CPY(V); + /* To string function */ + CMC_DEF_FTAB_STR(V); + /* Free from memory function */ + CMC_DEF_FTAB_FREE(V); + /* Hash function */ + CMC_DEF_FTAB_HASH(V); + /* Priority function */ + CMC_DEF_FTAB_PRI(V); +}; + +/* Collection Allocation and Deallocation */ +struct SNAME *CMC_(PFX, _new)(size_t capacity, double load, struct CMC_DEF_FKEY(SNAME) * f_key, + struct CMC_DEF_FVAL(SNAME) * f_val); +struct SNAME *CMC_(PFX, _new_custom)(size_t capacity, double load, struct CMC_DEF_FKEY(SNAME) * f_key, + struct CMC_DEF_FVAL(SNAME) * f_val, struct CMC_ALLOC_NODE_NAME *alloc, + struct CMC_CALLBACKS_NAME *callbacks); +void CMC_(PFX, _clear)(struct SNAME *_map_); +void CMC_(PFX, _free)(struct SNAME *_map_); +/* Customization of Allocation and Callbacks */ +void CMC_(PFX, _customize)(struct SNAME *_map_, struct CMC_ALLOC_NODE_NAME *alloc, + struct CMC_CALLBACKS_NAME *callbacks); +/* Collection Input and Output */ +bool CMC_(PFX, _insert)(struct SNAME *_map_, K key, V value); +bool CMC_(PFX, _update)(struct SNAME *_map_, K key, V new_value, V *old_value); +bool CMC_(PFX, _remove)(struct SNAME *_map_, K key, V *out_value); +/* Element Access */ +bool CMC_(PFX, _max)(struct SNAME *_map_, K *key, V *value); +bool CMC_(PFX, _min)(struct SNAME *_map_, K *key, V *value); +V CMC_(PFX, _get)(struct SNAME *_map_, K key); +V *CMC_(PFX, _get_ref)(struct SNAME *_map_, K key); +/* Collection State */ +bool CMC_(PFX, _contains)(struct SNAME *_map_, K key); +bool CMC_(PFX, _empty)(struct SNAME *_map_); +bool CMC_(PFX, _full)(struct SNAME *_map_); +size_t CMC_(PFX, _count)(struct SNAME *_map_); +size_t CMC_(PFX, _capacity)(struct SNAME *_map_); +double CMC_(PFX, _load)(struct SNAME *_map_); +int CMC_(PFX, _flag)(struct SNAME *_map_); +/* Collection Utility */ +bool CMC_(PFX, _resize)(struct SNAME *_map_, size_t capacity); +struct SNAME *CMC_(PFX, _copy_of)(struct SNAME *_map_); +bool CMC_(PFX, _equals)(struct SNAME *_map1_, struct SNAME *_map2_); diff --git a/src/cmc/cmc/hashmap/struct.h b/src/cmc/cmc/hashmap/struct.h new file mode 100644 index 0000000..7e75a0d --- /dev/null +++ b/src/cmc/cmc/hashmap/struct.h @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2019 Leonardo Vencovsky + * + * This file is part of the C Macro Collections Libray. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in 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: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * 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 + * AUTHORS OR COPYRIGHT HOLDERS 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 IN THE + * SOFTWARE. + */ + +/* HashMap Structure */ +struct SNAME +{ + /* Array of Entries */ + struct CMC_DEF_ENTRY(SNAME) * buffer; + /* Current array capacity */ + size_t capacity; + /* Current amount of keys */ + size_t count; + /* Load factor in range (0.0, 1.0) */ + double load; + /* Flags indicating errors or success */ + int flag; + /* Key function table */ + struct CMC_DEF_FKEY(SNAME) * f_key; + /* Value function table */ + struct CMC_DEF_FVAL(SNAME) * f_val; + /* Custom allocation functions */ + struct CMC_ALLOC_NODE_NAME *alloc; + /* Custom callback functions */ + CMC_CALLBACKS_DECL; +}; + +/* HashMap Entry */ +struct CMC_DEF_ENTRY(SNAME) +{ + /* Entry Key */ + K key; + /* Entry Value */ + V value; + /* The distance of this node to its original position, used by */ + /* robin-hood hashing */ + size_t dist; + /* The sate of this node (DELETED, EMPTY, FILLED) */ + enum cmc_entry_state state; +}; diff --git a/src/cmc/cmc/hashmultimap/code.h b/src/cmc/cmc/hashmultimap/code.h new file mode 100644 index 0000000..36a5e41 --- /dev/null +++ b/src/cmc/cmc/hashmultimap/code.h @@ -0,0 +1,870 @@ +/** + * Copyright (c) 2019 Leonardo Vencovsky + * + * This file is part of the C Macro Collections Libray. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in 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: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * 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 + * AUTHORS OR COPYRIGHT HOLDERS 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 IN THE + * SOFTWARE. + */ + +/* Implementation Detail Functions */ +struct CMC_DEF_ENTRY(SNAME) * CMC_(PFX, _impl_new_entry)(struct SNAME *_map_, K key, V value); +struct CMC_DEF_ENTRY(SNAME) * CMC_(PFX, _impl_get_entry)(struct SNAME *_map_, K key); +size_t CMC_(PFX, _impl_key_count)(struct SNAME *_map_, K key); +size_t CMC_(PFX, _impl_calculate_size)(size_t required); + +struct SNAME *CMC_(PFX, _new)(size_t capacity, double load, struct CMC_DEF_FKEY(SNAME) * f_key, + struct CMC_DEF_FVAL(SNAME) * f_val) +{ + return CMC_(PFX, _new_custom)(capacity, load, f_key, f_val, NULL, NULL); +} + +struct SNAME *CMC_(PFX, _new_custom)(size_t capacity, double load, struct CMC_DEF_FKEY(SNAME) * f_key, + struct CMC_DEF_FVAL(SNAME) * f_val, struct CMC_ALLOC_NODE_NAME *alloc, + struct CMC_CALLBACKS_NAME *callbacks) +{ + CMC_CALLBACKS_MAYBE_UNUSED(callbacks); + + if (capacity == 0 || load <= 0) + return NULL; + + /* Prevent integer overflow */ + if (capacity >= UINTMAX_MAX * load) + return NULL; + + if (!f_key || !f_val) + return NULL; + + if (!alloc) + alloc = &cmc_alloc_node_default; + + size_t real_capacity = CMC_(PFX, _impl_calculate_size)(capacity / load); + + struct SNAME *_map_ = alloc->malloc(sizeof(struct SNAME)); + + if (!_map_) + return NULL; + + _map_->buffer = alloc->calloc(real_capacity, sizeof(struct CMC_DEF_ENTRY(SNAME) *[2])); + + if (!_map_->buffer) + { + alloc->free(_map_); + return NULL; + } + + _map_->count = 0; + _map_->capacity = real_capacity; + _map_->load = load; + _map_->flag = CMC_FLAG_OK; + _map_->f_key = f_key; + _map_->f_val = f_val; + _map_->alloc = alloc; + CMC_CALLBACKS_ASSIGN(_map_, callbacks); + + return _map_; +} + +void CMC_(PFX, _clear)(struct SNAME *_map_) +{ + for (size_t i = 0; i < _map_->capacity; i++) + { + struct CMC_DEF_ENTRY(SNAME) *scan = _map_->buffer[i][0]; + + while (scan) + { + struct CMC_DEF_ENTRY(SNAME) *next = scan->next; + + if (_map_->f_key->free) + _map_->f_key->free(scan->key); + if (_map_->f_val->free) + _map_->f_val->free(scan->value); + + _map_->alloc->free(scan); + + scan = next; + } + } + + memset(_map_->buffer, 0, sizeof(struct CMC_DEF_ENTRY(SNAME) *[2]) * _map_->capacity); + + _map_->count = 0; + _map_->flag = CMC_FLAG_OK; +} + +void CMC_(PFX, _free)(struct SNAME *_map_) +{ + size_t index = 0; + + while (index < _map_->capacity) + { + struct CMC_DEF_ENTRY(SNAME) *scan = _map_->buffer[index][0]; + + if (scan) + { + if (scan->next == NULL && scan->prev == NULL) + { + if (_map_->f_key->free) + _map_->f_key->free(scan->key); + if (_map_->f_val->free) + _map_->f_val->free(scan->value); + + _map_->alloc->free(scan); + } + else + { + while (scan) + { + struct CMC_DEF_ENTRY(SNAME) *tmp = scan; + + scan = scan->next; + + if (_map_->f_key->free) + _map_->f_key->free(tmp->key); + if (_map_->f_val->free) + _map_->f_val->free(tmp->value); + + _map_->alloc->free(tmp); + } + } + } + + index++; + } + + _map_->alloc->free(_map_->buffer); + _map_->alloc->free(_map_); +} + +void CMC_(PFX, _customize)(struct SNAME *_map_, struct CMC_ALLOC_NODE_NAME *alloc, struct CMC_CALLBACKS_NAME *callbacks) +{ + CMC_CALLBACKS_MAYBE_UNUSED(callbacks); + + if (!alloc) + _map_->alloc = &cmc_alloc_node_default; + else + _map_->alloc = alloc; + + CMC_CALLBACKS_ASSIGN(_map_, callbacks); + + _map_->flag = CMC_FLAG_OK; +} + +bool CMC_(PFX, _insert)(struct SNAME *_map_, K key, V value) +{ + if (CMC_(PFX, _full)(_map_)) + { + if (!CMC_(PFX, _resize)(_map_, _map_->capacity + 1)) + return false; + } + + size_t hash = _map_->f_key->hash(key); + size_t pos = hash % _map_->capacity; + + struct CMC_DEF_ENTRY(SNAME) *entry = CMC_(PFX, _impl_new_entry)(_map_, key, value); + + if (_map_->buffer[pos][0] == NULL) + { + _map_->buffer[pos][0] = entry; + _map_->buffer[pos][1] = entry; + } + else + { + entry->prev = _map_->buffer[pos][1]; + + _map_->buffer[pos][1]->next = entry; + _map_->buffer[pos][1] = entry; + } + + _map_->count++; + _map_->flag = CMC_FLAG_OK; + + CMC_CALLBACKS_CALL(_map_, create); + + return true; +} + +bool CMC_(PFX, _update)(struct SNAME *_map_, K key, V new_value, V *old_value) +{ + if (CMC_(PFX, _empty)(_map_)) + { + _map_->flag = CMC_FLAG_EMPTY; + return false; + } + + struct CMC_DEF_ENTRY(SNAME) *entry = CMC_(PFX, _impl_get_entry)(_map_, key); + + if (!entry) + { + _map_->flag = CMC_FLAG_NOT_FOUND; + return false; + } + + if (old_value) + *old_value = entry->value; + + entry->value = new_value; + + _map_->flag = CMC_FLAG_OK; + + CMC_CALLBACKS_CALL(_map_, update); + + return true; +} + +size_t CMC_(PFX, _update_all)(struct SNAME *_map_, K key, V new_value, V **old_values) +{ + if (CMC_(PFX, _empty)(_map_)) + { + _map_->flag = CMC_FLAG_EMPTY; + return 0; + } + + size_t hash = _map_->f_key->hash(key); + + struct CMC_DEF_ENTRY(SNAME) *entry = _map_->buffer[hash % _map_->capacity][0]; + + if (entry == NULL) + { + _map_->flag = CMC_FLAG_NOT_FOUND; + return 0; + } + + if (old_values) + { + size_t total = CMC_(PFX, _impl_key_count)(_map_, key); + + if (total == 0) + { + _map_->flag = CMC_FLAG_NOT_FOUND; + return 0; + } + + *old_values = _map_->alloc->malloc(sizeof(V) * total); + + if (!(*old_values)) + { + _map_->flag = CMC_FLAG_ALLOC; + return 0; + } + } + + size_t index = 0; + + while (entry) + { + if (_map_->f_key->cmp(entry->key, key) == 0) + { + if (old_values) + (*old_values)[index] = entry->value; + + index++; + entry->value = new_value; + } + + entry = entry->next; + } + + _map_->flag = CMC_FLAG_OK; + + CMC_CALLBACKS_CALL(_map_, update); + + return index; +} + +bool CMC_(PFX, _remove)(struct SNAME *_map_, K key, V *out_value) +{ + if (CMC_(PFX, _empty)(_map_)) + { + _map_->flag = CMC_FLAG_EMPTY; + return false; + } + + size_t hash = _map_->f_key->hash(key); + + struct CMC_DEF_ENTRY(SNAME) **head = &(_map_->buffer[hash % _map_->capacity][0]); + struct CMC_DEF_ENTRY(SNAME) **tail = &(_map_->buffer[hash % _map_->capacity][1]); + + if (*head == NULL) + { + _map_->flag = CMC_FLAG_NOT_FOUND; + return false; + } + + struct CMC_DEF_ENTRY(SNAME) *entry = *head; + + if (entry->next == NULL && entry->prev == NULL) + { + if (_map_->f_key->cmp(entry->key, key) == 0) + { + *head = NULL; + *tail = NULL; + + if (out_value) + *out_value = entry->value; + } + else + { + _map_->flag = CMC_FLAG_NOT_FOUND; + return false; + } + } + else + { + bool found = false; + + while (entry) + { + if (_map_->f_key->cmp(entry->key, key) == 0) + { + if (*head == entry) + *head = entry->next; + if (*tail == entry) + *tail = entry->prev; + + if (entry->prev) + entry->prev->next = entry->next; + if (entry->next) + entry->next->prev = entry->prev; + + if (out_value) + *out_value = entry->value; + + found = true; + + break; + } + else + entry = entry->next; + } + + if (!found) + { + _map_->flag = CMC_FLAG_NOT_FOUND; + return false; + } + } + + _map_->alloc->free(entry); + + _map_->count--; + _map_->flag = CMC_FLAG_OK; + + CMC_CALLBACKS_CALL(_map_, delete); + + return true; +} + +size_t CMC_(PFX, _remove_all)(struct SNAME *_map_, K key, V **out_values) +{ + if (CMC_(PFX, _empty)(_map_)) + { + _map_->flag = CMC_FLAG_EMPTY; + return false; + } + + size_t hash = _map_->f_key->hash(key); + + struct CMC_DEF_ENTRY(SNAME) **head = &(_map_->buffer[hash % _map_->capacity][0]); + struct CMC_DEF_ENTRY(SNAME) **tail = &(_map_->buffer[hash % _map_->capacity][1]); + + if (*head == NULL) + { + _map_->flag = CMC_FLAG_NOT_FOUND; + return 0; + } + + if (out_values) + { + size_t total = CMC_(PFX, _impl_key_count)(_map_, key); + + if (total == 0) + { + _map_->flag = CMC_FLAG_NOT_FOUND; + return 0; + } + + *out_values = _map_->alloc->malloc(sizeof(V) * total); + + if (!(*out_values)) + { + _map_->flag = CMC_FLAG_ALLOC; + return 0; + } + } + + size_t index = 0; + struct CMC_DEF_ENTRY(SNAME) *entry = *head; + + if (entry->next == NULL) + { + *head = NULL; + *tail = NULL; + + if (out_values) + (*out_values)[index] = entry->value; + + index++; + _map_->alloc->free(entry); + } + else + { + while (entry) + { + if (_map_->f_key->cmp(entry->key, key) == 0) + { + if (*head == entry) + *head = entry->next; + if (*tail == entry) + *tail = entry->prev; + + struct CMC_DEF_ENTRY(SNAME) *next = entry->next; + + if (entry->prev) + entry->prev->next = entry->next; + if (entry->next) + entry->next->prev = entry->prev; + + if (out_values) + (*out_values)[index] = entry->value; + + index++; + _map_->alloc->free(entry); + + entry = next; + } + else + entry = entry->next; + } + } + + _map_->count -= index; + _map_->flag = CMC_FLAG_OK; + + CMC_CALLBACKS_CALL(_map_, delete); + + return index; +} + +bool CMC_(PFX, _max)(struct SNAME *_map_, K *key, V *value) +{ + if (CMC_(PFX, _empty)(_map_)) + { + _map_->flag = CMC_FLAG_EMPTY; + return false; + } + + bool first = true; + K max_key = (K){ 0 }; + V max_val = (V){ 0 }; + + for (size_t i = 0; i < _map_->capacity; i++) + { + struct CMC_DEF_ENTRY(SNAME) *scan = _map_->buffer[i][0]; + + while (scan) + { + if (first) + { + max_key = scan->key; + max_val = scan->value; + + first = false; + } + else if (_map_->f_key->cmp(scan->key, max_key) > 0) + { + max_key = scan->key; + max_val = scan->value; + } + + scan = scan->next; + } + } + + if (key) + *key = max_key; + if (value) + *value = max_val; + + _map_->flag = CMC_FLAG_OK; + + CMC_CALLBACKS_CALL(_map_, read); + + return true; +} + +bool CMC_(PFX, _min)(struct SNAME *_map_, K *key, V *value) +{ + if (CMC_(PFX, _empty)(_map_)) + { + _map_->flag = CMC_FLAG_EMPTY; + return false; + } + + bool first = true; + K min_key = (K){ 0 }; + V min_val = (V){ 0 }; + + for (size_t i = 0; i < _map_->capacity; i++) + { + struct CMC_DEF_ENTRY(SNAME) *scan = _map_->buffer[i][0]; + + while (scan) + { + if (first) + { + min_key = scan->key; + min_val = scan->value; + + first = false; + } + else if (_map_->f_key->cmp(scan->key, min_key) < 0) + { + min_key = scan->key; + min_val = scan->value; + } + + scan = scan->next; + } + } + + if (key) + *key = min_key; + if (value) + *value = min_val; + + _map_->flag = CMC_FLAG_OK; + + CMC_CALLBACKS_CALL(_map_, read); + + return true; +} + +V CMC_(PFX, _get)(struct SNAME *_map_, K key) +{ + if (CMC_(PFX, _empty)(_map_)) + { + _map_->flag = CMC_FLAG_EMPTY; + return (V){ 0 }; + } + + struct CMC_DEF_ENTRY(SNAME) *entry = CMC_(PFX, _impl_get_entry)(_map_, key); + + if (!entry) + { + _map_->flag = CMC_FLAG_NOT_FOUND; + return (V){ 0 }; + } + + _map_->flag = CMC_FLAG_OK; + + CMC_CALLBACKS_CALL(_map_, read); + + return entry->value; +} + +V *CMC_(PFX, _get_ref)(struct SNAME *_map_, K key) +{ + if (CMC_(PFX, _empty)(_map_)) + { + _map_->flag = CMC_FLAG_EMPTY; + return NULL; + } + + struct CMC_DEF_ENTRY(SNAME) *entry = CMC_(PFX, _impl_get_entry)(_map_, key); + + if (!entry) + { + _map_->flag = CMC_FLAG_NOT_FOUND; + return NULL; + } + + _map_->flag = CMC_FLAG_OK; + + CMC_CALLBACKS_CALL(_map_, read); + + return &(entry->value); +} + +bool CMC_(PFX, _contains)(struct SNAME *_map_, K key) +{ + _map_->flag = CMC_FLAG_OK; + + bool result = CMC_(PFX, _impl_get_entry)(_map_, key) != NULL; + + CMC_CALLBACKS_CALL(_map_, read); + + return result; +} + +bool CMC_(PFX, _empty)(struct SNAME *_map_) +{ + return _map_->count == 0; +} + +bool CMC_(PFX, _full)(struct SNAME *_map_) +{ + return (double)_map_->capacity * _map_->load <= (double)_map_->count; +} + +size_t CMC_(PFX, _count)(struct SNAME *_map_) +{ + return _map_->count; +} + +size_t CMC_(PFX, _key_count)(struct SNAME *_map_, K key) +{ + return CMC_(PFX, _impl_key_count)(_map_, key); +} + +size_t CMC_(PFX, _capacity)(struct SNAME *_map_) +{ + return _map_->capacity; +} + +double CMC_(PFX, _load)(struct SNAME *_map_) +{ + return _map_->load; +} + +int CMC_(PFX, _flag)(struct SNAME *_map_) +{ + return _map_->flag; +} + +bool CMC_(PFX, _resize)(struct SNAME *_map_, size_t capacity) +{ + _map_->flag = CMC_FLAG_OK; + + if (_map_->capacity == capacity) + goto success; + + if (_map_->capacity > capacity / _map_->load) + goto success; + + /* Prevent integer overflow */ + if (capacity >= UINTMAX_MAX * _map_->load) + { + _map_->flag = CMC_FLAG_ERROR; + return false; + } + + /* Calculate required capacity based on the prime numbers */ + size_t theoretical_size = CMC_(PFX, _impl_calculate_size)(capacity); + + /* Not possible to shrink with current available prime numbers */ + if (theoretical_size < _map_->count / _map_->load) + { + _map_->flag = CMC_FLAG_INVALID; + return false; + } + + /* No callbacks since _new_map_ is just a temporary hashtable */ + struct SNAME *_new_map_ = + CMC_(PFX, _new_custom)(capacity, _map_->load, _map_->f_key, _map_->f_val, _map_->alloc, NULL); + + if (!_new_map_) + { + _map_->flag = CMC_FLAG_ALLOC; + return false; + } + + for (size_t i = 0; i < _map_->capacity; i++) + { + struct CMC_DEF_ENTRY(SNAME) *scan = _map_->buffer[i][0]; + + while (scan) + { + /* TODO check for errors */ + CMC_(PFX, _insert)(_new_map_, scan->key, scan->value); + + scan = scan->next; + } + } + + if (_map_->count != _new_map_->count) + { + CMC_(PFX, _free)(_new_map_); + + _map_->flag = CMC_FLAG_ERROR; + return false; + } + + struct CMC_DEF_ENTRY(SNAME) * (*tmp_b)[2] = _map_->buffer; + _map_->buffer = _new_map_->buffer; + _new_map_->buffer = tmp_b; + + size_t tmp_c = _map_->capacity; + _map_->capacity = _new_map_->capacity; + _new_map_->capacity = tmp_c; + + /* Prevent the map from freeing the data */ + _new_map_->f_key = &(struct CMC_DEF_FKEY(SNAME)){ NULL }; + _new_map_->f_val = &(struct CMC_DEF_FVAL(SNAME)){ NULL }; + + CMC_(PFX, _free)(_new_map_); + +success: + + CMC_CALLBACKS_CALL(_map_, resize); + + return true; +} + +struct SNAME *CMC_(PFX, _copy_of)(struct SNAME *_map_) +{ + /* Callback will be added later */ + struct SNAME *result = CMC_(PFX, _new_custom)(_map_->capacity * _map_->load, _map_->load, _map_->f_key, + _map_->f_val, _map_->alloc, NULL); + + if (!result) + { + _map_->flag = CMC_FLAG_ERROR; + return NULL; + } + + for (size_t i = 0; i < _map_->capacity; i++) + { + struct CMC_DEF_ENTRY(SNAME) *scan = _map_->buffer[i][0]; + + while (scan) + { + K key; + V value; + + if (_map_->f_key->cpy) + key = _map_->f_key->cpy(scan->key); + else + key = scan->key; + if (_map_->f_val->cpy) + value = _map_->f_val->cpy(scan->value); + else + value = scan->value; + + /* TODO check for errors */ + CMC_(PFX, _insert)(result, key, value); + + scan = scan->next; + } + } + + CMC_CALLBACKS_ASSIGN(result, _map_->callbacks); + _map_->flag = CMC_FLAG_OK; + + return result; +} + +bool CMC_(PFX, _equals)(struct SNAME *_map1_, struct SNAME *_map2_) +{ + _map1_->flag = CMC_FLAG_OK; + _map2_->flag = CMC_FLAG_OK; + + if (_map1_->count != _map2_->count) + return false; + + /* Optimize loop using the smallest hashtable */ + struct SNAME *_map_a_; + struct SNAME *_map_b_; + + _map_a_ = _map1_->capacity < _map2_->capacity ? _map1_ : _map2_; + _map_b_ = _map_a_ == _map1_ ? _map2_ : _map1_; + + for (size_t i = 0; i < _map_a_->capacity; i++) + { + struct CMC_DEF_ENTRY(SNAME) *scan = _map_a_->buffer[i][0]; + + while (scan) + { + /* OPTIMIZE - This is calling key_count for repeating keys */ + if (CMC_(PFX, _impl_key_count)(_map_a_, scan->key) != CMC_(PFX, _impl_key_count)(_map_b_, scan->key)) + return false; + + scan = scan->next; + } + } + + return true; +} + +struct CMC_DEF_ENTRY(SNAME) * CMC_(PFX, _impl_new_entry)(struct SNAME *_map_, K key, V value) +{ + struct CMC_DEF_ENTRY(SNAME) *entry = _map_->alloc->malloc(sizeof(struct CMC_DEF_ENTRY(SNAME))); + + if (!entry) + return NULL; + + entry->key = key; + entry->value = value; + entry->next = NULL; + entry->prev = NULL; + + return entry; +} + +struct CMC_DEF_ENTRY(SNAME) * CMC_(PFX, _impl_get_entry)(struct SNAME *_map_, K key) +{ + size_t hash = _map_->f_key->hash(key); + + struct CMC_DEF_ENTRY(SNAME) *entry = _map_->buffer[hash % _map_->capacity][0]; + + while (entry) + { + if (_map_->f_key->cmp(entry->key, key) == 0) + return entry; + + entry = entry->next; + } + + return NULL; +} + +size_t CMC_(PFX, _impl_key_count)(struct SNAME *_map_, K key) +{ + size_t hash = _map_->f_key->hash(key); + + struct CMC_DEF_ENTRY(SNAME) *entry = _map_->buffer[hash % _map_->capacity][0]; + + size_t total_count = 0; + + if (!entry) + return total_count; + + while (entry) + { + if (_map_->f_key->cmp(entry->key, key) == 0) + total_count++; + + entry = entry->next; + } + + return total_count; +} + +size_t CMC_(PFX, _impl_calculate_size)(size_t required) +{ + const size_t count = sizeof(cmc_hashtable_primes) / sizeof(cmc_hashtable_primes[0]); + + if (cmc_hashtable_primes[count - 1] < required) + return required; + + size_t i = 0; + while (cmc_hashtable_primes[i] < required) + i++; + + return cmc_hashtable_primes[i]; +} diff --git a/src/cmc/cmc/hashmultimap/ext/code.h b/src/cmc/cmc/hashmultimap/ext/code.h new file mode 100644 index 0000000..4ba8efa --- /dev/null +++ b/src/cmc/cmc/hashmultimap/ext/code.h @@ -0,0 +1,397 @@ +/** + * Copyright (c) 2019 Leonardo Vencovsky + * + * This file is part of the C Macro Collections Libray. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in 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: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * 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 + * AUTHORS OR COPYRIGHT HOLDERS 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 IN THE + * SOFTWARE. + */ + +/** + * INIT + * + * The part 'INIT' gives a new way of initializing a collection. The collection + * struct is not heap allocated, only its internal structure (nodes, buffers). + */ +#ifdef CMC_EXT_HASHMULTIMAP_INIT + +#endif /* CMC_EXT_HASHMULTIMAP_INIT */ + +/** + * ITER + * + * List bi-directional iterator. + */ +#ifdef CMC_EXT_HASHMULTIMAP_ITER + +struct CMC_DEF_ITER(SNAME) CMC_(PFX, _iter_start)(struct SNAME *target) +{ + struct CMC_DEF_ITER(SNAME) iter; + + iter.target = target; + iter.curr_entry = NULL; + iter.cursor = 0; + iter.index = 0; + iter.first = 0; + iter.last = 0; + iter.start = true; + iter.end = CMC_(PFX, _empty)(target); + + if (!CMC_(PFX, _empty)(target)) + { + for (size_t i = 0; i < target->capacity; i++) + { + struct CMC_DEF_ENTRY(SNAME) *entry = target->buffer[i][0]; + + if (entry && entry != CMC_ENTRY_DELETED) + { + iter.curr_entry = target->buffer[i][0]; + iter.first = i; + break; + } + } + + iter.cursor = iter.first; + + for (size_t i = target->capacity; i > 0; i--) + { + struct CMC_DEF_ENTRY(SNAME) *entry = target->buffer[i - 1][1]; + + if (entry && entry != CMC_ENTRY_DELETED) + { + iter.last = i - 1; + break; + } + } + } + + return iter; +} + +struct CMC_DEF_ITER(SNAME) CMC_(PFX, _iter_end)(struct SNAME *target) +{ + struct CMC_DEF_ITER(SNAME) iter; + + iter.target = target; + iter.curr_entry = NULL; + iter.cursor = 0; + iter.index = 0; + iter.first = 0; + iter.last = 0; + iter.start = CMC_(PFX, _empty)(target); + iter.end = true; + + if (!CMC_(PFX, _empty)(target)) + { + for (size_t i = 0; i < target->capacity; i++) + { + struct CMC_DEF_ENTRY(SNAME) *entry = target->buffer[i][0]; + + if (entry && entry != CMC_ENTRY_DELETED) + { + iter.first = i; + break; + } + } + + for (size_t i = target->capacity; i > 0; i--) + { + struct CMC_DEF_ENTRY(SNAME) *entry = target->buffer[i - 1][1]; + + if (entry && entry != CMC_ENTRY_DELETED) + { + iter.curr_entry = target->buffer[i - 1][1]; + iter.last = i - 1; + break; + } + } + + iter.cursor = iter.last; + iter.index = target->count - 1; + } + + return iter; +} + +bool CMC_(PFX, _iter_at_start)(struct CMC_DEF_ITER(SNAME) * iter) +{ + return CMC_(PFX, _empty)(iter->target) || iter->start; +} + +bool CMC_(PFX, _iter_at_end)(struct CMC_DEF_ITER(SNAME) * iter) +{ + return CMC_(PFX, _empty)(iter->target) || iter->end; +} + +bool CMC_(PFX, _iter_to_start)(struct CMC_DEF_ITER(SNAME) * iter) +{ + if (!CMC_(PFX, _empty)(iter->target)) + { + iter->cursor = iter->first; + iter->index = 0; + iter->start = true; + iter->end = CMC_(PFX, _empty)(iter->target); + iter->curr_entry = iter->target->buffer[iter->first][0]; + + return true; + } + + return false; +} + +bool CMC_(PFX, _iter_to_end)(struct CMC_DEF_ITER(SNAME) * iter) +{ + if (!CMC_(PFX, _empty)(iter->target)) + { + iter->cursor = iter->last; + iter->index = iter->target->count - 1; + iter->start = CMC_(PFX, _empty)(iter->target); + iter->end = true; + iter->curr_entry = iter->target->buffer[iter->last][1]; + + return true; + } + + return false; +} + +bool CMC_(PFX, _iter_next)(struct CMC_DEF_ITER(SNAME) * iter) +{ + if (iter->end) + return false; + + if (iter->curr_entry->next != NULL) + { + iter->curr_entry = iter->curr_entry->next; + iter->index++; + } + else + { + if (iter->cursor == iter->last) + { + iter->end = true; + return false; + } + + iter->cursor++; + + while (iter->target->buffer[iter->cursor][0] == NULL) + iter->cursor++; + + iter->curr_entry = iter->target->buffer[iter->cursor][0]; + + iter->index++; + } + + iter->start = CMC_(PFX, _empty)(iter->target); + + return true; +} + +bool CMC_(PFX, _iter_prev)(struct CMC_DEF_ITER(SNAME) * iter) +{ + if (iter->start) + return false; + + if (iter->curr_entry->prev != NULL) + { + iter->curr_entry = iter->curr_entry->prev; + iter->index--; + } + else + { + if (iter->cursor == iter->first) + { + iter->start = true; + return false; + } + + iter->cursor--; + + while (iter->target->buffer[iter->cursor][1] == NULL) + iter->cursor--; + + iter->curr_entry = iter->target->buffer[iter->cursor][1]; + + iter->index--; + } + + iter->end = CMC_(PFX, _empty)(iter->target); + + return true; +} + +/* Returns true only if the iterator moved */ +bool CMC_(PFX, _iter_advance)(struct CMC_DEF_ITER(SNAME) * iter, size_t steps) +{ + if (iter->end) + return false; + + if (iter->index + 1 == iter->target->count) + { + iter->end = true; + return false; + } + + if (steps == 0 || iter->index + steps >= iter->target->count) + return false; + + for (size_t i = 0; i < steps; i++) + CMC_(PFX, _iter_next)(iter); + + return true; +} + +/* Returns true only if the iterator moved */ +bool CMC_(PFX, _iter_rewind)(struct CMC_DEF_ITER(SNAME) * iter, size_t steps) +{ + if (iter->start) + return false; + + if (iter->index == 0) + { + iter->start = true; + return false; + } + + if (steps == 0 || iter->index < steps) + return false; + + for (size_t i = 0; i < steps; i++) + CMC_(PFX, _iter_prev)(iter); + + return true; +} + +/* Returns true only if the iterator was able to be positioned at the */ +/* given index */ +bool CMC_(PFX, _iter_go_to)(struct CMC_DEF_ITER(SNAME) * iter, size_t index) +{ + if (index >= iter->target->count) + return false; + + if (iter->index > index) + return CMC_(PFX, _iter_rewind)(iter, iter->index - index); + else if (iter->index < index) + return CMC_(PFX, _iter_advance)(iter, index - iter->index); + + return true; +} + +K CMC_(PFX, _iter_key)(struct CMC_DEF_ITER(SNAME) * iter) +{ + if (CMC_(PFX, _empty)(iter->target)) + return (K){ 0 }; + + return iter->curr_entry->key; +} + +V CMC_(PFX, _iter_value)(struct CMC_DEF_ITER(SNAME) * iter) +{ + if (CMC_(PFX, _empty)(iter->target)) + return (V){ 0 }; + + return iter->curr_entry->value; +} + +V *CMC_(PFX, _iter_rvalue)(struct CMC_DEF_ITER(SNAME) * iter) +{ + if (CMC_(PFX, _empty)(iter->target)) + return NULL; + + return &(iter->curr_entry->value); +} + +size_t CMC_(PFX, _iter_index)(struct CMC_DEF_ITER(SNAME) * iter) +{ + return iter->index; +} + +#endif /* CMC_EXT_HASHMULTIMAP_ITER */ + +/** + * STR + * + * Print helper functions. + */ +#ifdef CMC_EXT_HASHMULTIMAP_STR + +bool CMC_(PFX, _to_string)(struct SNAME *_map_, FILE *fptr) +{ + struct SNAME *m_ = _map_; + + return 0 <= fprintf(fptr, + "struct %s<%s, %s> " + "at %p { " + "buffer:%p, " + "capacity:%" PRIuMAX ", " + "count:%" PRIuMAX ", " + "load:%lf, " + "flag:%d, " + "f_key:%p, " + "f_val:%p, " + "alloc:%p, " + "callbacks:%p }", + CMC_TO_STRING(SNAME), CMC_TO_STRING(K), CMC_TO_STRING(V), m_, m_->buffer, m_->capacity, + m_->count, m_->load, m_->flag, m_->f_key, m_->f_val, m_->alloc, CMC_CALLBACKS_GET(m_)); +} + +bool CMC_(PFX, _print)(struct SNAME *_map_, FILE *fptr, const char *start, const char *separator, const char *end, + const char *key_val_sep) +{ + fprintf(fptr, "%s", start); + + size_t last = 0; + for (size_t i = _map_->capacity; i > 0; i--) + { + struct CMC_DEF_ENTRY(SNAME) *entry = _map_->buffer[i - 1][1]; + + if (entry && entry != CMC_ENTRY_DELETED) + { + last = i - 1; + break; + } + } + + for (size_t i = 0; i < _map_->capacity; i++) + { + struct CMC_DEF_ENTRY(SNAME) *scan = _map_->buffer[i][0]; + + while (scan != NULL) + { + if (!_map_->f_key->str(fptr, scan->key)) + return false; + + fprintf(fptr, "%s", key_val_sep); + + if (!_map_->f_val->str(fptr, scan->value)) + return false; + + scan = scan->next; + + if (i + 1 < last) + fprintf(fptr, "%s", separator); + } + } + + fprintf(fptr, "%s", end); + + return true; +} + +#endif /* CMC_EXT_HASHMULTIMAP_STR */ diff --git a/src/cmc/cmc/hashmultimap/ext/header.h b/src/cmc/cmc/hashmultimap/ext/header.h new file mode 100644 index 0000000..4bf1c00 --- /dev/null +++ b/src/cmc/cmc/hashmultimap/ext/header.h @@ -0,0 +1,75 @@ +/** + * Copyright (c) 2019 Leonardo Vencovsky + * + * This file is part of the C Macro Collections Libray. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in 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: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * 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 + * AUTHORS OR COPYRIGHT HOLDERS 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 IN THE + * SOFTWARE. + */ + +/** + * INIT + * + * The part 'INIT' gives a new way of initializing a collection. The collection + * struct is not heap allocated, only its internal structure (nodes, buffers). + */ +#ifdef CMC_EXT_HASHMULTIMAP_INIT + +#endif /* CMC_EXT_HASHMULTIMAP_INIT */ + +/** + * ITER + * + * Hashmultimap bi-directional iterator. + */ +#ifdef CMC_EXT_HASHMULTIMAP_ITER + +/* Iterator Initialization */ +struct CMC_DEF_ITER(SNAME) CMC_(PFX, _iter_start)(struct SNAME *target); +struct CMC_DEF_ITER(SNAME) CMC_(PFX, _iter_end)(struct SNAME *target); +/* Iterator State */ +bool CMC_(PFX, _iter_at_start)(struct CMC_DEF_ITER(SNAME) * iter); +bool CMC_(PFX, _iter_at_end)(struct CMC_DEF_ITER(SNAME) * iter); +/* Iterator Movement */ +bool CMC_(PFX, _iter_to_start)(struct CMC_DEF_ITER(SNAME) * iter); +bool CMC_(PFX, _iter_to_end)(struct CMC_DEF_ITER(SNAME) * iter); +bool CMC_(PFX, _iter_next)(struct CMC_DEF_ITER(SNAME) * iter); +bool CMC_(PFX, _iter_prev)(struct CMC_DEF_ITER(SNAME) * iter); +bool CMC_(PFX, _iter_advance)(struct CMC_DEF_ITER(SNAME) * iter, size_t steps); +bool CMC_(PFX, _iter_rewind)(struct CMC_DEF_ITER(SNAME) * iter, size_t steps); +bool CMC_(PFX, _iter_go_to)(struct CMC_DEF_ITER(SNAME) * iter, size_t index); +/* Iterator Access */ +K CMC_(PFX, _iter_key)(struct CMC_DEF_ITER(SNAME) * iter); +V CMC_(PFX, _iter_value)(struct CMC_DEF_ITER(SNAME) * iter); +V *CMC_(PFX, _iter_rvalue)(struct CMC_DEF_ITER(SNAME) * iter); +size_t CMC_(PFX, _iter_index)(struct CMC_DEF_ITER(SNAME) * iter); + +#endif /* CMC_EXT_HASHMULTIMAP_ITER */ + +/** + * STR + * + * Print helper functions. + */ +#ifdef CMC_EXT_HASHMULTIMAP_STR + +bool CMC_(PFX, _to_string)(struct SNAME *_map_, FILE *fptr); +bool CMC_(PFX, _print)(struct SNAME *_map_, FILE *fptr, const char *start, const char *separator, const char *end, + const char *key_val_sep); + +#endif /* CMC_EXT_HASHMULTIMAP_STR */ diff --git a/src/cmc/cmc/hashmultimap/ext/struct.h b/src/cmc/cmc/hashmultimap/ext/struct.h new file mode 100644 index 0000000..cf15af2 --- /dev/null +++ b/src/cmc/cmc/hashmultimap/ext/struct.h @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2019 Leonardo Vencovsky + * + * This file is part of the C Macro Collections Libray. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in 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: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * 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 + * AUTHORS OR COPYRIGHT HOLDERS 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 IN THE + * SOFTWARE. + */ + +/** + * ITER + * + * Hashmultimap bi-directional iterator. + */ +#ifdef CMC_EXT_HASHMULTIMAP_ITER + +/* HashMultiMap Iterator */ +struct CMC_DEF_ITER(SNAME) +{ + /* Target hashmultimap */ + struct SNAME *target; + /* Current entry */ + struct CMC_DEF_ENTRY(SNAME) * curr_entry; + /* Cursor`s position (index) */ + size_t cursor; + /* Keeps track of relative index to the iteration of elements */ + size_t index; + /* The index of the first element */ + size_t first; + /* The index of the last element */ + size_t last; + /* If the iterator has reached the start of the iteration */ + bool start; + /* If the iterator has reached the end of the iteration */ + bool end; +}; + +#endif /* CMC_EXT_HASHMULTIMAP_ITER */ diff --git a/src/cmc/cmc/hashmultimap/header.h b/src/cmc/cmc/hashmultimap/header.h new file mode 100644 index 0000000..ec3f2e0 --- /dev/null +++ b/src/cmc/cmc/hashmultimap/header.h @@ -0,0 +1,93 @@ +/** + * Copyright (c) 2019 Leonardo Vencovsky + * + * This file is part of the C Macro Collections Libray. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in 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: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * 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 + * AUTHORS OR COPYRIGHT HOLDERS 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 IN THE + * SOFTWARE. + */ + +/* Key struct function table */ +struct CMC_DEF_FKEY(SNAME) +{ + /* Comparator function */ + CMC_DEF_FTAB_CMP(K); + /* Copy function */ + CMC_DEF_FTAB_CPY(K); + /* To string function */ + CMC_DEF_FTAB_STR(K); + /* Free from memory function */ + CMC_DEF_FTAB_FREE(K); + /* Hash function */ + CMC_DEF_FTAB_HASH(K); + /* Priority function */ + CMC_DEF_FTAB_PRI(K); +}; + +/* Value struct function table */ +struct CMC_DEF_FVAL(SNAME) +{ + /* Comparator function */ + CMC_DEF_FTAB_CMP(V); + /* Copy function */ + CMC_DEF_FTAB_CPY(V); + /* To string function */ + CMC_DEF_FTAB_STR(V); + /* Free from memory function */ + CMC_DEF_FTAB_FREE(V); + /* Hash function */ + CMC_DEF_FTAB_HASH(V); + /* Priority function */ + CMC_DEF_FTAB_PRI(V); +}; + +/* Collection Allocation and Deallocation */ +struct SNAME *CMC_(PFX, _new)(size_t capacity, double load, struct CMC_DEF_FKEY(SNAME) * f_key, + struct CMC_DEF_FVAL(SNAME) * f_val); +struct SNAME *CMC_(PFX, _new_custom)(size_t capacity, double load, struct CMC_DEF_FKEY(SNAME) * f_key, + struct CMC_DEF_FVAL(SNAME) * f_val, struct CMC_ALLOC_NODE_NAME *alloc, + struct CMC_CALLBACKS_NAME *callbacks); +void CMC_(PFX, _clear)(struct SNAME *_map_); +void CMC_(PFX, _free)(struct SNAME *_map_); +/* Customization of Allocation and Callbacks */ +void CMC_(PFX, _customize)(struct SNAME *_map_, struct CMC_ALLOC_NODE_NAME *alloc, + struct CMC_CALLBACKS_NAME *callbacks); +/* Collection Input and Output */ +bool CMC_(PFX, _insert)(struct SNAME *_map_, K key, V value); +bool CMC_(PFX, _update)(struct SNAME *_map_, K key, V new_value, V *old_value); +size_t CMC_(PFX, _update_all)(struct SNAME *_map_, K key, V new_value, V **old_values); +bool CMC_(PFX, _remove)(struct SNAME *_map_, K key, V *out_value); +size_t CMC_(PFX, _remove_all)(struct SNAME *_map_, K key, V **out_values); +/* Element Access */ +bool CMC_(PFX, _max)(struct SNAME *_map_, K *key, V *value); +bool CMC_(PFX, _min)(struct SNAME *_map_, K *key, V *value); +V CMC_(PFX, _get)(struct SNAME *_map_, K key); +V *CMC_(PFX, _get_ref)(struct SNAME *_map_, K key); +/* Collection State */ +bool CMC_(PFX, _contains)(struct SNAME *_map_, K key); +bool CMC_(PFX, _empty)(struct SNAME *_map_); +bool CMC_(PFX, _full)(struct SNAME *_map_); +size_t CMC_(PFX, _count)(struct SNAME *_map_); +size_t CMC_(PFX, _key_count)(struct SNAME *_map_, K key); +size_t CMC_(PFX, _capacity)(struct SNAME *_map_); +double CMC_(PFX, _load)(struct SNAME *_map_); +int CMC_(PFX, _flag)(struct SNAME *_map_); +/* Collection Utility */ +bool CMC_(PFX, _resize)(struct SNAME *_map_, size_t capacity); +struct SNAME *CMC_(PFX, _copy_of)(struct SNAME *_map_); +bool CMC_(PFX, _equals)(struct SNAME *_map1_, struct SNAME *_map2_); diff --git a/src/cmc/cmc/hashmultimap/struct.h b/src/cmc/cmc/hashmultimap/struct.h new file mode 100644 index 0000000..291e7a7 --- /dev/null +++ b/src/cmc/cmc/hashmultimap/struct.h @@ -0,0 +1,59 @@ +/** + * Copyright (c) 2019 Leonardo Vencovsky + * + * This file is part of the C Macro Collections Libray. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in 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: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * 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 + * AUTHORS OR COPYRIGHT HOLDERS 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 IN THE + * SOFTWARE. + */ + +/* HashMultimap Structure */ +struct SNAME +{ + /* Array of linked list to entries */ + struct CMC_DEF_ENTRY(SNAME) * (*buffer)[2]; + /* Current array capacity */ + size_t capacity; + /* Current amount of keys */ + size_t count; + /* Load factor in range (0.0, infinity) */ + double load; + /* Flags indicating errors or success */ + int flag; + /* Key function table */ + struct CMC_DEF_FKEY(SNAME) * f_key; + /* Value function table */ + struct CMC_DEF_FVAL(SNAME) * f_val; + /* Custom allocation functions */ + struct CMC_ALLOC_NODE_NAME *alloc; + /* Custom callback functions */ + CMC_CALLBACKS_DECL; +}; + +/* HashMultimap Entry */ +struct CMC_DEF_ENTRY(SNAME) +{ + /* Entry Key */ + K key; + /* Entry Value */ + V value; + /* Next entry on the linked list */ + struct CMC_DEF_ENTRY(SNAME) * next; + /* Previous entry on the linked list */ + struct CMC_DEF_ENTRY(SNAME) * prev; +}; diff --git a/src/cmc/cmc/list/ext/struct.h b/src/cmc/cmc/list/ext/struct.h index bbebe63..fb4c3f9 100644 --- a/src/cmc/cmc/list/ext/struct.h +++ b/src/cmc/cmc/list/ext/struct.h @@ -34,13 +34,10 @@ struct CMC_DEF_ITER(SNAME) { /* Target List */ struct SNAME *target; - /* Cursor's position (index) */ size_t cursor; - /* If the iterator has reached the start of the iteration */ bool start; - /* If the iterator has reached the end of the iteration */ bool end; }; diff --git a/src/cmc/cmc/list/header.h b/src/cmc/cmc/list/header.h index c15ed66..84979ba 100644 --- a/src/cmc/cmc/list/header.h +++ b/src/cmc/cmc/list/header.h @@ -22,6 +22,23 @@ * SOFTWARE. */ +/* Value function table */ +struct CMC_DEF_FVAL(SNAME) +{ + /* Comparator function */ + CMC_DEF_FTAB_CMP(V); + /* Copy function */ + CMC_DEF_FTAB_CPY(V); + /* To string function */ + CMC_DEF_FTAB_STR(V); + /* Free from memory function */ + CMC_DEF_FTAB_FREE(V); + /* Hash function */ + CMC_DEF_FTAB_HASH(V); + /* Priority function */ + CMC_DEF_FTAB_PRI(V); +}; + /* Collection Allocation and Deallocation */ struct SNAME *CMC_(PFX, _new)(size_t capacity, struct CMC_DEF_FVAL(SNAME) * f_val); struct SNAME *CMC_(PFX, _new_custom)(size_t capacity, struct CMC_DEF_FVAL(SNAME) * f_val, diff --git a/src/cmc/cmc/list/struct.h b/src/cmc/cmc/list/struct.h index e0b563c..25cb942 100644 --- a/src/cmc/cmc/list/struct.h +++ b/src/cmc/cmc/list/struct.h @@ -39,19 +39,3 @@ struct SNAME /* Custom callback functions */ CMC_CALLBACKS_DECL; }; - -struct CMC_DEF_FVAL(SNAME) -{ - /* Comparator function */ - CMC_DEF_FTAB_CMP(V); - /* Copy function */ - CMC_DEF_FTAB_CPY(V); - /* To string function */ - CMC_DEF_FTAB_STR(V); - /* Free from memory function */ - CMC_DEF_FTAB_FREE(V); - /* Hash function */ - CMC_DEF_FTAB_HASH(V); - /* Priority function */ - CMC_DEF_FTAB_PRI(V); -}; diff --git a/src/cmc/cmc/queue/code.h b/src/cmc/cmc/queue/code.h new file mode 100644 index 0000000..9f93f7d --- /dev/null +++ b/src/cmc/cmc/queue/code.h @@ -0,0 +1,324 @@ +/** + * Copyright (c) 2019 Leonardo Vencovsky + * + * This file is part of the C Macro Collections Libray. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in 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: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * 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 + * AUTHORS OR COPYRIGHT HOLDERS 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 IN THE + * SOFTWARE. + */ + +/* Implementation Detail Functions */ +/* None */ + +struct SNAME *CMC_(PFX, _new)(size_t capacity, struct CMC_DEF_FVAL(SNAME) * f_val) +{ + return CMC_(PFX, _new_custom)(capacity, f_val, NULL, NULL); +} + +struct SNAME *CMC_(PFX, _new_custom)(size_t capacity, struct CMC_DEF_FVAL(SNAME) * f_val, + struct CMC_ALLOC_NODE_NAME *alloc, struct CMC_CALLBACKS_NAME *callbacks) +{ + CMC_CALLBACKS_MAYBE_UNUSED(callbacks); + + if (capacity < 1) + return NULL; + + if (!f_val) + return NULL; + + if (!alloc) + alloc = &cmc_alloc_node_default; + + struct SNAME *_queue_ = alloc->malloc(sizeof(struct SNAME)); + + if (!_queue_) + return NULL; + + _queue_->buffer = alloc->calloc(capacity, sizeof(V)); + + if (!_queue_->buffer) + { + alloc->free(_queue_); + return NULL; + } + + _queue_->capacity = capacity; + _queue_->count = 0; + _queue_->front = 0; + _queue_->back = 0; + _queue_->flag = CMC_FLAG_OK; + _queue_->f_val = f_val; + _queue_->alloc = alloc; + CMC_CALLBACKS_ASSIGN(_queue_, callbacks); + + return _queue_; +} + +void CMC_(PFX, _clear)(struct SNAME *_queue_) +{ + if (_queue_->f_val->free) + { + for (size_t i = _queue_->front, j = 0; j < _queue_->count; j++) + { + _queue_->f_val->free(_queue_->buffer[i]); + + i = (i + 1) % _queue_->capacity; + } + } + + memset(_queue_->buffer, 0, sizeof(V) * _queue_->capacity); + + _queue_->count = 0; + _queue_->front = 0; + _queue_->back = 0; + _queue_->flag = CMC_FLAG_OK; +} + +void CMC_(PFX, _free)(struct SNAME *_queue_) +{ + if (_queue_->f_val->free) + { + for (size_t i = _queue_->front, j = 0; j < _queue_->count; j++) + { + _queue_->f_val->free(_queue_->buffer[i]); + + i = (i + 1) % _queue_->capacity; + } + } + + _queue_->alloc->free(_queue_->buffer); + _queue_->alloc->free(_queue_); +} + +void CMC_(PFX, _customize)(struct SNAME *_queue_, struct CMC_ALLOC_NODE_NAME *alloc, + struct CMC_CALLBACKS_NAME *callbacks) +{ + CMC_CALLBACKS_MAYBE_UNUSED(callbacks); + + if (!alloc) + _queue_->alloc = &cmc_alloc_node_default; + else + _queue_->alloc = alloc; + + CMC_CALLBACKS_ASSIGN(_queue_, callbacks); + + _queue_->flag = CMC_FLAG_OK; +} + +bool CMC_(PFX, _enqueue)(struct SNAME *_queue_, V value) +{ + if (CMC_(PFX, _full)(_queue_)) + { + if (!CMC_(PFX, _resize)(_queue_, _queue_->capacity * 2)) + return false; + } + + _queue_->buffer[_queue_->back] = value; + + _queue_->back = (_queue_->back == _queue_->capacity - 1) ? 0 : _queue_->back + 1; + _queue_->count++; + _queue_->flag = CMC_FLAG_OK; + + CMC_CALLBACKS_CALL(_queue_, create); + + return true; +} + +bool CMC_(PFX, _dequeue)(struct SNAME *_queue_) +{ + if (CMC_(PFX, _empty)(_queue_)) + { + _queue_->flag = CMC_FLAG_EMPTY; + return false; + } + + _queue_->buffer[_queue_->front] = (V){ 0 }; + + _queue_->front = (_queue_->front == _queue_->capacity - 1) ? 0 : _queue_->front + 1; + _queue_->count--; + _queue_->flag = CMC_FLAG_OK; + + CMC_CALLBACKS_CALL(_queue_, delete); + + return true; +} + +V CMC_(PFX, _peek)(struct SNAME *_queue_) +{ + if (CMC_(PFX, _empty)(_queue_)) + { + _queue_->flag = CMC_FLAG_EMPTY; + return (V){ 0 }; + } + + _queue_->flag = CMC_FLAG_OK; + + CMC_CALLBACKS_CALL(_queue_, read); + + return _queue_->buffer[_queue_->front]; +} + +bool CMC_(PFX, _contains)(struct SNAME *_queue_, V value) +{ + _queue_->flag = CMC_FLAG_OK; + + bool result = false; + + for (size_t i = _queue_->front, j = 0; j < _queue_->count; j++) + { + if (_queue_->f_val->cmp(_queue_->buffer[i], value) == 0) + { + result = true; + break; + } + + i = (i + 1) % _queue_->capacity; + } + + CMC_CALLBACKS_CALL(_queue_, read); + + return result; +} + +bool CMC_(PFX, _empty)(struct SNAME *_queue_) +{ + return _queue_->count == 0; +} + +bool CMC_(PFX, _full)(struct SNAME *_queue_) +{ + return _queue_->count >= _queue_->capacity; +} + +size_t CMC_(PFX, _count)(struct SNAME *_queue_) +{ + return _queue_->count; +} + +size_t CMC_(PFX, _capacity)(struct SNAME *_queue_) +{ + return _queue_->capacity; +} + +int CMC_(PFX, _flag)(struct SNAME *_queue_) +{ + return _queue_->flag; +} + +bool CMC_(PFX, _resize)(struct SNAME *_queue_, size_t capacity) +{ + if (_queue_->capacity == capacity) + goto success; + + if (capacity < _queue_->count) + { + _queue_->flag = CMC_FLAG_INVALID; + return false; + } + + V *new_buffer = _queue_->alloc->malloc(sizeof(V) * capacity); + + if (!new_buffer) + { + _queue_->flag = CMC_FLAG_ALLOC; + return false; + } + + for (size_t i = _queue_->front, j = 0; j < _queue_->count; j++) + { + new_buffer[j] = _queue_->buffer[i]; + + i = (i + 1) % _queue_->capacity; + } + + _queue_->alloc->free(_queue_->buffer); + + _queue_->buffer = new_buffer; + _queue_->capacity = capacity; + _queue_->front = 0; + _queue_->back = _queue_->count; + +success: + + _queue_->flag = CMC_FLAG_OK; + + CMC_CALLBACKS_CALL(_queue_, resize); + + return true; +} + +struct SNAME *CMC_(PFX, _copy_of)(struct SNAME *_queue_) +{ + struct SNAME *result = CMC_(PFX, _new_custom)(_queue_->capacity, _queue_->f_val, _queue_->alloc, NULL); + + if (!result) + { + _queue_->flag = CMC_FLAG_ERROR; + return NULL; + } + + CMC_CALLBACKS_ASSIGN(result, _queue_->callbacks); + + if (_queue_->f_val->cpy) + { + for (size_t i = _queue_->front, j = 0; j < _queue_->count; j++) + { + result->buffer[j] = _queue_->f_val->cpy(_queue_->buffer[i]); + + i = (i + 1) % _queue_->capacity; + } + } + else + { + for (size_t i = _queue_->front, j = 0; j < _queue_->count; j++) + { + result->buffer[j] = _queue_->buffer[i]; + + i = (i + 1) % _queue_->capacity; + } + } + + result->count = _queue_->count; + result->front = 0; + result->back = _queue_->count; + + _queue_->flag = CMC_FLAG_OK; + + return result; +} + +bool CMC_(PFX, _equals)(struct SNAME *_queue1_, struct SNAME *_queue2_) +{ + _queue1_->flag = CMC_FLAG_OK; + _queue2_->flag = CMC_FLAG_OK; + + if (_queue1_->count != _queue2_->count) + return false; + + size_t i, j, k; + for (i = _queue1_->front, j = _queue2_->front, k = 0; k < _queue1_->count; k++) + { + if (_queue1_->f_val->cmp(_queue1_->buffer[i], _queue2_->buffer[j]) != 0) + return false; + + i = (i + 1) % _queue1_->capacity; + j = (j + 1) % _queue2_->capacity; + } + + return true; +} diff --git a/src/cmc/cmc/queue/ext/code.h b/src/cmc/cmc/queue/ext/code.h new file mode 100644 index 0000000..3643611 --- /dev/null +++ b/src/cmc/cmc/queue/ext/code.h @@ -0,0 +1,354 @@ +/** + * Copyright (c) 2019 Leonardo Vencovsky + * + * This file is part of the C Macro Collections Libray. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in 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: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * 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 + * AUTHORS OR COPYRIGHT HOLDERS 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 IN THE + * SOFTWARE. + */ + +/** + * INIT + * + * The part 'INIT' gives a new way of initializing a collection. The collection + * struct is not heap allocated, only its internal structure (nodes, buffers). + */ +#ifdef CMC_EXT_QUEUE_INIT + +struct SNAME CMC_(PFX, _init)(size_t capacity, struct CMC_DEF_FVAL(SNAME) * f_val) +{ + return CMC_(PFX, _init_custom)(capacity, f_val, NULL, NULL); +} + +struct SNAME CMC_(PFX, _init_custom)(size_t capacity, struct CMC_DEF_FVAL(SNAME) * f_val, + struct CMC_ALLOC_NODE_NAME *alloc, struct CMC_CALLBACKS_NAME *callbacks) +{ + CMC_CALLBACKS_MAYBE_UNUSED(callbacks); + + struct SNAME _queue_ = { 0 }; + + if (capacity < 1) + return _queue_; + + if (!f_val) + return _queue_; + + if (!alloc) + alloc = &cmc_alloc_node_default; + + _queue_.buffer = alloc->calloc(capacity, sizeof(V)); + + if (!_queue_.buffer) + return _queue_; + + _queue_.capacity = capacity; + _queue_.count = 0; + _queue_.front = 0; + _queue_.back = 0; + _queue_.flag = CMC_FLAG_OK; + _queue_.f_val = f_val; + _queue_.alloc = alloc; + CMC_CALLBACKS_ASSIGN(&_queue_, callbacks); + + return _queue_; +} + +void CMC_(PFX, _release)(struct SNAME _queue_) +{ + if (_queue_.f_val->free) + { + for (size_t i = _queue_.front, j = 0; j < _queue_.count; j++) + { + _queue_.f_val->free(_queue_.buffer[i]); + + i = (i + 1) % _queue_.capacity; + } + } + + _queue_.alloc->free(_queue_.buffer); +} + +#endif /* CMC_EXT_QUEUE_INIT */ + +/** + * ITER + * + * Queue bi-directional iterator. + */ +#ifdef CMC_EXT_QUEUE_ITER + +struct CMC_DEF_ITER(SNAME) CMC_(PFX, _iter_start)(struct SNAME *target) +{ + struct CMC_DEF_ITER(SNAME) iter; + + iter.target = target; + iter.cursor = target->front; + iter.index = 0; + iter.start = true; + iter.end = CMC_(PFX, _empty)(target); + + return iter; +} + +struct CMC_DEF_ITER(SNAME) CMC_(PFX, _iter_end)(struct SNAME *target) +{ + struct CMC_DEF_ITER(SNAME) iter; + + iter.target = target; + + if (!CMC_(PFX, _empty)(target)) + { + if (iter.target->back == 0) + iter.cursor = iter.target->capacity - 1; + else + iter.cursor = iter.target->back - 1; + + iter.index = iter.target->count - 1; + } + else + { + iter.cursor = 0; + iter.index = 0; + } + + iter.start = CMC_(PFX, _empty)(target); + iter.end = true; + + return iter; +} + +bool CMC_(PFX, _iter_at_start)(struct CMC_DEF_ITER(SNAME) * iter) +{ + return CMC_(PFX, _empty)(iter->target) || iter->start; +} + +bool CMC_(PFX, _iter_at_end)(struct CMC_DEF_ITER(SNAME) * iter) +{ + return CMC_(PFX, _empty)(iter->target) || iter->end; +} + +bool CMC_(PFX, _iter_to_start)(struct CMC_DEF_ITER(SNAME) * iter) +{ + if (!CMC_(PFX, _empty)(iter->target)) + { + iter->cursor = iter->target->front; + iter->index = 0; + iter->start = true; + iter->end = false; + + return true; + } + + return false; +} + +bool CMC_(PFX, _iter_to_end)(struct CMC_DEF_ITER(SNAME) * iter) +{ + if (!CMC_(PFX, _empty)(iter->target)) + { + if (iter->target->back == 0) + iter->cursor = iter->target->capacity - 1; + else + iter->cursor = iter->target->back - 1; + + iter->index = iter->target->count - 1; + + iter->start = false; + iter->end = true; + + return true; + } + + return false; +} + +bool CMC_(PFX, _iter_next)(struct CMC_DEF_ITER(SNAME) * iter) +{ + if (iter->end) + return false; + + if (iter->index + 1 == iter->target->count) + { + iter->end = true; + return false; + } + + iter->start = CMC_(PFX, _empty)(iter->target); + + iter->cursor = (iter->cursor + 1) % (iter->target->capacity); + iter->index++; + + return true; +} + +bool CMC_(PFX, _iter_prev)(struct CMC_DEF_ITER(SNAME) * iter) +{ + if (iter->start) + return false; + + if (iter->index == 0) + { + iter->start = true; + return false; + } + + iter->end = CMC_(PFX, _empty)(iter->target); + + iter->cursor = (iter->cursor == 0) ? iter->target->capacity - 1 : iter->cursor - 1; + iter->index--; + + return true; +} + +/* Returns true only if the iterator moved */ +bool CMC_(PFX, _iter_advance)(struct CMC_DEF_ITER(SNAME) * iter, size_t steps) +{ + if (iter->end) + return false; + + if (iter->index + 1 == iter->target->count) + { + iter->end = true; + return false; + } + + if (steps == 0 || iter->index + steps >= iter->target->count) + return false; + + iter->start = CMC_(PFX, _empty)(iter->target); + + iter->index += steps; + iter->cursor = (iter->cursor + steps) % iter->target->capacity; + + return true; +} + +/* Returns true only if the iterator moved */ +bool CMC_(PFX, _iter_rewind)(struct CMC_DEF_ITER(SNAME) * iter, size_t steps) +{ + if (iter->start) + return false; + + if (iter->index == 0) + { + iter->start = true; + return false; + } + + if (steps == 0 || iter->index < steps) + return false; + + iter->end = CMC_(PFX, _empty)(iter->target); + + iter->index -= steps; + + /* Prevent underflow */ + if (iter->cursor < steps) + iter->cursor += iter->target->capacity; + + iter->cursor -= steps; + + return true; +} + +/* Returns true only if the iterator was able to be positioned at the */ +/* given index */ +bool CMC_(PFX, _iter_go_to)(struct CMC_DEF_ITER(SNAME) * iter, size_t index) +{ + if (index >= iter->target->count) + return false; + + if (iter->index > index) + return CMC_(PFX, _iter_rewind)(iter, iter->index - index); + else if (iter->index < index) + return CMC_(PFX, _iter_advance)(iter, index - iter->index); + + return true; +} + +V CMC_(PFX, _iter_value)(struct CMC_DEF_ITER(SNAME) * iter) +{ + if (CMC_(PFX, _empty)(iter->target)) + return (V){ 0 }; + + return iter->target->buffer[iter->cursor]; +} + +V *CMC_(PFX, _iter_rvalue)(struct CMC_DEF_ITER(SNAME) * iter) +{ + if (CMC_(PFX, _empty)(iter->target)) + return NULL; + + return &(iter->target->buffer[iter->cursor]); +} + +size_t CMC_(PFX, _iter_index)(struct CMC_DEF_ITER(SNAME) * iter) +{ + return iter->index; +} + +#endif /* CMC_EXT_QUEUE_ITER */ + +/** + * STR + * + * Print helper functions. + */ +#ifdef CMC_EXT_QUEUE_STR + +bool CMC_(PFX, _to_string)(struct SNAME *_queue_, FILE *fptr) +{ + struct SNAME *q_ = _queue_; + + return 0 <= fprintf(fptr, + "struct %s<%s> " + "at %p { " + "buffer:%p, " + "capacity:%" PRIuMAX ", " + "count:%" PRIuMAX ", " + "front:%" PRIuMAX ", " + "back:%" PRIuMAX ", " + "flag:%d, " + "f_val:%p, " + "alloc:%p, " + "callbacks:%p }", + CMC_TO_STRING(SNAME), CMC_TO_STRING(V), q_, q_->buffer, q_->capacity, q_->count, q_->front, + q_->back, q_->flag, q_->f_val, q_->alloc, CMC_CALLBACKS_GET(q_)); +} + +bool CMC_(PFX, _print)(struct SNAME *_queue_, FILE *fptr, const char *start, const char *separator, const char *end) +{ + fprintf(fptr, "%s", start); + + for (size_t i = _queue_->front, j = 0; j < _queue_->count; j++) + { + if (!_queue_->f_val->str(fptr, _queue_->buffer[i])) + return false; + + i = (i + 1) % _queue_->capacity; + + if (j + 1 < _queue_->count) + fprintf(fptr, "%s", separator); + } + + fprintf(fptr, "%s", end); + + return true; +} + +#endif /* CMC_EXT_QUEUE_STR */ diff --git a/src/cmc/cmc/queue/ext/header.h b/src/cmc/cmc/queue/ext/header.h new file mode 100644 index 0000000..7d9b357 --- /dev/null +++ b/src/cmc/cmc/queue/ext/header.h @@ -0,0 +1,80 @@ +/** + * Copyright (c) 2019 Leonardo Vencovsky + * + * This file is part of the C Macro Collections Libray. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in 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: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * 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 + * AUTHORS OR COPYRIGHT HOLDERS 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 IN THE + * SOFTWARE. + */ + +/** + * INIT + * + * The part 'INIT' gives a new way of initializing a collection. The collection + * struct is not heap allocated, only its internal structure (nodes, buffers). + */ +#ifdef CMC_EXT_QUEUE_INIT + +struct SNAME CMC_(PFX, _init)(size_t capacity, struct CMC_DEF_FVAL(SNAME) * f_val); +struct SNAME CMC_(PFX, _init_custom)(size_t capacity, struct CMC_DEF_FVAL(SNAME) * f_val, + struct CMC_ALLOC_NODE_NAME *alloc, struct CMC_CALLBACKS_NAME *callbacks); +void CMC_(PFX, _release)(struct SNAME _queue_); + +#endif /* CMC_EXT_QUEUE_INIT */ + +/** + * ITER + * + * Queue bi-directional iterator. + */ +#ifdef CMC_EXT_QUEUE_ITER + +/* Iterator Initialization */ +struct CMC_DEF_ITER(SNAME) CMC_(PFX, _iter_start)(struct SNAME *target); +struct CMC_DEF_ITER(SNAME) CMC_(PFX, _iter_end)(struct SNAME *target); +/* Iterator State */ +bool CMC_(PFX, _iter_at_start)(struct CMC_DEF_ITER(SNAME) * iter); +bool CMC_(PFX, _iter_at_end)(struct CMC_DEF_ITER(SNAME) * iter); +/* Iterator Movement */ +bool CMC_(PFX, _iter_to_start)(struct CMC_DEF_ITER(SNAME) * iter); +bool CMC_(PFX, _iter_to_end)(struct CMC_DEF_ITER(SNAME) * iter); +bool CMC_(PFX, _iter_next)(struct CMC_DEF_ITER(SNAME) * iter); +bool CMC_(PFX, _iter_prev)(struct CMC_DEF_ITER(SNAME) * iter); +bool CMC_(PFX, _iter_advance)(struct CMC_DEF_ITER(SNAME) * iter, size_t steps); +bool CMC_(PFX, _iter_rewind)(struct CMC_DEF_ITER(SNAME) * iter, size_t steps); +bool CMC_(PFX, _iter_go_to)(struct CMC_DEF_ITER(SNAME) * iter, size_t index); +/* Iterator Access */ +V CMC_(PFX, _iter_value)(struct CMC_DEF_ITER(SNAME) * iter); +V *CMC_(PFX, _iter_rvalue)(struct CMC_DEF_ITER(SNAME) * iter); +size_t CMC_(PFX, _iter_index)(struct CMC_DEF_ITER(SNAME) * iter); + +#endif /* CMC_EXT_QUEUE_ITER */ + +/** + * STR + * + * Print helper functions. + */ +#ifdef CMC_EXT_QUEUE_STR + +/* Debug prints the struct to fptr */ +bool CMC_(PFX, _to_string)(struct SNAME *_queue_, FILE *fptr); +/* Prints each item in the queue as an array */ +bool CMC_(PFX, _print)(struct SNAME *_queue_, FILE *fptr, const char *start, const char *separator, const char *end); + +#endif /* CMC_EXT_QUEUE_STR */ diff --git a/src/cmc/cmc/queue/ext/struct.h b/src/cmc/cmc/queue/ext/struct.h new file mode 100644 index 0000000..c773ad5 --- /dev/null +++ b/src/cmc/cmc/queue/ext/struct.h @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2019 Leonardo Vencovsky + * + * This file is part of the C Macro Collections Libray. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in 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: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * 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 + * AUTHORS OR COPYRIGHT HOLDERS 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 IN THE + * SOFTWARE. + */ + +/** + * ITER + * + * Queue bi-directional iterator. + */ +#ifdef CMC_EXT_QUEUE_ITER + +/* Queue Iterator */ +struct CMC_DEF_ITER(SNAME) +{ + /* Target queue */ + struct SNAME *target; + /* Cursor's position (index) */ + size_t cursor; + /* Keeps track of relative index to the iteration of elements */ + size_t index; + /* If the iterator has reached the start of the iteration */ + bool start; + /* If the iterator has reached the end of the iteration */ + bool end; +}; + +#endif /* CMC_EXT_QUEUE_ITER */ diff --git a/src/cmc/cmc/queue/header.h b/src/cmc/cmc/queue/header.h new file mode 100644 index 0000000..3c3648f --- /dev/null +++ b/src/cmc/cmc/queue/header.h @@ -0,0 +1,66 @@ +/** + * Copyright (c) 2019 Leonardo Vencovsky + * + * This file is part of the C Macro Collections Libray. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in 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: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * 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 + * AUTHORS OR COPYRIGHT HOLDERS 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 IN THE + * SOFTWARE. + */ + +/* Value function table */ +struct CMC_DEF_FVAL(SNAME) +{ + /* Comparator function */ + CMC_DEF_FTAB_CMP(V); + /* Copy function */ + CMC_DEF_FTAB_CPY(V); + /* To string function */ + CMC_DEF_FTAB_STR(V); + /* Free from memory function */ + CMC_DEF_FTAB_FREE(V); + /* Hash function */ + CMC_DEF_FTAB_HASH(V); + /* Priority function */ + CMC_DEF_FTAB_PRI(V); +}; + +/* Collection Allocation and Deallocation */ +struct SNAME *CMC_(PFX, _new)(size_t capacity, struct CMC_DEF_FVAL(SNAME) * f_val); +struct SNAME *CMC_(PFX, _new_custom)(size_t capacity, struct CMC_DEF_FVAL(SNAME) * f_val, + struct CMC_ALLOC_NODE_NAME *alloc, struct CMC_CALLBACKS_NAME *callbacks); +void CMC_(PFX, _clear)(struct SNAME *_queue_); +void CMC_(PFX, _free)(struct SNAME *_queue_); +/* Customization of Allocation and Callbacks */ +void CMC_(PFX, _customize)(struct SNAME *_queue_, struct CMC_ALLOC_NODE_NAME *alloc, + struct CMC_CALLBACKS_NAME *callbacks); +/* Collection Input and Output */ +bool CMC_(PFX, _enqueue)(struct SNAME *_queue_, V value); +bool CMC_(PFX, _dequeue)(struct SNAME *_queue_); +/* Element Access */ +V CMC_(PFX, _peek)(struct SNAME *_queue_); +/* Collection State */ +bool CMC_(PFX, _contains)(struct SNAME *_queue_, V value); +bool CMC_(PFX, _empty)(struct SNAME *_queue_); +bool CMC_(PFX, _full)(struct SNAME *_queue_); +size_t CMC_(PFX, _count)(struct SNAME *_queue_); +size_t CMC_(PFX, _capacity)(struct SNAME *_queue_); +int CMC_(PFX, _flag)(struct SNAME *_queue_); +/* Collection Utility */ +bool CMC_(PFX, _resize)(struct SNAME *_queue_, size_t capacity); +struct SNAME *CMC_(PFX, _copy_of)(struct SNAME *_queue_); +bool CMC_(PFX, _equals)(struct SNAME *_queue1_, struct SNAME *_queue2_); diff --git a/src/cmc/cmc/queue/struct.h b/src/cmc/cmc/queue/struct.h new file mode 100644 index 0000000..9860b07 --- /dev/null +++ b/src/cmc/cmc/queue/struct.h @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2019 Leonardo Vencovsky + * + * This file is part of the C Macro Collections Libray. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in 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: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * 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 + * AUTHORS OR COPYRIGHT HOLDERS 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 IN THE + * SOFTWARE. + */ + +struct SNAME +{ + /* Dynamic circular array of elements */ + V *buffer; + /* Current circular array capacity */ + size_t capacity; + /* Current amount of elements */ + size_t count; + /* Index representing the front of the queue */ + size_t front; + /* Index representing the back of the queue */ + size_t back; + /* Flags indicating errors or success */ + int flag; + /* Value function table */ + struct CMC_DEF_FVAL(SNAME) * f_val; + /* Custom allocation functions */ + struct CMC_ALLOC_NODE_NAME *alloc; + /* Custom callback functions */ + CMC_CALLBACKS_DECL; +}; diff --git a/src/cmc/cmc/stack/header.h b/src/cmc/cmc/stack/header.h index eaa6f13..9eabcab 100644 --- a/src/cmc/cmc/stack/header.h +++ b/src/cmc/cmc/stack/header.h @@ -22,6 +22,23 @@ * SOFTWARE. */ +/* Value function table */ +struct CMC_DEF_FVAL(SNAME) +{ + /* Comparator function */ + CMC_DEF_FTAB_CMP(V); + /* Copy function */ + CMC_DEF_FTAB_CPY(V); + /* To string function */ + CMC_DEF_FTAB_STR(V); + /* Free from memory function */ + CMC_DEF_FTAB_FREE(V); + /* Hash function */ + CMC_DEF_FTAB_HASH(V); + /* Priority function */ + CMC_DEF_FTAB_PRI(V); +}; + /* Collection Allocation and Deallocation */ struct SNAME *CMC_(PFX, _new)(size_t capacity, struct CMC_DEF_FVAL(SNAME) * f_val); struct SNAME *CMC_(PFX, _new_custom)(size_t capacity, struct CMC_DEF_FVAL(SNAME) * f_val, diff --git a/src/cmc/cmc/stack/struct.h b/src/cmc/cmc/stack/struct.h index 3160683..a0ddc5c 100644 --- a/src/cmc/cmc/stack/struct.h +++ b/src/cmc/cmc/stack/struct.h @@ -40,19 +40,3 @@ struct SNAME /* Custom callback functions */ CMC_CALLBACKS_DECL; }; -/* Value struct function table */ -struct CMC_DEF_FVAL(SNAME) -{ - /* Comparator function */ - CMC_DEF_FTAB_CMP(V); - /* Copy function */ - CMC_DEF_FTAB_CPY(V); - /* To string function */ - CMC_DEF_FTAB_STR(V); - /* Free from memory function */ - CMC_DEF_FTAB_FREE(V); - /* Hash function */ - CMC_DEF_FTAB_HASH(V); - /* Priority function */ - CMC_DEF_FTAB_PRI(V); -}; diff --git a/src/cmc/cor/undef.h b/src/cmc/cor/undef.h index 74bce3f..743eb7c 100644 --- a/src/cmc/cor/undef.h +++ b/src/cmc/cor/undef.h @@ -49,12 +49,22 @@ #undef CMC_EXT_DEQUE_ITER #undef CMC_EXT_DEQUE_STR +/* Hashbidimap */ +#undef CMC_EXT_HASHBIDIMAP_INIT +#undef CMC_EXT_HASHBIDIMAP_ITER +#undef CMC_EXT_HASHBIDIMAP_STR + /* List */ #undef CMC_EXT_LIST_INIT #undef CMC_EXT_LIST_ITER #undef CMC_EXT_LIST_SEQ #undef CMC_EXT_LIST_STR +/* Queue */ +#undef CMC_EXT_QUEUE_INIT +#undef CMC_EXT_QUEUE_ITER +#undef CMC_EXT_QUEUE_STR + /* Stack */ #undef CMC_EXT_STACK_INIT #undef CMC_EXT_STACK_ITER diff --git a/src/cmc/deque.h b/src/cmc/deque.h index 5d16067..82a4911 100644 --- a/src/cmc/deque.h +++ b/src/cmc/deque.h @@ -23,7 +23,7 @@ */ /** - * cmc_deque.h + * deque.h * * Creation Date: 20/03/2019 * @@ -77,7 +77,7 @@ * ITER - Deque iterator * STR - Print helper functions */ -#define CMC_EXT_CMC_DEQUE_PARTS INIT, ITER, STR +#define CMC_EXT_DEQUE_PARTS INIT, ITER, STR /**/ #include "cmc/deque/ext/struct.h" /**/ diff --git a/src/cmc/hashbidimap.h b/src/cmc/hashbidimap.h new file mode 100644 index 0000000..9a9a57b --- /dev/null +++ b/src/cmc/hashbidimap.h @@ -0,0 +1,85 @@ +/** + * Copyright (c) 2019 Leonardo Vencovsky + * + * This file is part of the C Macro Collections Libray. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in 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: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * 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 + * AUTHORS OR COPYRIGHT HOLDERS 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 IN THE + * SOFTWARE. + */ + +/** + * hashbidimap.h + * + * Creation Date: 26/09/2019 + * + * Authors: + * Leonardo Vencovsky (https://github.com/LeoVen) + * + */ + +/** + * HashBidiMap + * + * A bidirectional map is a map that allows you to create a bijection in both + * directions between two sets of elements (K <-> V). It is possible to retrieve + * the value using a key or retrieve a key using a value. The naming (key and + * value) is simply used to differentiate between one set of elements and the + * other set of elements. + * + * Implementation + * + * This implementation uses two arrays of pointers to an entry containing both + * the key and the value. + */ + +#include "cor/core.h" +#include "cor/hashtable.h" + +/** + * Used values + * K - hashbidimap key data type + * V - hashbidimap value data type + * SNAME - struct name and prefix of other related structs + * PFX - functions prefix + */ + +/* Structs definition */ +#include "cmc/hashbidimap/struct.h" + +/* Function declaration */ +#include "cmc/hashbidimap/header.h" + +/* Function implementation */ +#include "cmc/hashbidimap/code.h" + +/** + * Extensions + * + * INIT - Initializes the struct on the stack + * ITER - Hashbidimap iterator + * STR - Print helper functions + */ +#define CMC_EXT_HASHBIDIMAP_PARTS INIT, ITER, STR +/**/ +#include "cmc/hashbidimap/ext/struct.h" +/**/ +#include "cmc/hashbidimap/ext/header.h" +/**/ +#include "cmc/hashbidimap/ext/code.h" + +#include "cor/undef.h" diff --git a/src/cmc/hashmap.h b/src/cmc/hashmap.h new file mode 100644 index 0000000..9ef082e --- /dev/null +++ b/src/cmc/hashmap.h @@ -0,0 +1,78 @@ +/** + * Copyright (c) 2019 Leonardo Vencovsky + * + * This file is part of the C Macro Collections Libray. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in 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: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * 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 + * AUTHORS OR COPYRIGHT HOLDERS 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 IN THE + * SOFTWARE. + */ + +/** + * hashmap.h + * + * Creation Date: 03/04/2019 + * + * Authors: + * Leonardo Vencovsky (https://github.com/LeoVen) + * + */ + +/** + * HashMap + * + * A HashMap is an implementation of a Map with unique keys, where every key is + * mapped to a value. The keys are not sorted. It is implemented as a flat + * hashtable with linear probing and robin hood hashing. + */ + +#include "cor/core.h" +#include "cor/hashtable.h" + +/** + * Used values + * K - hashmap key data type + * V - hashmap value data type + * SNAME - struct name and prefix of other related structs + * PFX - functions prefix + */ + +/* Structs definition */ +#include "cmc/hashmap/struct.h" + +/* Function declaration */ +#include "cmc/hashmap/header.h" + +/* Function implementation */ +#include "cmc/hashmap/code.h" + +/** + * Extensions + * + * INIT - Initializes the struct on the stack + * ITER - hashmap iterator + * STR - Print helper functions + */ +#define CMC_EXT_HASHMAP_PARTS INIT, ITER, STR +/**/ +#include "cmc/hashmap/ext/struct.h" +/**/ +#include "cmc/hashmap/ext/header.h" +/**/ +#include "cmc/hashmap/ext/code.h" + +#include "cor/undef.h" diff --git a/src/cmc/hashmultimap.h b/src/cmc/hashmultimap.h new file mode 100644 index 0000000..03cc759 --- /dev/null +++ b/src/cmc/hashmultimap.h @@ -0,0 +1,103 @@ +/** + * Copyright (c) 2019 Leonardo Vencovsky + * + * This file is part of the C Macro Collections Libray. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in 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: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * 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 + * AUTHORS OR COPYRIGHT HOLDERS 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 IN THE + * SOFTWARE. + */ + +/** + * hashmultimap.h + * + * Creation Date: 26/04/2019 + * + * Authors: + * Leonardo Vencovsky (https://github.com/LeoVen) + * + */ + +/** + * HashMultiMap + * + * The HashMultiMap is a Map that allows for multiple keys. This is a data + * structure that has a very narrow usage. A Map (either TreeMap or HashMap) can + * also work like a HashMultiMap if a certain key is mapped to another + * collection. + * + * Map> maps an integer to a list of integer. + * HashMultiMap maps many integer keys to integer values. + * + * The difference is that in a HashMultiMap you can store keys that are the same + * but might be different instances of the same value. This is also relevant if + * your data type is more complex like a struct where its ID is the same but + * some other members of this data type are different. + * + * Implementation + * + * The map uses separate chaining and robin hood hashing. Its internal buffer + * is made of a pair of pointers to entries that represent a linked list. So + * accessing map->buffer[0] would give the first pair of pointers. Each entry + * has both a pointer to a previous entry and a next entry. This design choice + * was made so that every collection in this library has a two-way iterator + * with a very few exceptions. + * + * Each entry is composed of a Key and a Value. Entries with the same key should + * always hash to the same linked list. Also, keys that hash to the same bucket + * will also be in the same linked list. + * + * The order of inserting and removing the same keys will behave like a FIFO. So + * the first key added will be the first to be removed. + */ + +#include "cor/core.h" +#include "cor/hashtable.h" + +/** + * Used values + * K - hashmap key data type + * V - hashmap value data type + * SNAME - struct name and prefix of other related structs + * PFX - functions prefix + */ + +/* Structs definition */ +#include "cmc/hashmultimap/struct.h" + +/* Function declaration */ +#include "cmc/hashmultimap/header.h" + +/* Function implementation */ +#include "cmc/hashmultimap/code.h" + +/** + * Extensions + * + * INIT - Initializes the struct on the stack + * ITER - hashmultimap iterator + * STR - Print helper functions + */ +#define CMC_EXT_HASHMULTIMAP_PARTS INIT, ITER, STR +/**/ +#include "cmc/hashmultimap/ext/struct.h" +/**/ +#include "cmc/hashmultimap/ext/header.h" +/**/ +#include "cmc/hashmultimap/ext/code.h" + +#include "cor/undef.h" diff --git a/src/cmc/list.h b/src/cmc/list.h index 0a4ad9f..ebb1b4e 100644 --- a/src/cmc/list.h +++ b/src/cmc/list.h @@ -23,7 +23,7 @@ */ /** - * cmc_list.h + * list.h * * Creation Date: 12/02/2019 * @@ -85,11 +85,12 @@ /** * Extensions * + * INIT - Initializes the struct on the stack * ITER - List iterator * SEQ - Push and pop sequence of items * STR - Print helper functions */ -#define CMC_EXT_CMC_LIST_PARTS INIT, ITER, SEQ, STR +#define CMC_EXT_LIST_PARTS INIT, ITER, SEQ, STR /**/ #include "cmc/list/ext/struct.h" /**/ diff --git a/src/cmc/queue.h b/src/cmc/queue.h new file mode 100644 index 0000000..c16c3da --- /dev/null +++ b/src/cmc/queue.h @@ -0,0 +1,99 @@ +/** + * Copyright (c) 2019 Leonardo Vencovsky + * + * This file is part of the C Macro Collections Libray. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in 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: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * 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 + * AUTHORS OR COPYRIGHT HOLDERS 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 IN THE + * SOFTWARE. + */ + +/** + * queue.h + * + * Creation Date: 15/02/2019 + * + * Authors: + * Leonardo Vencovsky (https://github.com/LeoVen) + * + */ + +/** + * Queue + * + * A Queue is a First-In First-out (or Last-in Last-out) data structure. It is + * a Dynamic Circular Array where elements are added from one end of the array + * and removed from the other end. The circular array here (also known as + * circular buffer or ring buffer) is very important so that both adding and + * removing elements from the Queue are done instantly. The array is linear but + * with the modulo operator it is treated as a circular sequence of elements. + * + * If the Queue was implemented as a regular Dynamic Array, when adding or + * removing an element at the front, it would be necessary to shift all elements + * currently present in the Queue and this would add up a lot of computing time. + * Shifting `100000` elements in memory by one position every time an element is + * added to the Queue is simply not efficient. + * + * The Queue has two ends. The `front` and `back`. In this implementation all + * elements are added to the back of the Queue and removed from the front, which + * is more or less how queues work in real life. Unlike a Stack that only has + * operations at one end of the buffer, the Queue needs to be implemented as a + * circular array in order to quickly add or remove elements. + * + * The Queue has three main functions: `enqueue` which adds an element to the + * Queue; `dequeue` which removes an element from the Queue; and `peek` which + * return the element at the front of the Queue, that is, the next element to + * be removed from it. + * + * The Queue is used in many applications where a resource is shared among + * multiple consumers and the Queue is responsible for scheduling the access to + * the resource. + */ + +#include "cor/core.h" + +/** + * Used values + * V - queue data type + * SNAME - struct name and prefix of other related structs + * PFX - functions prefix + */ + +/* Structs definition */ +#include "cmc/queue/struct.h" + +/* Function declaration */ +#include "cmc/queue/header.h" + +/* Function implementation */ +#include "cmc/queue/code.h" + +/** + * Extensions + * + * ITER - List iterator + * STR - Print helper functions + */ +#define CMC_EXT_QUEUE_PARTS INIT, ITER, STR +/**/ +#include "cmc/queue/ext/struct.h" +/**/ +#include "cmc/queue/ext/header.h" +/**/ +#include "cmc/queue/ext/code.h" + +#include "cor/undef.h" diff --git a/src/cmc/stack.h b/src/cmc/stack.h index c86ca3b..f6ae4e0 100644 --- a/src/cmc/stack.h +++ b/src/cmc/stack.h @@ -23,7 +23,7 @@ */ /** - * cmc_stack.h + * stack.h * * Creation Date: 14/02/2019 * @@ -53,7 +53,7 @@ /** * Used values - * V - Stack data type + * V - stack data type * SNAME - struct name and prefix of other related structs * PFX - functions prefix */ @@ -73,7 +73,7 @@ * ITER - Stack iterator * STR - Print helper functions */ -#define CMC_EXT_CMC_STACK_PARTS INIT, ITER, STR +#define CMC_EXT_STACK_PARTS INIT, ITER, STR /**/ #include "cmc/stack/ext/struct.h" /**/ diff --git a/tests/Makefile b/tests/Makefile index 0d19864..6fdcc30 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -3,11 +3,15 @@ CFLAGS=-Wall -fprofile-arcs -ftest-coverage -g -O0 INCLUDE=-I ../src main: - $(CC) ./main.c $(INCLUDE) $(CFLAGS) -o $@.exe - valgrind --leak-check=yes ./$@.exe + mkdir -p ./out + $(CC) ./main.c $(INCLUDE) $(CFLAGS) -o ./out/$@.exe + valgrind --leak-check=yes ./out/$@.exe + gcov -p ./out/$@.gcda + +dev: main mkdir -p ./coverage - cd ./coverage && gcov -p ../main.gcda + mv *.gcov ./coverage clean: - rm ./*.gcda ./*.gcno ./*.exe -f - rm ./coverage -rf + rm ./*.gcov ./*.gcda ./*.gcno ./*.exe -f + rm ./coverage ./out -rf diff --git a/tests/main.c b/tests/main.c index 8d954bf..6ef88f7 100644 --- a/tests/main.c +++ b/tests/main.c @@ -3,9 +3,9 @@ // #include "unt_bitset.h" #include "unt_deque.h" -// #include "unt_hashbidimap.h" -// #include "unt_hashmap.h" -// #include "unt_hashmultimap.h" +#include "unt_hashbidimap.h" +#include "unt_hashmap.h" +#include "unt_hashmultimap.h" // #include "unt_hashmultiset.h" // #include "unt_hashset.h" // #include "unt_heap.h" @@ -56,12 +56,12 @@ int main(void) // cmc_run(CMCBitSetIter, units, tests); cmc_run(CMCDeque, units, tests); cmc_run(CMCDequeIter, units, tests); - // cmc_run(CMCHashBidiMap, units, tests); - // cmc_run(CMCHashBidiMapIter, units, tests); - // cmc_run(CMCHashMap, units, tests); - // cmc_run(CMCHashMapIter, units, tests); - // cmc_run(CMCHashMultiMap, units, tests); - // cmc_run(CMCHashMultiMapIter, units, tests); + cmc_run(CMCHashBidiMap, units, tests); + cmc_run(CMCHashBidiMapIter, units, tests); + cmc_run(CMCHashMap, units, tests); + cmc_run(CMCHashMapIter, units, tests); + cmc_run(CMCHashMultiMap, units, tests); + cmc_run(CMCHashMultiMapIter, units, tests); // cmc_run(CMCHashMultiSet, units, tests); // cmc_run(CMCHashMultiSetIter, units, tests); // cmc_run(CMCHashSet, units, tests); diff --git a/tests/unt_deque.h b/tests/unt_deque.h index 244b4a1..f765eda 100644 --- a/tests/unt_deque.h +++ b/tests/unt_deque.h @@ -8,6 +8,7 @@ #define SNAME deque #define CMC_EXT_DEQUE_INIT #define CMC_EXT_DEQUE_ITER +#define CMC_EXT_DEQUE_STR #include "cmc/deque.h" struct deque_fval *d_fval = &(struct deque_fval){ diff --git a/tests/unt_hashbidimap.h b/tests/unt_hashbidimap.h new file mode 100644 index 0000000..13db4ec --- /dev/null +++ b/tests/unt_hashbidimap.h @@ -0,0 +1,818 @@ +#ifndef CMC_TESTS_UNT_HASHBIDIMAP_H +#define CMC_TESTS_UNT_HASHBIDIMAP_H + +#include "utl.h" + +#define V size_t +#define K size_t +#define PFX hbm +#define SNAME hashbidimap +#define CMC_EXT_HASHBIDIMAP_INIT +#define CMC_EXT_HASHBIDIMAP_ITER +#define CMC_EXT_HASHBIDIMAP_STR +#include "cmc/hashbidimap.h" + +struct hashbidimap_fkey *hbm_fkey = &(struct hashbidimap_fkey){ + .cmp = cmc_size_cmp, .cpy = NULL, .str = cmc_size_str, .free = NULL, .hash = cmc_size_hash, .pri = cmc_size_cmp +}; + +struct hashbidimap_fval *hbm_fval = &(struct hashbidimap_fval){ + .cmp = cmc_size_cmp, .cpy = NULL, .str = cmc_size_str, .free = NULL, .hash = cmc_size_hash, .pri = cmc_size_cmp +}; + +struct hashbidimap_fkey *hbm_fkey_counter = &(struct hashbidimap_fkey){ + .cmp = k_c_cmp, .cpy = k_c_cpy, .str = k_c_str, .free = k_c_free, .hash = k_c_hash, .pri = k_c_pri +}; + +struct hashbidimap_fval *hbm_fval_counter = &(struct hashbidimap_fval){ + .cmp = v_c_cmp, .cpy = v_c_cpy, .str = v_c_str, .free = v_c_free, .hash = v_c_hash, .pri = v_c_pri +}; + +struct cmc_alloc_node *hbm_alloc_node = + &(struct cmc_alloc_node){ .malloc = malloc, .calloc = calloc, .realloc = realloc, .free = free }; + +CMC_CREATE_UNIT(CMCHashBidiMap, true, { + CMC_CREATE_TEST(PFX##_new(), { + struct hashbidimap *map = hbm_new(943722, 0.6, hbm_fkey, hbm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + cmc_assert_not_equals(ptr, NULL, map->buffer); + cmc_assert_equals(size_t, 0, map->count); + cmc_assert_equals(double, 0.6, map->load); + cmc_assert_equals(int32_t, CMC_FLAG_OK, map->flag); + cmc_assert_equals(ptr, hbm_fkey, map->f_key); + cmc_assert_equals(ptr, hbm_fval, map->f_val); + cmc_assert_equals(ptr, cmc_alloc_node_default.malloc, map->alloc->malloc); + cmc_assert_equals(ptr, cmc_alloc_node_default.calloc, map->alloc->calloc); + cmc_assert_equals(ptr, cmc_alloc_node_default.realloc, map->alloc->realloc); + cmc_assert_equals(ptr, cmc_alloc_node_default.free, map->alloc->free); + cmc_assert_equals(ptr, NULL, map->callbacks); + + cmc_assert_greater_equals(size_t, (943722 / 0.6), hbm_capacity(map)); + + hbm_free(map); + + map = hbm_new(100, 0.6, hbm_fkey, hbm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + for (size_t i = 0; i < map->capacity; i++) + { + cmc_assert_equals(ptr, NULL, map->buffer[i][0]); + cmc_assert_equals(ptr, NULL, map->buffer[i][1]); + } + + hbm_free(map); + + map = hbm_new(0, 0.6, hbm_fkey, hbm_fval); + cmc_assert_equals(ptr, NULL, map); + + map = hbm_new(UINT64_MAX, 0.99, hbm_fkey, hbm_fval); + cmc_assert_equals(ptr, NULL, map); + + map = hbm_new(1000, 0.6, hbm_fkey, NULL); + cmc_assert_equals(ptr, NULL, map); + + map = hbm_new(1000, 0.6, NULL, hbm_fval); + cmc_assert_equals(ptr, NULL, map); + + map = hbm_new(1000, 0.6, NULL, NULL); + cmc_assert_equals(ptr, NULL, map); + }); + + CMC_CREATE_TEST(PFX##_new_custom(), { + struct hashbidimap *map = hbm_new_custom(943722, 0.6, hbm_fkey, hbm_fval, hbm_alloc_node, callbacks); + + cmc_assert_not_equals(ptr, NULL, map); + cmc_assert_not_equals(ptr, NULL, map->buffer); + cmc_assert_equals(size_t, 0, map->count); + cmc_assert_equals(double, 0.6, map->load); + cmc_assert_equals(int32_t, CMC_FLAG_OK, map->flag); + cmc_assert_equals(ptr, hbm_fkey, map->f_key); + cmc_assert_equals(ptr, hbm_fval, map->f_val); + cmc_assert_equals(ptr, hbm_alloc_node, map->alloc); + cmc_assert_equals(ptr, callbacks, map->callbacks); + + cmc_assert_greater_equals(size_t, (943722 / 0.6), hbm_capacity(map)); + + hbm_free(map); + + map = hbm_new_custom(0, 0.6, hbm_fkey, hbm_fval, NULL, NULL); + cmc_assert_equals(ptr, NULL, map); + + map = hbm_new_custom(UINT64_MAX, 0.99, hbm_fkey, hbm_fval, NULL, NULL); + cmc_assert_equals(ptr, NULL, map); + + map = hbm_new_custom(1000, 0.6, hbm_fkey, NULL, NULL, NULL); + cmc_assert_equals(ptr, NULL, map); + + map = hbm_new_custom(1000, 0.6, NULL, hbm_fval, NULL, NULL); + cmc_assert_equals(ptr, NULL, map); + + map = hbm_new_custom(1000, 0.6, NULL, NULL, NULL, NULL); + cmc_assert_equals(ptr, NULL, map); + }); + + CMC_CREATE_TEST(PFX##_clear(), { + k_total_free = 0; + v_total_free = 0; + struct hashbidimap *map = hbm_new(100, 0.6, hbm_fkey_counter, hbm_fval_counter); + + cmc_assert_not_equals(ptr, NULL, map); + + for (size_t i = 1; i <= 1000; i++) + hbm_insert(map, i, i); + + cmc_assert_equals(size_t, 1000, map->count); + + map->flag = CMC_FLAG_ERROR; + hbm_clear(map); + + cmc_assert_equals(size_t, 0, map->count); + cmc_assert_equals(int32_t, CMC_FLAG_OK, map->flag); + cmc_assert_equals(int32_t, 1000, k_total_free); + cmc_assert_equals(int32_t, 1000, v_total_free); + + hbm_free(map); + k_total_free = 0; + v_total_free = 0; + }); + + CMC_CREATE_TEST(PFX##_free(), { + k_total_free = 0; + v_total_free = 0; + struct hashbidimap *map = hbm_new(100, 0.6, hbm_fkey_counter, hbm_fval_counter); + + cmc_assert_not_equals(ptr, NULL, map); + + for (size_t i = 1; i <= 1000; i++) + hbm_insert(map, i, i); + + cmc_assert_equals(size_t, 1000, map->count); + + hbm_free(map); + + cmc_assert_equals(int32_t, 1000, k_total_free); + cmc_assert_equals(int32_t, 1000, v_total_free); + + map = hbm_new(1000, 0.6, hbm_fkey_counter, hbm_fval_counter); + + cmc_assert_not_equals(ptr, NULL, map); + cmc_assert_not_equals(ptr, NULL, map->buffer); + + hbm_free(map); + + cmc_assert_equals(int32_t, 1000, k_total_free); + cmc_assert_equals(int32_t, 1000, v_total_free); + k_total_free = 0; + v_total_free = 0; + }); + + CMC_CREATE_TEST(customize, { + struct hashbidimap *map = hbm_new_custom(100, 0.6, hbm_fkey, hbm_fval, NULL, NULL); + + cmc_assert_not_equals(ptr, NULL, map); + + cmc_assert_equals(ptr, cmc_alloc_node_default.malloc, map->alloc->malloc); + cmc_assert_equals(ptr, cmc_alloc_node_default.calloc, map->alloc->calloc); + cmc_assert_equals(ptr, cmc_alloc_node_default.realloc, map->alloc->realloc); + cmc_assert_equals(ptr, cmc_alloc_node_default.free, map->alloc->free); + cmc_assert_equals(ptr, NULL, map->callbacks); + + hbm_free(map); + + struct cmc_alloc_node node; + node.malloc = malloc; + node.realloc = realloc; + node.free = free; + node.calloc = calloc; + + map = hbm_new_custom(100, 0.6, hbm_fkey, hbm_fval, &node, callbacks); + + cmc_assert_not_equals(ptr, NULL, map); + + cmc_assert_equals(ptr, &node, map->alloc); + cmc_assert_equals(ptr, callbacks, map->callbacks); + + hbm_free(map); + }); + + CMC_CREATE_TEST(buffer_growth[capacity = 1], { + struct hashbidimap *map = hbm_new(1, 0.7, hbm_fkey, hbm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + for (size_t i = 1; i <= 10000; i++) + cmc_assert(hbm_insert(map, i, i)); + + size_t sum = 0; + + for (size_t i = 0; i < hbm_capacity(map); i++) + { + if (map->buffer[i][0] && map->buffer[i][0] != CMC_ENTRY_DELETED) + { + sum += map->buffer[i][0]->key; + } + } + + cmc_assert_equals(size_t, 50005000, sum); + + hbm_free(map); + }); + + CMC_CREATE_TEST(insert, { + struct hashbidimap *map = hbm_new(100, 0.7, hbm_fkey, hbm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + cmc_assert(hbm_insert(map, 1, 1)); + cmc_assert(hbm_insert(map, 2, 2)); + cmc_assert(hbm_insert(map, 3, 3)); + + cmc_assert_equals(size_t, 3, hbm_count(map)); + + hbm_free(map); + }); + + CMC_CREATE_TEST(insert[duplicate], { + struct hashbidimap *map = hbm_new(100, 0.7, hbm_fkey, hbm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + cmc_assert(hbm_insert(map, 1, 1)); + cmc_assert(!hbm_insert(map, 1, 1)); + cmc_assert(!hbm_insert(map, 2, 1)); + cmc_assert(!hbm_insert(map, 1, 2)); + cmc_assert(hbm_insert(map, 2, 2)); + + cmc_assert_equals(size_t, 2, hbm_count(map)); + + hbm_free(map); + }); + + CMC_CREATE_TEST(insert[references], { + struct hashbidimap *map = hbm_new(500, 0.7, hbm_fkey, hbm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + for (size_t i = 0; i < 500; i++) + cmc_assert(hbm_insert(map, i, i)); + + for (size_t i = 0; i < map->capacity; i++) + { + struct hashbidimap_entry *kentry = map->buffer[i][0]; + struct hashbidimap_entry *ventry = map->buffer[i][1]; + + if (kentry && kentry != CMC_ENTRY_DELETED) + cmc_assert_equals(ptr, kentry, *(kentry->ref[0])); + if (ventry && ventry != CMC_ENTRY_DELETED) + cmc_assert_equals(ptr, ventry, *(ventry->ref[1])); + } + + hbm_free(map); + }); + + CMC_CREATE_TEST(insert[growth remove clear], { + struct hashbidimap *map = hbm_new(100, 0.7, hbm_fkey, hbm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + for (size_t i = 1; i <= 1000; i++) + cmc_assert(hbm_insert(map, i, i)); + + cmc_assert_equals(size_t, 1000, hbm_count(map)); + + size_t j = 1; + while (!hbm_empty(map)) + { + if (hbm_count(map) % 2 == 0) + cmc_assert(hbm_remove_by_key(map, j++, NULL, NULL)); + else + cmc_assert(hbm_remove_by_val(map, j++, NULL, NULL)); + } + + cmc_assert_equals(size_t, 0, hbm_count(map)); + + for (size_t i = 1; i <= 1000; i++) + cmc_assert(hbm_insert(map, i, i)); + + cmc_assert_equals(size_t, 1000, hbm_count(map)); + + hbm_clear(map); + + cmc_assert_equals(size_t, 0, hbm_count(map)); + + hbm_free(map); + }); + + CMC_CREATE_TEST(insert[ftab hash calls], { + struct hashbidimap *map = hbm_new(100, 0.7, hbm_fkey_counter, hbm_fval_counter); + + cmc_assert_not_equals(ptr, NULL, map); + + k_total_hash = 0; + v_total_hash = 0; + + for (size_t i = 1; i <= 1000; i++) + cmc_assert(hbm_insert(map, i, i)); + + cmc_assert_greater_equals(int32_t, 1000, k_total_hash); + cmc_assert_greater_equals(int32_t, 1000, v_total_hash); + + hbm_free(map); + + k_total_hash = 0; + v_total_hash = 0; + }); + + CMC_CREATE_TEST(update_key, { + struct hashbidimap *map = hbm_new(100, 0.7, hbm_fkey, hbm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + for (size_t i = 1; i <= 100; i++) + cmc_assert(hbm_insert(map, i, i)); + + cmc_assert_equals(size_t, 100, hbm_count(map)); + + for (size_t i = 100; i > 0; i--) + cmc_assert(hbm_update_key(map, i, i + 10)); + + cmc_assert_equals(size_t, 100, hbm_count(map)); + + for (size_t i = 1; i <= 100; i++) + cmc_assert_equals(size_t, i + 10, hbm_get_key(map, i)); + + cmc_assert_equals(size_t, 100, hbm_count(map)); + + hbm_free(map); + }); + + CMC_CREATE_TEST(update_key[empty], { + struct hashbidimap *map = hbm_new(100, 0.7, hbm_fkey, hbm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + cmc_assert_equals(size_t, 0, hbm_count(map)); + + cmc_assert(!hbm_update_key(map, 1, 1)); + + cmc_assert_equals(int32_t, CMC_FLAG_EMPTY, hbm_flag(map)); + + hbm_free(map); + }); + + CMC_CREATE_TEST(update_key[not_found duplicate], { + struct hashbidimap *map = hbm_new(100, 0.7, hbm_fkey, hbm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + cmc_assert(hbm_insert(map, 1, 1)); + + cmc_assert_equals(size_t, 1, hbm_count(map)); + + map->flag = CMC_FLAG_ERROR; + cmc_assert(hbm_update_key(map, 1, 1)); + + cmc_assert_equals(int32_t, CMC_FLAG_OK, hbm_flag(map)); + + cmc_assert(!hbm_update_key(map, 2, 2)); + + cmc_assert_equals(int32_t, CMC_FLAG_NOT_FOUND, hbm_flag(map)); + + cmc_assert(hbm_insert(map, 2, 2)); + + cmc_assert(!hbm_update_key(map, 1, 2)); + cmc_assert_equals(int32_t, CMC_FLAG_DUPLICATE, hbm_flag(map)); + cmc_assert(!hbm_update_key(map, 2, 1)); + cmc_assert_equals(int32_t, CMC_FLAG_DUPLICATE, hbm_flag(map)); + + cmc_assert(hbm_update_key(map, 2, 2)); + + hbm_free(map); + }); + + CMC_CREATE_TEST(update_val, { + struct hashbidimap *map = hbm_new(100, 0.7, hbm_fkey, hbm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + for (size_t i = 1; i <= 100; i++) + cmc_assert(hbm_insert(map, i, i)); + + cmc_assert_equals(size_t, 100, hbm_count(map)); + + for (size_t i = 100; i > 0; i--) + cmc_assert(hbm_update_val(map, i, i + 10)); + + cmc_assert_equals(size_t, 100, hbm_count(map)); + + for (size_t i = 1; i <= 100; i++) + cmc_assert_equals(size_t, i + 10, hbm_get_val(map, i)); + + cmc_assert_equals(size_t, 100, hbm_count(map)); + + hbm_free(map); + }); + + CMC_CREATE_TEST(update_val[empty], { + struct hashbidimap *map = hbm_new(100, 0.7, hbm_fkey, hbm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + cmc_assert_equals(size_t, 0, hbm_count(map)); + + cmc_assert(!hbm_update_val(map, 1, 1)); + + cmc_assert_equals(int32_t, CMC_FLAG_EMPTY, hbm_flag(map)); + + hbm_free(map); + }); + + CMC_CREATE_TEST(update_val[not_found duplicate], { + struct hashbidimap *map = hbm_new(100, 0.7, hbm_fkey, hbm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + cmc_assert(hbm_insert(map, 1, 1)); + + cmc_assert_equals(size_t, 1, hbm_count(map)); + + map->flag = CMC_FLAG_ERROR; + cmc_assert(hbm_update_val(map, 1, 1)); + + cmc_assert_equals(int32_t, CMC_FLAG_OK, hbm_flag(map)); + + cmc_assert(!hbm_update_val(map, 2, 2)); + + cmc_assert_equals(int32_t, CMC_FLAG_NOT_FOUND, hbm_flag(map)); + + cmc_assert(hbm_insert(map, 2, 2)); + + cmc_assert(!hbm_update_val(map, 1, 2)); + cmc_assert_equals(int32_t, CMC_FLAG_DUPLICATE, hbm_flag(map)); + cmc_assert(!hbm_update_val(map, 2, 1)); + cmc_assert_equals(int32_t, CMC_FLAG_DUPLICATE, hbm_flag(map)); + + cmc_assert(hbm_update_val(map, 2, 2)); + + hbm_free(map); + }); + + CMC_CREATE_TEST(remove_by_key[cleanup custom], { + struct hashbidimap *map = hbm_new_custom( + 10000, 0.6, hbm_fkey, hbm_fval, + &(struct cmc_alloc_node){ .malloc = malloc, .calloc = calloc, .realloc = realloc, .free = free }, + &(struct cmc_callbacks){ 0 }); + + cmc_assert_not_equals(ptr, NULL, map); + + for (size_t i = 0; i < 10000; i++) + { + cmc_assert(hbm_insert(map, i, i)); + } + + for (size_t i = 0; i < 10000; i++) + { + cmc_assert(hbm_remove_by_key(map, i, NULL, NULL)); + } + + for (size_t i = 0; i < map->capacity; i++) + { + struct hashbidimap_entry *k = map->buffer[i][0]; + struct hashbidimap_entry *v = map->buffer[i][1]; + + cmc_assert((k == NULL || k == CMC_ENTRY_DELETED) && (v == NULL || v == CMC_ENTRY_DELETED)); + } + + hbm_free(map); + }); + + CMC_CREATE_TEST(remove_by_val[cleanup custom], { + struct hashbidimap *map = hbm_new_custom( + 10000, 0.6, hbm_fkey, hbm_fval, + &(struct cmc_alloc_node){ .malloc = malloc, .calloc = calloc, .realloc = realloc, .free = free }, + &(struct cmc_callbacks){ 0 }); + + cmc_assert_not_equals(ptr, NULL, map); + + for (size_t i = 0; i < 10000; i++) + { + cmc_assert(hbm_insert(map, i, i)); + } + + for (size_t i = 0; i < 10000; i++) + { + cmc_assert(hbm_remove_by_val(map, i, NULL, NULL)); + } + + for (size_t i = 0; i < map->capacity; i++) + { + struct hashbidimap_entry *k = map->buffer[i][0]; + struct hashbidimap_entry *v = map->buffer[i][1]; + + cmc_assert((k == NULL || k == CMC_ENTRY_DELETED) && (v == NULL || v == CMC_ENTRY_DELETED)); + } + + hbm_free(map); + }); + + CMC_CREATE_TEST(copy_of, { + struct hashbidimap *map1 = hbm_new(100, 0.7, hbm_fkey, hbm_fval); + + for (size_t i = 0; i < 100; i++) + cmc_assert(hbm_insert(map1, i, i)); + + struct hashbidimap *map2 = hbm_copy_of(map1); + + cmc_assert_not_equals(ptr, NULL, map2); + cmc_assert_equals(size_t, hbm_count(map1), hbm_count(map2)); + + for (size_t i = 0; i < 100; i++) + { + cmc_assert(hbm_contains_key(map2, i) && hbm_contains_key(map1, i)); + cmc_assert(hbm_contains_val(map2, i) && hbm_contains_val(map1, i)); + } + + hbm_free(map1); + hbm_free(map2); + }); + + CMC_CREATE_TEST(equals, { + struct hashbidimap *map1 = hbm_new(100, 0.7, hbm_fkey, hbm_fval); + struct hashbidimap *map2 = hbm_new(1000, 0.9, hbm_fkey, hbm_fval); + + for (size_t i = 0; i < 100; i++) + { + cmc_assert(hbm_insert(map1, i, i)); + cmc_assert(hbm_insert(map2, 99 - i, 99 - i)); + } + + cmc_assert_not_equals(ptr, NULL, map1); + cmc_assert_not_equals(ptr, NULL, map2); + cmc_assert_equals(size_t, hbm_count(map1), hbm_count(map2)); + + for (size_t i = 0; i < 100; i++) + { + cmc_assert(hbm_contains_key(map2, i) && hbm_contains_key(map1, i)); + cmc_assert(hbm_contains_val(map2, i) && hbm_contains_val(map1, i)); + } + + hbm_free(map1); + hbm_free(map2); + }); + + CMC_CREATE_TEST(equals[from copy], { + struct hashbidimap *map1 = hbm_new(100, 0.7, hbm_fkey, hbm_fval); + + for (size_t i = 0; i < 100; i++) + cmc_assert(hbm_insert(map1, i, i)); + + struct hashbidimap *map2 = hbm_copy_of(map1); + + cmc_assert_not_equals(ptr, NULL, map2); + + cmc_assert(hbm_equals(map1, map2)); + + hbm_free(map1); + hbm_free(map2); + }); + + CMC_CREATE_TEST(flags, { + struct hashbidimap *map = hbm_new(100, 0.7, hbm_fkey, hbm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + cmc_assert_equals(int32_t, CMC_FLAG_OK, hbm_flag(map)); + + // customize + hbm_customize(map, &cmc_alloc_node_default, &(struct cmc_callbacks){ 0 }); + cmc_assert_equals(int32_t, CMC_FLAG_OK, hbm_flag(map)); + + // Insert + cmc_assert(hbm_insert(map, 1, 1)); + cmc_assert_equals(int32_t, CMC_FLAG_OK, hbm_flag(map)); + + cmc_assert(!hbm_insert(map, 1, 2)); + cmc_assert_equals(int32_t, CMC_FLAG_DUPLICATE, hbm_flag(map)); + map->flag = CMC_FLAG_ERROR; + cmc_assert(!hbm_insert(map, 2, 1)); + cmc_assert_equals(int32_t, CMC_FLAG_DUPLICATE, hbm_flag(map)); + + // clear + hbm_clear(map); + cmc_assert_equals(int32_t, CMC_FLAG_OK, hbm_flag(map)); + + // hbm_update_key + cmc_assert(!hbm_update_key(map, 1, 1)); + cmc_assert_equals(int32_t, CMC_FLAG_EMPTY, hbm_flag(map)); + + cmc_assert(hbm_insert(map, 1, 1)); + cmc_assert(!hbm_update_key(map, 2, 1)); + cmc_assert_equals(int32_t, CMC_FLAG_NOT_FOUND, hbm_flag(map)); + + cmc_assert(hbm_insert(map, 2, 2)); + cmc_assert(!hbm_update_key(map, 2, 1)); + cmc_assert_equals(int32_t, CMC_FLAG_DUPLICATE, hbm_flag(map)); + map->flag = CMC_FLAG_ERROR; + cmc_assert(!hbm_update_key(map, 1, 2)); + cmc_assert_equals(int32_t, CMC_FLAG_DUPLICATE, hbm_flag(map)); + + // clear + hbm_clear(map); + cmc_assert_equals(int32_t, CMC_FLAG_OK, hbm_flag(map)); + + // hbm_update_val + cmc_assert(!hbm_update_val(map, 1, 1)); + cmc_assert_equals(int32_t, CMC_FLAG_EMPTY, hbm_flag(map)); + + cmc_assert(hbm_insert(map, 1, 1)); + cmc_assert(!hbm_update_val(map, 2, 1)); + cmc_assert_equals(int32_t, CMC_FLAG_NOT_FOUND, hbm_flag(map)); + + cmc_assert(hbm_insert(map, 2, 2)); + cmc_assert(!hbm_update_val(map, 2, 1)); + cmc_assert_equals(int32_t, CMC_FLAG_DUPLICATE, hbm_flag(map)); + map->flag = CMC_FLAG_ERROR; + cmc_assert(!hbm_update_val(map, 1, 2)); + cmc_assert_equals(int32_t, CMC_FLAG_DUPLICATE, hbm_flag(map)); + + // remove_by_key + cmc_assert(hbm_remove_by_key(map, 1, NULL, NULL)); + cmc_assert_equals(int32_t, CMC_FLAG_OK, hbm_flag(map)); + + cmc_assert(!hbm_remove_by_key(map, 1, NULL, NULL)); + cmc_assert_equals(int32_t, CMC_FLAG_NOT_FOUND, hbm_flag(map)); + + cmc_assert(hbm_remove_by_key(map, 2, NULL, NULL)); + cmc_assert(!hbm_remove_by_key(map, 2, NULL, NULL)); + cmc_assert_equals(int32_t, CMC_FLAG_EMPTY, hbm_flag(map)); + + // remove_by_val + cmc_assert(hbm_insert(map, 1, 1) && hbm_insert(map, 2, 2)); + + cmc_assert(hbm_remove_by_val(map, 1, NULL, NULL)); + cmc_assert_equals(int32_t, CMC_FLAG_OK, hbm_flag(map)); + + cmc_assert(!hbm_remove_by_val(map, 1, NULL, NULL)); + cmc_assert_equals(int32_t, CMC_FLAG_NOT_FOUND, hbm_flag(map)); + + cmc_assert(hbm_remove_by_val(map, 2, NULL, NULL)); + cmc_assert(!hbm_remove_by_val(map, 2, NULL, NULL)); + cmc_assert_equals(int32_t, CMC_FLAG_EMPTY, hbm_flag(map)); + + // get_key and get_val + cmc_assert(hbm_insert(map, 2, 1)); + + cmc_assert_equals(size_t, 2, hbm_get_key(map, 1)); + cmc_assert_equals(int32_t, CMC_FLAG_OK, hbm_flag(map)); + map->flag = CMC_FLAG_ERROR; + cmc_assert_equals(size_t, 1, hbm_get_val(map, 2)); + cmc_assert_equals(int32_t, CMC_FLAG_OK, hbm_flag(map)); + + cmc_assert_equals(size_t, 0, hbm_get_key(map, 2)); + cmc_assert_equals(int32_t, CMC_FLAG_NOT_FOUND, hbm_flag(map)); + map->flag = CMC_FLAG_ERROR; + cmc_assert_equals(size_t, 0, hbm_get_val(map, 1)); + cmc_assert_equals(int32_t, CMC_FLAG_NOT_FOUND, hbm_flag(map)); + + // contains_key + cmc_assert(hbm_contains_key(map, 2)); + cmc_assert_equals(int32_t, CMC_FLAG_OK, hbm_flag(map)); + + // contains_val + map->flag = CMC_FLAG_ERROR; + cmc_assert(hbm_contains_val(map, 1)); + cmc_assert_equals(int32_t, CMC_FLAG_OK, hbm_flag(map)); + + // copy_of + map->flag = CMC_FLAG_ERROR; + struct hashbidimap *map2 = hbm_copy_of(map); + + cmc_assert_equals(int32_t, CMC_FLAG_OK, hbm_flag(map2)); + + size_t tmp = map->capacity; + map->capacity = 0; + + struct hashbidimap *map3 = hbm_copy_of(map); + cmc_assert_equals(ptr, NULL, map3); + cmc_assert_equals(int32_t, CMC_FLAG_ERROR, hbm_flag(map)); + + map->capacity = tmp; + + // equals + hbm_get_key(map, 100); + hbm_get_key(map2, 100); + cmc_assert_equals(int32_t, CMC_FLAG_NOT_FOUND, hbm_flag(map)); + cmc_assert_equals(int32_t, CMC_FLAG_NOT_FOUND, hbm_flag(map2)); + map->flag = CMC_FLAG_ERROR; + cmc_assert(hbm_equals(map, map2)); + cmc_assert_equals(int32_t, CMC_FLAG_OK, hbm_flag(map)); + cmc_assert_equals(int32_t, CMC_FLAG_OK, hbm_flag(map2)); + + hbm_free(map); + hbm_free(map2); + }); + + CMC_CREATE_TEST(callbacks, { + struct hashbidimap *map = hbm_new_custom(100, 0.7, hbm_fkey, hbm_fval, NULL, callbacks); + + cmc_assert_not_equals(ptr, NULL, map); + cmc_assert_equals(ptr, callbacks, map->callbacks); + + total_create = 0; + total_read = 0; + total_update = 0; + total_delete = 0; + total_resize = 0; + + cmc_assert(hbm_insert(map, 10, 10)); + cmc_assert_equals(int32_t, 1, total_create); + + cmc_assert(hbm_update_key(map, 10, 5)); + cmc_assert_equals(int32_t, 1, total_update); + + cmc_assert(hbm_update_val(map, 5, 5)); + cmc_assert_equals(int32_t, 2, total_update); + + cmc_assert(hbm_insert(map, 10, 10)); + cmc_assert_equals(int32_t, 2, total_create); + + cmc_assert(hbm_remove_by_key(map, 5, NULL, NULL)); + cmc_assert_equals(int32_t, 1, total_delete); + + cmc_assert(hbm_remove_by_val(map, 10, NULL, NULL)); + cmc_assert_equals(int32_t, 2, total_delete); + + cmc_assert(hbm_insert(map, 1, 2)); + cmc_assert_equals(int32_t, 3, total_create); + + cmc_assert_equals(size_t, 1, hbm_get_key(map, 2)); + cmc_assert_equals(int32_t, 1, total_read); + + cmc_assert_equals(size_t, 2, hbm_get_val(map, 1)); + cmc_assert_equals(int32_t, 2, total_read); + + cmc_assert(hbm_resize(map, 1000)); + cmc_assert_equals(int32_t, 1, total_resize); + + cmc_assert(hbm_resize(map, 200)); + cmc_assert_equals(int32_t, 2, total_resize); + + cmc_assert(hbm_contains_key(map, 1)); + cmc_assert_equals(int32_t, 3, total_read); + + cmc_assert(hbm_contains_val(map, 2)); + cmc_assert_equals(int32_t, 4, total_read); + + cmc_assert_equals(int32_t, 3, total_create); + cmc_assert_equals(int32_t, 4, total_read); + cmc_assert_equals(int32_t, 2, total_update); + cmc_assert_equals(int32_t, 2, total_delete); + cmc_assert_equals(int32_t, 2, total_resize); + + hbm_customize(map, NULL, NULL); + + cmc_assert_equals(ptr, NULL, map->callbacks); + + hbm_clear(map); + cmc_assert(hbm_insert(map, 10, 10)); + cmc_assert(hbm_update_key(map, 10, 5)); + cmc_assert(hbm_update_val(map, 5, 5)); + cmc_assert(hbm_insert(map, 10, 10)); + cmc_assert(hbm_remove_by_key(map, 5, NULL, NULL)); + cmc_assert(hbm_remove_by_val(map, 10, NULL, NULL)); + cmc_assert(hbm_insert(map, 1, 2)); + cmc_assert_equals(size_t, 1, hbm_get_key(map, 2)); + cmc_assert_equals(size_t, 2, hbm_get_val(map, 1)); + cmc_assert(hbm_resize(map, 1000)); + cmc_assert(hbm_resize(map, 200)); + cmc_assert(hbm_contains_key(map, 1)); + cmc_assert(hbm_contains_val(map, 2)); + + cmc_assert_equals(int32_t, 3, total_create); + cmc_assert_equals(int32_t, 4, total_read); + cmc_assert_equals(int32_t, 2, total_update); + cmc_assert_equals(int32_t, 2, total_delete); + cmc_assert_equals(int32_t, 2, total_resize); + + cmc_assert_equals(ptr, NULL, map->callbacks); + + hbm_free(map); + + total_create = 0; + total_read = 0; + total_update = 0; + total_delete = 0; + total_resize = 0; + }); +}); + +CMC_CREATE_UNIT(CMCHashBidiMapIter, true, { + CMC_CREATE_TEST(PFX##_iter_start, { + // WIP + cmc_assert(true); + cmc_assert(true); + }); +}); + +#endif /* CMC_TESTS_UNT_HASHBIDIMAP_H */ diff --git a/tests/unt_hashmap.h b/tests/unt_hashmap.h new file mode 100644 index 0000000..200d319 --- /dev/null +++ b/tests/unt_hashmap.h @@ -0,0 +1,1333 @@ +#ifndef CMC_TESTS_UNT_HASHMAP_H +#define CMC_TESTS_UNT_HASHMAP_H + +#include "utl.h" + +#define V size_t +#define K size_t +#define PFX hm +#define SNAME hashmap +#define CMC_EXT_HASHMAP_INIT +#define CMC_EXT_HASHMAP_ITER +#define CMC_EXT_HASHMAP_STR +#include "cmc/hashmap.h" + +struct hashmap_fkey *hm_fkey = &(struct hashmap_fkey){ + .cmp = cmc_size_cmp, .cpy = NULL, .str = cmc_size_str, .free = NULL, .hash = cmc_size_hash, .pri = cmc_size_cmp +}; + +struct hashmap_fval *hm_fval = &(struct hashmap_fval){ + .cmp = cmc_size_cmp, .cpy = NULL, .str = cmc_size_str, .free = NULL, .hash = cmc_size_hash, .pri = cmc_size_cmp +}; + +struct hashmap_fkey *hm_fkey_counter = &(struct hashmap_fkey){ + .cmp = k_c_cmp, .cpy = k_c_cpy, .str = k_c_str, .free = k_c_free, .hash = k_c_hash, .pri = k_c_pri +}; + +struct hashmap_fval *hm_fval_counter = &(struct hashmap_fval){ + .cmp = v_c_cmp, .cpy = v_c_cpy, .str = v_c_str, .free = v_c_free, .hash = v_c_hash, .pri = v_c_pri +}; + +struct cmc_alloc_node *hm_alloc_node = + &(struct cmc_alloc_node){ .malloc = malloc, .calloc = calloc, .realloc = realloc, .free = free }; + +CMC_CREATE_UNIT(CMCHashMap, true, { + CMC_CREATE_TEST(PFX##_new(), { + struct hashmap *map = hm_new(943722, 0.6, hm_fkey, hm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + cmc_assert_not_equals(ptr, NULL, map->buffer); + cmc_assert_equals(size_t, 0, map->count); + cmc_assert_equals(double, 0.6, map->load); + cmc_assert_equals(int32_t, CMC_FLAG_OK, map->flag); + cmc_assert_equals(ptr, hm_fkey, map->f_key); + cmc_assert_equals(ptr, hm_fval, map->f_val); + cmc_assert_equals(ptr, cmc_alloc_node_default.malloc, map->alloc->malloc); + cmc_assert_equals(ptr, cmc_alloc_node_default.calloc, map->alloc->calloc); + cmc_assert_equals(ptr, cmc_alloc_node_default.realloc, map->alloc->realloc); + cmc_assert_equals(ptr, cmc_alloc_node_default.free, map->alloc->free); + cmc_assert_equals(ptr, NULL, map->callbacks); + + cmc_assert_greater_equals(size_t, (943722 / 0.6), hm_capacity(map)); + + hm_free(map); + + map = hm_new(0, 0.6, hm_fkey, hm_fval); + cmc_assert_equals(ptr, NULL, map); + + map = hm_new(UINT64_MAX, 0.99, hm_fkey, hm_fval); + cmc_assert_equals(ptr, NULL, map); + + map = hm_new(1000, 0.6, hm_fkey, NULL); + cmc_assert_equals(ptr, NULL, map); + + map = hm_new(1000, 0.6, NULL, hm_fval); + cmc_assert_equals(ptr, NULL, map); + + map = hm_new(1000, 0.6, NULL, NULL); + cmc_assert_equals(ptr, NULL, map); + }); + + CMC_CREATE_TEST(PFX##_new_custom(), { + struct hashmap *map = hm_new_custom(943722, 0.6, hm_fkey, hm_fval, hm_alloc_node, callbacks); + + cmc_assert_not_equals(ptr, NULL, map); + cmc_assert_not_equals(ptr, NULL, map->buffer); + cmc_assert_equals(size_t, 0, map->count); + cmc_assert_equals(double, 0.6, map->load); + cmc_assert_equals(int32_t, CMC_FLAG_OK, map->flag); + cmc_assert_equals(ptr, hm_fkey, map->f_key); + cmc_assert_equals(ptr, hm_fval, map->f_val); + cmc_assert_equals(ptr, hm_alloc_node, map->alloc); + cmc_assert_equals(ptr, callbacks, map->callbacks); + + cmc_assert_greater_equals(size_t, (943722 / 0.6), hm_capacity(map)); + + hm_free(map); + + map = hm_new_custom(0, 0.6, hm_fkey, hm_fval, NULL, NULL); + cmc_assert_equals(ptr, NULL, map); + + map = hm_new_custom(UINT64_MAX, 0.99, hm_fkey, hm_fval, NULL, NULL); + cmc_assert_equals(ptr, NULL, map); + + map = hm_new_custom(1000, 0.6, hm_fkey, NULL, NULL, NULL); + cmc_assert_equals(ptr, NULL, map); + + map = hm_new_custom(1000, 0.6, NULL, hm_fval, NULL, NULL); + cmc_assert_equals(ptr, NULL, map); + + map = hm_new_custom(1000, 0.6, NULL, NULL, NULL, NULL); + cmc_assert_equals(ptr, NULL, map); + }); + + CMC_CREATE_TEST(PFX##_init(), { + struct hashmap map = hm_init(943722, 0.6, hm_fkey, hm_fval); + + cmc_assert_not_equals(ptr, NULL, map.buffer); + cmc_assert_equals(size_t, 0, map.count); + cmc_assert_equals(double, 0.6, map.load); + cmc_assert_equals(int32_t, CMC_FLAG_OK, map.flag); + cmc_assert_equals(ptr, hm_fkey, map.f_key); + cmc_assert_equals(ptr, hm_fval, map.f_val); + cmc_assert_equals(ptr, cmc_alloc_node_default.malloc, map.alloc->malloc); + cmc_assert_equals(ptr, cmc_alloc_node_default.calloc, map.alloc->calloc); + cmc_assert_equals(ptr, cmc_alloc_node_default.realloc, map.alloc->realloc); + cmc_assert_equals(ptr, cmc_alloc_node_default.free, map.alloc->free); + cmc_assert_equals(ptr, NULL, map.callbacks); + + cmc_assert_greater_equals(size_t, (943722 / 0.6), hm_capacity(&map)); + + hm_release(map); + + map = hm_init(0, 0.6, hm_fkey, hm_fval); + cmc_assert_equals(ptr, NULL, map.buffer); + + map = hm_init(UINT64_MAX, 0.99, hm_fkey, hm_fval); + cmc_assert_equals(ptr, NULL, map.buffer); + + map = hm_init(1000, 0.6, hm_fkey, NULL); + cmc_assert_equals(ptr, NULL, map.buffer); + + map = hm_init(1000, 0.6, NULL, hm_fval); + cmc_assert_equals(ptr, NULL, map.buffer); + + map = hm_init(1000, 0.6, NULL, NULL); + cmc_assert_equals(ptr, NULL, map.buffer); + }); + + CMC_CREATE_TEST(PFX##_init_custom(), { + struct hashmap map = hm_init_custom(943722, 0.6, hm_fkey, hm_fval, hm_alloc_node, callbacks); + + cmc_assert_not_equals(ptr, NULL, map.buffer); + cmc_assert_equals(size_t, 0, map.count); + cmc_assert_equals(double, 0.6, map.load); + cmc_assert_equals(int32_t, CMC_FLAG_OK, map.flag); + cmc_assert_equals(ptr, hm_fkey, map.f_key); + cmc_assert_equals(ptr, hm_fval, map.f_val); + cmc_assert_equals(ptr, hm_alloc_node, map.alloc); + cmc_assert_equals(ptr, callbacks, map.callbacks); + + cmc_assert_greater_equals(size_t, (943722 / 0.6), hm_capacity(&map)); + + hm_release(map); + + map = hm_init_custom(0, 0.6, hm_fkey, hm_fval, NULL, NULL); + cmc_assert_equals(ptr, NULL, map.buffer); + + map = hm_init_custom(UINT64_MAX, 0.99, hm_fkey, hm_fval, NULL, NULL); + cmc_assert_equals(ptr, NULL, map.buffer); + + map = hm_init_custom(1000, 0.6, hm_fkey, NULL, NULL, NULL); + cmc_assert_equals(ptr, NULL, map.buffer); + + map = hm_init_custom(1000, 0.6, NULL, hm_fval, NULL, NULL); + cmc_assert_equals(ptr, NULL, map.buffer); + + map = hm_init_custom(1000, 0.6, NULL, NULL, NULL, NULL); + cmc_assert_equals(ptr, NULL, map.buffer); + }); + + CMC_CREATE_TEST(PFX##_clear(), { + k_total_free = 0; + v_total_free = 0; + struct hashmap *map = hm_new(100, 0.6, hm_fkey_counter, hm_fval_counter); + + cmc_assert_not_equals(ptr, NULL, map); + + for (size_t i = 1; i <= 1000; i++) + hm_insert(map, i, i); + + cmc_assert_equals(size_t, 1000, map->count); + + size_t *r = hm_get_ref(map, 100); + + cmc_assert_equals(size_t, 100, *r); + + map->flag = CMC_FLAG_ERROR; + hm_clear(map); + + cmc_assert_equals(size_t, 0, map->count); + cmc_assert_equals(int32_t, CMC_FLAG_OK, map->flag); + cmc_assert_equals(int32_t, 1000, k_total_free); + cmc_assert_equals(int32_t, 1000, v_total_free); + + // The buffer is still valid and all zeroes + cmc_assert_equals(size_t, 0, *r); + + hm_free(map); + k_total_free = 0; + v_total_free = 0; + }); + + CMC_CREATE_TEST(PFX##_free(), { + k_total_free = 0; + v_total_free = 0; + struct hashmap *map = hm_new(100, 0.6, hm_fkey_counter, hm_fval_counter); + + cmc_assert_not_equals(ptr, NULL, map); + + for (size_t i = 1; i <= 1000; i++) + hm_insert(map, i, i); + + cmc_assert_equals(size_t, 1000, map->count); + + hm_free(map); + + cmc_assert_equals(int32_t, 1000, k_total_free); + cmc_assert_equals(int32_t, 1000, v_total_free); + + map = hm_new(1000, 0.6, hm_fkey_counter, hm_fval_counter); + + cmc_assert_not_equals(ptr, NULL, map); + cmc_assert_not_equals(ptr, NULL, map->buffer); + + hm_free(map); + + cmc_assert_equals(int32_t, 1000, k_total_free); + cmc_assert_equals(int32_t, 1000, v_total_free); + k_total_free = 0; + v_total_free = 0; + }); + + CMC_CREATE_TEST(PFX##_release(), { + k_total_free = 0; + v_total_free = 0; + struct hashmap map = hm_init(100, 0.6, hm_fkey_counter, hm_fval_counter); + + cmc_assert_not_equals(ptr, NULL, map.buffer); + + for (size_t i = 1; i <= 1000; i++) + hm_insert(&map, i, i); + + cmc_assert_equals(size_t, 1000, map.count); + + hm_release(map); + + cmc_assert_equals(int32_t, 1000, k_total_free); + cmc_assert_equals(int32_t, 1000, v_total_free); + + map = hm_init(1000, 0.6, hm_fkey_counter, hm_fval_counter); + + cmc_assert_not_equals(ptr, NULL, map.buffer); + + hm_release(map); + + cmc_assert_equals(int32_t, 1000, k_total_free); + cmc_assert_equals(int32_t, 1000, v_total_free); + k_total_free = 0; + v_total_free = 0; + }); + + CMC_CREATE_TEST(PFX##_customize(), { + struct hashmap *map = hm_new(100, 0.6, hm_fkey, hm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + map->flag = CMC_FLAG_ERROR; + hm_customize(map, hm_alloc_node, callbacks); + + cmc_assert_equals(ptr, hm_alloc_node, map->alloc); + cmc_assert_equals(ptr, callbacks, map->callbacks); + cmc_assert_equals(int32_t, CMC_FLAG_OK, map->flag); + + map->flag = CMC_FLAG_ERROR; + hm_customize(map, NULL, NULL); + + cmc_assert_equals(ptr, cmc_alloc_node_default.malloc, map->alloc->malloc); + cmc_assert_equals(ptr, cmc_alloc_node_default.calloc, map->alloc->calloc); + cmc_assert_equals(ptr, cmc_alloc_node_default.realloc, map->alloc->realloc); + cmc_assert_equals(ptr, cmc_alloc_node_default.free, map->alloc->free); + cmc_assert_equals(ptr, NULL, map->callbacks); + cmc_assert_equals(int32_t, CMC_FLAG_OK, map->flag); + + hm_free(map); + }); + + CMC_CREATE_TEST(PFX##_insert(), { + struct hashmap *map = hm_new(100, 0.6, hm_fkey, hm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + map->flag = CMC_FLAG_ERROR; + cmc_assert(hm_insert(map, 1, 1)); + cmc_assert_equals(int32_t, CMC_FLAG_OK, map->flag); + cmc_assert_equals(size_t, 1, hm_count(map)); + + hm_free(map); + + map = hm_new(1, 0.99, hm_fkey, hm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + cmc_assert_equals(size_t, cmc_hashtable_primes[0], hm_capacity(map)); + cmc_assert(hm_insert(map, 1, 1)); + + hm_free(map); + + // element position + map = hm_new(1, 0.99, hm_fkey, hm_fval); + + // Temporary change + // Using the numhash the key is the hash itself + hm_fkey->hash = numhash; + + cmc_assert_not_equals(ptr, NULL, map); + + size_t capacity = hm_capacity(map); + + cmc_assert_greater(size_t, 0, capacity); + + cmc_assert(hm_insert(map, capacity - 1, capacity - 1)); + cmc_assert(hm_insert(map, capacity, capacity)); + + cmc_assert_equals(size_t, capacity - 1, map->buffer[capacity - 1].key); + cmc_assert_equals(size_t, capacity, map->buffer[0].key); + + hm_fkey->hash = cmc_size_hash; + + hm_free(map); + + // distance + map = hm_new(500, 0.6, hm_fkey, hm_fval); + + // Temporary change + hm_fkey->hash = hash0; + + cmc_assert_not_equals(ptr, NULL, map); + + for (size_t i = 0; i < 200; i++) + cmc_assert(hm_insert(map, i, i)); + + // Everything is hashed to 0 so key 0 should have dist 0, key 1 dist 1, + // etc + for (size_t i = 0; i < 200; i++) + cmc_assert_equals(size_t, i, map->buffer[i].dist); + + hm_fkey->hash = cmc_size_hash; + + hm_free(map); + + // distance wrap + map = hm_new(1, 0.99, hm_fkey, hm_fval); + + // Temporary change + hm_fkey->hash = hashcapminus1; + + cmc_assert_not_equals(ptr, NULL, map); + + capacity = hm_capacity(map); + + // Just to be sure, not part of the test + cmc_assert_equals(size_t, cmc_hashtable_primes[0] - 1, hashcapminus1(0)); + cmc_assert_equals(size_t, capacity - 1, hashcapminus1(0)); + + cmc_assert(hm_insert(map, 0, 0)); + cmc_assert(hm_insert(map, 1, 1)); + + cmc_assert_equals(size_t, 0, map->buffer[capacity - 1].dist); + cmc_assert_equals(size_t, 1, map->buffer[0].dist); + + hm_fkey->hash = cmc_size_hash; + + hm_free(map); + + // distances + map = hm_new(1, 0.99, hm_fkey, hm_fval); + + // Temporary change + hm_fkey->hash = hashcapminus4; + + cmc_assert_not_equals(ptr, NULL, map); + + cmc_assert_equals(size_t, cmc_hashtable_primes[0], hm_capacity(map)); + + for (size_t i = 0; i < 6; i++) + cmc_assert(hm_insert(map, i, i)); + + // for (size_t i = 0; i < 6; i++) + // cmc_assert_equals(size_t, i, hm_impl_get_entry(map, i)->dist); + + hm_fkey->hash = cmc_size_hash; + + hm_free(map); + + // buffer growth and item preservation + map = hm_new(1, 0.99, hm_fkey, hm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + cmc_assert_equals(size_t, cmc_hashtable_primes[0], hm_capacity(map)); + + size_t p = 0; + for (size_t i = 0; i < 100000; i++) + { + if (hm_full(map)) + p++; + + cmc_assert(hm_insert(map, i, i)); + } + + cmc_assert_equals(size_t, cmc_hashtable_primes[p], hm_capacity(map)); + + for (size_t i = 0; i < 100000; i++) + cmc_assert(hm_contains(map, i)); + + hm_free(map); + }); + + CMC_CREATE_TEST(PFX##_update(), { + struct hashmap *map = hm_new(100, 0.6, hm_fkey, hm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + cmc_assert(hm_insert(map, 1, 1)); + + size_t old; + cmc_assert(hm_update(map, 1, 2, &old)); + + cmc_assert_equals(size_t, 1, old); + + hm_free(map); + + // key not found + map = hm_new(100, 0.6, hm_fkey, hm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + for (size_t i = 0; i < 100; i++) + hm_insert(map, i, i); + + cmc_assert(!hm_update(map, 120, 120, NULL)); + + hm_free(map); + }); + + CMC_CREATE_TEST(PFX##_remove(), { + struct hashmap *map = hm_new(100, 0.6, hm_fkey, hm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + for (size_t i = 0; i < 100; i++) + hm_insert(map, i, i); + + for (size_t i = 0; i < 100; i++) + cmc_assert(hm_remove(map, i, NULL)); + + hm_free(map); + + // count = 0 + map = hm_new(100, 0.6, hm_fkey, hm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + for (size_t i = 100; i < 200; i++) + cmc_assert(!hm_remove(map, i, NULL)); + + hm_free(map); + + // key not found + map = hm_new(100, 0.6, hm_fkey, hm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + for (size_t i = 0; i < 100; i++) + hm_insert(map, i, i); + + cmc_assert_equals(size_t, 100, map->count); + + for (size_t i = 100; i < 200; i++) + cmc_assert(!hm_remove(map, i, NULL)); + + hm_free(map); + }); + + CMC_CREATE_TEST(PFX##_max(), { + struct hashmap *map = hm_new(100, 0.6, hm_fkey, hm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + for (size_t i = 1; i <= 100; i++) + cmc_assert(hm_insert(map, i, i)); + + size_t key; + size_t val; + cmc_assert(hm_max(map, &key, &val)); + + cmc_assert_equals(size_t, 100, key); + cmc_assert_equals(size_t, 100, val); + + hm_free(map); + + // count = 0 + map = hm_new(100, 0.6, hm_fkey, hm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + cmc_assert(!hm_min(map, NULL, NULL)); + + hm_free(map); + }); + + CMC_CREATE_TEST(PFX##_min(), { + struct hashmap *map = hm_new(100, 0.6, hm_fkey, hm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + for (size_t i = 1; i <= 100; i++) + cmc_assert(hm_insert(map, i, i)); + + size_t key; + size_t val; + cmc_assert(hm_min(map, &key, &val)); + + cmc_assert_equals(size_t, 1, key); + cmc_assert_equals(size_t, 1, val); + + hm_free(map); + + // count = 0 + map = hm_new(100, 0.6, hm_fkey, hm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + cmc_assert(!hm_min(map, NULL, NULL)); + + hm_free(map); + }); + + CMC_CREATE_TEST(PFX##_get(), { + struct hashmap *map = hm_new(100, 0.6, hm_fkey, hm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + cmc_assert(hm_insert(map, 4321, 1234)); + + cmc_assert_equals(size_t, 1234, hm_get(map, 4321)); + + hm_free(map); + + // count = 0 + map = hm_new(100, 0.6, hm_fkey, hm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + cmc_assert_equals(size_t, (size_t){ 0 }, hm_get(map, 4321)); + + hm_free(map); + }); + + CMC_CREATE_TEST(PFX##_get_ref(), { + struct hashmap *map = hm_new(100, 0.6, hm_fkey, hm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + cmc_assert(hm_insert(map, 4321, 1234)); + + size_t *result = hm_get_ref(map, 4321); + + cmc_assert_not_equals(ptr, NULL, result); + cmc_assert_equals(size_t, 1234, *result); + + hm_free(map); + + // count = 0 + map = hm_new(100, 0.6, hm_fkey, hm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + cmc_assert_equals(ptr, NULL, hm_get_ref(map, 4321)); + + hm_free(map); + }); + + CMC_CREATE_TEST(PFX##_contains(), { + struct hashmap *map = hm_new(100, 0.6, hm_fkey, hm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + cmc_assert(hm_insert(map, 987654321, 1)); + + cmc_assert(hm_contains(map, 987654321)); + + hm_free(map); + + // count = 0 + map = hm_new(100, 0.6, hm_fkey, hm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + cmc_assert(!hm_contains(map, 987654321)); + + hm_free(map); + + // sum + map = hm_new(100, 0.6, hm_fkey, hm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + for (size_t i = 101; i <= 200; i++) + cmc_assert(hm_insert(map, i, i)); + + size_t sum = 0; + for (size_t i = 1; i <= 300; i++) + if (hm_contains(map, i)) + sum += i; + + cmc_assert_equals(size_t, 15050, sum); + + hm_free(map); + }); + + CMC_CREATE_TEST(PFX##_empty(), { + struct hashmap *map = hm_new(100, 0.6, hm_fkey, hm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + cmc_assert(hm_empty(map)); + + cmc_assert(hm_insert(map, 1, 1)); + + cmc_assert(!hm_empty(map)); + + hm_free(map); + }); + + CMC_CREATE_TEST(PFX##_full(), { + struct hashmap *map = hm_new(cmc_hashtable_primes[0], 0.99999, hm_fkey, hm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + cmc_assert_equals(size_t, cmc_hashtable_primes[0], hm_capacity(map)); + + for (size_t i = 0; i < hm_capacity(map); i++) + cmc_assert(hm_insert(map, i, i)); + + cmc_assert(hm_full(map)); + + cmc_assert(hm_insert(map, 10000, 10000)); + + cmc_assert_equals(size_t, cmc_hashtable_primes[1], hm_capacity(map)); + + cmc_assert(!hm_full(map)); + + hm_free(map); + }); + + CMC_CREATE_TEST(PFX##_count(), { + struct hashmap *map = hm_new(100, 0.6, hm_fkey, hm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + for (size_t i = 1; i < 100; i++) + { + cmc_assert(hm_insert(map, i, i)); + cmc_assert_equals(size_t, i, hm_count(map)); + } + + hm_free(map); + }); + + CMC_CREATE_TEST(PFX##_capacity(), { + struct hashmap *map = hm_new(2500, 0.6, hm_fkey, hm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + cmc_assert_greater_equals(size_t, (size_t)(2500 / 0.6), hm_capacity(map)); + + hm_free(map); + + // small capacity + map = hm_new(1, 0.99, hm_fkey, hm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + cmc_assert_equals(size_t, cmc_hashtable_primes[0], hm_capacity(map)); + + hm_free(map); + }); + + CMC_CREATE_TEST(PFX##_load(), { + struct hashmap *map = hm_new(1, 0.99, hm_fkey, hm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + cmc_assert_in_range(double, 0.99 - 0.0001, 0.99 + 0.0001, hm_load(map)); + + hm_free(map); + }); + + CMC_CREATE_TEST(flags, { + struct hashmap *map = hm_new(100, 0.6, hm_fkey, hm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + cmc_assert_equals(int32_t, CMC_FLAG_OK, hm_flag(map)); + + // insert + cmc_assert(hm_insert(map, 1, 1)); + cmc_assert_equals(int32_t, CMC_FLAG_OK, hm_flag(map)); + cmc_assert(!hm_insert(map, 1, 2)); + cmc_assert_equals(int32_t, CMC_FLAG_DUPLICATE, hm_flag(map)); + + // clear + hm_clear(map); + cmc_assert_equals(int32_t, CMC_FLAG_OK, hm_flag(map)); + + // update + cmc_assert(!hm_update(map, 1, 2, NULL)); + cmc_assert_equals(int32_t, CMC_FLAG_EMPTY, hm_flag(map)); + + cmc_assert(hm_insert(map, 1, 1)); + cmc_assert_equals(int32_t, CMC_FLAG_OK, hm_flag(map)); + + cmc_assert(!hm_update(map, 2, 1, NULL)); + cmc_assert_equals(int32_t, CMC_FLAG_NOT_FOUND, hm_flag(map)); + + cmc_assert(hm_update(map, 1, 2, NULL)); + cmc_assert_equals(int32_t, CMC_FLAG_OK, hm_flag(map)); + + // remove + cmc_assert(!hm_remove(map, 2, NULL)); + cmc_assert_equals(int32_t, CMC_FLAG_NOT_FOUND, hm_flag(map)); + + cmc_assert(hm_remove(map, 1, NULL)); + cmc_assert_equals(int32_t, CMC_FLAG_OK, hm_flag(map)); + + cmc_assert(!hm_remove(map, 1, NULL)); + cmc_assert_equals(int32_t, CMC_FLAG_EMPTY, hm_flag(map)); + + // max + cmc_assert(hm_insert(map, 1, 1)); + cmc_assert(hm_max(map, NULL, NULL)); + cmc_assert_equals(int32_t, CMC_FLAG_OK, hm_flag(map)); + + cmc_assert(hm_remove(map, 1, NULL)); + cmc_assert(!hm_max(map, NULL, NULL)); + cmc_assert_equals(int32_t, CMC_FLAG_EMPTY, hm_flag(map)); + + // min + cmc_assert(hm_insert(map, 1, 1)); + cmc_assert(hm_min(map, NULL, NULL)); + cmc_assert_equals(int32_t, CMC_FLAG_OK, hm_flag(map)); + + cmc_assert(hm_remove(map, 1, NULL)); + cmc_assert(!hm_min(map, NULL, NULL)); + cmc_assert_equals(int32_t, CMC_FLAG_EMPTY, hm_flag(map)); + + // get + hm_get(map, 1); + cmc_assert_equals(int32_t, CMC_FLAG_EMPTY, hm_flag(map)); + + cmc_assert(hm_insert(map, 1, 1)); + hm_get(map, 1); + cmc_assert_equals(int32_t, CMC_FLAG_OK, hm_flag(map)); + + hm_get(map, 2); + cmc_assert_equals(int32_t, CMC_FLAG_NOT_FOUND, hm_flag(map)); + + hm_clear(map); + + // get_ref + hm_get_ref(map, 1); + cmc_assert_equals(int32_t, CMC_FLAG_EMPTY, hm_flag(map)); + + cmc_assert(hm_insert(map, 1, 1)); + hm_get_ref(map, 1); + cmc_assert_equals(int32_t, CMC_FLAG_OK, hm_flag(map)); + + hm_get_ref(map, 2); + cmc_assert_equals(int32_t, CMC_FLAG_NOT_FOUND, hm_flag(map)); + + // contains + hm_contains(map, 1); + cmc_assert_equals(int32_t, CMC_FLAG_OK, hm_flag(map)); + + // copy + struct hashmap *map2 = hm_copy_of(map); + + cmc_assert_equals(int32_t, CMC_FLAG_OK, hm_flag(map)); + cmc_assert_equals(int32_t, CMC_FLAG_OK, hm_flag(map2)); + + size_t tmp = map->capacity; + map->capacity = 0; + + struct hashmap *map3 = hm_copy_of(map); + cmc_assert_equals(ptr, NULL, map3); + + cmc_assert_equals(int32_t, CMC_FLAG_ERROR, hm_flag(map)); + + map->capacity = tmp; + + cmc_assert(hm_equals(map, map2)); + cmc_assert_equals(int32_t, CMC_FLAG_OK, hm_flag(map)); + cmc_assert_equals(int32_t, CMC_FLAG_OK, hm_flag(map2)); + + hm_free(map); + hm_free(map2); + }); + + CMC_CREATE_TEST(callbacks, { + struct hashmap *map = hm_new_custom(100, 0.6, hm_fkey, hm_fval, NULL, callbacks); + + cmc_assert_not_equals(ptr, NULL, map); + + cmc_assert(hm_insert(map, 1, 2)); + cmc_assert_equals(int32_t, 1, total_create); + + cmc_assert(hm_update(map, 1, 10, NULL)); + cmc_assert_equals(int32_t, 1, total_update); + + cmc_assert(hm_remove(map, 1, NULL)); + cmc_assert_equals(int32_t, 1, total_delete); + + cmc_assert(hm_insert(map, 1, 2)); + cmc_assert_equals(int32_t, 2, total_create); + + cmc_assert(hm_max(map, NULL, NULL)); + cmc_assert_equals(int32_t, 1, total_read); + + cmc_assert(hm_min(map, NULL, NULL)); + cmc_assert_equals(int32_t, 2, total_read); + + cmc_assert_equals(size_t, 2, hm_get(map, 1)); + cmc_assert_equals(int32_t, 3, total_read); + + cmc_assert_not_equals(ptr, NULL, hm_get_ref(map, 1)); + cmc_assert_equals(int32_t, 4, total_read); + + cmc_assert(hm_contains(map, 1)); + cmc_assert_equals(int32_t, 5, total_read); + + cmc_assert(hm_resize(map, 10000)); + cmc_assert_equals(int32_t, 1, total_resize); + + cmc_assert(hm_resize(map, 500)); + cmc_assert_equals(int32_t, 2, total_resize); + + cmc_assert_equals(int32_t, 2, total_create); + cmc_assert_equals(int32_t, 5, total_read); + cmc_assert_equals(int32_t, 1, total_update); + cmc_assert_equals(int32_t, 1, total_delete); + cmc_assert_equals(int32_t, 2, total_resize); + + hm_customize(map, NULL, NULL); + + cmc_assert_equals(ptr, NULL, map->callbacks); + + hm_clear(map); + cmc_assert(hm_insert(map, 1, 2)); + cmc_assert(hm_update(map, 1, 10, NULL)); + cmc_assert(hm_remove(map, 1, NULL)); + cmc_assert(hm_insert(map, 1, 2)); + cmc_assert(hm_max(map, NULL, NULL)); + cmc_assert(hm_min(map, NULL, NULL)); + cmc_assert_equals(size_t, 2, hm_get(map, 1)); + cmc_assert_not_equals(ptr, NULL, hm_get_ref(map, 1)); + cmc_assert(hm_contains(map, 1)); + cmc_assert(hm_resize(map, 10000)); + cmc_assert(hm_resize(map, 500)); + + cmc_assert_equals(int32_t, 2, total_create); + cmc_assert_equals(int32_t, 5, total_read); + cmc_assert_equals(int32_t, 1, total_update); + cmc_assert_equals(int32_t, 1, total_delete); + cmc_assert_equals(int32_t, 2, total_resize); + + cmc_assert_equals(ptr, NULL, map->callbacks); + + hm_free(map); + + total_create = 0; + total_read = 0; + total_update = 0; + total_delete = 0; + total_resize = 0; + }); +}); + +struct hashmap_fkey *hm_fkey_numhash = &(struct hashmap_fkey){ + .cmp = cmc_size_cmp, .cpy = NULL, .str = cmc_size_str, .free = NULL, .hash = numhash, .pri = cmc_size_cmp +}; + +CMC_CREATE_UNIT(CMCHashMapIter, true, { + CMC_CREATE_TEST(PFX##_iter_start(), { + struct hashmap *map = hm_new(100, 0.6, hm_fkey_numhash, hm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + struct hashmap_iter it = hm_iter_start(map); + + cmc_assert_equals(ptr, map, it.target); + cmc_assert_equals(size_t, 0, it.cursor); + cmc_assert_equals(size_t, 0, it.index); + cmc_assert_equals(size_t, 0, it.first); + cmc_assert_equals(size_t, 0, it.last); + cmc_assert_equals(bool, true, it.start); + cmc_assert_equals(bool, true, it.end); + + cmc_assert(hm_iter_at_start(&it)); + cmc_assert(hm_iter_at_end(&it)); + + cmc_assert(hm_insert(map, 1, 1)); + cmc_assert(hm_insert(map, 2, 2)); + cmc_assert(hm_insert(map, 3, 3)); + + it = hm_iter_start(map); + + cmc_assert_equals(size_t, 0, it.index); + + cmc_assert_equals(size_t, 1, it.cursor); + cmc_assert_equals(size_t, 1, it.first); + cmc_assert_equals(size_t, 3, it.last); + cmc_assert_equals(bool, false, it.end); + + hm_free(map); + }); + + CMC_CREATE_TEST(PFX##_iter_end(), { + struct hashmap *map = hm_new(100, 0.6, hm_fkey_numhash, hm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + struct hashmap_iter it = hm_iter_end(map); + + cmc_assert_equals(ptr, map, it.target); + cmc_assert_equals(size_t, 0, it.cursor); + cmc_assert_equals(size_t, 0, it.index); + cmc_assert_equals(size_t, 0, it.first); + cmc_assert_equals(size_t, 0, it.last); + cmc_assert_equals(bool, true, it.start); + cmc_assert_equals(bool, true, it.end); + + cmc_assert(hm_iter_at_start(&it)); + cmc_assert(hm_iter_at_end(&it)); + + cmc_assert(hm_insert(map, 1, 1)); + cmc_assert(hm_insert(map, 2, 2)); + cmc_assert(hm_insert(map, 3, 3)); + + it = hm_iter_end(map); + + cmc_assert_equals(size_t, map->count - 1, it.index); + + cmc_assert_equals(size_t, 3, it.cursor); + cmc_assert_equals(size_t, 1, it.first); + cmc_assert_equals(size_t, 3, it.last); + cmc_assert_equals(bool, false, it.start); + + hm_free(map); + }); + + CMC_CREATE_TEST(PFX##_iter_at_start(), { + struct hashmap *map = hm_new(100, 0.6, hm_fkey, hm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + struct hashmap_iter it = hm_iter_start(map); + + // Empty checks + cmc_assert(hm_iter_at_start(&it)); + it = hm_iter_end(map); + cmc_assert(hm_iter_at_start(&it)); + + // Non-empty checks + cmc_assert(hm_insert(map, 1, 1)); + cmc_assert(hm_insert(map, 2, 2)); + it = hm_iter_end(map); + cmc_assert(!hm_iter_at_start(&it)); + it = hm_iter_start(map); + cmc_assert(hm_iter_at_start(&it)); + + hm_free(map); + }); + + CMC_CREATE_TEST(PFX##_iter_at_end(), { + struct hashmap *map = hm_new(100, 0.6, hm_fkey, hm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + struct hashmap_iter it = hm_iter_start(map); + + // Empty check + cmc_assert(hm_iter_at_end(&it)); + it = hm_iter_end(map); + cmc_assert(hm_iter_at_end(&it)); + + // Non-empty checks + cmc_assert(hm_insert(map, 1, 1)); + cmc_assert(hm_insert(map, 2, 2)); + it = hm_iter_end(map); + cmc_assert(hm_iter_at_end(&it)); + it = hm_iter_start(map); + cmc_assert(!hm_iter_at_end(&it)); + + hm_free(map); + }); + + CMC_CREATE_TEST(PFX##_iter_to_start(), { + struct hashmap *map = hm_new(100, 0.6, hm_fkey_numhash, hm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + struct hashmap_iter it = hm_iter_start(map); + + cmc_assert(!hm_iter_to_start(&it)); + + for (size_t i = 1; i <= 100; i++) + hm_insert(map, i, i); + + cmc_assert_equals(size_t, 100, map->count); + + it = hm_iter_end(map); + + cmc_assert(!hm_iter_at_start(&it)); + cmc_assert(hm_iter_at_end(&it)); + + cmc_assert_equals(size_t, 100, hm_iter_value(&it)); + + cmc_assert(hm_iter_to_start(&it)); + + cmc_assert(hm_iter_at_start(&it)); + cmc_assert(!hm_iter_at_end(&it)); + + cmc_assert_equals(size_t, 1, hm_iter_value(&it)); + + hm_free(map); + }); + + CMC_CREATE_TEST(PFX##_iter_to_end(), { + struct hashmap *map = hm_new(100, 0.6, hm_fkey_numhash, hm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + struct hashmap_iter it = hm_iter_end(map); + + cmc_assert(!hm_iter_to_end(&it)); + + for (size_t i = 1; i <= 100; i++) + hm_insert(map, i, i); + + it = hm_iter_start(map); + + cmc_assert(hm_iter_at_start(&it)); + cmc_assert(!hm_iter_at_end(&it)); + + cmc_assert_equals(size_t, 1, hm_iter_value(&it)); + + cmc_assert(hm_iter_to_end(&it)); + + cmc_assert(!hm_iter_at_start(&it)); + cmc_assert(hm_iter_at_end(&it)); + + cmc_assert_equals(size_t, 100, hm_iter_value(&it)); + + hm_free(map); + }); + + CMC_CREATE_TEST(PFX##_iter_next(), { + struct hashmap *map = hm_new(100, 0.6, hm_fkey, hm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + struct hashmap_iter it = hm_iter_start(map); + + cmc_assert(!hm_iter_next(&it)); + + for (size_t i = 1; i <= 1000; i++) + hm_insert(map, i, i); + + size_t sum = 0; + for (it = hm_iter_start(map); !hm_iter_at_end(&it); hm_iter_next(&it)) + { + sum += hm_iter_value(&it); + } + + cmc_assert_equals(size_t, 500500, sum); + + sum = 0; + + hm_iter_to_start(&it); + do + { + sum += hm_iter_value(&it); + } while (hm_iter_next(&it)); + + cmc_assert_equals(size_t, 500500, sum); + + hm_free(map); + }); + + CMC_CREATE_TEST(PFX##_iter_prev(), { + struct hashmap *map = hm_new(100, 0.6, hm_fkey, hm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + struct hashmap_iter it = hm_iter_end(map); + + cmc_assert(!hm_iter_prev(&it)); + + for (size_t i = 1; i <= 1000; i++) + hm_insert(map, i, i); + + size_t sum = 0; + for (it = hm_iter_end(map); !hm_iter_at_start(&it); hm_iter_prev(&it)) + { + sum += hm_iter_value(&it); + } + + cmc_assert_equals(size_t, 500500, sum); + + sum = 0; + + hm_iter_to_end(&it); + do + { + sum += hm_iter_value(&it); + } while (hm_iter_prev(&it)); + + cmc_assert_equals(size_t, 500500, sum); + + hm_free(map); + }); + + CMC_CREATE_TEST(PFX##_iter_advance(), { + struct hashmap *map = hm_new(100, 0.6, hm_fkey_numhash, hm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + struct hashmap_iter it = hm_iter_start(map); + + cmc_assert(!hm_iter_advance(&it, 1)); + + for (size_t i = 0; i <= 1000; i++) + hm_insert(map, i, i); + + it = hm_iter_start(map); + + cmc_assert(!hm_iter_advance(&it, 0)); + cmc_assert(!hm_iter_advance(&it, map->count)); + + size_t sum = 0; + for (it = hm_iter_start(map);;) + { + sum += hm_iter_value(&it); + + if (!hm_iter_advance(&it, 2)) + break; + } + + cmc_assert_equals(size_t, 250500, sum); + + hm_iter_to_start(&it); + cmc_assert(hm_iter_advance(&it, map->count - 1)); + + hm_free(map); + }); + + CMC_CREATE_TEST(PFX##_iter_rewind(), { + struct hashmap *map = hm_new(100, 0.6, hm_fkey_numhash, hm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + struct hashmap_iter it = hm_iter_end(map); + + cmc_assert(!hm_iter_rewind(&it, 1)); + + for (size_t i = 0; i <= 1000; i++) + hm_insert(map, i, i); + + it = hm_iter_end(map); + + cmc_assert(!hm_iter_rewind(&it, 0)); + cmc_assert(!hm_iter_rewind(&it, map->count)); + + size_t sum = 0; + for (it = hm_iter_end(map);;) + { + sum += hm_iter_value(&it); + + if (!hm_iter_rewind(&it, 2)) + break; + } + + cmc_assert_equals(size_t, 250500, sum); + + hm_iter_to_end(&it); + cmc_assert(hm_iter_rewind(&it, map->count - 1)); + + hm_free(map); + }); + + CMC_CREATE_TEST(PFX##_iter_go_to(), { + struct hashmap *map = hm_new(100, 0.6, hm_fkey_numhash, hm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + struct hashmap_iter it = hm_iter_end(map); + cmc_assert(!hm_iter_go_to(&it, 0)); + + it = hm_iter_start(map); + cmc_assert(!hm_iter_go_to(&it, 0)); + + for (size_t i = 0; i <= 1000; i++) + hm_insert(map, i, i); + + it = hm_iter_start(map); + + size_t sum = 0; + for (size_t i = 0; i < 1001; i++) + { + hm_iter_go_to(&it, i); + + sum += hm_iter_value(&it); + } + + cmc_assert_equals(size_t, 500500, sum); + + sum = 0; + for (size_t i = 1001; i > 0; i--) + { + cmc_assert(hm_iter_go_to(&it, i - 1)); + + sum += hm_iter_value(&it); + } + + cmc_assert_equals(size_t, 500500, sum); + + sum = 0; + for (size_t i = 0; i < 1001; i += 100) + { + cmc_assert(hm_iter_go_to(&it, i)); + cmc_assert_equals(size_t, i, hm_iter_index(&it)); + + sum += hm_iter_value(&it); + } + + cmc_assert_equals(size_t, 5500, sum); + + hm_free(map); + }); + + CMC_CREATE_TEST(PFX##_iter_key(), { + struct hashmap *map = hm_new(100, 0.6, hm_fkey, hm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + struct hashmap_iter it = hm_iter_end(map); + + cmc_assert_equals(size_t, (size_t){ 0 }, hm_iter_key(&it)); + + cmc_assert(hm_insert(map, 10, 10)); + + it = hm_iter_start(map); + + cmc_assert_equals(size_t, 10, hm_iter_key(&it)); + + hm_free(map); + }); + + CMC_CREATE_TEST(PFX##_iter_value(), { + struct hashmap *map = hm_new(100, 0.6, hm_fkey, hm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + struct hashmap_iter it = hm_iter_end(map); + + cmc_assert_equals(size_t, (size_t){ 0 }, hm_iter_value(&it)); + + cmc_assert(hm_insert(map, 10, 10)); + + it = hm_iter_start(map); + + cmc_assert_equals(size_t, 10, hm_iter_value(&it)); + + hm_free(map); + }); + + CMC_CREATE_TEST(PFX##_iter_rvalue(), { + struct hashmap *map = hm_new(100, 0.6, hm_fkey, hm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + struct hashmap_iter it = hm_iter_end(map); + + cmc_assert_equals(ptr, NULL, hm_iter_rvalue(&it)); + + cmc_assert(hm_insert(map, 10, 10)); + + it = hm_iter_start(map); + + cmc_assert_not_equals(ptr, NULL, hm_iter_rvalue(&it)); + cmc_assert_equals(size_t, 10, *hm_iter_rvalue(&it)); + + hm_free(map); + }); + + CMC_CREATE_TEST(PFX##_iter_index(), { + struct hashmap *map = hm_new(100, 0.6, hm_fkey, hm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + for (size_t i = 0; i <= 1000; i++) + hm_insert(map, i, i); + + struct hashmap_iter it = hm_iter_start(map); + + for (size_t i = 0; i < 1001; i++) + { + cmc_assert_equals(size_t, i, hm_iter_index(&it)); + hm_iter_next(&it); + } + + it = hm_iter_end(map); + for (size_t i = 1001; i > 0; i--) + { + cmc_assert_equals(size_t, i - 1, hm_iter_index(&it)); + hm_iter_prev(&it); + } + + hm_free(map); + }); +}); + +#endif /* CMC_TESTS_UNT_HASHMAP_H */ diff --git a/tests/unt_hashmultimap.h b/tests/unt_hashmultimap.h new file mode 100644 index 0000000..99a7886 --- /dev/null +++ b/tests/unt_hashmultimap.h @@ -0,0 +1,1031 @@ +#ifndef CMC_TESTS_UNT_HASHMULTIMAP_H +#define CMC_TESTS_UNT_HASHMULTIMAP_H + +#include "utl.h" + +#define V size_t +#define K size_t +#define PFX hmm +#define SNAME hashmultimap +#define CMC_EXT_HASHMULTIMAP_INIT +#define CMC_EXT_HASHMULTIMAP_ITER +#define CMC_EXT_HASHMULTIMAP_STR +#include "cmc/hashmultimap.h" + +struct hashmultimap_fkey *hmm_fkey = &(struct hashmultimap_fkey){ + .cmp = cmc_size_cmp, .cpy = NULL, .str = cmc_size_str, .free = NULL, .hash = cmc_size_hash, .pri = cmc_size_cmp +}; + +struct hashmultimap_fval *hmm_fval = &(struct hashmultimap_fval){ + .cmp = cmc_size_cmp, .cpy = NULL, .str = cmc_size_str, .free = NULL, .hash = cmc_size_hash, .pri = cmc_size_cmp +}; + +struct hashmultimap_fkey *hmm_fkey_counter = &(struct hashmultimap_fkey){ + .cmp = k_c_cmp, .cpy = k_c_cpy, .str = k_c_str, .free = k_c_free, .hash = k_c_hash, .pri = k_c_pri +}; + +struct hashmultimap_fval *hmm_fval_counter = &(struct hashmultimap_fval){ + .cmp = v_c_cmp, .cpy = v_c_cpy, .str = v_c_str, .free = v_c_free, .hash = v_c_hash, .pri = v_c_pri +}; + +struct cmc_alloc_node *hmm_alloc_node = + &(struct cmc_alloc_node){ .malloc = malloc, .calloc = calloc, .realloc = realloc, .free = free }; + +CMC_CREATE_UNIT(CMCHashMultiMap, true, { + CMC_CREATE_TEST(PFX##_new(), { + struct hashmultimap *map = hmm_new(943722, 0.6, hmm_fkey, hmm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + cmc_assert_not_equals(ptr, NULL, map->buffer); + cmc_assert_equals(size_t, 0, map->count); + cmc_assert_equals(double, 0.6, map->load); + cmc_assert_equals(int32_t, CMC_FLAG_OK, map->flag); + cmc_assert_equals(ptr, hmm_fkey, map->f_key); + cmc_assert_equals(ptr, hmm_fval, map->f_val); + cmc_assert_equals(ptr, cmc_alloc_node_default.malloc, map->alloc->malloc); + cmc_assert_equals(ptr, cmc_alloc_node_default.calloc, map->alloc->calloc); + cmc_assert_equals(ptr, cmc_alloc_node_default.realloc, map->alloc->realloc); + cmc_assert_equals(ptr, cmc_alloc_node_default.free, map->alloc->free); + cmc_assert_equals(ptr, NULL, map->callbacks); + + cmc_assert_greater_equals(size_t, (943722 / 0.6), hmm_capacity(map)); + + hmm_free(map); + + map = hmm_new(100, 0.6, hmm_fkey, hmm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + for (size_t i = 0; i < map->capacity; i++) + { + cmc_assert_equals(ptr, NULL, map->buffer[i][0]); + cmc_assert_equals(ptr, NULL, map->buffer[i][1]); + } + + hmm_free(map); + + map = hmm_new(0, 0.6, hmm_fkey, hmm_fval); + cmc_assert_equals(ptr, NULL, map); + + map = hmm_new(UINT64_MAX, 0.99, hmm_fkey, hmm_fval); + cmc_assert_equals(ptr, NULL, map); + + map = hmm_new(1000, 0.6, hmm_fkey, NULL); + cmc_assert_equals(ptr, NULL, map); + + map = hmm_new(1000, 0.6, NULL, hmm_fval); + cmc_assert_equals(ptr, NULL, map); + + map = hmm_new(1000, 0.6, NULL, NULL); + cmc_assert_equals(ptr, NULL, map); + }); + + CMC_CREATE_TEST(PFX##_new_custom(), { + struct hashmultimap *map = hmm_new_custom(943722, 0.6, hmm_fkey, hmm_fval, hmm_alloc_node, callbacks); + + cmc_assert_not_equals(ptr, NULL, map); + cmc_assert_not_equals(ptr, NULL, map->buffer); + cmc_assert_equals(size_t, 0, map->count); + cmc_assert_equals(double, 0.6, map->load); + cmc_assert_equals(int32_t, CMC_FLAG_OK, map->flag); + cmc_assert_equals(ptr, hmm_fkey, map->f_key); + cmc_assert_equals(ptr, hmm_fval, map->f_val); + cmc_assert_equals(ptr, hmm_alloc_node, map->alloc); + cmc_assert_equals(ptr, callbacks, map->callbacks); + + cmc_assert_greater_equals(size_t, (943722 / 0.6), hmm_capacity(map)); + + hmm_free(map); + + map = hmm_new_custom(0, 0.6, hmm_fkey, hmm_fval, NULL, NULL); + cmc_assert_equals(ptr, NULL, map); + + map = hmm_new_custom(UINT64_MAX, 0.99, hmm_fkey, hmm_fval, NULL, NULL); + cmc_assert_equals(ptr, NULL, map); + + map = hmm_new_custom(1000, 0.6, hmm_fkey, NULL, NULL, NULL); + cmc_assert_equals(ptr, NULL, map); + + map = hmm_new_custom(1000, 0.6, NULL, hmm_fval, NULL, NULL); + cmc_assert_equals(ptr, NULL, map); + + map = hmm_new_custom(1000, 0.6, NULL, NULL, NULL, NULL); + cmc_assert_equals(ptr, NULL, map); + }); + + CMC_CREATE_TEST(PFX##_clear(), { + k_total_free = 0; + v_total_free = 0; + struct hashmultimap *map = hmm_new(100, 0.6, hmm_fkey_counter, hmm_fval_counter); + + cmc_assert_not_equals(ptr, NULL, map); + + for (size_t i = 1; i <= 1000; i++) + hmm_insert(map, i, i); + + cmc_assert_equals(size_t, 1000, map->count); + + map->flag = CMC_FLAG_ERROR; + hmm_clear(map); + + cmc_assert_equals(size_t, 0, map->count); + cmc_assert_equals(int32_t, CMC_FLAG_OK, map->flag); + cmc_assert_equals(int32_t, 1000, k_total_free); + cmc_assert_equals(int32_t, 1000, v_total_free); + + hmm_free(map); + k_total_free = 0; + v_total_free = 0; + }); + + CMC_CREATE_TEST(PFX##_free(), { + k_total_free = 0; + v_total_free = 0; + struct hashmultimap *map = hmm_new(100, 0.6, hmm_fkey_counter, hmm_fval_counter); + + cmc_assert_not_equals(ptr, NULL, map); + + for (size_t i = 1; i <= 1000; i++) + hmm_insert(map, i, i); + + cmc_assert_equals(size_t, 1000, map->count); + + hmm_free(map); + + cmc_assert_equals(int32_t, 1000, k_total_free); + cmc_assert_equals(int32_t, 1000, v_total_free); + + map = hmm_new(1000, 0.6, hmm_fkey_counter, hmm_fval_counter); + + cmc_assert_not_equals(ptr, NULL, map); + cmc_assert_not_equals(ptr, NULL, map->buffer); + + hmm_free(map); + + cmc_assert_equals(int32_t, 1000, k_total_free); + cmc_assert_equals(int32_t, 1000, v_total_free); + k_total_free = 0; + v_total_free = 0; + }); + + CMC_CREATE_TEST(insert[count], { + struct hashmultimap *map = hmm_new(100, 0.8, hmm_fkey, hmm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + for (size_t i = 0; i < 100; i++) + cmc_assert(hmm_insert(map, i % 20, i % 20)); + + cmc_assert_equals(size_t, 100, hmm_count(map)); + + hmm_free(map); + }); + + CMC_CREATE_TEST(PFX##_remove(), { + struct hashmultimap *map = hmm_new(100, 0.8, hmm_fkey, hmm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + for (size_t i = 0; i < 200; i++) + cmc_assert(hmm_insert(map, i % 20, i % 20)); + + size_t r; + + for (size_t i = 0; i < 60; i++) + cmc_assert(hmm_remove(map, i % 20, &r)); + + cmc_assert_equals(size_t, 140, hmm_count(map)); + + hmm_free(map); + + // count = 0 + map = hmm_new(100, 0.8, hmm_fkey, hmm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + cmc_assert(!hmm_remove(map, 10, &r)); + + hmm_free(map); + + // count = 1 + map = hmm_new(100, 0.8, hmm_fkey, hmm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + cmc_assert(hmm_insert(map, 10, 11)); + cmc_assert(hmm_remove(map, 10, &r)); + + hmm_free(map); + }); + + CMC_CREATE_TEST(get[key ordering], { + struct hashmultimap *map = hmm_new(100, 0.8, hmm_fkey, hmm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + /* Assert that the value 100 is accessed before the value 101 for */ + /* the same key */ + cmc_assert(hmm_insert(map, 10, 100)); + cmc_assert(hmm_insert(map, 20, 102)); + cmc_assert(hmm_insert(map, 10, 101)); + + cmc_assert_equals(size_t, 100, hmm_get(map, 10)); + + hmm_free(map); + }); + + CMC_CREATE_TEST(PFX##_max(), { + struct hashmultimap *map = hmm_new(50, 0.8, hmm_fkey, hmm_fval); + + size_t max_key; + size_t max_val; + cmc_assert(!hmm_max(map, &max_key, &max_val)); + cmc_assert_equals(int32_t, CMC_FLAG_EMPTY, map->flag); + + cmc_assert(hmm_insert(map, 1, 2)); + cmc_assert(hmm_max(map, &max_key, &max_val)); + cmc_assert_equals(size_t, 1, max_key); + cmc_assert_equals(size_t, 2, max_val); + + cmc_assert(hmm_insert(map, 2, 3)); + cmc_assert(hmm_insert(map, 0, 1)); + cmc_assert(hmm_max(map, &max_key, &max_val)); + cmc_assert_equals(size_t, 2, max_key); + cmc_assert_equals(size_t, 3, max_val); + + for (size_t i = 3; i <= 100; i++) + { + cmc_assert(hmm_insert(map, i, i)); + } + + cmc_assert(hmm_max(map, &max_key, &max_val)); + cmc_assert_equals(size_t, 100, max_key); + cmc_assert_equals(size_t, 100, max_val); + + hmm_free(map); + }); + + CMC_CREATE_TEST(PFX##_min(), { + struct hashmultimap *map = hmm_new(50, 0.8, hmm_fkey, hmm_fval); + + size_t min_key; + size_t min_val; + cmc_assert(!hmm_min(map, &min_key, &min_val)); + cmc_assert_equals(int32_t, CMC_FLAG_EMPTY, map->flag); + + cmc_assert(hmm_insert(map, 1, 2)); + cmc_assert(hmm_min(map, &min_key, &min_val)); + cmc_assert_equals(size_t, 1, min_key); + cmc_assert_equals(size_t, 2, min_val); + + cmc_assert(hmm_insert(map, 2, 3)); + cmc_assert(hmm_insert(map, 0, 1)); + cmc_assert(hmm_min(map, &min_key, &min_val)); + cmc_assert_equals(size_t, 0, min_key); + cmc_assert_equals(size_t, 1, min_val); + + for (size_t i = 3; i <= 100; i++) + { + cmc_assert(hmm_insert(map, i, i)); + } + + cmc_assert(hmm_min(map, &min_key, &min_val)); + cmc_assert_equals(size_t, 0, min_key); + cmc_assert_equals(size_t, 1, min_val); + + hmm_free(map); + }); + + CMC_CREATE_TEST(PFX##_copy_of(), { + struct hashmultimap *map = hmm_new(100, 0.8, hmm_fkey, hmm_fval); + + for (size_t i = 0; i < 1000; i++) + hmm_insert(map, i, i); + + struct hashmultimap *map2 = hmm_copy_of(map); + + cmc_assert_equals(size_t, map->count, map2->count); + cmc_assert(hmm_equals(map, map2)); + + hmm_free(map); + }); + + CMC_CREATE_TEST(PFX##_equals(), { + struct hashmultimap *map1 = hmm_new(100, 0.8, hmm_fkey, hmm_fval); + struct hashmultimap *map2 = hmm_new(100, 0.8, hmm_fkey, hmm_fval); + + cmc_assert(hmm_equals(map1, map2)); + + cmc_assert(hmm_insert(map1, 1, 1)); + cmc_assert(hmm_insert(map2, 1, 1)); + cmc_assert(hmm_equals(map1, map2)); + + for (size_t i = 2; i < 200; i++) + { + cmc_assert(hmm_insert(map1, i, i) && hmm_insert(map2, i, i)); + } + + cmc_assert(hmm_equals(map1, map2)); + + hmm_free(map1); + hmm_free(map2); + }); + + CMC_CREATE_TEST(key_count, { + struct hashmultimap *map = hmm_new(50, 0.8, hmm_fkey, hmm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + for (size_t i = 0; i < 1000; i++) + cmc_assert(hmm_insert(map, i % 20, i % 20)); + + cmc_assert_equals(size_t, 1000, hmm_count(map)); + + for (size_t i = 0; i < 20; i++) + cmc_assert_equals(size_t, 50, hmm_key_count(map, i)); + + hmm_free(map); + }); + + CMC_CREATE_TEST(flags, { + struct hashmultimap *map = hmm_new(100, 0.8, hmm_fkey, hmm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + cmc_assert_equals(int32_t, CMC_FLAG_OK, hmm_flag(map)); + + // clear + map->flag = CMC_FLAG_ERROR; + hmm_clear(map); + cmc_assert_equals(int32_t, CMC_FLAG_OK, hmm_flag(map)); + + // customize + map->flag = CMC_FLAG_ERROR; + hmm_customize(map, NULL, NULL); + cmc_assert_equals(int32_t, CMC_FLAG_OK, hmm_flag(map)); + + // insert + map->flag = CMC_FLAG_ERROR; + cmc_assert(hmm_insert(map, 1, 1)); + cmc_assert_equals(int32_t, CMC_FLAG_OK, hmm_flag(map)); + + // update + cmc_assert(!hmm_update(map, 9, 10, NULL)); + cmc_assert_equals(int32_t, CMC_FLAG_NOT_FOUND, hmm_flag(map)); + + cmc_assert(hmm_update(map, 1, 20, NULL)); + cmc_assert_equals(int32_t, CMC_FLAG_OK, hmm_flag(map)); + + // update_all + map->flag = CMC_FLAG_ERROR; + cmc_assert_equals(size_t, 1, hmm_update_all(map, 1, 2, NULL)); + cmc_assert_equals(int32_t, CMC_FLAG_OK, hmm_flag(map)); + + map->flag = CMC_FLAG_ERROR; + cmc_assert_equals(size_t, 0, hmm_update_all(map, 2, 4, NULL)); + cmc_assert_equals(int32_t, CMC_FLAG_NOT_FOUND, hmm_flag(map)); + + hmm_clear(map); + cmc_assert_equals(size_t, 0, hmm_update_all(map, 1, 2, NULL)); + cmc_assert_equals(int32_t, CMC_FLAG_EMPTY, hmm_flag(map)); + + // remove + map->flag = CMC_FLAG_ERROR; + cmc_assert(!hmm_remove(map, 1, NULL)); + cmc_assert_equals(int32_t, CMC_FLAG_EMPTY, hmm_flag(map)); + + cmc_assert(hmm_insert(map, 1, 1)); + cmc_assert(!hmm_remove(map, 2, NULL)); + cmc_assert_equals(int32_t, CMC_FLAG_NOT_FOUND, hmm_flag(map)); + + cmc_assert(hmm_remove(map, 1, NULL)); + cmc_assert_equals(int32_t, CMC_FLAG_OK, hmm_flag(map)); + + // remove_all + cmc_assert(!hmm_remove_all(map, 1, NULL)); + cmc_assert_equals(int32_t, CMC_FLAG_EMPTY, hmm_flag(map)); + + cmc_assert(hmm_insert(map, 1, 1) && hmm_insert(map, 1, 1)); + cmc_assert_equals(size_t, 0, hmm_remove_all(map, 2, NULL)); + cmc_assert_equals(int32_t, CMC_FLAG_NOT_FOUND, hmm_flag(map)); + + cmc_assert_equals(size_t, 2, hmm_remove_all(map, 1, NULL)); + cmc_assert_equals(int32_t, CMC_FLAG_OK, hmm_flag(map)); + + // max min + cmc_assert(!hmm_max(map, NULL, NULL)); + cmc_assert_equals(int32_t, CMC_FLAG_EMPTY, hmm_flag(map)); + map->flag = CMC_FLAG_ERROR; + cmc_assert(!hmm_min(map, NULL, NULL)); + cmc_assert_equals(int32_t, CMC_FLAG_EMPTY, hmm_flag(map)); + + cmc_assert(hmm_insert(map, 1, 1)); + cmc_assert(hmm_max(map, NULL, NULL)); + cmc_assert_equals(int32_t, CMC_FLAG_OK, hmm_flag(map)); + map->flag = CMC_FLAG_ERROR; + cmc_assert(hmm_min(map, NULL, NULL)); + cmc_assert_equals(int32_t, CMC_FLAG_OK, hmm_flag(map)); + + // get get_ref + hmm_get(map, 2); + cmc_assert_equals(int32_t, CMC_FLAG_NOT_FOUND, hmm_flag(map)); + map->flag = CMC_FLAG_ERROR; + hmm_get_ref(map, 2); + cmc_assert_equals(int32_t, CMC_FLAG_NOT_FOUND, hmm_flag(map)); + + hmm_get(map, 1); + cmc_assert_equals(int32_t, CMC_FLAG_OK, hmm_flag(map)); + map->flag = CMC_FLAG_ERROR; + hmm_get_ref(map, 1); + cmc_assert_equals(int32_t, CMC_FLAG_OK, hmm_flag(map)); + + hmm_clear(map); + hmm_get(map, 1); + cmc_assert_equals(int32_t, CMC_FLAG_EMPTY, hmm_flag(map)); + map->flag = CMC_FLAG_ERROR; + hmm_get_ref(map, 1); + cmc_assert_equals(int32_t, CMC_FLAG_EMPTY, hmm_flag(map)); + + // contains + map->flag = CMC_FLAG_ERROR; + cmc_assert(!hmm_contains(map, 1000)); + cmc_assert_equals(int32_t, CMC_FLAG_OK, hmm_flag(map)); + + // copy_of + map->flag = CMC_FLAG_ERROR; + struct hashmultimap *map2 = hmm_copy_of(map); + cmc_assert_equals(int32_t, CMC_FLAG_OK, hmm_flag(map)); + cmc_assert_equals(int32_t, CMC_FLAG_OK, hmm_flag(map2)); + + // equals + map->flag = CMC_FLAG_ERROR; + map2->flag = CMC_FLAG_ERROR; + cmc_assert(hmm_equals(map, map2)); + cmc_assert_equals(int32_t, CMC_FLAG_OK, hmm_flag(map)); + cmc_assert_equals(int32_t, CMC_FLAG_OK, hmm_flag(map2)); + + hmm_free(map); + hmm_free(map2); + }); + + CMC_CREATE_TEST(callbacks, { + struct hashmultimap *map = hmm_new_custom(100, 0.8, hmm_fkey, hmm_fval, NULL, callbacks); + + cmc_assert_not_equals(ptr, NULL, map); + + total_create = 0; + total_read = 0; + total_update = 0; + total_delete = 0; + total_resize = 0; + + cmc_assert(hmm_insert(map, 1, 1)); + cmc_assert_equals(int32_t, 1, total_create); + + cmc_assert(hmm_update(map, 1, 2, NULL)); + cmc_assert_equals(int32_t, 1, total_update); + + cmc_assert(hmm_insert(map, 1, 1)); + cmc_assert_equals(int32_t, 2, total_create); + + cmc_assert_equals(size_t, 2, hmm_update_all(map, 1, 3, NULL)); + cmc_assert_equals(int32_t, 2, total_update); + + cmc_assert(hmm_remove(map, 1, NULL)); + cmc_assert_equals(int32_t, 1, total_delete); + + cmc_assert(hmm_insert(map, 1, 2)); + cmc_assert_equals(int32_t, 3, total_create); + + cmc_assert_equals(size_t, 2, hmm_remove_all(map, 1, NULL)); + cmc_assert_equals(int32_t, 2, total_delete); + + cmc_assert(hmm_insert(map, 1, 2)); + cmc_assert_equals(int32_t, 4, total_create); + + cmc_assert(hmm_max(map, NULL, NULL)); + cmc_assert_equals(int32_t, 1, total_read); + + cmc_assert(hmm_min(map, NULL, NULL)); + cmc_assert_equals(int32_t, 2, total_read); + + cmc_assert_equals(size_t, 2, hmm_get(map, 1)); + cmc_assert_equals(int32_t, 3, total_read); + + cmc_assert_not_equals(ptr, NULL, hmm_get_ref(map, 1)); + cmc_assert_equals(int32_t, 4, total_read); + + cmc_assert(hmm_contains(map, 1)); + cmc_assert_equals(int32_t, 5, total_read); + + cmc_assert(hmm_resize(map, 1000)); + cmc_assert_equals(int32_t, 1, total_resize); + + cmc_assert(hmm_resize(map, 100)); + cmc_assert_equals(int32_t, 2, total_resize); + + cmc_assert_equals(int32_t, 4, total_create); + cmc_assert_equals(int32_t, 5, total_read); + cmc_assert_equals(int32_t, 2, total_update); + cmc_assert_equals(int32_t, 2, total_delete); + cmc_assert_equals(int32_t, 2, total_resize); + + hmm_customize(map, NULL, NULL); + + cmc_assert_equals(ptr, NULL, map->callbacks); + + hmm_clear(map); + cmc_assert(hmm_insert(map, 1, 1)); + cmc_assert(hmm_update(map, 1, 2, NULL)); + cmc_assert(hmm_insert(map, 1, 1)); + cmc_assert_equals(size_t, 2, hmm_update_all(map, 1, 3, NULL)); + cmc_assert(hmm_remove(map, 1, NULL)); + cmc_assert(hmm_insert(map, 1, 2)); + cmc_assert_equals(size_t, 2, hmm_remove_all(map, 1, NULL)); + cmc_assert(hmm_insert(map, 1, 2)); + cmc_assert(hmm_max(map, NULL, NULL)); + cmc_assert(hmm_min(map, NULL, NULL)); + cmc_assert_equals(size_t, 2, hmm_get(map, 1)); + cmc_assert_not_equals(ptr, NULL, hmm_get_ref(map, 1)); + cmc_assert(hmm_contains(map, 1)); + cmc_assert(hmm_resize(map, 1000)); + cmc_assert(hmm_resize(map, 100)); + + cmc_assert_equals(int32_t, 4, total_create); + cmc_assert_equals(int32_t, 5, total_read); + cmc_assert_equals(int32_t, 2, total_update); + cmc_assert_equals(int32_t, 2, total_delete); + cmc_assert_equals(int32_t, 2, total_resize); + + cmc_assert_equals(ptr, NULL, map->callbacks); + + hmm_free(map); + + total_create = 0; + total_read = 0; + total_update = 0; + total_delete = 0; + total_resize = 0; + }); +}); + +struct hashmultimap_fkey *hmm_fkey_numhash = &(struct hashmultimap_fkey){ + .cmp = cmc_size_cmp, .cpy = NULL, .str = cmc_size_str, .free = NULL, .hash = numhash, .pri = cmc_size_cmp +}; + +CMC_CREATE_UNIT(CMCHashMultiMapIter, true, { + CMC_CREATE_TEST(PFX##_iter_start(), { + struct hashmultimap *map = hmm_new(100, 0.6, hmm_fkey_numhash, hmm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + struct hashmultimap_iter it = hmm_iter_start(map); + + cmc_assert_equals(ptr, map, it.target); + cmc_assert_equals(size_t, 0, it.cursor); + cmc_assert_equals(size_t, 0, it.index); + cmc_assert_equals(size_t, 0, it.first); + cmc_assert_equals(size_t, 0, it.last); + cmc_assert_equals(bool, true, it.start); + cmc_assert_equals(bool, true, it.end); + + cmc_assert(hmm_iter_at_start(&it)); + cmc_assert(hmm_iter_at_end(&it)); + + cmc_assert(hmm_insert(map, 1, 1)); + cmc_assert(hmm_insert(map, 2, 2)); + cmc_assert(hmm_insert(map, 3, 3)); + + it = hmm_iter_start(map); + + cmc_assert_equals(size_t, 0, it.index); + + cmc_assert_equals(size_t, 1, it.cursor); + cmc_assert_equals(size_t, 1, it.first); + cmc_assert_equals(size_t, 3, it.last); + cmc_assert_equals(bool, false, it.end); + + hmm_free(map); + }); + + CMC_CREATE_TEST(PFX##_iter_end(), { + struct hashmultimap *map = hmm_new(100, 0.6, hmm_fkey_numhash, hmm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + struct hashmultimap_iter it = hmm_iter_end(map); + + cmc_assert_equals(ptr, map, it.target); + cmc_assert_equals(size_t, 0, it.cursor); + cmc_assert_equals(size_t, 0, it.index); + cmc_assert_equals(size_t, 0, it.first); + cmc_assert_equals(size_t, 0, it.last); + cmc_assert_equals(bool, true, it.start); + cmc_assert_equals(bool, true, it.end); + + cmc_assert(hmm_iter_at_start(&it)); + cmc_assert(hmm_iter_at_end(&it)); + + cmc_assert(hmm_insert(map, 1, 1)); + cmc_assert(hmm_insert(map, 2, 2)); + cmc_assert(hmm_insert(map, 3, 3)); + + it = hmm_iter_end(map); + + cmc_assert_equals(size_t, map->count - 1, it.index); + + cmc_assert_equals(size_t, 3, it.cursor); + cmc_assert_equals(size_t, 1, it.first); + cmc_assert_equals(size_t, 3, it.last); + cmc_assert_equals(bool, false, it.start); + + hmm_free(map); + }); + + CMC_CREATE_TEST(PFX##_iter_at_start(), { + struct hashmultimap *map = hmm_new(100, 0.6, hmm_fkey, hmm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + struct hashmultimap_iter it = hmm_iter_start(map); + + // Empty checks + cmc_assert(hmm_iter_at_start(&it)); + it = hmm_iter_end(map); + cmc_assert(hmm_iter_at_start(&it)); + + // Non-empty checks + cmc_assert(hmm_insert(map, 1, 1)); + cmc_assert(hmm_insert(map, 2, 2)); + it = hmm_iter_end(map); + cmc_assert(!hmm_iter_at_start(&it)); + it = hmm_iter_start(map); + cmc_assert(hmm_iter_at_start(&it)); + + hmm_free(map); + }); + + CMC_CREATE_TEST(PFX##_iter_at_end(), { + struct hashmultimap *map = hmm_new(100, 0.6, hmm_fkey, hmm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + struct hashmultimap_iter it = hmm_iter_start(map); + + // Empty check + cmc_assert(hmm_iter_at_end(&it)); + it = hmm_iter_end(map); + cmc_assert(hmm_iter_at_end(&it)); + + // Non-empty checks + cmc_assert(hmm_insert(map, 1, 1)); + cmc_assert(hmm_insert(map, 2, 2)); + it = hmm_iter_end(map); + cmc_assert(hmm_iter_at_end(&it)); + it = hmm_iter_start(map); + cmc_assert(!hmm_iter_at_end(&it)); + + hmm_free(map); + }); + + CMC_CREATE_TEST(PFX##_iter_to_start(), { + struct hashmultimap *map = hmm_new(100, 0.6, hmm_fkey_numhash, hmm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + struct hashmultimap_iter it = hmm_iter_start(map); + + cmc_assert(!hmm_iter_to_start(&it)); + + for (size_t i = 1; i <= 100; i++) + hmm_insert(map, i, i); + + cmc_assert_equals(size_t, 100, map->count); + + it = hmm_iter_end(map); + + cmc_assert(!hmm_iter_at_start(&it)); + cmc_assert(hmm_iter_at_end(&it)); + + cmc_assert_equals(size_t, 100, hmm_iter_value(&it)); + + cmc_assert(hmm_iter_to_start(&it)); + + cmc_assert(hmm_iter_at_start(&it)); + cmc_assert(!hmm_iter_at_end(&it)); + + cmc_assert_equals(size_t, 1, hmm_iter_value(&it)); + + hmm_free(map); + }); + + CMC_CREATE_TEST(PFX##_iter_to_end(), { + struct hashmultimap *map = hmm_new(100, 0.6, hmm_fkey_numhash, hmm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + struct hashmultimap_iter it = hmm_iter_end(map); + + cmc_assert(!hmm_iter_to_end(&it)); + + for (size_t i = 1; i <= 100; i++) + hmm_insert(map, i, i); + + it = hmm_iter_start(map); + + cmc_assert(hmm_iter_at_start(&it)); + cmc_assert(!hmm_iter_at_end(&it)); + + cmc_assert_equals(size_t, 1, hmm_iter_value(&it)); + + cmc_assert(hmm_iter_to_end(&it)); + + cmc_assert(!hmm_iter_at_start(&it)); + cmc_assert(hmm_iter_at_end(&it)); + + cmc_assert_equals(size_t, 100, hmm_iter_value(&it)); + + hmm_free(map); + }); + + CMC_CREATE_TEST(PFX##_iter_next(), { + struct hashmultimap *map = hmm_new(100, 0.6, hmm_fkey, hmm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + struct hashmultimap_iter it = hmm_iter_start(map); + + cmc_assert(!hmm_iter_next(&it)); + + for (size_t i = 1; i <= 1000; i++) + hmm_insert(map, i, i); + + size_t sum = 0; + for (it = hmm_iter_start(map); !hmm_iter_at_end(&it); hmm_iter_next(&it)) + { + sum += hmm_iter_value(&it); + } + + cmc_assert_equals(size_t, 500500, sum); + + sum = 0; + + hmm_iter_to_start(&it); + do + { + sum += hmm_iter_value(&it); + } while (hmm_iter_next(&it)); + + cmc_assert_equals(size_t, 500500, sum); + + hmm_free(map); + }); + + CMC_CREATE_TEST(PFX##_iter_prev(), { + struct hashmultimap *map = hmm_new(100, 0.6, hmm_fkey, hmm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + struct hashmultimap_iter it = hmm_iter_end(map); + + cmc_assert(!hmm_iter_prev(&it)); + + for (size_t i = 1; i <= 1000; i++) + hmm_insert(map, i, i); + + size_t sum = 0; + for (it = hmm_iter_end(map); !hmm_iter_at_start(&it); hmm_iter_prev(&it)) + { + sum += hmm_iter_value(&it); + } + + cmc_assert_equals(size_t, 500500, sum); + + sum = 0; + + hmm_iter_to_end(&it); + do + { + sum += hmm_iter_value(&it); + } while (hmm_iter_prev(&it)); + + cmc_assert_equals(size_t, 500500, sum); + + hmm_free(map); + }); + + CMC_CREATE_TEST(PFX##_iter_advance(), { + struct hashmultimap *map = hmm_new(100, 0.6, hmm_fkey_numhash, hmm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + struct hashmultimap_iter it = hmm_iter_start(map); + + cmc_assert(!hmm_iter_advance(&it, 1)); + + for (size_t i = 0; i <= 1000; i++) + hmm_insert(map, i, i); + + it = hmm_iter_start(map); + + cmc_assert(!hmm_iter_advance(&it, 0)); + cmc_assert(!hmm_iter_advance(&it, map->count)); + + size_t sum = 0; + for (it = hmm_iter_start(map);;) + { + sum += hmm_iter_value(&it); + + if (!hmm_iter_advance(&it, 2)) + break; + } + + cmc_assert_equals(size_t, 250500, sum); + + hmm_iter_to_start(&it); + cmc_assert(hmm_iter_advance(&it, map->count - 1)); + + hmm_free(map); + }); + + CMC_CREATE_TEST(PFX##_iter_rewind(), { + struct hashmultimap *map = hmm_new(100, 0.6, hmm_fkey_numhash, hmm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + struct hashmultimap_iter it = hmm_iter_end(map); + + cmc_assert(!hmm_iter_rewind(&it, 1)); + + for (size_t i = 0; i <= 1000; i++) + hmm_insert(map, i, i); + + it = hmm_iter_end(map); + + cmc_assert(!hmm_iter_rewind(&it, 0)); + cmc_assert(!hmm_iter_rewind(&it, map->count)); + + size_t sum = 0; + for (it = hmm_iter_end(map);;) + { + sum += hmm_iter_value(&it); + + if (!hmm_iter_rewind(&it, 2)) + break; + } + + cmc_assert_equals(size_t, 250500, sum); + + hmm_iter_to_end(&it); + cmc_assert(hmm_iter_rewind(&it, map->count - 1)); + + hmm_free(map); + }); + + CMC_CREATE_TEST(PFX##_iter_go_to(), { + struct hashmultimap *map = hmm_new(100, 0.6, hmm_fkey_numhash, hmm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + struct hashmultimap_iter it = hmm_iter_end(map); + cmc_assert(!hmm_iter_go_to(&it, 0)); + + it = hmm_iter_start(map); + cmc_assert(!hmm_iter_go_to(&it, 0)); + + for (size_t i = 0; i <= 1000; i++) + hmm_insert(map, i, i); + + it = hmm_iter_start(map); + + size_t sum = 0; + for (size_t i = 0; i < 1001; i++) + { + hmm_iter_go_to(&it, i); + + sum += hmm_iter_value(&it); + } + + cmc_assert_equals(size_t, 500500, sum); + + sum = 0; + for (size_t i = 1001; i > 0; i--) + { + cmc_assert(hmm_iter_go_to(&it, i - 1)); + + sum += hmm_iter_value(&it); + } + + cmc_assert_equals(size_t, 500500, sum); + + sum = 0; + for (size_t i = 0; i < 1001; i += 100) + { + cmc_assert(hmm_iter_go_to(&it, i)); + cmc_assert_equals(size_t, i, hmm_iter_index(&it)); + + sum += hmm_iter_value(&it); + } + + cmc_assert_equals(size_t, 5500, sum); + + hmm_free(map); + }); + + CMC_CREATE_TEST(PFX##_iter_key(), { + struct hashmultimap *map = hmm_new(100, 0.6, hmm_fkey, hmm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + struct hashmultimap_iter it = hmm_iter_end(map); + + cmc_assert_equals(size_t, (size_t){ 0 }, hmm_iter_key(&it)); + + cmc_assert(hmm_insert(map, 10, 10)); + + it = hmm_iter_start(map); + + cmc_assert_equals(size_t, 10, hmm_iter_key(&it)); + + hmm_free(map); + }); + + CMC_CREATE_TEST(PFX##_iter_value(), { + struct hashmultimap *map = hmm_new(100, 0.6, hmm_fkey, hmm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + struct hashmultimap_iter it = hmm_iter_end(map); + + cmc_assert_equals(size_t, (size_t){ 0 }, hmm_iter_value(&it)); + + cmc_assert(hmm_insert(map, 10, 10)); + + it = hmm_iter_start(map); + + cmc_assert_equals(size_t, 10, hmm_iter_value(&it)); + + hmm_free(map); + }); + + CMC_CREATE_TEST(PFX##_iter_rvalue(), { + struct hashmultimap *map = hmm_new(100, 0.6, hmm_fkey, hmm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + struct hashmultimap_iter it = hmm_iter_end(map); + + cmc_assert_equals(ptr, NULL, hmm_iter_rvalue(&it)); + + cmc_assert(hmm_insert(map, 10, 10)); + + it = hmm_iter_start(map); + + cmc_assert_not_equals(ptr, NULL, hmm_iter_rvalue(&it)); + cmc_assert_equals(size_t, 10, *hmm_iter_rvalue(&it)); + + hmm_free(map); + }); + + CMC_CREATE_TEST(PFX##_iter_index(), { + struct hashmultimap *map = hmm_new(100, 0.6, hmm_fkey, hmm_fval); + + cmc_assert_not_equals(ptr, NULL, map); + + for (size_t i = 0; i <= 1000; i++) + hmm_insert(map, i, i); + + struct hashmultimap_iter it = hmm_iter_start(map); + + for (size_t i = 0; i < 1001; i++) + { + cmc_assert_equals(size_t, i, hmm_iter_index(&it)); + hmm_iter_next(&it); + } + + it = hmm_iter_end(map); + for (size_t i = 1001; i > 0; i--) + { + cmc_assert_equals(size_t, i - 1, hmm_iter_index(&it)); + hmm_iter_prev(&it); + } + + hmm_free(map); + }); +}); + +#ifdef CMC_TEST_MAIN +int main(void) +{ + int result = CMCHashMultiMap() + CMCHashMultiMapIter(); + + printf(" +---------------------------------------------------------------+"); + printf("\n"); + printf(" | CMCHashMultiMap Suit : %-41s |\n", result == 0 ? "PASSED" : "FAILED"); + printf(" +---------------------------------------------------------------+"); + printf("\n\n\n"); + + return result; +} +#endif + +#endif /* CMC_TESTS_UNT_HASHMULTIMAP_H */ diff --git a/tests/unt_list.h b/tests/unt_list.h index 38993fb..2e2c22d 100644 --- a/tests/unt_list.h +++ b/tests/unt_list.h @@ -9,6 +9,7 @@ #define CMC_EXT_LIST_INIT #define CMC_EXT_LIST_ITER #define CMC_EXT_LIST_SEQ +#define CMC_EXT_LIST_STR #include "cmc/list.h" struct list_fval *l_fval = &(struct list_fval){ diff --git a/tests/unt_queue.h b/tests/unt_queue.h new file mode 100644 index 0000000..07ba548 --- /dev/null +++ b/tests/unt_queue.h @@ -0,0 +1,958 @@ +#ifndef CMC_TESTS_UNT_QUEUE_H +#define CMC_TESTS_UNT_QUEUE_H + +#include "utl.h" + +#define V size_t +#define PFX q +#define SNAME queue +#define CMC_EXT_QUEUE_INIT +#define CMC_EXT_QUEUE_ITER +#define CMC_EXT_QUEUE_STR +#include "cmc/queue.h" + +struct queue_fval *q_fval = &(struct queue_fval){ + .cmp = cmc_size_cmp, .cpy = NULL, .str = cmc_size_str, .free = NULL, .hash = cmc_size_hash, .pri = cmc_size_cmp +}; + +CMC_CREATE_UNIT(CMCQueue, true, { + CMC_CREATE_TEST(new, { + struct queue *q = q_new(1000000, q_fval); + + cmc_assert_not_equals(ptr, NULL, q); + cmc_assert_not_equals(ptr, NULL, q->buffer); + cmc_assert_equals(size_t, 1000000, q_capacity(q)); + cmc_assert_equals(size_t, 0, q_count(q)); + cmc_assert_not_equals(ptr, NULL, q->buffer); + + q_free(q); + }); + + CMC_CREATE_TEST(new[capacity = 0], { + struct queue *q = q_new(0, q_fval); + + cmc_assert_equals(ptr, NULL, q); + }); + + CMC_CREATE_TEST(new[capacity = UINT64_MAX], { + struct queue *q = q_new(UINT64_MAX, q_fval); + + cmc_assert_equals(ptr, NULL, q); + }); + + CMC_CREATE_TEST(clear[count capacity], { + struct queue *q = q_new(100, q_fval); + + cmc_assert_not_equals(ptr, NULL, q); + + for (size_t i = 0; i < 50; i++) + cmc_assert(q_enqueue(q, i)); + + cmc_assert_equals(size_t, 50, q_count(q)); + + q_clear(q); + + cmc_assert_equals(size_t, 0, q_count(q)); + cmc_assert_equals(size_t, 100, q_capacity(q)); + + q_free(q); + }); + + CMC_CREATE_TEST(buffer_growth[capacity = 1], { + struct queue *q = q_new(1, q_fval); + + cmc_assert_not_equals(ptr, NULL, q); + + for (size_t i = 0; i < 50; i++) + cmc_assert(q_enqueue(q, i)); + + cmc_assert_equals(size_t, 50, q_count(q)); + + q_free(q); + }); + + CMC_CREATE_TEST(buffer_growth[item preservation], { + struct queue *q = q_new(100, q_fval); + + cmc_assert_not_equals(ptr, NULL, q); + + for (size_t i = 1; i <= 1200; i++) + { + q_enqueue(q, i); + } + + size_t sum = 0; + for (size_t i = q->front, j = 0; j < q->count; i = (i + 1) % q->capacity, j++) + sum += q->buffer[i]; + + cmc_assert_equals(size_t, 1200, q_count(q)); + cmc_assert_equals(size_t, 720600, sum); + + q_free(q); + }); + + CMC_CREATE_TEST(enqueue[count capacity], { + struct queue *q = q_new(100, q_fval); + + cmc_assert_not_equals(ptr, NULL, q); + + for (size_t i = 0; i < 150; i++) + cmc_assert(q_enqueue(q, i)); + + cmc_assert_equals(size_t, 150, q_count(q)); + cmc_assert_greater(size_t, 100, q_capacity(q)); + + q_free(q); + }); + + CMC_CREATE_TEST(enqueue[item preservation], { + struct queue *q = q_new(100, q_fval); + + cmc_assert_not_equals(ptr, NULL, q); + + for (size_t i = 1; i <= 100; i++) + cmc_assert(q_enqueue(q, i)); + + size_t sum = 0; + + for (size_t i = q->front, j = 0; j < q->count; i = (i + 1) % q->capacity, j++) + { + sum += q->buffer[i]; + } + + cmc_assert_equals(size_t, 5050, sum); + + q_free(q); + }); + + CMC_CREATE_TEST(dequeue[count capacity], { + struct queue *q = q_new(100, q_fval); + + cmc_assert_not_equals(ptr, NULL, q); + + cmc_assert(q_enqueue(q, 10)); + cmc_assert(q_dequeue(q)); + + cmc_assert_equals(size_t, 0, q_count(q)); + + cmc_assert(q_enqueue(q, 10)); + cmc_assert(q_dequeue(q)); + + cmc_assert_equals(size_t, 0, q_count(q)); + + q_free(q); + }); + + CMC_CREATE_TEST(dequeue[item preservation], { + struct queue *q = q_new(100, q_fval); + + cmc_assert_not_equals(ptr, NULL, q); + + for (size_t i = 200; i > 0; i--) + cmc_assert(q_enqueue(q, i)); + + for (size_t i = 0; i < 100; i++) + cmc_assert(q_dequeue(q)); + + size_t sum = 0; + + for (size_t i = q->front, j = 0; j < q->count; i = (i + 1) % q->capacity, j++) + { + sum += q->buffer[i]; + } + + cmc_assert_equals(size_t, 5050, sum); + + q_free(q); + }); + + CMC_CREATE_TEST(peek[count = 0], { + struct queue *q = q_new(100, q_fval); + + cmc_assert_not_equals(ptr, NULL, q); + + size_t r = q_peek(q); + + cmc_assert_equals(size_t, (size_t){ 0 }, r); + + q_free(q); + }); + + CMC_CREATE_TEST(peek[sum], { + struct queue *q = q_new(100, q_fval); + + cmc_assert_not_equals(ptr, NULL, q); + + size_t sum = 0; + + for (size_t i = 0; i <= 100; i++) + cmc_assert(q_enqueue(q, i)); + + while (!q_empty(q)) + { + size_t r = q_peek(q); + + sum += r; + + cmc_assert(q_dequeue(q)); + } + + cmc_assert_equals(size_t, 5050, sum); + + q_free(q); + }); + + CMC_CREATE_TEST(contains, { + struct queue *q = q_new(100, q_fval); + + cmc_assert_not_equals(ptr, NULL, q); + + for (size_t i = 1; i <= 20; i++) + cmc_assert(q_enqueue(q, i)); + + for (size_t i = 1; i <= 20; i++) + cmc_assert(q_contains(q, i)); + + q_clear(q); + + for (size_t i = 1; i <= 20; i++) + cmc_assert(!q_contains(q, i)); + + q_free(q); + }); + + CMC_CREATE_TEST(contains[count = 0], { + struct queue *q = q_new(100, q_fval); + + cmc_assert_not_equals(ptr, NULL, q); + + cmc_assert(!q_contains(q, 10)); + + q_free(q); + }); + + CMC_CREATE_TEST(contains[count = 1], { + struct queue *q = q_new(100, q_fval); + + cmc_assert_not_equals(ptr, NULL, q); + + cmc_assert(!q_contains(q, 10)); + + cmc_assert(q_enqueue(q, 100)); + + cmc_assert(q_contains(q, 100)); + cmc_assert(!q_contains(q, 101)); + + q_free(q); + }); + + CMC_CREATE_TEST(empty, { + struct queue *q = q_new(100, q_fval); + + cmc_assert_not_equals(ptr, NULL, q); + + cmc_assert(q_empty(q)); + + cmc_assert(q_enqueue(q, 1)); + + cmc_assert(!q_empty(q)); + + q_free(q); + }); + + CMC_CREATE_TEST(full, { + struct queue *q = q_new(100, q_fval); + + cmc_assert_not_equals(ptr, NULL, q); + + cmc_assert(!q_full(q)); + + for (size_t i = 0; i != q_capacity(q); i++) + cmc_assert(q_enqueue(q, i)); + + cmc_assert(q_full(q)); + + q_free(q); + }); + + CMC_CREATE_TEST(count, { + struct queue *q = q_new(100, q_fval); + + cmc_assert_not_equals(ptr, NULL, q); + + cmc_assert_equals(size_t, 0, q_count(q)); + + for (size_t i = 0; i < 100; i++) + cmc_assert(q_enqueue(q, i)); + + cmc_assert_equals(size_t, 100, q_count(q)); + + q_free(q); + }); + + CMC_CREATE_TEST(capacity, { + struct queue *q = q_new(100, q_fval); + + cmc_assert_not_equals(ptr, NULL, q); + + cmc_assert_equals(size_t, 100, q_capacity(q)); + + q_free(q); + }); + + CMC_CREATE_TEST(resize, { + struct queue *q = q_new(100, q_fval); + + cmc_assert_not_equals(ptr, NULL, q); + + for (size_t i = 0; i < 150; i++) + cmc_assert(q_enqueue(q, i)); + + cmc_assert_equals(size_t, 150, q_count(q)); + cmc_assert(q_resize(q, q_count(q))); + cmc_assert_equals(size_t, 150, q_capacity(q)); + + q_free(q); + }); + + CMC_CREATE_TEST(resize[item preservation], { + struct queue *q = q_new(100, q_fval); + + cmc_assert_not_equals(ptr, NULL, q); + + for (size_t i = 1; i <= 150; i++) + cmc_assert(q_enqueue(q, i)); + + cmc_assert(q_resize(q, q_count(q))); + + size_t sum = 0; + + for (size_t i = q->front, j = 0; j < q->count; i = (i + 1) % q->capacity, j++) + { + sum += q->buffer[i]; + } + + cmc_assert_equals(size_t, 11325, sum); + + q_free(q); + }); + + CMC_CREATE_TEST(copy_of, { + struct queue *q1 = q_new(100, q_fval); + + cmc_assert_not_equals(ptr, NULL, q1); + + for (size_t i = 1; i <= 150; i++) + cmc_assert(q_enqueue(q1, i)); + + struct queue *q2 = q_copy_of(q1); + + cmc_assert_not_equals(ptr, NULL, q2); + cmc_assert_equals(size_t, q_count(q1), q_count(q2)); + + for (size_t i1 = q1->front, i2 = q2->front, j = 0; j < q1->count; j++) + { + cmc_assert_equals(size_t, q1->buffer[i1], q2->buffer[i2]); + + i1 = (i1 + 1) % q1->capacity; + i2 = (i2 + 1) % q2->capacity; + } + + q_free(q1); + q_free(q2); + }); + + CMC_CREATE_TEST(equals, { + struct queue *q1 = q_new(100, q_fval); + struct queue *q2 = q_new(100, q_fval); + + cmc_assert_not_equals(ptr, NULL, q1); + cmc_assert_not_equals(ptr, NULL, q2); + + for (size_t i = 1; i <= 150; i++) + { + cmc_assert(q_enqueue(q1, i)); + cmc_assert(q_enqueue(q2, i)); + } + + cmc_assert(q_equals(q1, q2)); + + q_free(q1); + q_free(q2); + }); + + CMC_CREATE_TEST(equals[from copy], { + struct queue *q1 = q_new(100, q_fval); + + cmc_assert_not_equals(ptr, NULL, q1); + + for (size_t i = 1; i <= 150; i++) + { + cmc_assert(q_enqueue(q1, i)); + } + + struct queue *q2 = q_copy_of(q1); + + cmc_assert_not_equals(ptr, NULL, q2); + + cmc_assert(q_equals(q1, q2)); + + q_free(q1); + q_free(q2); + }); + + CMC_CREATE_TEST(equals[count = 0], { + struct queue *q1 = q_new(100, q_fval); + + cmc_assert_not_equals(ptr, NULL, q1); + + struct queue *q2 = q_copy_of(q1); + + cmc_assert_not_equals(ptr, NULL, q2); + + cmc_assert(q_equals(q1, q2)); + + q_free(q1); + q_free(q2); + }); + + CMC_CREATE_TEST(flag, { + struct queue *q = q_new(100, q_fval); + + cmc_assert_not_equals(ptr, NULL, q); + cmc_assert_equals(int32_t, CMC_FLAG_OK, q_flag(q)); + + // clear + q->flag = CMC_FLAG_ERROR; + q_clear(q); + cmc_assert_equals(int32_t, CMC_FLAG_OK, q_flag(q)); + + // customize + q->flag = CMC_FLAG_ERROR; + q_customize(q, NULL, NULL); + cmc_assert_equals(int32_t, CMC_FLAG_OK, q_flag(q)); + + // enqueue + q->flag = CMC_FLAG_ERROR; + q_enqueue(q, 10); + cmc_assert_equals(int32_t, CMC_FLAG_OK, q_flag(q)); + + // dequeue + q->flag = CMC_FLAG_ERROR; + q_dequeue(q); + cmc_assert_equals(int32_t, CMC_FLAG_OK, q_flag(q)); + + q_dequeue(q); + cmc_assert_equals(int32_t, CMC_FLAG_EMPTY, q_flag(q)); + + // peek + q->flag = CMC_FLAG_ERROR; + q_peek(q); + cmc_assert_equals(int32_t, CMC_FLAG_EMPTY, q_flag(q)); + + cmc_assert(q_enqueue(q, 10)); + q_peek(q); + cmc_assert_equals(int32_t, CMC_FLAG_OK, q_flag(q)); + + // contains + q->flag = CMC_FLAG_ERROR; + cmc_assert(q_contains(q, 10)); + cmc_assert_equals(int32_t, CMC_FLAG_OK, q_flag(q)); + + // copy_of + q->flag = CMC_FLAG_OK; + struct queue *q2 = q_copy_of(q); + cmc_assert_equals(int32_t, CMC_FLAG_OK, q_flag(q)); + cmc_assert_equals(int32_t, CMC_FLAG_OK, q_flag(q2)); + + // equals + q->flag = CMC_FLAG_OK; + q2->flag = CMC_FLAG_OK; + cmc_assert(q_equals(q, q2)); + cmc_assert_equals(int32_t, CMC_FLAG_OK, q_flag(q)); + cmc_assert_equals(int32_t, CMC_FLAG_OK, q_flag(q2)); + + q_free(q); + q_free(q2); + }); + + CMC_CREATE_TEST(callbacks, { + struct queue *q = q_new_custom(100, q_fval, NULL, callbacks); + + cmc_assert_not_equals(ptr, NULL, q); + + total_create = 0; + total_read = 0; + total_update = 0; + total_delete = 0; + total_resize = 0; + + cmc_assert(q_enqueue(q, 10)); + cmc_assert_equals(int32_t, 1, total_create); + + cmc_assert(q_dequeue(q)); + cmc_assert_equals(int32_t, 1, total_delete); + + cmc_assert(q_enqueue(q, 10)); + cmc_assert_equals(int32_t, 2, total_create); + + cmc_assert_equals(size_t, 10, q_peek(q)); + cmc_assert_equals(int32_t, 1, total_read); + + cmc_assert(q_contains(q, 10)); + cmc_assert_equals(int32_t, 2, total_read); + + cmc_assert(q_resize(q, 1000)); + cmc_assert_equals(int32_t, 1, total_resize); + + cmc_assert(q_resize(q, 100)); + cmc_assert_equals(int32_t, 2, total_resize); + + cmc_assert_equals(int32_t, 2, total_create); + cmc_assert_equals(int32_t, 2, total_read); + cmc_assert_equals(int32_t, 0, total_update); + cmc_assert_equals(int32_t, 1, total_delete); + cmc_assert_equals(int32_t, 2, total_resize); + + q_customize(q, NULL, NULL); + + cmc_assert_equals(ptr, NULL, q->callbacks); + + q_clear(q); + cmc_assert(q_enqueue(q, 10)); + cmc_assert(q_dequeue(q)); + cmc_assert(q_enqueue(q, 10)); + cmc_assert_equals(size_t, 10, q_peek(q)); + cmc_assert(q_contains(q, 10)); + cmc_assert(q_resize(q, 1000)); + cmc_assert(q_resize(q, 100)); + + cmc_assert_equals(int32_t, 2, total_create); + cmc_assert_equals(int32_t, 2, total_read); + cmc_assert_equals(int32_t, 0, total_update); + cmc_assert_equals(int32_t, 1, total_delete); + cmc_assert_equals(int32_t, 2, total_resize); + + cmc_assert_equals(ptr, NULL, q->callbacks); + + q_free(q); + + total_create = 0; + total_read = 0; + total_update = 0; + total_delete = 0; + total_resize = 0; + }); +}); + +CMC_CREATE_UNIT(CMCQueueIter, true, { + CMC_CREATE_TEST(PFX##_iter_start(), { + struct queue *q = q_new(100, q_fval); + + cmc_assert_not_equals(ptr, NULL, q); + + struct queue_iter it = q_iter_start(q); + + cmc_assert_equals(ptr, q, it.target); + cmc_assert_equals(size_t, q->front, it.cursor); + cmc_assert_equals(size_t, 0, it.index); + cmc_assert_equals(bool, true, it.start); + cmc_assert_equals(bool, true, it.end); + + cmc_assert(q_iter_at_start(&it)); + cmc_assert(q_iter_at_end(&it)); + + cmc_assert(q_enqueue(q, 1)); + cmc_assert(q_enqueue(q, 2)); + + it = q_iter_start(q); + + cmc_assert_equals(size_t, 0, it.index); + cmc_assert_equals(bool, false, it.end); + cmc_assert_equals(size_t, q->front, it.cursor); + + q_free(q); + }); + + CMC_CREATE_TEST(PFX##_iter_end(), { + struct queue *q = q_new(100, q_fval); + + cmc_assert_not_equals(ptr, NULL, q); + + struct queue_iter it = q_iter_end(q); + + cmc_assert_equals(ptr, q, it.target); + cmc_assert_equals(size_t, q->back, it.cursor); + cmc_assert_equals(size_t, 0, it.index); + cmc_assert_equals(bool, true, it.start); + cmc_assert_equals(bool, true, it.end); + + cmc_assert(q_iter_at_start(&it)); + cmc_assert(q_iter_at_end(&it)); + + cmc_assert(q_enqueue(q, 1)); + cmc_assert(q_enqueue(q, 2)); + cmc_assert(q_enqueue(q, 3)); + + it = q_iter_end(q); + + cmc_assert_equals(size_t, q->count - 1, it.index); + cmc_assert_equals(size_t, q->back - 1, it.cursor); + cmc_assert_equals(bool, false, it.start); + + q_free(q); + }); + + CMC_CREATE_TEST(PFX##_iter_at_start(), { + struct queue *q = q_new(100, q_fval); + + cmc_assert_not_equals(ptr, NULL, q); + + struct queue_iter it = q_iter_start(q); + + // Empty checks + cmc_assert(q_iter_at_start(&it)); + it = q_iter_end(q); + cmc_assert(q_iter_at_start(&it)); + + // Non-empty check + cmc_assert(q_enqueue(q, 1)); + it = q_iter_end(q); + cmc_assert(!q_iter_at_start(&it)); + it = q_iter_start(q); + cmc_assert(q_iter_at_start(&it)); + + q_free(q); + }); + + CMC_CREATE_TEST(PFX##_iter_at_end(), { + struct queue *q = q_new(100, q_fval); + + cmc_assert_not_equals(ptr, NULL, q); + + struct queue_iter it = q_iter_start(q); + + // Empty check + cmc_assert(q_iter_at_end(&it)); + it = q_iter_end(q); + cmc_assert(q_iter_at_end(&it)); + + // Non-empty check + cmc_assert(q_enqueue(q, 1)); + it = q_iter_end(q); + cmc_assert(q_iter_at_end(&it)); + it = q_iter_start(q); + cmc_assert(!q_iter_at_end(&it)); + + q_free(q); + }); + + CMC_CREATE_TEST(PFX##_iter_to_start(), { + struct queue *q = q_new(100, q_fval); + + cmc_assert_not_equals(ptr, NULL, q); + + struct queue_iter it = q_iter_start(q); + + cmc_assert(!q_iter_to_start(&it)); + + for (size_t i = 1; i <= 100; i++) + q_enqueue(q, i); + + cmc_assert_equals(size_t, 100, q->count); + + it = q_iter_end(q); + + cmc_assert(!q_iter_at_start(&it)); + cmc_assert(q_iter_at_end(&it)); + + cmc_assert_equals(size_t, 100, q_iter_value(&it)); + + cmc_assert(q_iter_to_start(&it)); + + cmc_assert(q_iter_at_start(&it)); + cmc_assert(!q_iter_at_end(&it)); + + cmc_assert_equals(size_t, 1, q_iter_value(&it)); + + q_free(q); + }); + + CMC_CREATE_TEST(PFX##_iter_to_end(), { + struct queue *q = q_new(100, q_fval); + + cmc_assert_not_equals(ptr, NULL, q); + + struct queue_iter it = q_iter_end(q); + + cmc_assert(!q_iter_to_end(&it)); + + for (size_t i = 1; i <= 100; i++) + q_enqueue(q, i); + + it = q_iter_start(q); + + cmc_assert(q_iter_at_start(&it)); + cmc_assert(!q_iter_at_end(&it)); + + cmc_assert_equals(size_t, 1, q_iter_value(&it)); + + cmc_assert(q_iter_to_end(&it)); + + cmc_assert(!q_iter_at_start(&it)); + cmc_assert(q_iter_at_end(&it)); + + cmc_assert_equals(size_t, 100, q_iter_value(&it)); + + q_free(q); + }); + + CMC_CREATE_TEST(PFX##_iter_next(), { + struct queue *q = q_new(100, q_fval); + + cmc_assert_not_equals(ptr, NULL, q); + + struct queue_iter it = q_iter_start(q); + + cmc_assert(!q_iter_next(&it)); + + for (size_t i = 1; i <= 1000; i++) + q_enqueue(q, i); + + size_t sum = 0; + for (it = q_iter_start(q); !q_iter_at_end(&it); q_iter_next(&it)) + { + sum += q_iter_value(&it); + } + + cmc_assert_equals(size_t, 500500, sum); + + sum = 0; + + q_iter_to_start(&it); + do + { + sum += q_iter_value(&it); + } while (q_iter_next(&it)); + + cmc_assert_equals(size_t, 500500, sum); + + q_free(q); + }); + + CMC_CREATE_TEST(PFX##_iter_prev(), { + struct queue *q = q_new(100, q_fval); + + cmc_assert_not_equals(ptr, NULL, q); + + struct queue_iter it = q_iter_end(q); + + cmc_assert(!q_iter_prev(&it)); + + for (size_t i = 1; i <= 1000; i++) + q_enqueue(q, i); + + size_t sum = 0; + for (it = q_iter_end(q); !q_iter_at_start(&it); q_iter_prev(&it)) + { + sum += q_iter_value(&it); + } + + cmc_assert_equals(size_t, 500500, sum); + + sum = 0; + + q_iter_to_end(&it); + do + { + sum += q_iter_value(&it); + } while (q_iter_prev(&it)); + + cmc_assert_equals(size_t, 500500, sum); + + q_free(q); + }); + + CMC_CREATE_TEST(PFX##_iter_advance(), { + struct queue *q = q_new(100, q_fval); + + cmc_assert_not_equals(ptr, NULL, q); + + struct queue_iter it = q_iter_start(q); + + cmc_assert(!q_iter_advance(&it, 1)); + + for (size_t i = 0; i <= 1000; i++) + q_enqueue(q, i); + + it = q_iter_start(q); + + cmc_assert(!q_iter_advance(&it, 0)); + cmc_assert(!q_iter_advance(&it, q->count)); + + size_t sum = 0; + for (it = q_iter_start(q);;) + { + sum += q_iter_value(&it); + + if (!q_iter_advance(&it, 2)) + break; + } + + cmc_assert_equals(size_t, 250500, sum); + + q_iter_to_start(&it); + cmc_assert(q_iter_advance(&it, q->count - 1)); + + q_free(q); + }); + + CMC_CREATE_TEST(PFX##_iter_rewind(), { + struct queue *q = q_new(100, q_fval); + + cmc_assert_not_equals(ptr, NULL, q); + + struct queue_iter it = q_iter_end(q); + + cmc_assert(!q_iter_rewind(&it, 1)); + + for (size_t i = 0; i <= 1000; i++) + q_enqueue(q, i); + + it = q_iter_end(q); + + cmc_assert(!q_iter_rewind(&it, 0)); + cmc_assert(!q_iter_rewind(&it, q->count)); + + size_t sum = 0; + for (it = q_iter_end(q);;) + { + sum += q_iter_value(&it); + + if (!q_iter_rewind(&it, 2)) + break; + } + + cmc_assert_equals(size_t, 250500, sum); + + q_iter_to_end(&it); + cmc_assert(q_iter_rewind(&it, q->count - 1)); + + q_free(q); + }); + + CMC_CREATE_TEST(PFX##_iter_go_to(), { + struct queue *q = q_new(100, q_fval); + + cmc_assert_not_equals(ptr, NULL, q); + + struct queue_iter it = q_iter_end(q); + cmc_assert(!q_iter_go_to(&it, 0)); + + it = q_iter_start(q); + cmc_assert(!q_iter_go_to(&it, 0)); + + for (size_t i = 0; i <= 1000; i++) + q_enqueue(q, i); + + it = q_iter_start(q); + + size_t sum = 0; + for (size_t i = 0; i < 1001; i++) + { + q_iter_go_to(&it, i); + + sum += q_iter_value(&it); + } + + cmc_assert_equals(size_t, 500500, sum); + + sum = 0; + for (size_t i = 1001; i > 0; i--) + { + cmc_assert(q_iter_go_to(&it, i - 1)); + + sum += q_iter_value(&it); + } + + cmc_assert_equals(size_t, 500500, sum); + + sum = 0; + for (size_t i = 0; i < 1001; i += 100) + { + cmc_assert(q_iter_go_to(&it, i)); + cmc_assert_equals(size_t, i, q_iter_index(&it)); + + sum += q_iter_value(&it); + } + + cmc_assert_equals(size_t, 5500, sum); + + q_free(q); + }); + + CMC_CREATE_TEST(PFX##_iter_value(), { + struct queue *q = q_new(100, q_fval); + + cmc_assert_not_equals(ptr, NULL, q); + + struct queue_iter it = q_iter_end(q); + + cmc_assert_equals(size_t, (size_t){ 0 }, q_iter_value(&it)); + + cmc_assert(q_enqueue(q, 10)); + + it = q_iter_start(q); + + cmc_assert_equals(size_t, 10, q_iter_value(&it)); + + q_free(q); + }); + + CMC_CREATE_TEST(PFX##_iter_rvalue(), { + struct queue *q = q_new(100, q_fval); + + cmc_assert_not_equals(ptr, NULL, q); + + struct queue_iter it = q_iter_end(q); + + cmc_assert_equals(ptr, NULL, q_iter_rvalue(&it)); + + cmc_assert(q_enqueue(q, 10)); + + it = q_iter_start(q); + + cmc_assert_not_equals(ptr, NULL, q_iter_rvalue(&it)); + cmc_assert_equals(size_t, 10, *q_iter_rvalue(&it)); + + q_free(q); + }); + + CMC_CREATE_TEST(PFX##_iter_index(), { + struct queue *q = q_new(100, q_fval); + + cmc_assert_not_equals(ptr, NULL, q); + + for (size_t i = 0; i <= 1000; i++) + q_enqueue(q, i); + + struct queue_iter it = q_iter_start(q); + + for (size_t i = 0; i < 1001; i++) + { + cmc_assert_equals(size_t, i, q_iter_index(&it)); + q_iter_next(&it); + } + + it = q_iter_end(q); + for (size_t i = 1001; i > 0; i--) + { + cmc_assert_equals(size_t, i - 1, q_iter_index(&it)); + q_iter_prev(&it); + } + + q_free(q); + }); +}); + +#endif /* CMC_TESTS_UNT_QUEUE_H */ diff --git a/tests/unt_stack.h b/tests/unt_stack.h index 6e365c9..7a79ba4 100644 --- a/tests/unt_stack.h +++ b/tests/unt_stack.h @@ -8,6 +8,7 @@ #define SNAME stack #define CMC_EXT_STACK_INIT #define CMC_EXT_STACK_ITER +#define CMC_EXT_STACK_STR #include "cmc/stack.h" struct stack_fval *s_fval = &(struct stack_fval){