diff --git a/options/glibc/generic/search.cpp b/options/glibc/generic/search.cpp new file mode 100644 index 0000000000..1faa7087f6 --- /dev/null +++ b/options/glibc/generic/search.cpp @@ -0,0 +1,142 @@ +#include +#include +#include + +// This code was lifted from musl! + +#define MINSIZE 8 +#define MAXSIZE ((size_t)-1/2 + 1) + +struct _ENTRY { + ENTRY *entries; + size_t mask; + size_t used; +}; + +static struct hsearch_data htab; + +static size_t keyhash(char *k) { + unsigned char *p = (unsigned char *)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; + size_t oldsize = htab->table->mask + 1; + ENTRY *e, *newe; + ENTRY *oldtab = htab->table->entries; + + if(nel > MAXSIZE) { + nel = MAXSIZE; + } + + for(newsize = MINSIZE; newsize < nel; newsize *= 2); + + htab->table->entries = (ENTRY *)calloc(newsize, sizeof *htab->table->entries); + + if(!htab->table->entries) { + htab->table->entries = oldtab; + return 0; + } + htab->table->mask = newsize - 1; + + if(!oldtab) + return 1; + + for(e = oldtab; e < oldtab + oldsize; e++) { + if(e->key) { + for(i = keyhash(e->key),j = 1; ; i += j++) { + newe = htab->table->entries + (i & htab->table->mask); + if(!newe->key) { + break; + } + } + *newe = *e; + } + } + free(oldtab); + return 1; +} + +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->table->entries + (i & htab->table->mask); + if(!e->key || strcmp(e->key, key) == 0) { + break; + } + } + return e; +} + +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->table->used > htab->table->mask - htab->table->mask / 4) { + if(!resize(2 * htab->table->used, htab)) { + htab->table->used--; + e->key = 0; + *retval = 0; + return 0; + } + e = lookup(item.key, hash, htab); + } + *retval = e; + return 1; +} + +int hcreate_r(size_t nel, struct hsearch_data *htab) { + int r; + + htab->table = (_ENTRY *)calloc(1, sizeof *htab->table); + if(!htab->table) { + return 0; + } + r = resize(nel, htab); + if(r == 0) { + free(htab->table); + htab->table = 0; + } + return r; +} + +void hdestroy_r(struct hsearch_data *htab) { + if(htab->table) { + free(htab->table->entries); + } + free(htab->table); + htab->table = 0; +} + +int hcreate(size_t nel) { + return hcreate_r(nel, &htab); +} + +void hdestroy(void) { + hdestroy_r(&htab); +} + +ENTRY *hsearch(ENTRY item, ACTION action) { + ENTRY *e; + + hsearch_r(item, action, &e, &htab); + return e; +} diff --git a/options/glibc/include/bits/glibc/glibc_search.h b/options/glibc/include/bits/glibc/glibc_search.h new file mode 100644 index 0000000000..cd73eefb1f --- /dev/null +++ b/options/glibc/include/bits/glibc/glibc_search.h @@ -0,0 +1,44 @@ +#ifndef _GLIBC_SEARCH_H +#define _GLIBC_SEARCH_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +typedef enum { + FIND, + ENTER +} ACTION; + +typedef struct entry { + char *key; + void *data; +} ENTRY; + +struct _ENTRY; + +struct hsearch_data { + struct _ENTRY *table; + unsigned int size; + unsigned int filled; +}; + +#ifndef __MLIBC_ABI_ONLY + +int hcreate(size_t nel); +void hdestroy(void); +ENTRY *hsearch(ENTRY item, ACTION action); + +int hsearch_r(ENTRY item, ACTION action, ENTRY **ret, struct hsearch_data *htab); +int hcreate_r(size_t num_elements, struct hsearch_data *htab); +void hdestroy_r(struct hsearch_data *htab); + +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* _GLIBC_SEARCH_H */ diff --git a/options/glibc/meson.build b/options/glibc/meson.build index 2201d5a7e1..ff48459f35 100644 --- a/options/glibc/meson.build +++ b/options/glibc/meson.build @@ -19,6 +19,7 @@ libc_sources += files( 'generic/glibc-assert.cpp', 'generic/malloc.cpp', 'generic/sys-io.cpp', + 'generic/search.cpp', ) if not no_headers @@ -84,6 +85,7 @@ if not no_headers 'include/bits/glibc/glibc_signal.h', 'include/bits/glibc/glibc_assert.h', 'include/bits/glibc/glibc_malloc.h', + 'include/bits/glibc/glibc_search.h', subdir: 'bits/glibc' ) endif diff --git a/options/posix/include/search.h b/options/posix/include/search.h index 02e1913dda..581c51712c 100644 --- a/options/posix/include/search.h +++ b/options/posix/include/search.h @@ -3,6 +3,7 @@ #define _SEARCH_H #include +#include #ifdef __cplusplus extern "C" { @@ -15,6 +16,10 @@ typedef enum { leaf } VISIT; +#if __MLIBC_GLIBC_OPTION +#include +#endif + #ifndef __MLIBC_ABI_ONLY void *tsearch(const void *, void **, int(*compar)(const void *, const void *));