Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

options/glibc: Add the hsearch_r family of functions #941

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 142 additions & 0 deletions options/glibc/generic/search.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
#include <bits/glibc/glibc_search.h>
#include <stdlib.h>
#include <string.h>

// 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;
}
44 changes: 44 additions & 0 deletions options/glibc/include/bits/glibc/glibc_search.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#ifndef _GLIBC_SEARCH_H
#define _GLIBC_SEARCH_H

#ifdef __cplusplus
extern "C" {
#endif

#include <bits/size_t.h>

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 */
2 changes: 2 additions & 0 deletions options/glibc/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions options/posix/include/search.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#define _SEARCH_H

#include <stddef.h>
#include <mlibc-config.h>

#ifdef __cplusplus
extern "C" {
Expand All @@ -15,6 +16,10 @@ typedef enum {
leaf
} VISIT;

#if __MLIBC_GLIBC_OPTION
#include <bits/glibc/glibc_search.h>
#endif

#ifndef __MLIBC_ABI_ONLY

void *tsearch(const void *, void **, int(*compar)(const void *, const void *));
Expand Down
Loading