Skip to content

Instantly share code, notes, and snippets.

@leok7v
Last active November 29, 2025 07:43
Show Gist options
  • Select an option

  • Save leok7v/a8fd17d649a4de6673c40c0fefda327f to your computer and use it in GitHub Desktop.

Select an option

Save leok7v/a8fd17d649a4de6673c40c0fefda327f to your computer and use it in GitHub Desktop.
Simple (almost trivial) map. Subset of C++ std::map good enough to limited usage for str::map<std::string,something> replacement
#include "map.h"
#include <assert.h>
#include <limits.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#undef MAP_DUMP_CONVERT
#define MAP_MIN_CAPACITY 8
#define MAP_LOAD_FACTOR_NUM 3 // numerator
#define MAP_LOAD_FACTOR_DEN 4 // denominator
static inline const struct map_kv* map_convert(const struct map_kv * p) {
#if defined(DEBUG) && defined(MAP_DUMP_CONVERT)
switch (p->kind) {
case MAP_KIND_INT:
fprintf(stderr, "int: %llu %lld\n",
(unsigned long long)p->i, p->i);
break;
case MAP_KIND_FLT:
fprintf(stderr, "float: %f\n", (double)*(float*)&p->i);
break;
case MAP_KIND_DBL:
fprintf(stderr, "double: %f\n", *(double*)&p->i);
break;
case MAP_KIND_STR:
fprintf(stderr, "str: \"%s\"\n", *(char**)&p->i);
break;
default:
fprintf(stderr, "unsuported kind: %d\n", p->kind);
assert(false);
exit(1);
}
#endif
return p;
}
static size_t next_power_of_two(size_t c) {
if (c == 0) return 1;
c--;
c |= c >> 1;
c |= c >> 2;
c |= c >> 4;
c |= c >> 8;
c |= c >> 16;
#if SIZE_MAX > UINT32_MAX
c |= c >> 32;
#endif
c++;
return c;
}
static int32_t map_hash(const void * data, size_t bytes) {
const uint8_t* p = (const uint8_t*)data;
uint32_t h = 2166136261u;
for (size_t i = 0; i < bytes; i++) {
h ^= p[i];
h *= 16777619u;
}
h &= INT32_MAX; // get rid of sign bit
if (h == 0u) { h = 1u; } // never zero
return (int32_t)h;
}
static inline bool is_str(enum map_kv_kind kind) {
return kind == MAP_KIND_STR;
}
static inline bool is_float(enum map_kv_kind kind) {
return kind == MAP_KIND_FLT;
}
static inline bool is_double(enum map_kv_kind kind) {
return kind == MAP_KIND_DBL;
}
static void check_kv(const struct map_kv * kv, enum map_kv_kind kind) {
if ((kv->kind != kind) ||
(is_str(kind) && (char*)(uintptr_t)kv->i == NULL) ||
(is_float(kind) && !isfinite(*(float*)&kv->i)) ||
(is_double(kind) && !isfinite(*(double*)&kv->i))
) {
assert(false);
fprintf(stderr, "FATAL: invalid key or value\n");
abort();
}
}
static inline char * kv_data(const struct map_kv * kv) {
return is_str(kv->kind) ? (char*)(uintptr_t)kv->i : (char*)&kv->i;
}
static inline size_t kv_bytes(const struct map_kv * kv, size_t size) {
return is_str(kv->kind) ? strlen((char*)(uintptr_t)kv->i) : size;
}
static bool kv_dup(struct map_kv * kv) {
if (is_str(kv->kind)) {
char * d = strdup(kv_data(kv));
if (d == NULL) { return false; }
kv->i = (uintptr_t)d;
}
return true;
}
static void kv_free(struct map_kv * kv) {
if (is_str(kv->kind)) {
free(kv_data(kv));
*(char**)(uintptr_t*)&kv->i = NULL;
}
}
static int32_t _map_find_slot(const struct _map * m,
const char * data, size_t bytes,
enum map_kv_kind kind, size_t ks,
int32_t h, bool * found) {
assert(h > 0);
int32_t cap = m->capacity;
*found = false;
if (cap <= 0) { return -1; }
// Ensure power-of-2 (for probing coverage and & optimization)
assert(cap > 0 && (cap & (cap - 1)) == 0);
int32_t first_tomb = -1;
uint32_t start = (uint32_t)h & (uint32_t)(cap - 1);
for (int32_t k = 0; k < cap; k++) {
uint32_t offset = (uint32_t)((uint64_t)k * ((uint64_t)k + 1) / 2);
int32_t i = (int32_t)((start + offset) & (uint32_t)(cap - 1));
int32_t hv = m->hash[i];
if (hv == 0) {
return first_tomb >= 0 ? first_tomb : i;
}
if (hv < 0) {
if (first_tomb < 0) { first_tomb = i; }
} else if (hv == h) {
char* key = m->keys + (size_t)i * ks;
if (is_str(kind) ? strcmp(*(char**)key, data) == 0 :
memcmp(key, data, bytes) == 0) {
*found = true;
return i;
}
}
}
return first_tomb >= 0 ? first_tomb : -1;
}
bool _map_alloc(struct _map* m, int32_t capacity, size_t ks, size_t vs) {
assert(m);
m->keys = NULL;
m->vals = NULL;
m->hash = NULL;
m->count = 0;
m->capacity = 0;
if (capacity <= 0) { return true; } // will grow on first map_put()
assert((size_t)capacity <= SIZE_MAX / 2);
if ((size_t)capacity > SIZE_MAX / 2) { return false; }
size_t cap = next_power_of_two((size_t)capacity);
assert(cap <= INT32_MAX && cap <= SIZE_MAX / ks && cap <= SIZE_MAX / vs);
if (cap > INT32_MAX || cap > SIZE_MAX / ks || cap > SIZE_MAX / vs) {
return false;
}
void* keys = malloc(cap * ks);
if (!keys) { return false; }
void* vals = malloc(cap * vs);
if (!vals) { free(keys); return false; }
int* hash = (int*)calloc(cap, sizeof(m->hash[0])); // .zero init
if (!hash) { free(keys); free(vals); return false; }
m->keys = keys;
m->vals = vals;
m->hash = hash;
m->capacity = (int32_t)cap;
return true;
}
static bool _map_rehash_into(struct _map * dst,
const struct _map * src,
enum map_kv_kind kind, // key's kind
size_t ks, size_t vs) {
const bool key_is_str = is_str(kind);
int32_t cap = src->capacity;
int32_t i = 0;
while (i < cap) {
int32_t h = src->hash[i];
if (h > 0) {
char* key = src->keys + (size_t)i * ks;
char* val = src->vals + (size_t)i * vs;
char* data = key_is_str ? *(char**)key : key;
size_t bytes = key_is_str ? strlen(data) : ks;
bool found = false;
int32_t slot = _map_find_slot(dst, data, bytes,
kind, ks, h, &found);
if (slot < 0 || found) { return false; } // oom
memcpy(dst->keys + (size_t)slot * ks, key, ks);
memcpy(dst->vals + (size_t)slot * vs, val, vs);
dst->hash[slot] = h;
dst->count++;
}
i++;
}
return true;
}
static void _map_free_deallocate(struct _map * m);
static bool _map_resize(struct _map * m, int32_t capacity,
enum map_kv_kind k_kind,
size_t ks, size_t vs) {
if (capacity > INT32_MAX) { return false; }
int32_t was = m->capacity;
int32_t cap = MAP_MIN_CAPACITY;
while (cap < capacity) {
if (cap >= INT32_MAX / 2) { cap = INT32_MAX; break; }
cap *= 2;
}
if (cap == was) { return true; }
if (cap / MAP_LOAD_FACTOR_DEN <= m->count / MAP_LOAD_FACTOR_NUM) {
return false; // cannot shrink smaller than count * 4 / 3
}
struct _map map = {};
if (!_map_alloc(&map, cap, ks, vs)) {
return false;
}
if (was > 0 && m->keys && m->vals && m->hash) {
if (!_map_rehash_into(&map, m, k_kind, ks, vs)) {
_map_free_deallocate(&map);
return false;
}
}
_map_free_deallocate(m);
m->keys = map.keys;
m->vals = map.vals;
m->hash = map.hash;
m->count = map.count;
m->capacity = map.capacity;
return true;
}
bool _map_grow(struct _map * m, int32_t capacity,
enum map_kv_kind k_kind,
size_t ks, size_t vs) {
return capacity > m->capacity ?
_map_resize(m, capacity, k_kind, ks, vs) : true;
}
bool _map_shrink(struct _map * m, int32_t capacity,
enum map_kv_kind k_kind,
size_t ks, size_t vs) {
return m->count < capacity && capacity < m->capacity ?
_map_resize(m, capacity, k_kind, ks, vs) : true;
}
bool _map_put(struct _map* m,
struct map_kv key, struct map_kv val,
enum map_kv_kind k_kind, enum map_kv_kind v_kind,
size_t ks, size_t vs) {
assert(m);
check_kv(&key, k_kind);
check_kv(&val, v_kind);
// const bool key_is_str = is_str(key.kind);
// const bool val_is_str = is_str(val.kind);
if (!kv_dup(&val)) { return false; }
if (m->capacity <= 0 || (m->count + 1) * MAP_LOAD_FACTOR_DEN >
m->capacity * MAP_LOAD_FACTOR_NUM) {
int32_t cap;
if (m->capacity < MAP_MIN_CAPACITY) {
cap = MAP_MIN_CAPACITY;
} else if (m->capacity <= INT32_MAX / 2) {
cap = m->capacity * 2;
} else {
cap = INT32_MAX;
}
if (!_map_grow(m, cap, k_kind, ks, vs)) {
kv_free(&val);
return false;
}
}
const void* data = kv_data(&key);
const size_t bytes = kv_bytes(&key, ks);
const int32_t h = map_hash(data, bytes);
bool found = 0;
int32_t slot = _map_find_slot(m, data, bytes, k_kind, ks, h, &found);
if (slot < 0) {
return false;
}
if (found) {
if (is_str(v_kind)) {
char* was = *(char**)(m->vals + (size_t)slot * vs);
if (was) { free(was); }
}
memcpy(m->vals + (size_t)slot * vs, &val.i, vs);
} else {
if (!kv_dup(&key)) { kv_free(&val); return false; }
memcpy(m->keys + (size_t)slot * ks, &key.i, ks);
memcpy(m->vals + (size_t)slot * vs, &val.i, vs);
m->hash[slot] = h;
m->count++;
}
return true;
}
int32_t _map_idx(const struct _map * m,
struct map_kv key, enum map_kv_kind kind, size_t size) {
assert(m);
check_kv(&key, kind);
int32_t r = -1;
if (m->capacity > 0 && m->count > 0) {
const void* data = kv_data(&key);
const size_t bytes = kv_bytes(&key, size);
int32_t h = map_hash(data, bytes);
bool found = false;
int32_t slot = _map_find_slot(m, data, bytes,
kind, size, h, &found);
if (slot >= 0 && found) {
r = slot;
}
}
return r;
}
bool _map_has(const struct _map * m, struct map_kv k,
enum map_kv_kind k_kind, size_t k_size) {
return _map_idx(m, k, k_kind, k_size) != -1;
}
void* _map_get(const struct _map* m, struct map_kv k,
enum map_kv_kind k_kind, size_t ks, size_t vs) {
int32_t index = _map_idx(m, k, k_kind, ks);
return index < 0 ? NULL : m->vals + (size_t)index * vs;
}
bool _map_remove(struct _map* m, struct map_kv key,
enum map_kv_kind k_kind, enum map_kv_kind v_kind,
size_t ks, size_t vs) {
assert(m);
check_kv(&key, k_kind);
if (m->capacity <= 0 || m->count <= 0) { return false; }
const char* data = kv_data(&key);
const size_t bytes = kv_bytes(&key, ks);
int32_t h = map_hash(data, bytes);
bool found = false;
int32_t slot = _map_find_slot(m, data, bytes, k_kind, ks, h, &found);
if (slot < 0 || !found) { return false; }
if (is_str(k_kind)) {
char* was = *(char**)(m->keys + (size_t)slot * ks);
if (was) { free(was); }
}
if (is_str(v_kind)) {
char* was = *(char**)(m->vals + (size_t)slot * vs);
if (was) { free(was); }
}
m->hash[slot] = -m->hash[slot];
m->count--;
if (m->count > 0 && m->count < m->capacity / 4) {
(void)_map_shrink(m, m->count * 2, k_kind, ks, vs); // ignore result
}
return true;
}
void _map_clear(struct _map* m, enum map_kv_kind k_kind,
enum map_kv_kind v_kind, size_t ks, size_t vs) {
_map_free(m, k_kind, v_kind);
_map_alloc(m, 0, ks, vs);
}
static void _map_free_deallocate(struct _map* m) {
// Frees the arrays but doesn't free the string data within them;
// That content of strdup() values is freed inside _map_free().
if (m->keys) { free(m->keys); }
if (m->vals) { free(m->vals); }
if (m->hash) { free(m->hash); }
m->keys = NULL;
m->vals = NULL;
m->hash = NULL;
m->count = 0;
m->capacity = 0;
}
void _map_free(struct _map* m,
enum map_kv_kind k_kind, enum map_kv_kind v_kind) {
assert(m);
static_assert(sizeof(char*) <= sizeof(char*));
if (k_kind == MAP_KIND_STR) {
for (int32_t i = 0; i < m->capacity; i++) {
if (m->hash[i] > 0) {
char* s = *(char**)(m->keys + i * sizeof(char*));
if (s) { free((void*)s); }
}
}
}
if (v_kind == MAP_KIND_STR) {
for (int32_t i = 0; i < m->capacity; i++) {
if (m->hash[i] > 0) {
char* s = *(char**)(m->vals + i * sizeof(char*));
if (s) { free((void*)s); }
}
}
}
_map_free_deallocate(m);
}
#define _map_convert_int(n, T, UT) \
struct map_kv _map_kv_##n(T v) { \
struct map_kv kv = { .i = (uint64_t)(int64_t)v, \
.kind = MAP_KIND_INT \
}; \
return *map_convert(&kv); \
} \
struct map_kv _map_kv_u##n(UT v) { \
struct map_kv kv = { .i = (uint64_t)v, .kind = MAP_KIND_INT \
}; \
return *map_convert(&kv); \
}
_map_convert_int(char, signed char, unsigned char)
_map_convert_int(short, short, unsigned short)
_map_convert_int(int, int, unsigned int)
_map_convert_int(long, long, unsigned long)
_map_convert_int(ll, long long, unsigned long long)
static_assert(sizeof(double) == sizeof(uint64_t), "double must be 64 bits");
static_assert(sizeof(char*) <= sizeof(uint64_t), "pointers");
struct map_kv _map_kv_flt(float f) {
struct map_kv kv = {.i = 0, .kind = MAP_KIND_FLT};
union { float f; uint32_t u; } pun = { .f = (f == 0 ? 0.0f : f) };
kv.i = pun.u; // zero-extended into low 32 bits logically
return *map_convert(&kv);
}
struct map_kv _map_kv_dbl(double d) {
struct map_kv kv = {.i = 0, .kind = MAP_KIND_DBL};
union { double d; uint64_t u; } pun = { .d = (d == 0 ? 0.0 : d) };
kv.i = pun.u;
return *map_convert(&kv);
}
struct map_kv _map_kv_str(const char* s) {
struct map_kv kv = {.kind = MAP_KIND_STR};
memcpy(&kv.i, &s, sizeof(s));
return *map_convert(&kv);
}
// smoke tests:
static void map_test_i2i(void) {
struct i2i map_of(int16_t, int32_t);
struct i2i m = {};
bool ok = map_alloc(&m); assert(ok);
ok = map_put(&m, 10, 42); assert(ok);
ok = map_has(&m, 10); assert(ok);
int* r = map_get(&m, 10);
assert(*r == 42); (void)r;
ok = map_remove(&m, 10); assert(ok);
for (int16_t k = 0; k < 1024; k += 13) {
int32_t v = (int32_t)k * (int32_t)k;
ok = map_put(&m, k, v); assert(ok);
ok = map_has(&m, k); assert(ok);
int* res = map_get(&m, k); assert(res);
assert(*res == v);
}
map_foreach(&m, k, v, {
assert((int32_t)k * (int32_t)k == v);
});
map_free(&m);
}
static void map_test_s2s(void) {
struct s2s map_of(const char*, const char*);
struct s2s m = {};
bool ok = map_alloc(&m, 1); assert(ok);
ok = map_put(&m, "hello", "world"); assert(ok);
ok = map_put(&m, "goodbye", "universe"); assert(ok);
ok = map_put(&m, "foo", "bar"); assert(ok);
ok = map_has(&m, "hello"); assert(ok);
ok = map_has(&m, "goodbye"); assert(ok);
const char* rs = *map_get(&m, "hello");
assert(strcmp(rs, "world") == 0);
rs = *map_get(&m, "goodbye");
assert(strcmp(rs, "universe") == 0);
ok = map_remove(&m, "foo");
ok = !map_has(&m, "foo"); assert(ok);
const char** ps = map_get(&m, "foo");
assert(ps == NULL);
(void)ps; // unused in release
(void)rs; // unused in release
map_free(&m);
}
static void map_test_s2d(void) {
struct str2dbl map_of(const char *, double);
struct str2dbl m = {};
bool ok = map_alloc(&m); assert(ok);
ok = map_put(&m, "e", 2.71828); assert(ok);
ok = map_put(&m, "pi", 3.14159); assert(ok);
assert(*map_get(&m, "e") == 2.71828);
assert(*map_get(&m, "pi") == 3.14159);
map_free(&m);
}
void map_smoke_test(void) { // not all code paths are covered
map_test_i2i();
map_test_s2s();
map_test_s2d();
}
#pragma once
#ifndef MAP_H
#define MAP_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
/*
Limited to 2^31 - 1 entries.
Not thread-safe; use external synchronization.
Big-endian is NOT supported.
Keys/Values support:
const char* (always strdup()-ed, NULLs not allowed)
int8_t .. int64_t
uint8_t .. uint64_t
float/double (infinity, NaN are not allowed)
(both -0.0/+0.0 are normalized to +0.0)
map_put(&m, -0.0, -0.0) will result in actual
map_put(&m, +0.0, +0.0)
Modifications during map_foreach() are NOT supported.
Zero terminated strings for values and keys always strdup()-ed.
To achieve values (only!) of structural or arrays types with memory
managed by the caller pointers to struct and array can be cast to
(uintptr_t) type.
Usage example: (results checks are omitted for simplicity:
struct int2int map_of(int, int);
struct int2int m = {}; // important: must be .zero initialized
bool ok = map_alloc(&m); // defaults to 16 slots initialization
ok = map_put(&m, 13, 42);
ok = map_has(&m, 13);
int* r = map_get(&m, 13);
assert(*r == 42);
map_foreach(&m, k, v, { printf("%d: %d\n", k, v); });
ok = map_grow(&m, 1024);
ok = map_remove(&m, 13);
map_clear(&m); // instead of free/alloc
map_free(&m);
struct str2str map_of(const char*, const char*);
struct str2str s2s = {};
bool ok = map_alloc(&s2s);
ok = map_put(&s2s, "hello", "world");
char** s = map_get(&s2s, "hello");
assert(s);
asser(strcmp(*s, "world") == 0);
map_free(&m);
*/
#if __STDC_VERSION__ < 201112L // macOS C23 -> Swift fails with: 202311L
#error "Requires C11 (preferably C23) or later"
#endif
enum map_kv_kind {
MAP_KIND_NIL = 0, // never used
MAP_KIND_STR = 1, // char *
MAP_KIND_INT = 2, // all integer types
MAP_KIND_FLT = 3, // float
MAP_KIND_DBL = 4, // double
};
struct map_kv {
/*
union {
float f;
double d;
uint64_t i;
const char* s;
};
*/
uint64_t i;
enum map_kv_kind kind;
};
struct _map {
char* keys;
char* vals;
int32_t* hash;
int32_t count;
int32_t capacity;
};
#define map_of(ktype, vtype) { \
ktype* keys; \
vtype* vals; \
int32_t* hash; \
int32_t count; \
int32_t capacity; \
}
struct map_kv _map_kv_char (signed char v);
struct map_kv _map_kv_short (short v);
struct map_kv _map_kv_int (int v);
struct map_kv _map_kv_long (long v);
struct map_kv _map_kv_ll (long long v);
struct map_kv _map_kv_uchar (unsigned char v);
struct map_kv _map_kv_ushort(unsigned short v);
struct map_kv _map_kv_uint (unsigned int v);
struct map_kv _map_kv_ulong (unsigned long v);
struct map_kv _map_kv_ull (unsigned long long v);
struct map_kv _map_kv_flt (float v);
struct map_kv _map_kv_dbl (double v);
struct map_kv _map_kv_str (const char* s);
#define _map_kv_kind(x) \
_Generic((x), \
signed char: MAP_KIND_INT, \
short: MAP_KIND_INT, \
int: MAP_KIND_INT, \
long: MAP_KIND_INT, \
long long: MAP_KIND_INT, \
unsigned char: MAP_KIND_INT, \
unsigned short: MAP_KIND_INT, \
unsigned int: MAP_KIND_INT, \
unsigned long: MAP_KIND_INT, \
unsigned long long: MAP_KIND_INT, \
float: MAP_KIND_FLT, \
double: MAP_KIND_DBL, \
const char*: MAP_KIND_STR, \
char*: MAP_KIND_STR \
)
#define _map_kv(x) \
_Generic((x), \
signed char: _map_kv_char, \
short: _map_kv_short, \
int: _map_kv_int, \
long: _map_kv_long, \
long long: _map_kv_ll, \
unsigned char: _map_kv_uchar, \
unsigned short: _map_kv_ushort, \
unsigned int: _map_kv_uint, \
unsigned long: _map_kv_ulong, \
unsigned long long: _map_kv_ull, \
float: _map_kv_flt, \
double: _map_kv_dbl, \
const char*: _map_kv_str, \
char*: _map_kv_str \
)
#define _map_ks(m) sizeof((m)->keys[0])
#define _map_vs(m) sizeof((m)->vals[0])
bool _map_alloc (struct _map* m, int32_t capacity, size_t ks, size_t vs);
bool _map_grow (struct _map* m, int32_t min_capacity,
enum map_kv_kind k_kind, size_t ks, size_t vs);
bool _map_shrink (struct _map* m, int32_t min_capacity,
enum map_kv_kind k_kind, size_t ks, size_t vs);
bool _map_put (struct _map* m, struct map_kv k, struct map_kv v,
enum map_kv_kind k_kind, enum map_kv_kind v_kind,
size_t k_size, size_t v_size);
bool _map_remove (struct _map* m, struct map_kv k,
enum map_kv_kind k_kind, enum map_kv_kind v_kind,
size_t ks, size_t vs);
bool _map_has (const struct _map* m, struct map_kv k,
enum map_kv_kind k_kind, size_t k_size);
int32_t _map_idx (const struct _map* m, struct map_kv k,
enum map_kv_kind k_kind, size_t k_size);
void* _map_get (const struct _map* m, struct map_kv k,
enum map_kv_kind k_kind, size_t ks, size_t vs);
void _map_clear (struct _map* m,
enum map_kv_kind k_kind, enum map_kv_kind v_kind,
size_t ks, size_t vs);
void _map_free (struct _map* m,
enum map_kv_kind k_kind, enum map_kv_kind v_kind);
void map_smoke_test(void); // quick and dirty test
void map_test(void);
void map_test_memory_pressure(void); // runs map_test 1,000,000 times
#if __STDC_VERSION__ >= 202311L
#define _map_typeof(t) typeof(t)
#else
#define _map_typeof(t) __typeof__(t)
#endif
#define _map_get_3rd(_1,_2,_3,...) _3
#define _map_chooser(...) \
_map_get_3rd(__VA_ARGS__, _map_alloc2, _map_alloc1, _map_bad)
#define map_alloc(...) _map_chooser(__VA_ARGS__)(__VA_ARGS__)
#define _map_alloc1(m) \
_map_alloc((struct _map*)(m), 16, _map_ks(m), _map_vs(m))
#define _map_alloc2(m, n) \
_map_alloc((struct _map*)(m), n, _map_ks(m), _map_vs(m))
#define map_grow(m, n) \
_map_grow((struct _map*)(m), n, \
_map_kv_kind((m)->keys[0]), \
_map_ks(m), _map_vs(m))
#define map_shrink(m, n) \
_map_shrink((struct _map*)(m), n, \
_map_kv_kind((m)->keys[0]), \
_map_ks(m), _map_vs(m))
#define map_put(m, k, v) \
_map_put((struct _map*)(m), \
_map_kv(k)(k), \
_map_kv(v)(v), \
_map_kv_kind(k), \
_map_kv_kind(v), \
_map_ks(m), _map_vs(m))
#define map_has(m, k) \
_map_has((struct _map*)(m), _map_kv(k)(k), _map_kv_kind(k), _map_ks(m))
#define map_get(m, k) ( \
_map_typeof((m)->vals)) ( \
_map_get((struct _map*)(m), \
_map_kv(k)(k), \
_map_kv_kind(k), \
_map_ks(m), \
_map_vs(m)) \
)
#define map_remove(m, k) \
_map_remove((struct _map*)(m), \
_map_kv(k)(k), \
_map_kv_kind(k), \
_map_kv_kind((m)->vals[0]), \
_map_ks(m), _map_vs(m))
#define map_clear(m) \
_map_clear((struct _map*)(m), \
_map_kv_kind((m)->keys[0]), \
_map_kv_kind((m)->vals[0]), \
_map_ks(m), _map_vs(m))
#define map_free(m) \
_map_free((struct _map*)(m), \
_map_kv_kind((m)->keys[0]), \
_map_kv_kind((m)->vals[0]))
#define map_foreach(m, k_var, v_var, code) do { \
for(int32_t _i = 0; _i < (m)->capacity; _i++) { \
if ((m)->hash[_i] > 0) { \
_map_typeof((m)->keys[0]) k_var = (m)->keys[_i]; \
_map_typeof((m)->vals[0]) v_var = (m)->vals[_i]; \
code; \
} \
} \
} while(0)
#ifdef __cplusplus
}
#endif
#endif /* MAP_H */
@leok7v
Copy link
Author

leok7v commented Nov 29, 2025

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment