master
  1#define _GNU_SOURCE
  2#include <stdlib.h>
  3#include <string.h>
  4#include <search.h>
  5
  6/*
  7open addressing hash table with 2^n table size
  8quadratic probing is used in case of hash collision
  9tab indices and hash are size_t
 10after resize fails with ENOMEM the state of tab is still usable
 11
 12with the posix api items cannot be iterated and length cannot be queried
 13*/
 14
 15#define MINSIZE 8
 16#define MAXSIZE ((size_t)-1/2 + 1)
 17
 18struct __tab {
 19	ENTRY *entries;
 20	size_t mask;
 21	size_t used;
 22};
 23
 24static struct hsearch_data htab;
 25
 26static int __hcreate_r(size_t, struct hsearch_data *);
 27static void __hdestroy_r(struct hsearch_data *);
 28static int __hsearch_r(ENTRY, ACTION, ENTRY **, struct hsearch_data *);
 29
 30static size_t keyhash(char *k)
 31{
 32	unsigned char *p = (void *)k;
 33	size_t h = 0;
 34
 35	while (*p)
 36		h = 31*h + *p++;
 37	return h;
 38}
 39
 40static int resize(size_t nel, struct hsearch_data *htab)
 41{
 42	size_t newsize;
 43	size_t i, j;
 44	size_t oldsize = htab->__tab->mask + 1;
 45	ENTRY *e, *newe;
 46	ENTRY *oldtab = htab->__tab->entries;
 47
 48	if (nel > MAXSIZE)
 49		nel = MAXSIZE;
 50	for (newsize = MINSIZE; newsize < nel; newsize *= 2);
 51	htab->__tab->entries = calloc(newsize, sizeof *htab->__tab->entries);
 52	if (!htab->__tab->entries) {
 53		htab->__tab->entries = oldtab;
 54		return 0;
 55	}
 56	htab->__tab->mask = newsize - 1;
 57	if (!oldtab)
 58		return 1;
 59	for (e = oldtab; e < oldtab + oldsize; e++)
 60		if (e->key) {
 61			for (i=keyhash(e->key),j=1; ; i+=j++) {
 62				newe = htab->__tab->entries + (i & htab->__tab->mask);
 63				if (!newe->key)
 64					break;
 65			}
 66			*newe = *e;
 67		}
 68	free(oldtab);
 69	return 1;
 70}
 71
 72int hcreate(size_t nel)
 73{
 74	return __hcreate_r(nel, &htab);
 75}
 76
 77void hdestroy(void)
 78{
 79	__hdestroy_r(&htab);
 80}
 81
 82static ENTRY *lookup(char *key, size_t hash, struct hsearch_data *htab)
 83{
 84	size_t i, j;
 85	ENTRY *e;
 86
 87	for (i=hash,j=1; ; i+=j++) {
 88		e = htab->__tab->entries + (i & htab->__tab->mask);
 89		if (!e->key || strcmp(e->key, key) == 0)
 90			break;
 91	}
 92	return e;
 93}
 94
 95ENTRY *hsearch(ENTRY item, ACTION action)
 96{
 97	ENTRY *e;
 98
 99	__hsearch_r(item, action, &e, &htab);
100	return e;
101}
102
103static int __hcreate_r(size_t nel, struct hsearch_data *htab)
104{
105	int r;
106
107	htab->__tab = calloc(1, sizeof *htab->__tab);
108	if (!htab->__tab)
109		return 0;
110	r = resize(nel, htab);
111	if (r == 0) {
112		free(htab->__tab);
113		htab->__tab = 0;
114	}
115	return r;
116}
117weak_alias(__hcreate_r, hcreate_r);
118
119static void __hdestroy_r(struct hsearch_data *htab)
120{
121	if (htab->__tab) free(htab->__tab->entries);
122	free(htab->__tab);
123	htab->__tab = 0;
124}
125weak_alias(__hdestroy_r, hdestroy_r);
126
127static int __hsearch_r(ENTRY item, ACTION action, ENTRY **retval, struct hsearch_data *htab)
128{
129	size_t hash = keyhash(item.key);
130	ENTRY *e = lookup(item.key, hash, htab);
131
132	if (e->key) {
133		*retval = e;
134		return 1;
135	}
136	if (action == FIND) {
137		*retval = 0;
138		return 0;
139	}
140	*e = item;
141	if (++htab->__tab->used > htab->__tab->mask - htab->__tab->mask/4) {
142		if (!resize(2*htab->__tab->used, htab)) {
143			htab->__tab->used--;
144			e->key = 0;
145			*retval = 0;
146			return 0;
147		}
148		e = lookup(item.key, hash, htab);
149	}
150	*retval = e;
151	return 1;
152}
153weak_alias(__hsearch_r, hsearch_r);