1570af302Sopenharmony_ci#define _GNU_SOURCE
2570af302Sopenharmony_ci#include <stdlib.h>
3570af302Sopenharmony_ci#include <string.h>
4570af302Sopenharmony_ci#include <search.h>
5570af302Sopenharmony_ci
6570af302Sopenharmony_ci/*
7570af302Sopenharmony_ciopen addressing hash table with 2^n table size
8570af302Sopenharmony_ciquadratic probing is used in case of hash collision
9570af302Sopenharmony_citab indices and hash are size_t
10570af302Sopenharmony_ciafter resize fails with ENOMEM the state of tab is still usable
11570af302Sopenharmony_ci
12570af302Sopenharmony_ciwith the posix api items cannot be iterated and length cannot be queried
13570af302Sopenharmony_ci*/
14570af302Sopenharmony_ci
15570af302Sopenharmony_ci#define MINSIZE 8
16570af302Sopenharmony_ci#define MAXSIZE ((size_t)-1/2 + 1)
17570af302Sopenharmony_ci
18570af302Sopenharmony_cistruct __tab {
19570af302Sopenharmony_ci	ENTRY *entries;
20570af302Sopenharmony_ci	size_t mask;
21570af302Sopenharmony_ci	size_t used;
22570af302Sopenharmony_ci};
23570af302Sopenharmony_ci
24570af302Sopenharmony_cistatic struct hsearch_data htab;
25570af302Sopenharmony_ci
26570af302Sopenharmony_cistatic int __hcreate_r(size_t, struct hsearch_data *);
27570af302Sopenharmony_cistatic void __hdestroy_r(struct hsearch_data *);
28570af302Sopenharmony_cistatic int __hsearch_r(ENTRY, ACTION, ENTRY **, struct hsearch_data *);
29570af302Sopenharmony_ci
30570af302Sopenharmony_cistatic size_t keyhash(char *k)
31570af302Sopenharmony_ci{
32570af302Sopenharmony_ci	unsigned char *p = (void *)k;
33570af302Sopenharmony_ci	size_t h = 0;
34570af302Sopenharmony_ci
35570af302Sopenharmony_ci	while (*p)
36570af302Sopenharmony_ci		h = 31*h + *p++;
37570af302Sopenharmony_ci	return h;
38570af302Sopenharmony_ci}
39570af302Sopenharmony_ci
40570af302Sopenharmony_cistatic int resize(size_t nel, struct hsearch_data *htab)
41570af302Sopenharmony_ci{
42570af302Sopenharmony_ci	size_t newsize;
43570af302Sopenharmony_ci	size_t i, j;
44570af302Sopenharmony_ci	size_t oldsize = htab->__tab->mask + 1;
45570af302Sopenharmony_ci	ENTRY *e, *newe;
46570af302Sopenharmony_ci	ENTRY *oldtab = htab->__tab->entries;
47570af302Sopenharmony_ci
48570af302Sopenharmony_ci	if (nel > MAXSIZE)
49570af302Sopenharmony_ci		nel = MAXSIZE;
50570af302Sopenharmony_ci	for (newsize = MINSIZE; newsize < nel; newsize *= 2);
51570af302Sopenharmony_ci	htab->__tab->entries = calloc(newsize, sizeof *htab->__tab->entries);
52570af302Sopenharmony_ci	if (!htab->__tab->entries) {
53570af302Sopenharmony_ci		htab->__tab->entries = oldtab;
54570af302Sopenharmony_ci		return 0;
55570af302Sopenharmony_ci	}
56570af302Sopenharmony_ci	htab->__tab->mask = newsize - 1;
57570af302Sopenharmony_ci	if (!oldtab)
58570af302Sopenharmony_ci		return 1;
59570af302Sopenharmony_ci	for (e = oldtab; e < oldtab + oldsize; e++)
60570af302Sopenharmony_ci		if (e->key) {
61570af302Sopenharmony_ci			for (i=keyhash(e->key),j=1; ; i+=j++) {
62570af302Sopenharmony_ci				newe = htab->__tab->entries + (i & htab->__tab->mask);
63570af302Sopenharmony_ci				if (!newe->key)
64570af302Sopenharmony_ci					break;
65570af302Sopenharmony_ci			}
66570af302Sopenharmony_ci			*newe = *e;
67570af302Sopenharmony_ci		}
68570af302Sopenharmony_ci	free(oldtab);
69570af302Sopenharmony_ci	return 1;
70570af302Sopenharmony_ci}
71570af302Sopenharmony_ci
72570af302Sopenharmony_ciint hcreate(size_t nel)
73570af302Sopenharmony_ci{
74570af302Sopenharmony_ci	return __hcreate_r(nel, &htab);
75570af302Sopenharmony_ci}
76570af302Sopenharmony_ci
77570af302Sopenharmony_civoid hdestroy(void)
78570af302Sopenharmony_ci{
79570af302Sopenharmony_ci	__hdestroy_r(&htab);
80570af302Sopenharmony_ci}
81570af302Sopenharmony_ci
82570af302Sopenharmony_cistatic ENTRY *lookup(char *key, size_t hash, struct hsearch_data *htab)
83570af302Sopenharmony_ci{
84570af302Sopenharmony_ci	size_t i, j;
85570af302Sopenharmony_ci	ENTRY *e;
86570af302Sopenharmony_ci
87570af302Sopenharmony_ci	for (i=hash,j=1; ; i+=j++) {
88570af302Sopenharmony_ci		e = htab->__tab->entries + (i & htab->__tab->mask);
89570af302Sopenharmony_ci		if (!e->key || strcmp(e->key, key) == 0)
90570af302Sopenharmony_ci			break;
91570af302Sopenharmony_ci	}
92570af302Sopenharmony_ci	return e;
93570af302Sopenharmony_ci}
94570af302Sopenharmony_ci
95570af302Sopenharmony_ciENTRY *hsearch(ENTRY item, ACTION action)
96570af302Sopenharmony_ci{
97570af302Sopenharmony_ci	ENTRY *e;
98570af302Sopenharmony_ci
99570af302Sopenharmony_ci	__hsearch_r(item, action, &e, &htab);
100570af302Sopenharmony_ci	return e;
101570af302Sopenharmony_ci}
102570af302Sopenharmony_ci
103570af302Sopenharmony_cistatic int __hcreate_r(size_t nel, struct hsearch_data *htab)
104570af302Sopenharmony_ci{
105570af302Sopenharmony_ci	int r;
106570af302Sopenharmony_ci
107570af302Sopenharmony_ci	htab->__tab = calloc(1, sizeof *htab->__tab);
108570af302Sopenharmony_ci	if (!htab->__tab)
109570af302Sopenharmony_ci		return 0;
110570af302Sopenharmony_ci	r = resize(nel, htab);
111570af302Sopenharmony_ci	if (r == 0) {
112570af302Sopenharmony_ci		free(htab->__tab);
113570af302Sopenharmony_ci		htab->__tab = 0;
114570af302Sopenharmony_ci	}
115570af302Sopenharmony_ci	return r;
116570af302Sopenharmony_ci}
117570af302Sopenharmony_ciweak_alias(__hcreate_r, hcreate_r);
118570af302Sopenharmony_ci
119570af302Sopenharmony_cistatic void __hdestroy_r(struct hsearch_data *htab)
120570af302Sopenharmony_ci{
121570af302Sopenharmony_ci	if (htab->__tab) free(htab->__tab->entries);
122570af302Sopenharmony_ci	free(htab->__tab);
123570af302Sopenharmony_ci	htab->__tab = 0;
124570af302Sopenharmony_ci}
125570af302Sopenharmony_ciweak_alias(__hdestroy_r, hdestroy_r);
126570af302Sopenharmony_ci
127570af302Sopenharmony_cistatic int __hsearch_r(ENTRY item, ACTION action, ENTRY **retval, struct hsearch_data *htab)
128570af302Sopenharmony_ci{
129570af302Sopenharmony_ci	size_t hash = keyhash(item.key);
130570af302Sopenharmony_ci	ENTRY *e = lookup(item.key, hash, htab);
131570af302Sopenharmony_ci
132570af302Sopenharmony_ci	if (e->key) {
133570af302Sopenharmony_ci		*retval = e;
134570af302Sopenharmony_ci		return 1;
135570af302Sopenharmony_ci	}
136570af302Sopenharmony_ci	if (action == FIND) {
137570af302Sopenharmony_ci		*retval = 0;
138570af302Sopenharmony_ci		return 0;
139570af302Sopenharmony_ci	}
140570af302Sopenharmony_ci	*e = item;
141570af302Sopenharmony_ci	if (++htab->__tab->used > htab->__tab->mask - htab->__tab->mask/4) {
142570af302Sopenharmony_ci		if (!resize(2*htab->__tab->used, htab)) {
143570af302Sopenharmony_ci			htab->__tab->used--;
144570af302Sopenharmony_ci			e->key = 0;
145570af302Sopenharmony_ci			*retval = 0;
146570af302Sopenharmony_ci			return 0;
147570af302Sopenharmony_ci		}
148570af302Sopenharmony_ci		e = lookup(item.key, hash, htab);
149570af302Sopenharmony_ci	}
150570af302Sopenharmony_ci	*retval = e;
151570af302Sopenharmony_ci	return 1;
152570af302Sopenharmony_ci}
153570af302Sopenharmony_ciweak_alias(__hsearch_r, hsearch_r);
154