#ifndef CLAY_CLAY_MAP_H #define CLAY_CLAY_MAP_H #ifndef __GNUC__ #error "this requires GCC" // todo: make clang-compatible if possible #endif #include #include #include "clay-list.h" #include "cmoball.h" #ifndef CLAY_MAP_DEFAULT_INITIAL_SIZE # define CLAY_MAP_DEFAULT_INITIAL_SIZE 16 #endif #define CLAY_MAP_TYPE(map_type$name, map_type$key_type, map_type$value_type) \ typedef struct { \ map_type$key_type key; \ map_type$value_type value; \ } map_type$name ## _entry_s, *map_type$name ## _entry; \ typedef int (* map_type$name ## _compare_fn)(map_type$key_type, map_type$key_type); \ typedef struct { \ map_type$name ## _entry_s *elements; \ size_t length; \ size_t size; \ map_type$name ## _compare_fn compare_fn; \ } map_type$name ## _s, *map_type$name; \ typedef const map_type$name ## _s * map_type$name ## _const; #define CLAY_STRING_MAP_TYPE(map_type$name, map_type$value_type) CLAY_MAP_TYPE(map_type$name, const char *, map_type$value_type) #define CLAY_MAP_FOREACH(map_foreach$map, map_foreach$key, map_foreach$valueptr) \ for (int map_foreach$next = 1; map_foreach$next; (map_foreach$next = 0)) \ for (size_t map_foreach$i = 0; map_foreach$next && map_foreach$i < (map_foreach$map)->length; ++map_foreach$i) \ for (typeof((map_foreach$map)->elements) map_foreach$el = &(map_foreach$map)->elements[map_foreach$i]; map_foreach$next; ({ break; })) \ for (typeof(map_foreach$el->key) map_foreach$key = map_foreach$el->key; map_foreach$next; ({ break; })) \ for (typeof(map_foreach$el->value) *map_foreach$valueptr = &map_foreach$el->value; map_foreach$next;) \ for (; !(map_foreach$next = 0); ({map_foreach$next = 1;break;})) #define clay_map_init(map$, map$initial_size) clay_list_init((map$), (map$initial_size)) // note: map$type is never in parantheses in the substitution, because it is a type and not an expression! #define clay_map_create_1(map$type) clay_map_create_default_cmp(map$type, CLAY_MAP_DEFAULT_INITIAL_SIZE) #define clay_map_create_2(map$type, map$size_or_cmp) _Generic((map$size_or_cmp), int: clay_map_create_default_cmp(map$type, (size_t)(map$size_or_cmp)), default: clay_map_create_3(map$type, CLAY_MAP_DEFAULT_INITIAL_SIZE, (void*)(map$size_or_cmp))) #define clay_map_create_default_cmp(map$type, map$size) clay_map_create_3(map$type, map$size, ({ map$type map$dummy; _Generic(map$dummy->elements->key, const char *: strcmp, default: NULL);})) #define clay_map_create_3(map$type, map$size, map$cmp) ({ \ assert((map$size) > 0); \ map$type map$map = malloc(sizeof(*map$map)); \ clay_map_init(map$map, (map$size)); \ map$map->compare_fn = map$cmp; \ map$map; \ }) #define clay_map_create(...) CMOBALL(clay_map_create, __VA_ARGS__) CLAY_MAP_TYPE(some_map, int, int) CLAY_STRING_MAP_TYPE(other_map, int) #define clay_map_empty(map$) free((map$)->elements) #define clay_map_destroy(map$) ({ \ clay_map_empty(map$); \ free(map$); \ }) #define clay_map_set(map_set$map, map_set$key, map_set$value) ({ \ bool map_set$found = false; \ CLAY_LIST_FOREACH((map_set$map), map_set$el, map_set$_) { \ if (((map_set$map)->compare_fn == NULL && map_set$el->key == map_set$key) || \ ((map_set$map)->compare_fn != NULL && (map_set$map)->compare_fn(map_set$el->key, (map_set$key)) == 0)) { \ map_set$el->value = (map_set$value); \ map_set$found = true; \ break; \ } \ } \ if (!map_set$found) { \ typeof(*(map_set$map)->elements) map_set$el = {.key = (map_set$key), .value = (map_set$value)}; \ clay_list_append((map_set$map), map_set$el); \ } \ }) #define clay_map_get(map_get$map, map_get$key, map_get$value) ({ \ bool map_get$found = false; \ CLAY_LIST_FOREACH((map_get$map), map_get$el, map_get$_) { \ if (((map_get$map)->compare_fn == NULL && map_get$el->key == (map_get$key)) || \ ((map_get$map)->compare_fn != NULL && (map_get$map)->compare_fn(map_get$el->key, (map_get$key)) == 0)) { \ *(map_get$value) = map_get$el->value; \ map_get$found = true; \ break; \ } \ } \ map_get$found; \ }) #define clay_map_get_ptr(map_get$map, map_get$key, map_get$value) ({ \ bool map_get$found = false; \ CLAY_LIST_FOREACH((map_get$map), map_get$el, map_get$_) { \ if (((map_get$map)->compare_fn == NULL && map_get$el->key == (map_get$key)) || \ ((map_get$map)->compare_fn != NULL && (map_get$map)->compare_fn(map_get$el->key, (map_get$key)) == 0)) { \ *(map_get$value) = &map_get$el->value; \ map_get$found = true; \ break; \ } \ } \ map_get$found; \ }) #define clay_map_get_default(map_get$map, map_get$key, map_get$default) ({ \ typeof((map_get$map)->elements->value) map_get$value = map_get$default; \ CLAY_LIST_FOREACH((map_get$map), map_get$el, map_get$_) { \ if (((map_get$map)->compare_fn == NULL && map_get$el->key == (map_get$key)) || \ ((map_get$map)->compare_fn != NULL && (map_get$map)->compare_fn(map_get$el->key, (map_get$key)) == 0)) { \ map_get$value = map_get$el->value; \ break; \ } \ } \ map_get$value; \ }) #define clay_map_has_key(map_has_key$map, map_has_key$key) ({ \ bool map_has_key$found = false; \ CLAY_LIST_FOREACH((map_has_key$map), map_has_key$el, map_has_key$) { \ if (((map_has_key$map)->compare_fn == NULL && map_has_key$el->key == (map_has_key$key)) || \ ((map_has_key$map)->compare_fn != NULL && (map_has_key$map)->compare_fn(map_has_key$el->key, (map_has_key$key)) == 0)) { \ map_has_key$found = true; \ break; \ } \ } \ map_has_key$found; \ }) #define clay_map_remove(map_remove$map, map_remove$key) ({ \ bool map_remove$found = false; \ CLAY_LIST_FOREACH((map_remove$map), map_remove$el, map_remove$idx) { \ if (((map_remove$map)->compare_fn == NULL && map_remove$el->key == (map_remove$key)) || \ ((map_remove$map)->compare_fn != NULL && (map_remove$map)->compare_fn(map_remove$el->key, (map_remove$key)) == 0)) { \ clay_list_remove((map_remove$map), map_remove$idx); \ map_remove$found = true; \ } \ } \ map_remove$found; \ }) #define clay_map_clone(map_clone$map) ({ \ typeof(map_clone$map) map_clone$map_new = clay_map_create(typeof(map_clone$map), (map_clone$map)->length); \ memcpy(map_clone$map_new->elements, (map_clone$map)->elements, (map_clone$map)->length * sizeof(*map_clone$map_new->elements)); \ map_clone$map_new->length = (map_clone$map)->length; \ map_clone$map_new->compare_fn = (map_clone$map)->compare_fn; \ map_clone$map_new; \ }) #endif //CLAY_CLAY_MAP_H