xref: /netbsd-src/external/bsd/mdocml/dist/compat_ohash.c (revision 37ef69ed1fae26d067c28449d9c2ba453665ba23)
170f041f9Sjoerg #include "config.h"
270f041f9Sjoerg 
3fec65c98Schristos #if HAVE_OHASH
470f041f9Sjoerg 
570f041f9Sjoerg int dummy;
670f041f9Sjoerg 
770f041f9Sjoerg #else
870f041f9Sjoerg 
9fec65c98Schristos /* $OpenBSD: ohash.c,v 1.1 2014/06/02 18:52:03 deraadt Exp $ */
1070f041f9Sjoerg 
1170f041f9Sjoerg /* Copyright (c) 1999, 2004 Marc Espie <espie@openbsd.org>
1270f041f9Sjoerg  *
1370f041f9Sjoerg  * Permission to use, copy, modify, and distribute this software for any
1470f041f9Sjoerg  * purpose with or without fee is hereby granted, provided that the above
1570f041f9Sjoerg  * copyright notice and this permission notice appear in all copies.
1670f041f9Sjoerg  *
1770f041f9Sjoerg  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1870f041f9Sjoerg  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1970f041f9Sjoerg  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
2070f041f9Sjoerg  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
2170f041f9Sjoerg  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
2270f041f9Sjoerg  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
2370f041f9Sjoerg  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
2470f041f9Sjoerg  */
2570f041f9Sjoerg 
26fec65c98Schristos #include <sys/types.h>
27fec65c98Schristos 
28fec65c98Schristos #include <stddef.h>
29fec65c98Schristos #include <stdint.h>
30fec65c98Schristos #include <stdlib.h>
31fec65c98Schristos #include <string.h>
32fec65c98Schristos #include <limits.h>
33f47368cfSchristos #include "main.h"
34fec65c98Schristos #include "compat_ohash.h"
35fec65c98Schristos 
36fec65c98Schristos struct _ohash_record {
37fec65c98Schristos 	uint32_t	hv;
38fec65c98Schristos 	const char	*p;
39fec65c98Schristos };
40fec65c98Schristos 
41fec65c98Schristos #define DELETED		((const char *)h)
42fec65c98Schristos #define NONE		(h->size)
43fec65c98Schristos 
44fec65c98Schristos /* Don't bother changing the hash table if the change is small enough.  */
45fec65c98Schristos #define MINSIZE		(1UL << 4)
46fec65c98Schristos #define MINDELETED	4
47fec65c98Schristos 
48fec65c98Schristos static void ohash_resize(struct ohash *);
49fec65c98Schristos 
50fec65c98Schristos 
5170f041f9Sjoerg /* This handles the common case of variable length keys, where the
5270f041f9Sjoerg  * key is stored at the end of the record.
5370f041f9Sjoerg  */
5470f041f9Sjoerg void *
ohash_create_entry(struct ohash_info * i,const char * start,const char ** end)5570f041f9Sjoerg ohash_create_entry(struct ohash_info *i, const char *start, const char **end)
5670f041f9Sjoerg {
5770f041f9Sjoerg 	char *p;
5870f041f9Sjoerg 
5970f041f9Sjoerg 	if (!*end)
6070f041f9Sjoerg 		*end = start + strlen(start);
6170f041f9Sjoerg 	p = (i->alloc)(i->key_offset + (*end - start) + 1, i->data);
6270f041f9Sjoerg 	if (p) {
6370f041f9Sjoerg 		memcpy(p+i->key_offset, start, *end-start);
6470f041f9Sjoerg 		p[i->key_offset + (*end - start)] = '\0';
6570f041f9Sjoerg 	}
6670f041f9Sjoerg 	return (void *)p;
6770f041f9Sjoerg }
6870f041f9Sjoerg 
6970f041f9Sjoerg /* hash_delete only frees the hash structure. Use hash_first/hash_next
7070f041f9Sjoerg  * to free entries as well.  */
7170f041f9Sjoerg void
ohash_delete(struct ohash * h)7270f041f9Sjoerg ohash_delete(struct ohash *h)
7370f041f9Sjoerg {
74fec65c98Schristos 	(h->info.free)(h->t, h->info.data);
7570f041f9Sjoerg #ifndef NDEBUG
7670f041f9Sjoerg 	h->t = NULL;
7770f041f9Sjoerg #endif
7870f041f9Sjoerg }
7970f041f9Sjoerg 
8070f041f9Sjoerg static void
ohash_resize(struct ohash * h)8170f041f9Sjoerg ohash_resize(struct ohash *h)
8270f041f9Sjoerg {
8370f041f9Sjoerg 	struct _ohash_record *n;
84fec65c98Schristos 	size_t ns;
85fec65c98Schristos 	unsigned int	j;
8670f041f9Sjoerg 	unsigned int	i, incr;
8770f041f9Sjoerg 
88fec65c98Schristos 	if (4 * h->deleted < h->total) {
89fec65c98Schristos 		if (h->size >= (UINT_MAX >> 1U))
90fec65c98Schristos 			ns = UINT_MAX;
91fec65c98Schristos 		else
92fec65c98Schristos 			ns = h->size << 1U;
93fec65c98Schristos 	} else if (3 * h->deleted > 2 * h->total)
94fec65c98Schristos 		ns = h->size >> 1U;
9570f041f9Sjoerg 	else
9670f041f9Sjoerg 		ns = h->size;
9770f041f9Sjoerg 	if (ns < MINSIZE)
9870f041f9Sjoerg 		ns = MINSIZE;
9970f041f9Sjoerg #ifdef STATS_HASH
10070f041f9Sjoerg 	STAT_HASH_EXPAND++;
10170f041f9Sjoerg 	STAT_HASH_SIZE += ns - h->size;
10270f041f9Sjoerg #endif
103fec65c98Schristos 
104fec65c98Schristos 	n = (h->info.calloc)(ns, sizeof(struct _ohash_record), h->info.data);
10570f041f9Sjoerg 	if (!n)
10670f041f9Sjoerg 		return;
10770f041f9Sjoerg 
10870f041f9Sjoerg 	for (j = 0; j < h->size; j++) {
10970f041f9Sjoerg 		if (h->t[j].p != NULL && h->t[j].p != DELETED) {
11070f041f9Sjoerg 			i = h->t[j].hv % ns;
11170f041f9Sjoerg 			incr = ((h->t[j].hv % (ns - 2)) & ~1) + 1;
11270f041f9Sjoerg 			while (n[i].p != NULL) {
11370f041f9Sjoerg 				i += incr;
11470f041f9Sjoerg 				if (i >= ns)
11570f041f9Sjoerg 					i -= ns;
11670f041f9Sjoerg 			}
11770f041f9Sjoerg 			n[i].hv = h->t[j].hv;
11870f041f9Sjoerg 			n[i].p = h->t[j].p;
11970f041f9Sjoerg 		}
12070f041f9Sjoerg 	}
121fec65c98Schristos 	(h->info.free)(h->t, h->info.data);
12270f041f9Sjoerg 	h->t = n;
12370f041f9Sjoerg 	h->size = ns;
12470f041f9Sjoerg 	h->total -= h->deleted;
12570f041f9Sjoerg 	h->deleted = 0;
12670f041f9Sjoerg }
12770f041f9Sjoerg 
12870f041f9Sjoerg void *
ohash_remove(struct ohash * h,unsigned int i)12970f041f9Sjoerg ohash_remove(struct ohash *h, unsigned int i)
13070f041f9Sjoerg {
131*37ef69edSchristos 	void		*result = __UNCONST(h->t[i].p);
13270f041f9Sjoerg 
13370f041f9Sjoerg 	if (result == NULL || result == DELETED)
13470f041f9Sjoerg 		return NULL;
13570f041f9Sjoerg 
13670f041f9Sjoerg #ifdef STATS_HASH
13770f041f9Sjoerg 	STAT_HASH_ENTRIES--;
13870f041f9Sjoerg #endif
13970f041f9Sjoerg 	h->t[i].p = DELETED;
14070f041f9Sjoerg 	h->deleted++;
14170f041f9Sjoerg 	if (h->deleted >= MINDELETED && 4 * h->deleted > h->total)
14270f041f9Sjoerg 		ohash_resize(h);
14370f041f9Sjoerg 	return result;
14470f041f9Sjoerg }
14570f041f9Sjoerg 
14670f041f9Sjoerg void *
ohash_find(struct ohash * h,unsigned int i)14770f041f9Sjoerg ohash_find(struct ohash *h, unsigned int i)
14870f041f9Sjoerg {
14970f041f9Sjoerg 	if (h->t[i].p == DELETED)
15070f041f9Sjoerg 		return NULL;
15170f041f9Sjoerg 	else
152*37ef69edSchristos 		return __UNCONST(h->t[i].p);
15370f041f9Sjoerg }
15470f041f9Sjoerg 
15570f041f9Sjoerg void *
ohash_insert(struct ohash * h,unsigned int i,void * p)15670f041f9Sjoerg ohash_insert(struct ohash *h, unsigned int i, void *p)
15770f041f9Sjoerg {
15870f041f9Sjoerg #ifdef STATS_HASH
15970f041f9Sjoerg 	STAT_HASH_ENTRIES++;
16070f041f9Sjoerg #endif
16170f041f9Sjoerg 	if (h->t[i].p == DELETED) {
16270f041f9Sjoerg 		h->deleted--;
16370f041f9Sjoerg 		h->t[i].p = p;
16470f041f9Sjoerg 	} else {
16570f041f9Sjoerg 		h->t[i].p = p;
16670f041f9Sjoerg 		/* Arbitrary resize boundary.  Tweak if not efficient enough.  */
16770f041f9Sjoerg 		if (++h->total * 4 > h->size * 3)
16870f041f9Sjoerg 			ohash_resize(h);
16970f041f9Sjoerg 	}
17070f041f9Sjoerg 	return p;
17170f041f9Sjoerg }
17270f041f9Sjoerg 
17370f041f9Sjoerg unsigned int
ohash_entries(struct ohash * h)17470f041f9Sjoerg ohash_entries(struct ohash *h)
17570f041f9Sjoerg {
17670f041f9Sjoerg 	return h->total - h->deleted;
17770f041f9Sjoerg }
17870f041f9Sjoerg 
17970f041f9Sjoerg void *
ohash_first(struct ohash * h,unsigned int * pos)18070f041f9Sjoerg ohash_first(struct ohash *h, unsigned int *pos)
18170f041f9Sjoerg {
18270f041f9Sjoerg 	*pos = 0;
18370f041f9Sjoerg 	return ohash_next(h, pos);
18470f041f9Sjoerg }
18570f041f9Sjoerg 
18670f041f9Sjoerg void *
ohash_next(struct ohash * h,unsigned int * pos)18770f041f9Sjoerg ohash_next(struct ohash *h, unsigned int *pos)
18870f041f9Sjoerg {
18970f041f9Sjoerg 	for (; *pos < h->size; (*pos)++)
19070f041f9Sjoerg 		if (h->t[*pos].p != DELETED && h->t[*pos].p != NULL)
191*37ef69edSchristos 			return __UNCONST(h->t[(*pos)++].p);
19270f041f9Sjoerg 	return NULL;
19370f041f9Sjoerg }
19470f041f9Sjoerg 
19570f041f9Sjoerg void
ohash_init(struct ohash * h,unsigned int size,struct ohash_info * info)19670f041f9Sjoerg ohash_init(struct ohash *h, unsigned int size, struct ohash_info *info)
19770f041f9Sjoerg {
19870f041f9Sjoerg 	h->size = 1UL << size;
19970f041f9Sjoerg 	if (h->size < MINSIZE)
20070f041f9Sjoerg 		h->size = MINSIZE;
20170f041f9Sjoerg #ifdef STATS_HASH
20270f041f9Sjoerg 	STAT_HASH_CREATION++;
20370f041f9Sjoerg 	STAT_HASH_SIZE += h->size;
20470f041f9Sjoerg #endif
20570f041f9Sjoerg 	/* Copy info so that caller may free it.  */
20670f041f9Sjoerg 	h->info.key_offset = info->key_offset;
207fec65c98Schristos 	h->info.calloc = info->calloc;
208fec65c98Schristos 	h->info.free = info->free;
20970f041f9Sjoerg 	h->info.alloc = info->alloc;
21070f041f9Sjoerg 	h->info.data = info->data;
211fec65c98Schristos 	h->t = (h->info.calloc)(h->size, sizeof(struct _ohash_record),
21270f041f9Sjoerg 		    h->info.data);
21370f041f9Sjoerg 	h->total = h->deleted = 0;
21470f041f9Sjoerg }
21570f041f9Sjoerg 
21670f041f9Sjoerg uint32_t
ohash_interval(const char * s,const char ** e)21770f041f9Sjoerg ohash_interval(const char *s, const char **e)
21870f041f9Sjoerg {
21970f041f9Sjoerg 	uint32_t k;
22070f041f9Sjoerg 
22170f041f9Sjoerg 	if (!*e)
22270f041f9Sjoerg 		*e = s + strlen(s);
22370f041f9Sjoerg 	if (s == *e)
22470f041f9Sjoerg 		k = 0;
22570f041f9Sjoerg 	else
22670f041f9Sjoerg 		k = *s++;
22770f041f9Sjoerg 	while (s != *e)
22870f041f9Sjoerg 		k =  ((k << 2) | (k >> 30)) ^ *s++;
22970f041f9Sjoerg 	return k;
23070f041f9Sjoerg }
23170f041f9Sjoerg 
23270f041f9Sjoerg unsigned int
ohash_lookup_interval(struct ohash * h,const char * start,const char * end,uint32_t hv)23370f041f9Sjoerg ohash_lookup_interval(struct ohash *h, const char *start, const char *end,
23470f041f9Sjoerg     uint32_t hv)
23570f041f9Sjoerg {
23670f041f9Sjoerg 	unsigned int	i, incr;
23770f041f9Sjoerg 	unsigned int	empty;
23870f041f9Sjoerg 
23970f041f9Sjoerg #ifdef STATS_HASH
24070f041f9Sjoerg 	STAT_HASH_LOOKUP++;
24170f041f9Sjoerg #endif
24270f041f9Sjoerg 	empty = NONE;
24370f041f9Sjoerg 	i = hv % h->size;
24470f041f9Sjoerg 	incr = ((hv % (h->size-2)) & ~1) + 1;
24570f041f9Sjoerg 	while (h->t[i].p != NULL) {
24670f041f9Sjoerg #ifdef STATS_HASH
24770f041f9Sjoerg 		STAT_HASH_LENGTH++;
24870f041f9Sjoerg #endif
24970f041f9Sjoerg 		if (h->t[i].p == DELETED) {
25070f041f9Sjoerg 			if (empty == NONE)
25170f041f9Sjoerg 				empty = i;
25270f041f9Sjoerg 		} else if (h->t[i].hv == hv &&
25370f041f9Sjoerg 		    strncmp(h->t[i].p+h->info.key_offset, start,
25470f041f9Sjoerg 			end - start) == 0 &&
25570f041f9Sjoerg 		    (h->t[i].p+h->info.key_offset)[end-start] == '\0') {
25670f041f9Sjoerg 			if (empty != NONE) {
25770f041f9Sjoerg 				h->t[empty].hv = hv;
25870f041f9Sjoerg 				h->t[empty].p = h->t[i].p;
25970f041f9Sjoerg 				h->t[i].p = DELETED;
26070f041f9Sjoerg 				return empty;
26170f041f9Sjoerg 			} else {
26270f041f9Sjoerg #ifdef STATS_HASH
26370f041f9Sjoerg 				STAT_HASH_POSITIVE++;
26470f041f9Sjoerg #endif
26570f041f9Sjoerg 				return i;
26670f041f9Sjoerg 			}
26770f041f9Sjoerg 		}
26870f041f9Sjoerg 		i += incr;
26970f041f9Sjoerg 		if (i >= h->size)
27070f041f9Sjoerg 			i -= h->size;
27170f041f9Sjoerg 	}
27270f041f9Sjoerg 
27370f041f9Sjoerg 	/* Found an empty position.  */
27470f041f9Sjoerg 	if (empty != NONE)
27570f041f9Sjoerg 		i = empty;
27670f041f9Sjoerg 	h->t[i].hv = hv;
27770f041f9Sjoerg 	return i;
27870f041f9Sjoerg }
27970f041f9Sjoerg 
28070f041f9Sjoerg unsigned int
ohash_lookup_memory(struct ohash * h,const char * k,size_t size,uint32_t hv)28170f041f9Sjoerg ohash_lookup_memory(struct ohash *h, const char *k, size_t size, uint32_t hv)
28270f041f9Sjoerg {
28370f041f9Sjoerg 	unsigned int	i, incr;
28470f041f9Sjoerg 	unsigned int	empty;
28570f041f9Sjoerg 
28670f041f9Sjoerg #ifdef STATS_HASH
28770f041f9Sjoerg 	STAT_HASH_LOOKUP++;
28870f041f9Sjoerg #endif
28970f041f9Sjoerg 	empty = NONE;
29070f041f9Sjoerg 	i = hv % h->size;
29170f041f9Sjoerg 	incr = ((hv % (h->size-2)) & ~1) + 1;
29270f041f9Sjoerg 	while (h->t[i].p != NULL) {
29370f041f9Sjoerg #ifdef STATS_HASH
29470f041f9Sjoerg 		STAT_HASH_LENGTH++;
29570f041f9Sjoerg #endif
29670f041f9Sjoerg 		if (h->t[i].p == DELETED) {
29770f041f9Sjoerg 			if (empty == NONE)
29870f041f9Sjoerg 				empty = i;
29970f041f9Sjoerg 		} else if (h->t[i].hv == hv &&
30070f041f9Sjoerg 		    memcmp(h->t[i].p+h->info.key_offset, k, size) == 0) {
30170f041f9Sjoerg 			if (empty != NONE) {
30270f041f9Sjoerg 				h->t[empty].hv = hv;
30370f041f9Sjoerg 				h->t[empty].p = h->t[i].p;
30470f041f9Sjoerg 				h->t[i].p = DELETED;
30570f041f9Sjoerg 				return empty;
30670f041f9Sjoerg 			} else {
30770f041f9Sjoerg #ifdef STATS_HASH
30870f041f9Sjoerg 				STAT_HASH_POSITIVE++;
30970f041f9Sjoerg #endif
31070f041f9Sjoerg 			}	return i;
31170f041f9Sjoerg 		}
31270f041f9Sjoerg 		i += incr;
31370f041f9Sjoerg 		if (i >= h->size)
31470f041f9Sjoerg 			i -= h->size;
31570f041f9Sjoerg 	}
31670f041f9Sjoerg 
31770f041f9Sjoerg 	/* Found an empty position.  */
31870f041f9Sjoerg 	if (empty != NONE)
31970f041f9Sjoerg 		i = empty;
32070f041f9Sjoerg 	h->t[i].hv = hv;
32170f041f9Sjoerg 	return i;
32270f041f9Sjoerg }
32370f041f9Sjoerg 
32470f041f9Sjoerg unsigned int
ohash_qlookup(struct ohash * h,const char * s)32570f041f9Sjoerg ohash_qlookup(struct ohash *h, const char *s)
32670f041f9Sjoerg {
32770f041f9Sjoerg 	const char *e = NULL;
32870f041f9Sjoerg 	return ohash_qlookupi(h, s, &e);
32970f041f9Sjoerg }
33070f041f9Sjoerg 
33170f041f9Sjoerg unsigned int
ohash_qlookupi(struct ohash * h,const char * s,const char ** e)33270f041f9Sjoerg ohash_qlookupi(struct ohash *h, const char *s, const char **e)
33370f041f9Sjoerg {
33470f041f9Sjoerg 	uint32_t hv;
33570f041f9Sjoerg 
33670f041f9Sjoerg 	hv = ohash_interval(s, e);
33770f041f9Sjoerg 	return ohash_lookup_interval(h, s, *e, hv);
33870f041f9Sjoerg }
33970f041f9Sjoerg 
34070f041f9Sjoerg #endif /*!HAVE_OHASH*/
341