uCBQN/src/utils/hashmapTemplate.h
2022-10-23 22:08:08 -04:00

180 lines
4.2 KiB
C

// args:
// N(X) - X##_mapName
// HT - stored hash type (u32 or u64)
// KT - key type
// H1(K) - function to compute index in hashmap, returns u64; the expensive part
// H2(K,h1) - function to compute HT (returning just h1 (or K if applicable) is safe)
// H1R(K,h2) - calculate H1 from H2, preferrably fast
// EMPTY(S,K) - test whether value counts as unpopulated (ignore K if !KEYS)
// HDEF - if defined, the value to initialize HT to (otherwise it's undefined)
// KEYS - whether to separately store and check keys (otherwise only HT is compared)
// EQUAL(A,B) - compare two keys
// KDEF - if defined, the value to initialize keys to (otherwise it's undefined)
// VALS - whether to store values corresponding to keys (otherwise this is a hashset)
// VT - value type
#define Map N(H)
#define Ent N(Ent)
#ifdef KEYS
#define IFKEY(...) __VA_ARGS__
#else
#define IFKEY(...)
#endif
#ifdef VALS
#define IFVAL(...) __VA_ARGS__
#else
#define IFVAL(...)
#endif
typedef struct Ent {
#ifdef KEYS
KT key;
#endif
HT hash;
#ifdef VALS
VT val;
#endif
} Ent;
typedef struct Map {
struct Value;
u64 pop; // count of defined entries
u64 mask; // sz-1
u64 sz; // count of allocated entries, a power of 2
Ent a[];
} Map;
static Map* N(m) (u64 sz) {
assert(sz && (sz & sz-1)==0);
Map* r = mm_alloc(fsizeof(Map,a,Ent,sz), t_hashmap);
#ifdef HDEF
for (u64 i = 0; i < sz; i++) r->a[i].hash = HDEF;
#endif
#ifdef KDEF
for (u64 i = 0; i < sz; i++) r->a[i].key = KDEF;
#endif
r->sz = sz;
r->mask = sz-1;
r->pop = 0;
return r;
}
static void N(free) (Map* m) {
mm_free((Value*) m);
}
static inline u64 N(find) (Map* m, KT k, u64 h1, u64 h2, bool* had) {
assert(m->sz > m->pop);
*had = true;
u64 mask = m->mask;
u64 p = h1 & mask;
while (true) {
Ent e = m->a[p];
if (e.hash==h2 IFKEY(&& LIKELY(EQUAL(e.key, k)))) return p;
if (EMPTY(e.hash, e.key)) { *had=false; return p; }
if (RARE(p++==mask)) p = 0;
}
}
static inline bool N(has) (Map* m, KT k) {
bool has;
u64 h1 = H1(k); u64 h2 = H2(k, h1);
N(find)(m, k, h1, h2, &has);
return has;
}
#ifdef VALS
static inline u64 N(getD) (Map* m, KT k, VT def) {
bool has;
u64 h1 = H1(k); u64 h2 = H2(k, h1);
u64 p = N(find)(m, k, h1, h2, &has);
return has? m->a[p].val : def;
}
static inline u64 N(getQ) (Map* m, KT k, bool* had) {
u64 h1 = H1(k); u64 h2 = H2(k, h1);
return N(find)(m, k, h1, h2, had);
}
#endif
static inline void N(qins) (Map* m, u64 h1, HT h2 IFKEY(, KT k) IFVAL(, VT v)) { // if guaranteed that k doesn't exist in the map yet and there's space for this
u64 mask = m->mask;
u64 p = h1 & mask;
while (true) {
u64 ch2 = m->a[p].hash;
if (EMPTY(ch2, m->a[p].key)) {
m->a[p].hash = h2;
IFKEY(m->a[p].key = k);
IFVAL(m->a[p].val = v);
m->pop++;
return;
}
if (p++==mask) p = 0;
}
}
NOINLINE void N(dbl) (Map** m);
#if HASH_C
NOINLINE void N(dbl) (Map** m) {
Map* pm = *m;
u64 psz = pm->sz;
Map* nm = N(m)(psz*2);
for (u64 i = 0; i < psz; i++) {
Ent e = pm->a[i];
if(!EMPTY(e.hash, e.key)) N(qins)(nm, H1R(e.key, e.hash), e.hash IFKEY(, e.key) IFVAL(, e.val));
}
mm_free((Value*)pm);
*m = nm;
}
#endif
static inline u64 N(mk) (Map** mp, KT k, bool* had) {
Map* m = *mp;
if (m->pop*2 > m->sz) { N(dbl)(mp); m=*mp; }
u64 h1 = H1(k); u64 h2 = H2(k, h1);
u64 p = N(find)(m, k, h1, h2, had);
if (*had) return p;
m->a[p].hash = h2;
IFKEY(m->a[p].key = k);
m->pop++;
return p;
}
static inline bool N(ins) (Map** mp, KT k IFVAL(, VT v)) { // returns whether element was replaced
bool had;
IFVAL(u64 p =) N(mk)(mp, k, &had);
IFVAL((*mp)->a[p].val = v);
return had;
}
#ifdef VALS
static inline VT N(swap) (Map** mp, KT k, VT v, VT def) { // returns either previous element or def; sets new element to v
bool had;
u64 p = N(mk)(mp, k, &had);
VT prev = (*mp)->a[p].val;
(*mp)->a[p].val = v;
return had? prev : def;
}
#endif
#undef IFKEY
#undef IFVAL
#undef Ent
#undef Map
#undef N
#undef HT
#undef KT
#undef H1
#undef H1R
#undef H2
#undef EMPTY
#undef HDEF
#ifdef KEYS
#undef KEYS
#undef EQUAL
#undef KDEF
#endif
#ifdef VALS
#undef VALS
#undef VT
#endif