main shrubtools / nviz / htab.c
  1#include <assert.h>
  2#include <limits.h>
  3#include <stdbool.h>
  4#include <stdint.h>
  5#include <stdlib.h>
  6#include <string.h>
  7#include "util.h"
  8#include "htab.h"
  9
 10struct hashtable {
 11	size_t len, cap;
 12	struct hashtablekey *keys;
 13	void **vals;
 14};
 15
 16void
 17htabkey(struct hashtablekey *k, const char *s, size_t n)
 18{
 19	k->str = s;
 20	k->len = n;
 21	k->hash = rapidhashv1(s, n);
 22}
 23
 24struct hashtable *
 25mkhtab(size_t cap)
 26{
 27	struct hashtable *h;
 28	size_t i;
 29
 30	assert(!(cap & (cap - 1)));
 31	h = xmalloc(sizeof(*h));
 32	h->len = 0;
 33	h->cap = cap;
 34	h->keys = xreallocarray(NULL, cap, sizeof(h->keys[0]));
 35	h->vals = xreallocarray(NULL, cap, sizeof(h->vals[0]));
 36	for (i = 0; i < cap; ++i)
 37		h->keys[i].str = NULL;
 38
 39	return h;
 40}
 41
 42void
 43delhtab(struct hashtable *h, void del(void *))
 44{
 45	size_t i;
 46
 47	if (!h)
 48		return;
 49	if (del) {
 50		for (i = 0; i < h->cap; ++i) {
 51			if (h->keys[i].str)
 52				del(h->vals[i]);
 53		}
 54	}
 55	free(h->keys);
 56	free(h->vals);
 57	free(h);
 58}
 59
 60static bool
 61keyequal(struct hashtablekey *k1, struct hashtablekey *k2)
 62{
 63	if (k1->hash != k2->hash || k1->len != k2->len)
 64		return false;
 65	return memcmp(k1->str, k2->str, k1->len) == 0;
 66}
 67
 68static size_t
 69keyindex(struct hashtable *h, struct hashtablekey *k)
 70{
 71	size_t i;
 72
 73	i = k->hash & (h->cap - 1);
 74	while (h->keys[i].str && !keyequal(&h->keys[i], k))
 75		i = (i + 1) & (h->cap - 1);
 76	return i;
 77}
 78
 79void **
 80htabput(struct hashtable *h, struct hashtablekey *k)
 81{
 82	struct hashtablekey *oldkeys;
 83	void **oldvals;
 84	size_t i, j, oldcap;
 85
 86	if (h->cap / 2 < h->len) {
 87		oldkeys = h->keys;
 88		oldvals = h->vals;
 89		oldcap = h->cap;
 90		h->cap *= 2;
 91		h->keys = xreallocarray(NULL, h->cap, sizeof(h->keys[0]));
 92		h->vals = xreallocarray(NULL, h->cap, sizeof(h->vals[0]));
 93		for (i = 0; i < h->cap; ++i)
 94			h->keys[i].str = NULL;
 95		for (i = 0; i < oldcap; ++i) {
 96			if (oldkeys[i].str) {
 97				j = keyindex(h, &oldkeys[i]);
 98				h->keys[j] = oldkeys[i];
 99				h->vals[j] = oldvals[i];
100			}
101		}
102		free(oldkeys);
103		free(oldvals);
104	}
105	i = keyindex(h, k);
106	if (!h->keys[i].str) {
107		h->keys[i] = *k;
108		h->vals[i] = NULL;
109		++h->len;
110	}
111
112	return &h->vals[i];
113}
114
115void *
116htabget(struct hashtable *h, struct hashtablekey *k)
117{
118	size_t i;
119
120	i = keyindex(h, k);
121	return h->keys[i].str ? h->vals[i] : NULL;
122}
123
124static inline uint_least32_t
125getle32(const void *p)
126{
127	const unsigned char *b = p;
128	uint_least32_t v;
129
130	v = b[0] & 0xfful;
131	v |= (b[1] & 0xfful) << 8;
132	v |= (b[2] & 0xfful) << 16;
133	v |= (b[3] & 0xfful) << 24;
134	return v;
135}
136
137static inline uint_least64_t
138getle64(const void *p)
139{
140	const unsigned char *b = p;
141	uint_least64_t v;
142
143	v = b[0] & 0xffull;
144	v |= (b[1] & 0xffull) << 8;
145	v |= (b[2] & 0xffull) << 16;
146	v |= (b[3] & 0xffull) << 24;
147	v |= (b[4] & 0xffull) << 32;
148	v |= (b[5] & 0xffull) << 40;
149	v |= (b[6] & 0xffull) << 48;
150	v |= (b[7] & 0xffull) << 56;
151	return v;
152}
153
154#if __STDC_VERSION__ >= 202311L && BITINT_MAXWIDTH >= 128
155#define uint128 unsigned _BitInt(128)
156#elif __SIZEOF_INT128__
157#define uint128 __uint128_t
158#endif
159
160static inline void
161mum(uint64_t *a, uint64_t *b)
162{
163#ifdef uint128
164	uint128 r;
165
166	r = *a;
167	r *= *b;
168	*a = r;
169	*b = r >> 64;
170#else
171	uint64_t al, ah, bl, bh, rl, rh;
172	uint64_t ll, lh, hl, hh, m;
173
174	al = (uint32_t)*a;
175	bl = (uint32_t)*b;
176	ah = *a >> 32;
177	bh = *b >> 32;
178	ll = al * bl;
179	lh = al * bh;
180	hl = ah * bl;
181	hh = ah * bh;
182
183	m = (ll >> 32) + lh + (hl & 0xffffffff);
184	rl = ll & 0xffffffff | m << 32;
185	rh = hh + (m >> 32) + (hl >> 32);
186	*a = rl;
187	*b = rh;
188#endif
189}
190
191static inline uint64_t
192mix(uint64_t a, uint64_t b)
193{
194	mum(&a, &b);
195	return a ^ b;
196}
197
198uint64_t
199rapidhashv1(const void *ptr, size_t len)
200{
201	static const uint64_t secret[] = {
202		0x2d358dccaa6c78a5ull,
203		0x8bb84b93962eacc9ull,
204		0x4b33a62ed433d4a3ull,
205	};
206	uint64_t seed[3];
207	const unsigned char *pos, *end;
208	int i;
209
210	pos = ptr;
211	end = pos + len;
212	seed[0] = 0xbdd89aa982704029ull;
213	seed[0] ^= mix(seed[0] ^ secret[0], secret[1]) ^ len;
214	if (len == 0) {
215		seed[1] = seed[2] = 0;
216	} else if (len < 4) {
217		seed[1] = (uint64_t)pos[0] << 56 | (uint64_t)pos[len > 1] << 32 | end[-1];
218		seed[2] = 0;
219	} else if (len <= 16) {
220		seed[1] = (uint64_t)getle32(pos) << 32 | getle32(end - 4);
221		if (len >= 8)
222			pos += 4, end -= 4;
223		seed[2] = (uint64_t)getle32(pos) << 32 | getle32(end - 4);
224	} else {
225		seed[1] = seed[2] = seed[0];
226		if (len > 48) {
227			do {
228				for (i = 0; i < 3; ++i, pos += 16)
229					seed[i] = mix(getle64(pos) ^ secret[i], getle64(pos + 8) ^ seed[i]);
230			} while (end - pos >= 48);
231			seed[0] ^= seed[1] ^ seed[2];
232		}
233		if (end - pos > 16) {
234			seed[0] ^= secret[1];
235			do seed[0] = mix(getle64(pos) ^ secret[2], getle64(pos + 8) ^ seed[0]);
236			while (pos += 16, end - pos > 16);
237		}
238		seed[1] = getle64(end - 16);
239		seed[2] = getle64(end -  8);
240	}
241	seed[1] ^= secret[1];
242	seed[2] ^= seed[0];
243	mum(&seed[1], &seed[2]);
244	return mix(seed[1] ^ secret[0] ^ len, seed[2] ^ secret[1]);
245}