2025-03-22 00:47:12 +01:00

159 lines
9.3 KiB
C

#pragma once
#include "vec.h"
#define DEFINE_KV_MAP(KEY, VALUE, MAP_TYPE, FN_PREFIX) \
typedef KEY MAP_TYPE##Key; \
typedef VALUE MAP_TYPE##Value; \
typedef struct { \
MAP_TYPE##Key key; \
MAP_TYPE##Value value; \
} MAP_TYPE_Entry; \
\
DEFINE_VEC(MAP_TYPE_Entry, MAP_TYPE, FN_PREFIX##_entry_vec) \
\
MAYBE_UNUSED static inline int FN_PREFIX##_construct(MAP_TYPE* map) \
{ \
return FN_PREFIX##_entry_vec_construct(map); \
} \
\
MAYBE_UNUSED static inline void FN_PREFIX##_destroy(MAP_TYPE* map) \
{ \
FN_PREFIX##_entry_vec_destroy(map); \
} \
\
MAYBE_UNUSED static inline size_t FN_PREFIX##_internal_insert_idx( \
const MAP_TYPE* map, size_t begin, size_t end, MAP_TYPE##Key key) \
{ \
if (begin == end) { \
return begin; \
} \
size_t middle = (end - begin) / 2 + begin; \
if (key < map->data[middle].key) { \
return FN_PREFIX##_internal_insert_idx(map, begin, middle, key); \
} else if (key > map->data[middle].key) { \
return FN_PREFIX##_internal_insert_idx(map, middle + 1, end, key); \
} else { \
return middle; \
} \
} \
\
MAYBE_UNUSED static inline int FN_PREFIX##_set( \
MAP_TYPE* map, MAP_TYPE##Key key, MAP_TYPE##Value value) \
{ \
size_t idx = FN_PREFIX##_internal_insert_idx(map, 0, map->size, key); \
if (idx >= map->size) { \
int push_res = FN_PREFIX##_entry_vec_push( \
map, (MAP_TYPE_Entry) { key, value }); \
if (push_res != 0) \
return -1; \
return 0; \
} \
if (map->data[idx].key == key) { \
map->data[idx].value = value; \
} \
int push_res \
= FN_PREFIX##_entry_vec_push(map, map->data[map->size - 1]); \
if (push_res != 0) \
return -1; \
for (size_t i = idx; i < map->size - 1; ++i) { \
map->data[i + 1] = map->data[i]; \
} \
map->data[idx] = (MAP_TYPE_Entry) { key, value }; \
return 0; \
} \
\
MAYBE_UNUSED static inline MAP_TYPE_Entry* FN_PREFIX##_internal_find( \
MAP_TYPE* map, size_t begin, size_t end, MAP_TYPE##Key key) \
{ \
if (begin == end) { \
return NULL; \
} \
size_t middle = (end - begin) / 2 + begin; \
if (key < map->data[middle].key) { \
return FN_PREFIX##_internal_find(map, begin, middle, key); \
} else if (key > map->data[middle].key) { \
return FN_PREFIX##_internal_find(map, middle + 1, end, key); \
} else { \
return &map->data[middle]; \
} \
} \
\
MAYBE_UNUSED static inline MAP_TYPE##Value* FN_PREFIX##_get( \
MAP_TYPE* map, MAP_TYPE##Key key) \
{ \
MAP_TYPE_Entry* found \
= FN_PREFIX##_internal_find(map, 0, map->size, key); \
if (!found) \
return NULL; \
return &found->value; \
} \
\
MAYBE_UNUSED static inline const MAP_TYPE##Value* FN_PREFIX##_get_const( \
const MAP_TYPE* map, MAP_TYPE##Key key) \
{ \
/* \
WARNING: Casting away const !! \
This is okay, because we know '_internal_find' doesn't \
mutate. \
*/ \
MAP_TYPE* not_const_map = (MAP_TYPE*)map; \
\
MAP_TYPE_Entry* found \
= FN_PREFIX##_internal_find(not_const_map, 0, map->size, key); \
if (!found) \
return NULL; \
return &found->value; \
}
#ifdef INCLUDE_TESTS
#include "../utils/panic.h"
DEFINE_KV_MAP(int, int, TestIntMap, test_int_map)
static inline void test_collections_kv_map(void)
{
TestIntMap map;
test_int_map_construct(&map);
test_int_map_set(&map, 1, 10);
test_int_map_set(&map, 3, 30);
test_int_map_set(&map, 5, 50);
int data[][2] = {
{ 0, 0 },
{ 1, 0 },
{ 2, 1 },
{ 3, 1 },
{ 4, 2 },
{ 5, 2 },
{ 6, 3 },
};
for (size_t i = 0; i < sizeof(data) / sizeof(data[0]); ++i) {
int idx = (int)test_int_map_internal_insert_idx(
&map, 0, map.size, data[i][0]);
if (idx != data[i][1]) {
PANIC("wrong insert index, expected %d, got %d", data[i][1], idx);
}
}
int* val = test_int_map_get(&map, 3);
if (!val || *val != 30) {
PANIC("failed to find value");
}
val = test_int_map_get(&map, 4);
if (val != NULL) {
PANIC("found wrong value");
}
const int* const_val = test_int_map_get_const(&map, 5);
if (!const_val || *const_val != 50) {
PANIC("failed to find value");
}
test_int_map_destroy(&map);
}
#endif