|  | #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); |