/* lib9p/map.h - A really dumb map/dict data structur * * Copyright (C) 2024 Luke T. Shumaker * SPDX-License-Identifier: AGPL-3.0-or-later */ #include "internal.h" /** * `#define` `NAME`, `KEY_T`, `VAL_T`, and `CAP`; then `#include * "map.h". */ #ifndef NAME #error NAME must be defined #endif #ifndef KEY_T #error KEY_T must be defined #endif #ifndef VAL_T #error VAL_T must be defined #endif #ifndef CAP #error CAP must be defined #endif #ifndef MAP_KEY #define MAP_KV(TNAME) CAT3(_,TNAME,_kv) #define MAP_METHOD(TNAME, MNAME) CAT3(TNAME,_,MNAME) #define MAP_FOREACH(m, k, v) \ for (size_t i = 0; i < ARRAY_LEN((m)->items); i++) \ if ( ({ k = (m)->items[i].key; v = &(m)->items[i].val; (m)->items[i].set; }) ) #endif /* This implementation is just an array that we brute-force search * over for a slot. I don't want to use the heap, which means * statically-sized maps, and I'm probably going to choose a low * static size, so this is fine. */ struct MAP_KV(NAME) { bool set; KEY_T key; VAL_T val; }; struct NAME { size_t len; struct MAP_KV(NAME) items[CAP]; }; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-function" /** * Load an item from the map; return a pointer to the in-map value, or * NULL if the item is not in the map. */ static VAL_T *MAP_METHOD(NAME,load)(struct NAME *m, KEY_T k) { if (!m->len) return NULL; for (size_t i = 0; i < ARRAY_LEN(m->items); i++) if (m->items[i].set && m->items[i].key == k) return &(m->items[i].val); return NULL; } /** * Store an item into the map, perhaps replacing an existing value. * Return a pointer to the in-map value, or NULL if the map is full. */ static VAL_T *MAP_METHOD(NAME,store)(struct NAME *m, KEY_T k, VAL_T v) { VAL_T *old = MAP_METHOD(NAME,load)(m, k); if (old) { *old = v; return old; } if (m->len == ARRAY_LEN(m->items)) return NULL; for (size_t i = 0; i < ARRAY_LEN(m->items); i++) if (!m->items[i].set) { m->len++; m->items[i].set = true; m->items[i].key = k; m->items[i].val = v; return &(m->items[i].val); } __builtin_unreachable(); } /** * Delete an item from the map. Returns true if an item was deleted, * false if no such item was in the map. */ static bool MAP_METHOD(NAME,del)(struct NAME *m, KEY_T k) { if (!m->len) return NULL; for (size_t i = 0; i < ARRAY_LEN(m->items); i++) if (m->items[i].set && m->items[i].key == k) { m->items[i].set = false; m->len--; return true; } return false; } #pragma GCC diagnostic pop #undef NAME #undef KEY_T #undef VAL_T #undef CAP /* Keep the linter happy. */ #ifndef _LIB9P_MAP_H_ #define _LIB9P_MAP_H_ #endif /* _LIB9P_MAP_H_ */