From b13604239b3509272d1546ef6ee4da4df07dc31c Mon Sep 17 00:00:00 2001 From: Anotra Date: Sun, 25 Feb 2024 14:04:11 -0500 Subject: [PATCH] feat(anomap): add anomap_clone and anomap_move, and NULL cmp --- core/anomap.c | 107 ++++++++++++++++++++++++++++++++++++++++++-------- core/anomap.h | 8 ++++ 2 files changed, 98 insertions(+), 17 deletions(-) diff --git a/core/anomap.c b/core/anomap.c index 760c8ad0..8ef5a35d 100644 --- a/core/anomap.c +++ b/core/anomap.c @@ -81,7 +81,6 @@ static const struct anomap_lock null_lock = { static unsigned anomap_has_locks = #if !defined ANOMAP_NATIVE_LOCKS || ANOMAP_NATIVE_LOCKS == NATIVE_LOCK_NONE 0; - static const struct anomap_lock lock_functions = null_lock; #else anomap_use_lock; @@ -280,7 +279,7 @@ anomap_init(struct anomap *map, int (*cmp)(const void *, const void *), anomap_options options) { - if (!map || !key_size || !cmp || (options & ~ANOMAP_ALLOWED_OPTIONS)) + if (!map || !key_size || (options & ~ANOMAP_ALLOWED_OPTIONS)) return false; memset(map, 0, sizeof *map); map->free_on_cleanup = false; @@ -288,9 +287,13 @@ anomap_init(struct anomap *map, map->cmp = cmp; map->keys.size = key_size; map->vals.size = val_size; +#if !defined ANOMAP_NATIVE_LOCKS || ANOMAP_NATIVE_LOCKS == NATIVE_LOCK_NONE + map->lock.functions = &null_lock; +#else map->lock.functions = options & anomap_use_lock ? &lock_functions : &null_lock; +#endif if ((map->lock.lock = map->lock.functions->create())) return true; @@ -324,6 +327,65 @@ anomap_destroy(struct anomap *map) { free(map); } +struct anomap * +anomap_clone(struct anomap *map, anomap_clone_options options) { + if (options) return NULL; + struct anomap *clone = malloc(sizeof *clone); + struct { + void *ptrs[16]; + size_t len; + } cleanup = { 0 }; + if (!clone) return NULL; + + LOCK_R_ACQUIRE; + memcpy(clone, map, sizeof *clone); + clone->free_on_cleanup = true; + + if (!(clone->lock.lock = clone->lock.functions->create())) + goto lock_create_fail; + +#define CLONE_ARRAY(ARRAY, SIZE) \ + do { \ + if ((clone->ARRAY.arr = malloc(SIZE))) { \ + memcpy(clone->ARRAY.arr, map->ARRAY.arr, SIZE); \ + cleanup.ptrs[cleanup.len++] = clone->ARRAY.arr; \ + } else goto array_copy_fail; \ + } while (0) + + if (clone->map.len) { + CLONE_ARRAY(map, clone->map.cap * sizeof *clone->map.arr); + CLONE_ARRAY(keys, clone->keys.cap * clone->keys.size); + if (clone->vals.size) + CLONE_ARRAY(vals, clone->vals.cap * clone->vals.size); + if (clone->options & anomap_preserve_order) + CLONE_ARRAY(order, clone->order.cap * sizeof *clone->order.arr); + } + + LOCK_R_RELEASE; + return clone; + + array_copy_fail: + while (cleanup.len) + free(cleanup.ptrs[--cleanup.len]); + clone->lock.functions->destroy(clone->lock.lock); + lock_create_fail: + LOCK_R_RELEASE; + free(clone); + return NULL; +} + +void +anomap_move(struct anomap *dest, bool free_on_destroy, struct anomap *map) { + LOCK_W_ACQUIRE; + memcpy(dest, map, sizeof *dest); + bool free_map = map->free_on_cleanup; + dest->free_on_cleanup = free_on_destroy; + memset(map, 0, sizeof *map); + if (free_map) free(map); + map = dest; + LOCK_W_RELEASE; +} + void anomap_set_on_item_changed( struct anomap *map, anomap_on_item_changed *on_changed, void *data) @@ -394,24 +456,35 @@ anomap_index_of_no_lock(struct anomap *map, void *key, size_t *position) { int result; if (0 == map->map.len) goto on_empty; - result = map->cmp(key, keys + key_size * map->map.arr[map->map.len - 1]); - if (0 == result) return *position = map->map.len - 1, true; - -# define BINARY_SEARCH(cmp_operator) \ - if (result cmp_operator 0) \ - return *position = map->map.len, false; \ - while (lo < hi) { \ - mid = lo + (hi - lo) / 2; \ - result = map->cmp(key, keys + key_size * map->map.arr[mid]); \ - if (result == 0) return *position = mid, true; \ - if (result cmp_operator 0) lo = mid + 1; \ - else hi = mid; \ - } + const size_t last_item = map->map.arr[map->map.len - 1]; + result = map->cmp + ? map->cmp(key, keys + key_size * last_item) + : memcmp(key, keys + key_size * last_item, key_size); + if (0 == result) return *position = last_item, true; + +# define BSEARCH(cmp_operator, cmp_func, ...) \ + do { \ + if (result cmp_operator 0) \ + return *position = map->map.len, false; \ + while (lo < hi) { \ + mid = lo + (hi - lo) / 2; \ + result = cmp_func(__VA_ARGS__); \ + if (result == 0) return *position = mid, true; \ + if (result cmp_operator 0) lo = mid + 1; \ + else hi = mid; \ + } \ + } while (0) if (map->options & anomap_reverse_order) { - BINARY_SEARCH(<); + if (map->cmp) + BSEARCH(<, map->cmp, key, keys + key_size * map->map.arr[mid]); + else + BSEARCH(<, memcmp, key, keys + key_size * map->map.arr[mid], key_size); } else { - BINARY_SEARCH(>); + if (map->cmp) + BSEARCH(>, map->cmp, key, keys + key_size * map->map.arr[mid]); + else + BSEARCH(>, memcmp, key, keys + key_size * map->map.arr[mid], key_size); } # undef BINARY_SEARCH diff --git a/core/anomap.h b/core/anomap.h index 073147a2..a56422e0 100644 --- a/core/anomap.h +++ b/core/anomap.h @@ -61,6 +61,14 @@ struct anomap *anomap_create(size_t key_size, size_t val_size, anomap_options options); void anomap_destroy(struct anomap *map); +typedef enum { +#define ANOMAP_CLONE_OPTIONS_NONE anomap_clone_options_none + anomap_clone_options_none, +} anomap_clone_options; + +struct anomap *anomap_clone(struct anomap *map, anomap_clone_options options); +void anomap_move(struct anomap *dest, bool free_on_destroy, struct anomap *map); + struct anomap_item_changed { struct anomap *map; void *data;