From ad2ae84c13ca5a31d67a48e94a07cd546a8575cd Mon Sep 17 00:00:00 2001 From: Leonardo Date: Wed, 13 Nov 2019 23:23:54 -0300 Subject: [PATCH] Finishing up some functions of bidimap that were left off Also gearing up bidimap for next update containing callbacks and custom allocation functions --- src/cmc/bidimap.h | 707 +++++++++++++++++++++++++++------------ tests/main/unt/bidimap.c | 83 ++++- 2 files changed, 572 insertions(+), 218 deletions(-) diff --git a/src/cmc/bidimap.h b/src/cmc/bidimap.h index 23114f2..960d58d 100644 --- a/src/cmc/bidimap.h +++ b/src/cmc/bidimap.h @@ -8,6 +8,21 @@ * */ +/** + * BidiMap + * + * 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 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. + */ + #ifndef CMC_BIDIMAP_H #define CMC_BIDIMAP_H @@ -16,63 +31,96 @@ #include #include #include -#include "../utl/cmc_string.h" + +/* ------------------------------------------------------------------------------------------------- + * Core functionalities of the C Macro Collections Library + ------------------------------------------------------------------------------------------------ */ +#ifndef CMC_CORE_H +#define CMC_CORE_H /** - * BidiMap + * struct cmc_string * - * A bidirectional map is a map that allows you to create a bijection in both - * directions between two sets of elements (K <-> V). - * - * This implementation uses two arrays of pointers to an entry containing both - * the key and the value. + * Used by all collections when calling the to_string function. */ +struct cmc_string +{ + char s[300]; +}; -/* to_string format */ -static const char *cmc_string_fmt_bidimap = "%s at %p { key_buffer:%p, val_buffer:%p, capacity:%" PRIuMAX ", count:%" PRIuMAX ", load:%lf, key_cmp:%p, val_cmp:%p, key_hash:%p, val_hash:%p }"; +static const size_t cmc_string_len = 300; -#ifndef CMC_ENTRY_DELETED +/** + * struct cmc_alloc_node + * + * Custom allocation node. Allows collections to use custom allocation functions. + */ +struct cmc_alloc_node +{ + void *(*malloc)(size_t); + void *(*calloc)(size_t, size_t); + void *(*realloc)(void *, size_t); + void (*free)(void *); +} cmc_alloc_node_default = {malloc, calloc, realloc, free}; + +#endif /* CMC_CORE_H */ + +/* ------------------------------------------------------------------------------------------------- + * Hashtable Implementation + ------------------------------------------------------------------------------------------------ */ +#ifndef CMC_IMPL_ENTRY_DELETED +#define CMC_IMPL_ENTRY_DELETED #define CMC_ENTRY_DELETED ((void *)1) -#endif /* CMC_ENTRY_DELETED */ +#endif /* CMC_IMPL_ENTRY_DELETED */ -#ifndef CMC_IMPL_HASHTABLE_SETUP -#define CMC_IMPL_HASHTABLE_SETUP +#ifndef CMC_IMPL_HASHTABLE_PRIMES +#define CMC_IMPL_HASHTABLE_PRIMES -static const size_t cmc_hashtable_primes[] = {53, 97, 191, 383, 769, 1531, - 3067, 6143, 12289, 24571, 49157, - 98299, 196613, 393209, 786431, - 1572869, 3145721, 6291449, - 12582917, 25165813, 50331653, - 100663291, 201326611, 402653189, - 805306357, 1610612741, - 3221225473, 6442450939, - 12884901893, 25769803799, - 51539607551, 103079215111, - 206158430209, 412316860441, - 824633720831, 1649267441651, - 3298534883309, 6597069766657, - 13194139533299, 26388279066623, - 52776558133303, 105553116266489, - 211106232532969, 422212465066001, - 844424930131963, - 1688849860263953, - 3377699720527861, - 6755399441055731, - 13510798882111483, - 27021597764222939, - 54043195528445957, - 108086391056891903, - 216172782113783773, - 432345564227567621, - 864691128455135207, - 1729382256910270481, - 3458764513820540933, - 6917529027641081903, - 13835058055282163729llu}; +static const size_t cmc_hashtable_primes[] = + {53, 97, 191, 383, 769, 1531, 3067, 6143, 12289, 24571, 49157, 98299, 196613, 393209, 786431, + 1572869, 3145721, 6291449, 12582917, 25165813, 50331653, 100663291, 201326611, 402653189, + 805306357, 1610612741, 3221225473, 6442450939, 12884901893, 25769803799, 51539607551, + 103079215111, 206158430209, 412316860441, 824633720831, 1649267441651, 3298534883309, + 6597069766657, 13194139533299, 26388279066623, 52776558133303, 105553116266489, + 211106232532969, 422212465066001, 844424930131963, 1688849860263953, 3377699720527861, + 6755399441055731, 13510798882111483, 27021597764222939, 54043195528445957, 108086391056891903, + 216172782113783773, 432345564227567621, 864691128455135207, 1729382256910270481, + 3458764513820540933, 6917529027641081903, 13835058055282163729llu}; -#endif /* CMC_IMPL_HASHTABLE_SETUP */ +#endif /* CMC_IMPL_HASHTABLE_PRIMES */ + +/* ------------------------------------------------------------------------------------------------- + * BidiMap specific + ------------------------------------------------------------------------------------------------ */ +/* to_string format */ +static const char *cmc_string_fmt_bidimap = "struct %s<%s, %s> " + "at %p { " + "key_buffer:%p, " + "val_buffer:%p, " + "capacity:%" PRIuMAX ", " + "count:%" PRIuMAX ", " + "load:%lf, " + "key_cmp:%p, " + "val_cmp:%p, " + "key_hash:%p, " + "val_hash:%p }"; + +/** + * Custom BidiMap callbacks. + * + * There are two types of callbacks, 'before' and 'after': + * _ + */ +struct cmc_callbacks_bidimap +{ + void (*before_clear)(void *); + void (*after_clear)(void *); + void (*before_free)(void *); + void (*after_free)(void *); + // TODO implement all callbacks +}; #define CMC_GENERATE_BIDIMAP(PFX, SNAME, K, V) \ CMC_GENERATE_BIDIMAP_HEADER(PFX, SNAME, K, V) \ @@ -85,146 +133,162 @@ static const size_t cmc_hashtable_primes[] = {53, 97, 191, 383, 769, 1531, CMC_GENERATE_BIDIMAP_SOURCE(PFX, SNAME, K, V) /* HEADER ********************************************************************/ -#define CMC_GENERATE_BIDIMAP_HEADER(PFX, SNAME, K, V) \ - \ - /* BidiMap Structure */ \ - struct SNAME \ - { \ - /* Array of Entries mapping K -> V */ \ - struct SNAME##_entry **key_buffer; \ - \ - /* Array of entries mapping V -> K */ \ - struct SNAME##_entry **val_buffer; \ - \ - /* Current arrays capacity */ \ - size_t capacity; \ - \ - /* Current amount of keys */ \ - size_t count; \ - \ - /* Load factor in range (0.0, 1.0) */ \ - double load; \ - \ - /* Key comparison function */ \ - int (*key_cmp)(K, K); \ - \ - /* Value comparison function */ \ - int (*val_cmp)(V, V); \ - \ - /* Key hash function */ \ - size_t (*key_hash)(K); \ - \ - /* Value hash function */ \ - size_t (*val_hash)(V); \ - \ - /* Function that returns an iterator to the start of the bidimap */ \ - struct SNAME##_iter (*it_start)(struct SNAME *); \ - \ - /* Function that returns an iterator to the end of the bidimap */ \ - struct SNAME##_iter (*it_end)(struct SNAME *); \ - }; \ - \ - /* BidiMap Entry */ \ - struct SNAME##_entry \ - { \ - /* Entry Key */ \ - K key; \ - \ - /* Entry Value */ \ - V value; \ - \ - /* The distance of this node to its original position, used by */ \ - /* robin-hood hashing relative to the key_buffer */ \ - size_t key_dist; \ - \ - /* The distance of this node to its original position, used by */ \ - /* robin-hood hashing relative to the value_buffer */ \ - size_t val_dist; \ - }; \ - \ - /* BidiMap Iterator */ \ - struct SNAME##_iter \ - { \ - /* Target bidimap */ \ - 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; \ - }; \ - \ - /* Collection Functions */ \ - /* Collection Allocation and Deallocation */ \ - struct SNAME *PFX##_new(size_t capacity, double load, \ - int (*key_cmp)(K, K), size_t (*key_hash)(K), \ - int (*val_cmp)(V, V), size_t (*val_hash)(V)); \ - void PFX##_clear(struct SNAME *_map_, void (*deallocator)(K, V)); \ - void PFX##_free(struct SNAME *_map_, void (*deallocator)(K, V)); \ - /* Collection Input and Output */ \ - bool PFX##_insert(struct SNAME *_map_, K key, V value); \ - bool PFX##_update_key(struct SNAME *_map_, K key, K *old_key); \ - bool PFX##_update_val(struct SNAME *_map_, V val, V *old_val); \ - bool PFX##_remove_by_key(struct SNAME *_map_, K key, V *out_value); \ - bool PFX##_remove_by_val(struct SNAME *_map_, V value, K *out_key); \ - /* Element Access */ \ - K PFX##_get_key(struct SNAME *_map_, V val); \ - V PFX##_get_val(struct SNAME *_map_, K key); \ - /* Collection State */ \ - bool PFX##_contains_key(struct SNAME *_map_, K key); \ - bool PFX##_contains_val(struct SNAME *_map_, V val); \ - bool PFX##_empty(struct SNAME *_map_); \ - bool PFX##_full(struct SNAME *_map_); \ - size_t PFX##_count(struct SNAME *_map_); \ - size_t PFX##_capacity(struct SNAME *_map_); \ - double PFX##_load(struct SNAME *_map_); \ - /* Collection Utility */ \ - bool PFX##_resize(struct SNAME *_map_, size_t capacity); \ - struct SNAME *PFX##_copy_of(struct SNAME *_map_, K (*key_copy_func)(K), \ - V (*value_copy_func)(V)); \ - bool PFX##_equals(struct SNAME *_map1_, struct SNAME *_map2_); \ - struct cmc_string PFX##_to_string(struct SNAME *_map_); \ - \ - /* Iterator Functions */ \ - /* Iterator Allocation and Deallocation */ \ - struct SNAME##_iter *PFX##_iter_new(struct SNAME *target); \ - void PFX##_iter_free(struct SNAME##_iter *iter); \ - /* Iterator Initialization */ \ - void PFX##_iter_init(struct SNAME##_iter *iter, struct SNAME *target); \ - /* Iterator State */ \ - bool PFX##_iter_start(struct SNAME##_iter *iter); \ - bool PFX##_iter_end(struct SNAME##_iter *iter); \ - /* Iterator Movement */ \ - void PFX##_iter_to_start(struct SNAME##_iter *iter); \ - void PFX##_iter_to_end(struct SNAME##_iter *iter); \ - bool PFX##_iter_next(struct SNAME##_iter *iter); \ - bool PFX##_iter_prev(struct SNAME##_iter *iter); \ - bool PFX##_iter_advance(struct SNAME##_iter *iter, size_t steps); \ - bool PFX##_iter_rewind(struct SNAME##_iter *iter, size_t steps); \ - bool PFX##_iter_go_to(struct SNAME##_iter *iter, size_t index); \ - /* Iterator Access */ \ - K PFX##_iter_key(struct SNAME##_iter *iter); \ - V PFX##_iter_value(struct SNAME##_iter *iter); \ - size_t PFX##_iter_index(struct SNAME##_iter *iter); \ - \ +#define CMC_GENERATE_BIDIMAP_HEADER(PFX, SNAME, K, V) \ + \ + /* BidiMap Structure */ \ + struct SNAME \ + { \ + /* Array of Entries mapping K -> V */ \ + struct SNAME##_entry **key_buffer; \ + \ + /* Array of entries mapping V -> K */ \ + struct SNAME##_entry **val_buffer; \ + \ + /* Current arrays capacity */ \ + size_t capacity; \ + \ + /* Current amount of keys */ \ + size_t count; \ + \ + /* Load factor in range (0.0, 1.0) */ \ + double load; \ + \ + /* Key comparison function */ \ + int (*key_cmp)(K, K); \ + \ + /* Value comparison function */ \ + int (*val_cmp)(V, V); \ + \ + /* Key hash function */ \ + size_t (*key_hash)(K); \ + \ + /* Value hash function */ \ + size_t (*val_hash)(V); \ + \ + /* Function that returns an iterator to the start of the bidimap */ \ + struct SNAME##_iter (*it_start)(struct SNAME *); \ + \ + /* Function that returns an iterator to the end of the bidimap */ \ + struct SNAME##_iter (*it_end)(struct SNAME *); \ + \ + /* Custom allocation functions */ \ + struct cmc_alloc_node *alloc; \ + \ + /* Custom callback functions */ \ + struct cmc_callbacks_bidimap *callbacks; \ + }; \ + \ + /* BidiMap Entry */ \ + struct SNAME##_entry \ + { \ + /* Entry Key */ \ + K key; \ + \ + /* Entry Value */ \ + V value; \ + \ + /* The distance of this node to its original position, used by */ \ + /* robin-hood hashing relative to the key_buffer */ \ + size_t key_dist; \ + \ + /* The distance of this node to its original position, used by */ \ + /* robin-hood hashing relative to the value_buffer */ \ + size_t val_dist; \ + }; \ + \ + /* BidiMap Iterator */ \ + struct SNAME##_iter \ + { \ + /* Target bidimap */ \ + 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; \ + }; \ + \ + /* Collection Functions */ \ + /* Collection Allocation and Deallocation */ \ + struct SNAME *PFX##_new(size_t capacity, double load, \ + int (*key_cmp)(K, K), size_t (*key_hash)(K), \ + int (*val_cmp)(V, V), size_t (*val_hash)(V)); \ + struct SNAME *PFX##_new_custom(size_t capacity, double load, \ + int (*key_cmp)(K, K), size_t (*key_hash)(K), \ + int (*val_cmp)(V, V), size_t (*val_hash)(V), \ + struct cmc_alloc_node *alloc, \ + struct cmc_callbacks_bidimap *callbacks); \ + void PFX##_clear(struct SNAME *_map_, void (*deallocator)(K, V)); \ + void PFX##_free(struct SNAME *_map_, void (*deallocator)(K, V)); \ + /* Customization of Allocation and Callbacks */ \ + void PFX##_customize(struct SNAME *_map_, struct cmc_alloc_node *alloc, \ + struct cmc_callbacks_bidimap *callbacks); \ + /* Collection Input and Output */ \ + bool PFX##_insert(struct SNAME *_map_, K key, V value); \ + bool PFX##_update_key(struct SNAME *_map_, K key, K new_key); \ + bool PFX##_update_key_by_val(struct SNAME *_map_, V val, K new_key); \ + bool PFX##_update_val(struct SNAME *_map_, V val, V new_val); \ + bool PFX##_update_val_by_key(struct SNAME *_map_, K key, V new_val); \ + bool PFX##_remove_by_key(struct SNAME *_map_, K key, K *out_key, V *out_val); \ + bool PFX##_remove_by_val(struct SNAME *_map_, V val, K *out_key, V *out_val); \ + /* Element Access */ \ + K PFX##_get_key(struct SNAME *_map_, V val); \ + V PFX##_get_val(struct SNAME *_map_, K key); \ + /* Collection State */ \ + bool PFX##_contains_key(struct SNAME *_map_, K key); \ + bool PFX##_contains_val(struct SNAME *_map_, V val); \ + bool PFX##_empty(struct SNAME *_map_); \ + bool PFX##_full(struct SNAME *_map_); \ + size_t PFX##_count(struct SNAME *_map_); \ + size_t PFX##_capacity(struct SNAME *_map_); \ + double PFX##_load(struct SNAME *_map_); \ + /* Collection Utility */ \ + bool PFX##_resize(struct SNAME *_map_, size_t capacity); \ + struct SNAME *PFX##_copy_of(struct SNAME *_map_, K (*key_copy_func)(K), \ + V (*value_copy_func)(V)); \ + bool PFX##_equals(struct SNAME *_map1_, struct SNAME *_map2_); \ + struct cmc_string PFX##_to_string(struct SNAME *_map_); \ + \ + /* Iterator Functions */ \ + /* Iterator Allocation and Deallocation */ \ + struct SNAME##_iter *PFX##_iter_new(struct SNAME *target); \ + void PFX##_iter_free(struct SNAME##_iter *iter); \ + /* Iterator Initialization */ \ + void PFX##_iter_init(struct SNAME##_iter *iter, struct SNAME *target); \ + /* Iterator State */ \ + bool PFX##_iter_start(struct SNAME##_iter *iter); \ + bool PFX##_iter_end(struct SNAME##_iter *iter); \ + /* Iterator Movement */ \ + void PFX##_iter_to_start(struct SNAME##_iter *iter); \ + void PFX##_iter_to_end(struct SNAME##_iter *iter); \ + bool PFX##_iter_next(struct SNAME##_iter *iter); \ + bool PFX##_iter_prev(struct SNAME##_iter *iter); \ + bool PFX##_iter_advance(struct SNAME##_iter *iter, size_t steps); \ + bool PFX##_iter_rewind(struct SNAME##_iter *iter, size_t steps); \ + bool PFX##_iter_go_to(struct SNAME##_iter *iter, size_t index); \ + /* Iterator Access */ \ + K PFX##_iter_key(struct SNAME##_iter *iter); \ + V PFX##_iter_value(struct SNAME##_iter *iter); \ + size_t PFX##_iter_index(struct SNAME##_iter *iter); \ + \ /* SOURCE ********************************************************************/ #define CMC_GENERATE_BIDIMAP_SOURCE(PFX, SNAME, K, V) \ \ /* Implementation Detail Functions */ \ - static struct SNAME##_entry *PFX##_impl_new_entry(K key, V value); \ + static struct SNAME##_entry *PFX##_impl_new_entry(struct SNAME *_map_, K key, V value); \ static struct SNAME##_entry **PFX##_impl_get_entry_by_key(struct SNAME *_map_, K key); \ static struct SNAME##_entry **PFX##_impl_get_entry_by_val(struct SNAME *_map_, V val); \ static struct SNAME##_entry **PFX##_impl_add_entry_to_key(struct SNAME *_map_, \ @@ -239,6 +303,8 @@ static const size_t cmc_hashtable_primes[] = {53, 97, 191, 383, 769, 1531, int (*key_cmp)(K, K), size_t (*key_hash)(K), \ int (*val_cmp)(V, V), size_t (*val_hash)(V)) \ { \ + struct cmc_alloc_node *alloc = &cmc_alloc_node_default; \ + \ if (capacity == 0 || load <= 0 || load >= 1) \ return NULL; \ \ @@ -248,19 +314,19 @@ static const size_t cmc_hashtable_primes[] = {53, 97, 191, 383, 769, 1531, \ size_t real_capacity = PFX##_impl_calculate_size(capacity / load); \ \ - struct SNAME *_map_ = malloc(sizeof(struct SNAME)); \ + struct SNAME *_map_ = alloc->malloc(sizeof(struct SNAME)); \ \ if (!_map_) \ return NULL; \ \ - _map_->key_buffer = calloc(real_capacity, sizeof(struct SNAME##_entry *)); \ - _map_->val_buffer = calloc(real_capacity, sizeof(struct SNAME##_entry *)); \ + _map_->key_buffer = alloc->calloc(real_capacity, sizeof(struct SNAME##_entry *)); \ + _map_->val_buffer = alloc->calloc(real_capacity, sizeof(struct SNAME##_entry *)); \ \ if (!_map_->key_buffer || !_map_->val_buffer) \ { \ - free(_map_->key_buffer); \ - free(_map_->val_buffer); \ - free(_map_); \ + alloc->free(_map_->key_buffer); \ + alloc->free(_map_->val_buffer); \ + alloc->free(_map_); \ return NULL; \ } \ \ @@ -275,6 +341,60 @@ static const size_t cmc_hashtable_primes[] = {53, 97, 191, 383, 769, 1531, _map_->it_start = PFX##_impl_it_start; \ _map_->it_end = PFX##_impl_it_end; \ \ + _map_->alloc = &cmc_alloc_node_default; \ + _map_->callbacks = NULL; \ + \ + return _map_; \ + } \ + \ + struct SNAME *PFX##_new_custom(size_t capacity, double load, \ + int (*key_cmp)(K, K), size_t (*key_hash)(K), \ + int (*val_cmp)(V, V), size_t (*val_hash)(V), \ + struct cmc_alloc_node *alloc, \ + struct cmc_callbacks_bidimap *callbacks) \ + { \ + if (capacity == 0 || load <= 0 || load >= 1) \ + return NULL; \ + \ + /* Prevent integer overflow */ \ + if (capacity >= UINTMAX_MAX * load) \ + return NULL; \ + \ + size_t real_capacity = 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_->key_buffer = alloc->calloc(real_capacity, sizeof(struct SNAME##_entry *)); \ + _map_->val_buffer = alloc->calloc(real_capacity, sizeof(struct SNAME##_entry *)); \ + \ + if (!_map_->key_buffer || !_map_->val_buffer) \ + { \ + alloc->free(_map_->key_buffer); \ + alloc->free(_map_->val_buffer); \ + alloc->free(_map_); \ + return NULL; \ + } \ + \ + _map_->count = 0; \ + _map_->capacity = real_capacity; \ + _map_->load = load; \ + _map_->key_cmp = key_cmp; \ + _map_->val_cmp = val_cmp; \ + _map_->key_hash = key_hash; \ + _map_->val_hash = val_hash; \ + \ + _map_->it_start = PFX##_impl_it_start; \ + _map_->it_end = PFX##_impl_it_end; \ + \ + _map_->alloc = alloc; \ + _map_->callbacks = callbacks; \ + \ return _map_; \ } \ \ @@ -289,7 +409,7 @@ static const size_t cmc_hashtable_primes[] = {53, 97, 191, 383, 769, 1531, if (deallocator) \ deallocator(entry->key, entry->value); \ \ - free(entry); \ + _map_->alloc->free(entry); \ } \ \ _map_->key_buffer[i] = NULL; \ @@ -303,9 +423,19 @@ static const size_t cmc_hashtable_primes[] = {53, 97, 191, 383, 769, 1531, { \ PFX##_clear(_map_, deallocator); \ \ - free(_map_->key_buffer); \ - free(_map_->val_buffer); \ - free(_map_); \ + _map_->alloc->free(_map_->key_buffer); \ + _map_->alloc->free(_map_->val_buffer); \ + _map_->alloc->free(_map_); \ + } \ + \ + void PFX##_customize(struct SNAME *_map_, struct cmc_alloc_node *alloc, \ + struct cmc_callbacks_bidimap *callbacks) \ + { \ + if (alloc) \ + _map_->alloc = alloc; \ + \ + if (callbacks) \ + _map_->callbacks = callbacks; \ } \ \ bool PFX##_insert(struct SNAME *_map_, K key, V value) \ @@ -320,7 +450,7 @@ static const size_t cmc_hashtable_primes[] = {53, 97, 191, 383, 769, 1531, PFX##_impl_get_entry_by_val(_map_, value) != NULL) \ return false; \ \ - struct SNAME##_entry *entry = PFX##_impl_new_entry(key, value); \ + struct SNAME##_entry *entry = PFX##_impl_new_entry(_map_, key, value); \ \ struct SNAME##_entry **key_entry = PFX##_impl_add_entry_to_key(_map_, entry); \ struct SNAME##_entry **val_entry = PFX##_impl_add_entry_to_val(_map_, entry); \ @@ -332,7 +462,7 @@ static const size_t cmc_hashtable_primes[] = {53, 97, 191, 383, 769, 1531, if (val_entry) \ *val_entry = CMC_ENTRY_DELETED; \ \ - free(entry); \ + _map_->alloc->free(entry); \ \ return false; \ } \ @@ -342,35 +472,171 @@ static const size_t cmc_hashtable_primes[] = {53, 97, 191, 383, 769, 1531, return true; \ } \ \ - bool PFX##_update_key(struct SNAME *_map_, K key, K *old_key) \ + bool PFX##_update_key(struct SNAME *_map_, K key, K new_key) \ { \ if (PFX##_empty(_map_)) \ return false; \ \ + struct SNAME##_entry **entry = PFX##_impl_get_entry_by_key(_map_, key); \ + \ + if (!entry) \ + return false; \ + \ + if (PFX##_contains_key(_map_, new_key)) \ + return false; \ + \ + struct SNAME##_entry *to_add = *entry; \ + K tmp_key = to_add->key; \ + to_add->key = new_key; \ + \ + *entry = CMC_ENTRY_DELETED; \ + \ + if (!PFX##_impl_add_entry_to_key(_map_, to_add)) \ + { \ + to_add->key = tmp_key; \ + *entry = to_add; \ + \ + return false; \ + } \ + \ return true; \ } \ \ - bool PFX##_update_val(struct SNAME *_map_, V val, V *old_val) \ + bool PFX##_update_key_by_val(struct SNAME *_map_, V val, K new_key) \ { \ if (PFX##_empty(_map_)) \ return false; \ \ + struct SNAME##_entry **entry = PFX##_impl_get_entry_by_val(_map_, val); \ + \ + if (!entry) \ + return false; \ + \ + if (PFX##_contains_key(_map_, new_key)) \ + return false; \ + \ + struct SNAME##_entry *to_add = *entry; \ + K tmp_key = to_add->key; \ + to_add->key = new_key; \ + \ + *entry = CMC_ENTRY_DELETED; \ + \ + if (!PFX##_impl_add_entry_to_key(_map_, to_add)) \ + { \ + to_add->key = tmp_key; \ + *entry = to_add; \ + \ + return false; \ + } \ + \ return true; \ } \ \ - bool PFX##_remove_by_key(struct SNAME *_map_, K key, V *out_value) \ + bool PFX##_update_val(struct SNAME *_map_, V val, V new_val) \ { \ if (PFX##_empty(_map_)) \ return false; \ \ + struct SNAME##_entry **entry = PFX##_impl_get_entry_by_val(_map_, val); \ + \ + if (!entry) \ + return false; \ + \ + if (PFX##_contains_val(_map_, new_val)) \ + return false; \ + \ + struct SNAME##_entry *to_add = *entry; \ + V tmp_val = to_add->value; \ + to_add->value = new_val; \ + \ + *entry = CMC_ENTRY_DELETED; \ + \ + if (!PFX##_impl_add_entry_to_val(_map_, to_add)) \ + { \ + to_add->value = tmp_val; \ + *entry = to_add; \ + \ + return false; \ + } \ + \ return true; \ } \ \ - bool PFX##_remove_by_val(struct SNAME *_map_, V value, K *out_key) \ + bool PFX##_update_val_by_key(struct SNAME *_map_, K key, V new_val) \ { \ if (PFX##_empty(_map_)) \ return false; \ \ + struct SNAME##_entry **entry = PFX##_impl_get_entry_by_key(_map_, key); \ + \ + if (!entry) \ + return false; \ + \ + if (PFX##_contains_val(_map_, new_val)) \ + return false; \ + \ + struct SNAME##_entry *to_add = *entry; \ + V tmp_val = to_add->value; \ + to_add->value = new_val; \ + \ + *entry = CMC_ENTRY_DELETED; \ + \ + if (!PFX##_impl_add_entry_to_val(_map_, to_add)) \ + { \ + to_add->value = tmp_val; \ + *entry = to_add; \ + \ + return false; \ + } \ + \ + return true; \ + } \ + \ + bool PFX##_remove_by_key(struct SNAME *_map_, K key, K *out_key, V *out_val) \ + { \ + if (PFX##_empty(_map_)) \ + return false; \ + \ + struct SNAME##_entry **entry = PFX##_impl_get_entry_by_key(_map_, key); \ + \ + if (!entry) \ + return false; \ + \ + if (out_key) \ + *out_key = (*entry)->key; \ + if (out_val) \ + *out_val = (*entry)->value; \ + \ + _map_->alloc->free(*entry); \ + \ + *entry = CMC_ENTRY_DELETED; \ + \ + _map_->count--; \ + \ + return true; \ + } \ + \ + bool PFX##_remove_by_val(struct SNAME *_map_, V val, K *out_key, V *out_val) \ + { \ + if (PFX##_empty(_map_)) \ + return false; \ + \ + struct SNAME##_entry **entry = PFX##_impl_get_entry_by_val(_map_, val); \ + \ + if (!entry) \ + return false; \ + \ + if (out_key) \ + *out_key = (*entry)->key; \ + if (out_val) \ + *out_val = (*entry)->value; \ + \ + _map_->alloc->free(*entry); \ + \ + *entry = CMC_ENTRY_DELETED; \ + \ + _map_->count--; \ + \ return true; \ } \ \ @@ -448,9 +714,10 @@ static const size_t cmc_hashtable_primes[] = {53, 97, 191, 383, 769, 1531, if (new_cap < PFX##_count(_map_) / PFX##_load(_map_)) \ return false; \ \ - struct SNAME *_new_map_ = PFX##_new(capacity, PFX##_load(_map_), \ - _map_->key_cmp, _map_->key_hash, \ - _map_->val_cmp, _map_->val_hash); \ + struct SNAME *_new_map_ = PFX##_new_custom(capacity, PFX##_load(_map_), \ + _map_->key_cmp, _map_->key_hash, \ + _map_->val_cmp, _map_->val_hash, \ + _map_->alloc, _map_->callbacks); \ \ if (!_new_map_) \ return false; \ @@ -466,9 +733,9 @@ static const size_t cmc_hashtable_primes[] = {53, 97, 191, 383, 769, 1531, \ if (!e1 || !e2) \ { \ - free(_new_map_->key_buffer); \ - free(_new_map_->val_buffer); \ - free(_new_map_); \ + _map_->alloc->free(_new_map_->key_buffer); \ + _map_->alloc->free(_new_map_->val_buffer); \ + _map_->alloc->free(_new_map_); \ \ return false; \ } \ @@ -479,9 +746,9 @@ static const size_t cmc_hashtable_primes[] = {53, 97, 191, 383, 769, 1531, \ if (PFX##_count(_map_) != PFX##_count(_new_map_)) \ { \ - free(_new_map_->key_buffer); \ - free(_new_map_->val_buffer); \ - free(_new_map_); \ + _map_->alloc->free(_new_map_->key_buffer); \ + _map_->alloc->free(_new_map_->val_buffer); \ + _map_->alloc->free(_new_map_); \ \ return false; \ } \ @@ -493,9 +760,9 @@ static const size_t cmc_hashtable_primes[] = {53, 97, 191, 383, 769, 1531, _map_->val_buffer = _new_map_->val_buffer; \ _map_->capacity = _new_map_->capacity; \ \ - free(tmp_key_buf); \ - free(tmp_val_buf); \ - free(_new_map_); \ + _map_->alloc->free(tmp_key_buf); \ + _map_->alloc->free(tmp_val_buf); \ + _map_->alloc->free(_new_map_); \ \ return true; \ } \ @@ -577,16 +844,24 @@ static const size_t cmc_hashtable_primes[] = {53, 97, 191, 383, 769, 1531, struct SNAME *m_ = _map_; \ const char *name = #SNAME; \ \ - snprintf(str.s, cmc_string_len, cmc_string_fmt_bidimap, \ - name, m_, m_->key_buffer, m_->val_buffer, m_->capacity, m_->count, \ - m_->load, m_->key_cmp, m_->val_cmp, m_->key_hash, m_->val_hash); \ + int n = snprintf(str.s, cmc_string_len, cmc_string_fmt_bidimap, \ + name, #K, #V, m_, m_->key_buffer, m_->val_buffer, m_->capacity, \ + m_->count, m_->load, m_->key_cmp, m_->val_cmp, m_->key_hash, \ + m_->val_hash); \ + \ + if (n < 0) \ + { \ + return (struct cmc_string){0}; \ + } \ + \ + str.s[n - 1] = '\0'; \ \ return str; \ } \ \ struct SNAME##_iter *PFX##_iter_new(struct SNAME *target) \ { \ - struct SNAME##_iter *iter = malloc(sizeof(struct SNAME##_iter)); \ + struct SNAME##_iter *iter = target->alloc->malloc(sizeof(struct SNAME##_iter)); \ \ if (!iter) \ return NULL; \ @@ -598,7 +873,7 @@ static const size_t cmc_hashtable_primes[] = {53, 97, 191, 383, 769, 1531, \ void PFX##_iter_free(struct SNAME##_iter *iter) \ { \ - free(iter); \ + iter->target->alloc->free(iter); \ } \ \ void PFX##_iter_init(struct SNAME##_iter *iter, struct SNAME *target) \ @@ -804,9 +1079,9 @@ static const size_t cmc_hashtable_primes[] = {53, 97, 191, 383, 769, 1531, return iter->index; \ } \ \ - static struct SNAME##_entry *PFX##_impl_new_entry(K key, V value) \ + static struct SNAME##_entry *PFX##_impl_new_entry(struct SNAME *_map_, K key, V value) \ { \ - struct SNAME##_entry *entry = malloc(sizeof(struct SNAME##_entry)); \ + struct SNAME##_entry *entry = _map_->alloc->malloc(sizeof(struct SNAME##_entry)); \ \ if (!entry) \ return NULL; \ diff --git a/tests/main/unt/bidimap.c b/tests/main/unt/bidimap.c index 05ef3bc..e04479f 100644 --- a/tests/main/unt/bidimap.c +++ b/tests/main/unt/bidimap.c @@ -16,15 +16,76 @@ CMC_CREATE_UNIT(bidimap_test, true, { bm_free(map, NULL); }); + CMC_CREATE_TEST(new_custom, { + struct bidimap *map = bm_new_custom(100, 0.6, cmp, hash, cmp, hash, + &(struct cmc_alloc_node){ + .malloc = malloc, + .calloc = calloc, + .realloc = realloc, + .free = free}, + &(struct cmc_callbacks_bidimap){0}); + + cmc_assert_not_equals(ptr, NULL, map); + + bm_free(map, NULL); + }); + + CMC_CREATE_TEST(new_custom, { + struct bidimap *map = bm_new_custom(100, 0.6, cmp, hash, cmp, hash, + &(struct cmc_alloc_node){ + .malloc = malloc, + .calloc = calloc, + .realloc = realloc, + .free = free}, + &(struct cmc_callbacks_bidimap){0}); + + cmc_assert_not_equals(ptr, NULL, map); + + bm_free(map, NULL); + }); + + CMC_CREATE_TEST(new_custom[insert remove], { + struct bidimap *map = bm_new_custom(100, 0.6, cmp, hash, cmp, hash, + &(struct cmc_alloc_node){ + .malloc = malloc, + .calloc = calloc, + .realloc = realloc, + .free = free}, + &(struct cmc_callbacks_bidimap){0}); + + cmc_assert_not_equals(ptr, NULL, map); + + for (size_t i = 0; i < 500; i++) + cmc_assert(bm_insert(map, i, i)); + + cmc_assert_equals(size_t, 500, bm_count(map)); + + for (size_t i = 0; i < 500; i++) + { + if (i % 2 == 0) + { + cmc_assert(bm_remove_by_key(map, i, NULL, NULL)); + } + else + { + cmc_assert(bm_remove_by_val(map, i, NULL, NULL)); + } + } + + cmc_assert_equals(size_t, 0, bm_count(map)); + + bm_free(map, NULL); + }); + CMC_CREATE_TEST(clear[count], { struct bidimap *map = bm_new(100, 0.6, cmp, hash, cmp, hash); cmc_assert_not_equals(ptr, NULL, map); - for (size_t i = 0; i < 50; i++) + for (size_t i = 0; i < 500; i++) cmc_assert(bm_insert(map, i, i)); - cmc_assert_equals(size_t, 50, bm_count(map)); + cmc_assert_equals(size_t, 500, bm_count(map)); bm_clear(map, NULL); @@ -36,6 +97,8 @@ CMC_CREATE_UNIT(bidimap_test, true, { CMC_CREATE_TEST(buffer_growth[capacity = 1], { struct bidimap *map = bm_new(1, 0.7, cmp, hash, cmp, hash); + cmc_assert_not_equals(ptr, NULL, map); + for (size_t i = 1; i <= 10000; i++) cmc_assert(bm_insert(map, i, i)); @@ -54,6 +117,22 @@ CMC_CREATE_UNIT(bidimap_test, true, { bm_free(map, NULL); }); + CMC_CREATE_TEST(insert, { + struct bidimap *map = bm_new(100, 0.7, cmp, hash, cmp, hash); + + cmc_assert_not_equals(ptr, NULL, map); + + cmc_assert(bm_insert(map, 1, 1)); + cmc_assert(!bm_insert(map, 1, 1)); + cmc_assert(!bm_insert(map, 2, 1)); + cmc_assert(!bm_insert(map, 1, 2)); + cmc_assert(bm_insert(map, 2, 2)); + + cmc_assert_equals(size_t, 2, bm_count(map)); + + bm_free(map, NULL); + }); + CMC_CREATE_TEST(copy_of, { struct bidimap *map1 = bm_new(100, 0.7, cmp, hash, cmp, hash);