diff options
Diffstat (limited to 'src/htab.c')
-rw-r--r-- | src/htab.c | 150 |
1 files changed, 150 insertions, 0 deletions
diff --git a/src/htab.c b/src/htab.c new file mode 100644 index 0000000..dde8430 --- /dev/null +++ b/src/htab.c @@ -0,0 +1,150 @@ +/* since hsearch_r does not support deletions dirctly, this is an abstraction on + * top of it with support for deletions, for this to work properly we have to + * strdup the key, this is not a big issue for 100k drv_path strings, it's + * either this or pulling in a external library + */ + +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "htab.h" +#include "util.h" + +#define MAX_NIX_PKG_COUNT 200000 + +static int htab_keys_insert(struct htab *htab, char *key); + +static int htab_keys_insert(struct htab *htab, char *key) +{ + size_t newsize; + void *ret; + + if (htab->key_filled < htab->keys_size) { + htab->keys[htab->key_filled++] = key; + return 0; + } + + newsize = htab->keys_size == 0 ? 1 : htab->keys_size * 2; + ret = realloc(htab->keys, newsize * sizeof(*htab->keys)); + if (ret == NULL) { + print_err("%s", strerror(errno)); + return -errno; + } + + htab->keys = ret; + htab->keys_size = newsize; + htab->keys[htab->key_filled++] = key; + + return 0; +} + +int htab_search(struct htab *htab, char *key, ENTRY **ep) +{ + ENTRY e; + int ret; + + e.key = key; + e.data = NULL; + ret = hsearch_r(e, FIND, ep, htab->table); + if (ret == 0) { + if (errno != ESRCH) { + print_err("%s", strerror(errno)); + return -errno; + } + return ESRCH; + } + + if ((*ep)->data == NULL) { + return ESRCH; + } else { + return 0; + } +} + +int htab_enter(struct htab *htab, const char *key, void *data) +{ + ENTRY e, *ep; + int ret; + + e.key = strdup(key); + if (e.key == NULL) { + print_err("%s", strerror(errno)); + return -errno; + } + + e.data = data; + ret = hsearch_r(e, ENTER, &ep, htab->table); + if (ret == 0) { + print_err("%s", strerror(errno)); + return -errno; + } + ep->data = NULL; + + if (ep->key != e.key) { + free(e.key); + } else { + ret = htab_keys_insert(htab, e.key); + if (ret < 0) + free(e.key); + } + + return ret; +} + +int htab_delete(struct htab *htab, const char *key) +{ + return htab_enter(htab, key, NULL); +} + +int htab_init(struct htab **htab) +{ + int ret; + struct htab *h; + + h = malloc(sizeof(*h)); + if (h == NULL) { + print_err("%s", strerror(errno)); + return -errno; + } + + h->table = calloc(1, sizeof(*h->table)); + if (h == NULL) { + print_err("%s", strerror(errno)); + ret = -errno; + goto out_free_h; + } + + ret = hcreate_r(MAX_NIX_PKG_COUNT, h->table); + if (ret == 0) { + print_err("%s", strerror(errno)); + ret = -errno; + goto out_free_table; + } + ret = 0; + + h->keys = NULL; + h->keys_size = 0; + h->key_filled = 0; + +out_free_table: + if (ret < 0) + free(h->table); +out_free_h: + if (ret < 0) + free(h); + else + *htab = h; + + return ret; +} + +void htab_free(struct htab *htab) +{ + for (size_t i = 0; i < htab->key_filled; i++) + free(htab->keys[i]); + + free(htab->keys); + free(htab->table); + free(htab); +} |