about summary refs log tree commit diff
path: root/src/search
diff options
context:
space:
mode:
authorsin <sin@2f30.org>2014-03-25 16:37:51 +0000
committerRich Felker <dalias@aerifal.cx>2014-04-02 18:37:45 -0400
commitfe1ba7dbf187e34985896b40d469d84a7a4a98d0 (patch)
tree5a42ee06c3f98d6a150e3294699a32cd02ce0dd2 /src/search
parent5446303328adf4b4e36d9fba21848e6feb55fab4 (diff)
downloadmusl-fe1ba7dbf187e34985896b40d469d84a7a4a98d0.tar.gz
musl-fe1ba7dbf187e34985896b40d469d84a7a4a98d0.tar.xz
musl-fe1ba7dbf187e34985896b40d469d84a7a4a98d0.zip
implement hcreate_r, hdestroy_r and hsearch_r
the size and alignment of struct hsearch_data are matched to the glibc
definition for binary compatibility. the members of the structure do
not match, which should not be a problem as long as applications
correctly treat the structure as opaque.

unlike the glibc implementation, this version of hcreate_r does not
require the caller to zero-fill the structure before use.
Diffstat (limited to 'src/search')
-rw-r--r--src/search/hsearch.c103
1 files changed, 73 insertions, 30 deletions
diff --git a/src/search/hsearch.c b/src/search/hsearch.c
index 6fe5ced0..88edb263 100644
--- a/src/search/hsearch.c
+++ b/src/search/hsearch.c
@@ -1,6 +1,8 @@
+#define _GNU_SOURCE
 #include <stdlib.h>
 #include <string.h>
 #include <search.h>
+#include "libc.h"
 
 /*
 open addressing hash table with 2^n table size
@@ -19,9 +21,17 @@ struct elem {
 	size_t hash;
 };
 
-static size_t mask;
-static size_t used;
-static struct elem *tab;
+struct __tab {
+	struct elem *elems;
+	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 *);
 
 static size_t keyhash(char *k)
 {
@@ -33,29 +43,29 @@ static size_t keyhash(char *k)
 	return h;
 }
 
-static int resize(size_t nel)
+static int resize(size_t nel, struct hsearch_data *htab)
 {
 	size_t newsize;
 	size_t i, j;
 	struct elem *e, *newe;
-	struct elem *oldtab = tab;
-	struct elem *oldend = tab + mask + 1;
+	struct elem *oldtab = htab->__tab->elems;
+	struct elem *oldend = htab->__tab->elems + htab->__tab->mask + 1;
 
 	if (nel > MAXSIZE)
 		nel = MAXSIZE;
 	for (newsize = MINSIZE; newsize < nel; newsize *= 2);
-	tab = calloc(newsize, sizeof *tab);
-	if (!tab) {
-		tab = oldtab;
+	htab->__tab->elems = calloc(newsize, sizeof *htab->__tab->elems);
+	if (!htab->__tab->elems) {
+		htab->__tab->elems = oldtab;
 		return 0;
 	}
-	mask = newsize - 1;
+	htab->__tab->mask = newsize - 1;
 	if (!oldtab)
 		return 1;
 	for (e = oldtab; e < oldend; e++)
 		if (e->item.key) {
 			for (i=e->hash,j=1; ; i+=j++) {
-				newe = tab + (i & mask);
+				newe = htab->__tab->elems + (i & htab->__tab->mask);
 				if (!newe->item.key)
 					break;
 			}
@@ -67,27 +77,21 @@ static int resize(size_t nel)
 
 int hcreate(size_t nel)
 {
-	mask = 0;
-	used = 0;
-	tab = 0;
-	return resize(nel);
+	return __hcreate_r(nel, &htab);
 }
 
 void hdestroy(void)
 {
-	free(tab);
-	tab = 0;
-	mask = 0;
-	used = 0;
+	__hdestroy_r(&htab);
 }
 
-static struct elem *lookup(char *key, size_t hash)
+static struct elem *lookup(char *key, size_t hash, struct hsearch_data *htab)
 {
 	size_t i, j;
 	struct elem *e;
 
 	for (i=hash,j=1; ; i+=j++) {
-		e = tab + (i & mask);
+		e = htab->__tab->elems + (i & htab->__tab->mask);
 		if (!e->item.key ||
 		    (e->hash==hash && strcmp(e->item.key, key)==0))
 			break;
@@ -97,22 +101,61 @@ static struct elem *lookup(char *key, size_t hash)
 
 ENTRY *hsearch(ENTRY item, ACTION action)
 {
+	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)
+{
+	if (htab->__tab) free(htab->__tab->elems);
+	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)
+{
 	size_t hash = keyhash(item.key);
-	struct elem *e = lookup(item.key, hash);
+	struct elem *e = lookup(item.key, hash, htab);
 
-	if (e->item.key)
-		return &e->item;
-	if (action == FIND)
+	if (e->item.key) {
+		*retval = &e->item;
+		return 1;
+	}
+	if (action == FIND) {
+		*retval = 0;
 		return 0;
+	}
 	e->item = item;
 	e->hash = hash;
-	if (++used > mask - mask/4) {
-		if (!resize(2*used)) {
-			used--;
+	if (++htab->__tab->used > htab->__tab->mask - htab->__tab->mask/4) {
+		if (!resize(2*htab->__tab->used, htab)) {
+			htab->__tab->used--;
 			e->item.key = 0;
+			*retval = 0;
 			return 0;
 		}
-		e = lookup(item.key, hash);
+		e = lookup(item.key, hash, htab);
 	}
-	return &e->item;
+	*retval = &e->item;
+	return 1;
 }
+weak_alias(__hsearch_r, hsearch_r);