Index: fusl/src/search/hsearch.c |
diff --git a/fusl/src/search/hsearch.c b/fusl/src/search/hsearch.c |
new file mode 100644 |
index 0000000000000000000000000000000000000000..5c8965114bdcb320562ae4661c05ac8bd9374a66 |
--- /dev/null |
+++ b/fusl/src/search/hsearch.c |
@@ -0,0 +1,154 @@ |
+#define _GNU_SOURCE |
+#include <stdlib.h> |
+#include <string.h> |
+#include <search.h> |
+#include "libc.h" |
+ |
+/* |
+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) |
+ |
+struct __tab { |
+ ENTRY *entries; |
+ 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) |
+{ |
+ unsigned char *p = (void *)k; |
+ size_t h = 0; |
+ |
+ while (*p) |
+ h = 31*h + *p++; |
+ return h; |
+} |
+ |
+static int resize(size_t nel, struct hsearch_data *htab) |
+{ |
+ size_t newsize; |
+ size_t i, j; |
+ ENTRY *e, *newe; |
+ ENTRY *oldtab = htab->__tab->entries; |
+ ENTRY *oldend = htab->__tab->entries + htab->__tab->mask + 1; |
+ |
+ if (nel > MAXSIZE) |
+ nel = MAXSIZE; |
+ for (newsize = MINSIZE; newsize < nel; newsize *= 2); |
+ htab->__tab->entries = calloc(newsize, sizeof *htab->__tab->entries); |
+ if (!htab->__tab->entries) { |
+ htab->__tab->entries = oldtab; |
+ return 0; |
+ } |
+ htab->__tab->mask = newsize - 1; |
+ if (!oldtab) |
+ return 1; |
+ for (e = oldtab; e < oldend; e++) |
+ if (e->key) { |
+ for (i=keyhash(e->key),j=1; ; i+=j++) { |
+ newe = htab->__tab->entries + (i & htab->__tab->mask); |
+ if (!newe->key) |
+ break; |
+ } |
+ *newe = *e; |
+ } |
+ free(oldtab); |
+ return 1; |
+} |
+ |
+int hcreate(size_t nel) |
+{ |
+ return __hcreate_r(nel, &htab); |
+} |
+ |
+void hdestroy(void) |
+{ |
+ __hdestroy_r(&htab); |
+} |
+ |
+static ENTRY *lookup(char *key, size_t hash, struct hsearch_data *htab) |
+{ |
+ size_t i, j; |
+ ENTRY *e; |
+ |
+ for (i=hash,j=1; ; i+=j++) { |
+ e = htab->__tab->entries + (i & htab->__tab->mask); |
+ if (!e->key || strcmp(e->key, key) == 0) |
+ break; |
+ } |
+ return e; |
+} |
+ |
+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->entries); |
+ 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); |
+ ENTRY *e = lookup(item.key, hash, htab); |
+ |
+ if (e->key) { |
+ *retval = e; |
+ return 1; |
+ } |
+ if (action == FIND) { |
+ *retval = 0; |
+ return 0; |
+ } |
+ *e = item; |
+ if (++htab->__tab->used > htab->__tab->mask - htab->__tab->mask/4) { |
+ if (!resize(2*htab->__tab->used, htab)) { |
+ htab->__tab->used--; |
+ e->key = 0; |
+ *retval = 0; |
+ return 0; |
+ } |
+ e = lookup(item.key, hash, htab); |
+ } |
+ *retval = e; |
+ return 1; |
+} |
+weak_alias(__hsearch_r, hsearch_r); |