hsearch.c 3.0 KB
Newer Older
1
#define _GNU_SOURCE
2 3 4
#include <stdlib.h>
#include <string.h>
#include <search.h>
5
#include "libc.h"
6 7 8 9 10 11 12 13 14 15 16 17 18

/*
open addressing hash table with 2^n table size
quadratic probing is used in case of hash collision
tab indices and hash are size_t
after resize fails with ENOMEM the state of tab is still usable

with the posix api items cannot be iterated and length cannot be queried
*/

#define MINSIZE 8
#define MAXSIZE ((size_t)-1/2 + 1)

19
struct __tab {
S
sin 已提交
20
	ENTRY *entries;
21 22 23 24 25 26 27 28 29
	size_t mask;
	size_t used;
};

static struct hsearch_data htab;

int __hcreate_r(size_t, struct hsearch_data *);
void __hdestroy_r(struct hsearch_data *);
int __hsearch_r(ENTRY, ACTION, ENTRY **, struct hsearch_data *);
30 31 32 33 34 35 36 37 38 39 40

static size_t keyhash(char *k)
{
	unsigned char *p = (void *)k;
	size_t h = 0;

	while (*p)
		h = 31*h + *p++;
	return h;
}

41
static int resize(size_t nel, struct hsearch_data *htab)
42 43 44
{
	size_t newsize;
	size_t i, j;
S
sin 已提交
45 46 47
	ENTRY *e, *newe;
	ENTRY *oldtab = htab->__tab->entries;
	ENTRY *oldend = htab->__tab->entries + htab->__tab->mask + 1;
48 49 50 51

	if (nel > MAXSIZE)
		nel = MAXSIZE;
	for (newsize = MINSIZE; newsize < nel; newsize *= 2);
S
sin 已提交
52 53 54
	htab->__tab->entries = calloc(newsize, sizeof *htab->__tab->entries);
	if (!htab->__tab->entries) {
		htab->__tab->entries = oldtab;
55 56
		return 0;
	}
57
	htab->__tab->mask = newsize - 1;
58 59 60
	if (!oldtab)
		return 1;
	for (e = oldtab; e < oldend; e++)
S
sin 已提交
61 62 63 64
		if (e->key) {
			for (i=keyhash(e->key),j=1; ; i+=j++) {
				newe = htab->__tab->entries + (i & htab->__tab->mask);
				if (!newe->key)
65 66 67 68 69 70 71 72 73 74
					break;
			}
			*newe = *e;
		}
	free(oldtab);
	return 1;
}

int hcreate(size_t nel)
{
75
	return __hcreate_r(nel, &htab);
76 77 78 79
}

void hdestroy(void)
{
80
	__hdestroy_r(&htab);
81 82
}

S
sin 已提交
83
static ENTRY *lookup(char *key, size_t hash, struct hsearch_data *htab)
84 85
{
	size_t i, j;
S
sin 已提交
86
	ENTRY *e;
87 88

	for (i=hash,j=1; ; i+=j++) {
S
sin 已提交
89 90
		e = htab->__tab->entries + (i & htab->__tab->mask);
		if (!e->key || strcmp(e->key, key) == 0)
91 92 93 94 95 96
			break;
	}
	return e;
}

ENTRY *hsearch(ENTRY item, ACTION action)
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
{
	ENTRY *e;

	__hsearch_r(item, action, &e, &htab);
	return e;
}

int __hcreate_r(size_t nel, struct hsearch_data *htab)
{
	int r;

	htab->__tab = calloc(1, sizeof *htab->__tab);
	if (!htab->__tab)
		return 0;
	r = resize(nel, htab);
	if (r == 0) {
		free(htab->__tab);
		htab->__tab = 0;
	}
	return r;
}
weak_alias(__hcreate_r, hcreate_r);

void __hdestroy_r(struct hsearch_data *htab)
{
S
sin 已提交
122
	if (htab->__tab) free(htab->__tab->entries);
123 124 125 126 127 128
	free(htab->__tab);
	htab->__tab = 0;
}
weak_alias(__hdestroy_r, hdestroy_r);

int __hsearch_r(ENTRY item, ACTION action, ENTRY **retval, struct hsearch_data *htab)
129 130
{
	size_t hash = keyhash(item.key);
S
sin 已提交
131
	ENTRY *e = lookup(item.key, hash, htab);
132

S
sin 已提交
133 134
	if (e->key) {
		*retval = e;
135 136 137 138
		return 1;
	}
	if (action == FIND) {
		*retval = 0;
139
		return 0;
140
	}
S
sin 已提交
141
	*e = item;
142 143 144
	if (++htab->__tab->used > htab->__tab->mask - htab->__tab->mask/4) {
		if (!resize(2*htab->__tab->used, htab)) {
			htab->__tab->used--;
S
sin 已提交
145
			e->key = 0;
146
			*retval = 0;
147 148
			return 0;
		}
149
		e = lookup(item.key, hash, htab);
150
	}
S
sin 已提交
151
	*retval = e;
152
	return 1;
153
}
154
weak_alias(__hsearch_r, hsearch_r);