| /* Interface to hashtable implementations. |
| Copyright (C) 2006-2023 Free Software Foundation, Inc. |
| |
| This file is part of libctf. |
| |
| libctf is free software; you can redistribute it and/or modify it under |
| the terms of the GNU General Public License as published by the Free |
| Software Foundation; either version 3, or (at your option) any later |
| version. |
| |
| This program is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program; see the file COPYING. If not see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include <ctf-impl.h> |
| #include <string.h> |
| #include "libiberty.h" |
| #include "hashtab.h" |
| |
| /* We have three hashtable implementations: |
| |
| - ctf_hash_* is an interface to a fixed-size hash from const char * -> |
| ctf_id_t with number of elements specified at creation time, that should |
| support addition of items but need not support removal. |
| |
| - ctf_dynhash_* is an interface to a dynamically-expanding hash with |
| unknown size that should support addition of large numbers of items, and |
| removal as well, and is used only at type-insertion time and during |
| linking. |
| |
| - ctf_dynset_* is an interface to a dynamically-expanding hash that contains |
| only keys: no values. |
| |
| These can be implemented by the same underlying hashmap if you wish. */ |
| |
| /* The helem is used for general key/value mappings in both the ctf_hash and |
| ctf_dynhash: the owner may not have space allocated for it, and will be |
| garbage (not NULL!) in that case. */ |
| |
| typedef struct ctf_helem |
| { |
| void *key; /* Either a pointer, or a coerced ctf_id_t. */ |
| void *value; /* The value (possibly a coerced int). */ |
| ctf_dynhash_t *owner; /* The hash that owns us. */ |
| } ctf_helem_t; |
| |
| /* Equally, the key_free and value_free may not exist. */ |
| |
| struct ctf_dynhash |
| { |
| struct htab *htab; |
| ctf_hash_free_fun key_free; |
| ctf_hash_free_fun value_free; |
| }; |
| |
| /* Hash and eq functions for the dynhash and hash. */ |
| |
| unsigned int |
| ctf_hash_integer (const void *ptr) |
| { |
| ctf_helem_t *hep = (ctf_helem_t *) ptr; |
| |
| return htab_hash_pointer (hep->key); |
| } |
| |
| int |
| ctf_hash_eq_integer (const void *a, const void *b) |
| { |
| ctf_helem_t *hep_a = (ctf_helem_t *) a; |
| ctf_helem_t *hep_b = (ctf_helem_t *) b; |
| |
| return htab_eq_pointer (hep_a->key, hep_b->key); |
| } |
| |
| unsigned int |
| ctf_hash_string (const void *ptr) |
| { |
| ctf_helem_t *hep = (ctf_helem_t *) ptr; |
| |
| return htab_hash_string (hep->key); |
| } |
| |
| int |
| ctf_hash_eq_string (const void *a, const void *b) |
| { |
| ctf_helem_t *hep_a = (ctf_helem_t *) a; |
| ctf_helem_t *hep_b = (ctf_helem_t *) b; |
| |
| return !strcmp((const char *) hep_a->key, (const char *) hep_b->key); |
| } |
| |
| /* Hash a type_key. */ |
| unsigned int |
| ctf_hash_type_key (const void *ptr) |
| { |
| ctf_helem_t *hep = (ctf_helem_t *) ptr; |
| ctf_link_type_key_t *k = (ctf_link_type_key_t *) hep->key; |
| |
| return htab_hash_pointer (k->cltk_fp) + 59 |
| * htab_hash_pointer ((void *) (uintptr_t) k->cltk_idx); |
| } |
| |
| int |
| ctf_hash_eq_type_key (const void *a, const void *b) |
| { |
| ctf_helem_t *hep_a = (ctf_helem_t *) a; |
| ctf_helem_t *hep_b = (ctf_helem_t *) b; |
| ctf_link_type_key_t *key_a = (ctf_link_type_key_t *) hep_a->key; |
| ctf_link_type_key_t *key_b = (ctf_link_type_key_t *) hep_b->key; |
| |
| return (key_a->cltk_fp == key_b->cltk_fp) |
| && (key_a->cltk_idx == key_b->cltk_idx); |
| } |
| |
| /* Hash a type_id_key. */ |
| unsigned int |
| ctf_hash_type_id_key (const void *ptr) |
| { |
| ctf_helem_t *hep = (ctf_helem_t *) ptr; |
| ctf_type_id_key_t *k = (ctf_type_id_key_t *) hep->key; |
| |
| return htab_hash_pointer ((void *) (uintptr_t) k->ctii_input_num) |
| + 59 * htab_hash_pointer ((void *) (uintptr_t) k->ctii_type); |
| } |
| |
| int |
| ctf_hash_eq_type_id_key (const void *a, const void *b) |
| { |
| ctf_helem_t *hep_a = (ctf_helem_t *) a; |
| ctf_helem_t *hep_b = (ctf_helem_t *) b; |
| ctf_type_id_key_t *key_a = (ctf_type_id_key_t *) hep_a->key; |
| ctf_type_id_key_t *key_b = (ctf_type_id_key_t *) hep_b->key; |
| |
| return (key_a->ctii_input_num == key_b->ctii_input_num) |
| && (key_a->ctii_type == key_b->ctii_type); |
| } |
| |
| /* The dynhash, used for hashes whose size is not known at creation time. */ |
| |
| /* Free a single ctf_helem with arbitrary key/value functions. */ |
| |
| static void |
| ctf_dynhash_item_free (void *item) |
| { |
| ctf_helem_t *helem = item; |
| |
| if (helem->owner->key_free && helem->key) |
| helem->owner->key_free (helem->key); |
| if (helem->owner->value_free && helem->value) |
| helem->owner->value_free (helem->value); |
| free (helem); |
| } |
| |
| ctf_dynhash_t * |
| ctf_dynhash_create (ctf_hash_fun hash_fun, ctf_hash_eq_fun eq_fun, |
| ctf_hash_free_fun key_free, ctf_hash_free_fun value_free) |
| { |
| ctf_dynhash_t *dynhash; |
| htab_del del = ctf_dynhash_item_free; |
| |
| if (key_free || value_free) |
| dynhash = malloc (sizeof (ctf_dynhash_t)); |
| else |
| dynhash = malloc (offsetof (ctf_dynhash_t, key_free)); |
| if (!dynhash) |
| return NULL; |
| |
| if (key_free == NULL && value_free == NULL) |
| del = free; |
| |
| /* 7 is arbitrary and untested for now. */ |
| if ((dynhash->htab = htab_create_alloc (7, (htab_hash) hash_fun, eq_fun, |
| del, xcalloc, free)) == NULL) |
| { |
| free (dynhash); |
| return NULL; |
| } |
| |
| if (key_free || value_free) |
| { |
| dynhash->key_free = key_free; |
| dynhash->value_free = value_free; |
| } |
| |
| return dynhash; |
| } |
| |
| static ctf_helem_t ** |
| ctf_hashtab_lookup (struct htab *htab, const void *key, enum insert_option insert) |
| { |
| ctf_helem_t tmp = { .key = (void *) key }; |
| return (ctf_helem_t **) htab_find_slot (htab, &tmp, insert); |
| } |
| |
| static ctf_helem_t * |
| ctf_hashtab_insert (struct htab *htab, void *key, void *value, |
| ctf_hash_free_fun key_free, |
| ctf_hash_free_fun value_free) |
| { |
| ctf_helem_t **slot; |
| |
| slot = ctf_hashtab_lookup (htab, key, INSERT); |
| |
| if (!slot) |
| { |
| errno = ENOMEM; |
| return NULL; |
| } |
| |
| if (!*slot) |
| { |
| /* Only spend space on the owner if we're going to use it: if there is a |
| key or value freeing function. */ |
| if (key_free || value_free) |
| *slot = malloc (sizeof (ctf_helem_t)); |
| else |
| *slot = malloc (offsetof (ctf_helem_t, owner)); |
| if (!*slot) |
| return NULL; |
| (*slot)->key = key; |
| } |
| else |
| { |
| if (key_free) |
| key_free (key); |
| if (value_free) |
| value_free ((*slot)->value); |
| } |
| (*slot)->value = value; |
| return *slot; |
| } |
| |
| int |
| ctf_dynhash_insert (ctf_dynhash_t *hp, void *key, void *value) |
| { |
| ctf_helem_t *slot; |
| ctf_hash_free_fun key_free = NULL, value_free = NULL; |
| |
| if (hp->htab->del_f == ctf_dynhash_item_free) |
| { |
| key_free = hp->key_free; |
| value_free = hp->value_free; |
| } |
| slot = ctf_hashtab_insert (hp->htab, key, value, |
| key_free, value_free); |
| |
| if (!slot) |
| return errno; |
| |
| /* Keep track of the owner, so that the del function can get at the key_free |
| and value_free functions. Only do this if one of those functions is set: |
| if not, the owner is not even present in the helem. */ |
| |
| if (key_free || value_free) |
| slot->owner = hp; |
| |
| return 0; |
| } |
| |
| void |
| ctf_dynhash_remove (ctf_dynhash_t *hp, const void *key) |
| { |
| ctf_helem_t hep = { (void *) key, NULL, NULL }; |
| htab_remove_elt (hp->htab, &hep); |
| } |
| |
| void |
| ctf_dynhash_empty (ctf_dynhash_t *hp) |
| { |
| htab_empty (hp->htab); |
| } |
| |
| size_t |
| ctf_dynhash_elements (ctf_dynhash_t *hp) |
| { |
| return htab_elements (hp->htab); |
| } |
| |
| void * |
| ctf_dynhash_lookup (ctf_dynhash_t *hp, const void *key) |
| { |
| ctf_helem_t **slot; |
| |
| slot = ctf_hashtab_lookup (hp->htab, key, NO_INSERT); |
| |
| if (slot) |
| return (*slot)->value; |
| |
| return NULL; |
| } |
| |
| /* TRUE/FALSE return. */ |
| int |
| ctf_dynhash_lookup_kv (ctf_dynhash_t *hp, const void *key, |
| const void **orig_key, void **value) |
| { |
| ctf_helem_t **slot; |
| |
| slot = ctf_hashtab_lookup (hp->htab, key, NO_INSERT); |
| |
| if (slot) |
| { |
| if (orig_key) |
| *orig_key = (*slot)->key; |
| if (value) |
| *value = (*slot)->value; |
| return 1; |
| } |
| return 0; |
| } |
| |
| typedef struct ctf_traverse_cb_arg |
| { |
| ctf_hash_iter_f fun; |
| void *arg; |
| } ctf_traverse_cb_arg_t; |
| |
| static int |
| ctf_hashtab_traverse (void **slot, void *arg_) |
| { |
| ctf_helem_t *helem = *((ctf_helem_t **) slot); |
| ctf_traverse_cb_arg_t *arg = (ctf_traverse_cb_arg_t *) arg_; |
| |
| arg->fun (helem->key, helem->value, arg->arg); |
| return 1; |
| } |
| |
| void |
| ctf_dynhash_iter (ctf_dynhash_t *hp, ctf_hash_iter_f fun, void *arg_) |
| { |
| ctf_traverse_cb_arg_t arg = { fun, arg_ }; |
| htab_traverse (hp->htab, ctf_hashtab_traverse, &arg); |
| } |
| |
| typedef struct ctf_traverse_find_cb_arg |
| { |
| ctf_hash_iter_find_f fun; |
| void *arg; |
| void *found_key; |
| } ctf_traverse_find_cb_arg_t; |
| |
| static int |
| ctf_hashtab_traverse_find (void **slot, void *arg_) |
| { |
| ctf_helem_t *helem = *((ctf_helem_t **) slot); |
| ctf_traverse_find_cb_arg_t *arg = (ctf_traverse_find_cb_arg_t *) arg_; |
| |
| if (arg->fun (helem->key, helem->value, arg->arg)) |
| { |
| arg->found_key = helem->key; |
| return 0; |
| } |
| return 1; |
| } |
| |
| void * |
| ctf_dynhash_iter_find (ctf_dynhash_t *hp, ctf_hash_iter_find_f fun, void *arg_) |
| { |
| ctf_traverse_find_cb_arg_t arg = { fun, arg_, NULL }; |
| htab_traverse (hp->htab, ctf_hashtab_traverse_find, &arg); |
| return arg.found_key; |
| } |
| |
| typedef struct ctf_traverse_remove_cb_arg |
| { |
| struct htab *htab; |
| ctf_hash_iter_remove_f fun; |
| void *arg; |
| } ctf_traverse_remove_cb_arg_t; |
| |
| static int |
| ctf_hashtab_traverse_remove (void **slot, void *arg_) |
| { |
| ctf_helem_t *helem = *((ctf_helem_t **) slot); |
| ctf_traverse_remove_cb_arg_t *arg = (ctf_traverse_remove_cb_arg_t *) arg_; |
| |
| if (arg->fun (helem->key, helem->value, arg->arg)) |
| htab_clear_slot (arg->htab, slot); |
| return 1; |
| } |
| |
| void |
| ctf_dynhash_iter_remove (ctf_dynhash_t *hp, ctf_hash_iter_remove_f fun, |
| void *arg_) |
| { |
| ctf_traverse_remove_cb_arg_t arg = { hp->htab, fun, arg_ }; |
| htab_traverse (hp->htab, ctf_hashtab_traverse_remove, &arg); |
| } |
| |
| /* Traverse a dynhash in arbitrary order, in _next iterator form. |
| |
| Mutating the dynhash while iterating is not supported (just as it isn't for |
| htab_traverse). |
| |
| Note: unusually, this returns zero on success and a *positive* value on |
| error, because it does not take an fp, taking an error pointer would be |
| incredibly clunky, and nearly all error-handling ends up stuffing the result |
| of this into some sort of errno or ctf_errno, which is invariably |
| positive. So doing this simplifies essentially all callers. */ |
| int |
| ctf_dynhash_next (ctf_dynhash_t *h, ctf_next_t **it, void **key, void **value) |
| { |
| ctf_next_t *i = *it; |
| ctf_helem_t *slot; |
| |
| if (!i) |
| { |
| size_t size = htab_size (h->htab); |
| |
| /* If the table has too many entries to fit in an ssize_t, just give up. |
| This might be spurious, but if any type-related hashtable has ever been |
| nearly as large as that then something very odd is going on. */ |
| if (((ssize_t) size) < 0) |
| return EDOM; |
| |
| if ((i = ctf_next_create ()) == NULL) |
| return ENOMEM; |
| |
| i->u.ctn_hash_slot = h->htab->entries; |
| i->cu.ctn_h = h; |
| i->ctn_n = 0; |
| i->ctn_size = (ssize_t) size; |
| i->ctn_iter_fun = (void (*) (void)) ctf_dynhash_next; |
| *it = i; |
| } |
| |
| if ((void (*) (void)) ctf_dynhash_next != i->ctn_iter_fun) |
| return ECTF_NEXT_WRONGFUN; |
| |
| if (h != i->cu.ctn_h) |
| return ECTF_NEXT_WRONGFP; |
| |
| if ((ssize_t) i->ctn_n == i->ctn_size) |
| goto hash_end; |
| |
| while ((ssize_t) i->ctn_n < i->ctn_size |
| && (*i->u.ctn_hash_slot == HTAB_EMPTY_ENTRY |
| || *i->u.ctn_hash_slot == HTAB_DELETED_ENTRY)) |
| { |
| i->u.ctn_hash_slot++; |
| i->ctn_n++; |
| } |
| |
| if ((ssize_t) i->ctn_n == i->ctn_size) |
| goto hash_end; |
| |
| slot = *i->u.ctn_hash_slot; |
| |
| if (key) |
| *key = slot->key; |
| if (value) |
| *value = slot->value; |
| |
| i->u.ctn_hash_slot++; |
| i->ctn_n++; |
| |
| return 0; |
| |
| hash_end: |
| ctf_next_destroy (i); |
| *it = NULL; |
| return ECTF_NEXT_END; |
| } |
| |
| int |
| ctf_dynhash_sort_by_name (const ctf_next_hkv_t *one, const ctf_next_hkv_t *two, |
| void *unused _libctf_unused_) |
| { |
| return strcmp ((char *) one->hkv_key, (char *) two->hkv_key); |
| } |
| |
| /* Traverse a sorted dynhash, in _next iterator form. |
| |
| See ctf_dynhash_next for notes on error returns, etc. |
| |
| Sort keys before iterating over them using the SORT_FUN and SORT_ARG. |
| |
| If SORT_FUN is null, thunks to ctf_dynhash_next. */ |
| int |
| ctf_dynhash_next_sorted (ctf_dynhash_t *h, ctf_next_t **it, void **key, |
| void **value, ctf_hash_sort_f sort_fun, void *sort_arg) |
| { |
| ctf_next_t *i = *it; |
| |
| if (sort_fun == NULL) |
| return ctf_dynhash_next (h, it, key, value); |
| |
| if (!i) |
| { |
| size_t els = ctf_dynhash_elements (h); |
| ctf_next_t *accum_i = NULL; |
| void *key, *value; |
| int err; |
| ctf_next_hkv_t *walk; |
| |
| if (((ssize_t) els) < 0) |
| return EDOM; |
| |
| if ((i = ctf_next_create ()) == NULL) |
| return ENOMEM; |
| |
| if ((i->u.ctn_sorted_hkv = calloc (els, sizeof (ctf_next_hkv_t))) == NULL) |
| { |
| ctf_next_destroy (i); |
| return ENOMEM; |
| } |
| walk = i->u.ctn_sorted_hkv; |
| |
| i->cu.ctn_h = h; |
| |
| while ((err = ctf_dynhash_next (h, &accum_i, &key, &value)) == 0) |
| { |
| walk->hkv_key = key; |
| walk->hkv_value = value; |
| walk++; |
| } |
| if (err != ECTF_NEXT_END) |
| { |
| ctf_next_destroy (i); |
| return err; |
| } |
| |
| if (sort_fun) |
| ctf_qsort_r (i->u.ctn_sorted_hkv, els, sizeof (ctf_next_hkv_t), |
| (int (*) (const void *, const void *, void *)) sort_fun, |
| sort_arg); |
| i->ctn_n = 0; |
| i->ctn_size = (ssize_t) els; |
| i->ctn_iter_fun = (void (*) (void)) ctf_dynhash_next_sorted; |
| *it = i; |
| } |
| |
| if ((void (*) (void)) ctf_dynhash_next_sorted != i->ctn_iter_fun) |
| return ECTF_NEXT_WRONGFUN; |
| |
| if (h != i->cu.ctn_h) |
| return ECTF_NEXT_WRONGFP; |
| |
| if ((ssize_t) i->ctn_n == i->ctn_size) |
| { |
| ctf_next_destroy (i); |
| *it = NULL; |
| return ECTF_NEXT_END; |
| } |
| |
| if (key) |
| *key = i->u.ctn_sorted_hkv[i->ctn_n].hkv_key; |
| if (value) |
| *value = i->u.ctn_sorted_hkv[i->ctn_n].hkv_value; |
| i->ctn_n++; |
| return 0; |
| } |
| |
| void |
| ctf_dynhash_destroy (ctf_dynhash_t *hp) |
| { |
| if (hp != NULL) |
| htab_delete (hp->htab); |
| free (hp); |
| } |
| |
| /* The dynset, used for sets of keys with no value. The implementation of this |
| can be much simpler, because without a value the slot can simply be the |
| stored key, which means we don't need to store the freeing functions and the |
| dynset itself is just a htab. */ |
| |
| ctf_dynset_t * |
| ctf_dynset_create (htab_hash hash_fun, htab_eq eq_fun, |
| ctf_hash_free_fun key_free) |
| { |
| /* 7 is arbitrary and untested for now. */ |
| return (ctf_dynset_t *) htab_create_alloc (7, (htab_hash) hash_fun, eq_fun, |
| key_free, xcalloc, free); |
| } |
| |
| /* The dynset has one complexity: the underlying implementation reserves two |
| values for internal hash table implementation details (empty versus deleted |
| entries). These values are otherwise very useful for pointers cast to ints, |
| so transform the ctf_dynset_inserted value to allow for it. (This |
| introduces an ambiguity in that one can no longer store these two values in |
| the dynset, but if we pick high enough values this is very unlikely to be a |
| problem.) |
| |
| We leak this implementation detail to the freeing functions on the grounds |
| that any use of these functions is overwhelmingly likely to be in sets using |
| real pointers, which will be unaffected. */ |
| |
| #define DYNSET_EMPTY_ENTRY_REPLACEMENT ((void *) (uintptr_t) -64) |
| #define DYNSET_DELETED_ENTRY_REPLACEMENT ((void *) (uintptr_t) -63) |
| |
| static void * |
| key_to_internal (const void *key) |
| { |
| if (key == HTAB_EMPTY_ENTRY) |
| return DYNSET_EMPTY_ENTRY_REPLACEMENT; |
| else if (key == HTAB_DELETED_ENTRY) |
| return DYNSET_DELETED_ENTRY_REPLACEMENT; |
| |
| return (void *) key; |
| } |
| |
| static void * |
| internal_to_key (const void *internal) |
| { |
| if (internal == DYNSET_EMPTY_ENTRY_REPLACEMENT) |
| return HTAB_EMPTY_ENTRY; |
| else if (internal == DYNSET_DELETED_ENTRY_REPLACEMENT) |
| return HTAB_DELETED_ENTRY; |
| return (void *) internal; |
| } |
| |
| int |
| ctf_dynset_insert (ctf_dynset_t *hp, void *key) |
| { |
| struct htab *htab = (struct htab *) hp; |
| void **slot; |
| |
| slot = htab_find_slot (htab, key, INSERT); |
| |
| if (!slot) |
| { |
| errno = ENOMEM; |
| return -errno; |
| } |
| |
| if (*slot) |
| { |
| if (htab->del_f) |
| (*htab->del_f) (*slot); |
| } |
| |
| *slot = key_to_internal (key); |
| |
| return 0; |
| } |
| |
| void |
| ctf_dynset_remove (ctf_dynset_t *hp, const void *key) |
| { |
| htab_remove_elt ((struct htab *) hp, key_to_internal (key)); |
| } |
| |
| void |
| ctf_dynset_destroy (ctf_dynset_t *hp) |
| { |
| if (hp != NULL) |
| htab_delete ((struct htab *) hp); |
| } |
| |
| void * |
| ctf_dynset_lookup (ctf_dynset_t *hp, const void *key) |
| { |
| void **slot = htab_find_slot ((struct htab *) hp, |
| key_to_internal (key), NO_INSERT); |
| |
| if (slot) |
| return internal_to_key (*slot); |
| return NULL; |
| } |
| |
| size_t |
| ctf_dynset_elements (ctf_dynset_t *hp) |
| { |
| return htab_elements ((struct htab *) hp); |
| } |
| |
| /* TRUE/FALSE return. */ |
| int |
| ctf_dynset_exists (ctf_dynset_t *hp, const void *key, const void **orig_key) |
| { |
| void **slot = htab_find_slot ((struct htab *) hp, |
| key_to_internal (key), NO_INSERT); |
| |
| if (orig_key && slot) |
| *orig_key = internal_to_key (*slot); |
| return (slot != NULL); |
| } |
| |
| /* Look up a completely random value from the set, if any exist. |
| Keys with value zero cannot be distinguished from a nonexistent key. */ |
| void * |
| ctf_dynset_lookup_any (ctf_dynset_t *hp) |
| { |
| struct htab *htab = (struct htab *) hp; |
| void **slot = htab->entries; |
| void **limit = slot + htab_size (htab); |
| |
| while (slot < limit |
| && (*slot == HTAB_EMPTY_ENTRY || *slot == HTAB_DELETED_ENTRY)) |
| slot++; |
| |
| if (slot < limit) |
| return internal_to_key (*slot); |
| return NULL; |
| } |
| |
| /* Traverse a dynset in arbitrary order, in _next iterator form. |
| |
| Otherwise, just like ctf_dynhash_next. */ |
| int |
| ctf_dynset_next (ctf_dynset_t *hp, ctf_next_t **it, void **key) |
| { |
| struct htab *htab = (struct htab *) hp; |
| ctf_next_t *i = *it; |
| void *slot; |
| |
| if (!i) |
| { |
| size_t size = htab_size (htab); |
| |
| /* If the table has too many entries to fit in an ssize_t, just give up. |
| This might be spurious, but if any type-related hashtable has ever been |
| nearly as large as that then somthing very odd is going on. */ |
| |
| if (((ssize_t) size) < 0) |
| return EDOM; |
| |
| if ((i = ctf_next_create ()) == NULL) |
| return ENOMEM; |
| |
| i->u.ctn_hash_slot = htab->entries; |
| i->cu.ctn_s = hp; |
| i->ctn_n = 0; |
| i->ctn_size = (ssize_t) size; |
| i->ctn_iter_fun = (void (*) (void)) ctf_dynset_next; |
| *it = i; |
| } |
| |
| if ((void (*) (void)) ctf_dynset_next != i->ctn_iter_fun) |
| return ECTF_NEXT_WRONGFUN; |
| |
| if (hp != i->cu.ctn_s) |
| return ECTF_NEXT_WRONGFP; |
| |
| if ((ssize_t) i->ctn_n == i->ctn_size) |
| goto set_end; |
| |
| while ((ssize_t) i->ctn_n < i->ctn_size |
| && (*i->u.ctn_hash_slot == HTAB_EMPTY_ENTRY |
| || *i->u.ctn_hash_slot == HTAB_DELETED_ENTRY)) |
| { |
| i->u.ctn_hash_slot++; |
| i->ctn_n++; |
| } |
| |
| if ((ssize_t) i->ctn_n == i->ctn_size) |
| goto set_end; |
| |
| slot = *i->u.ctn_hash_slot; |
| |
| if (key) |
| *key = internal_to_key (slot); |
| |
| i->u.ctn_hash_slot++; |
| i->ctn_n++; |
| |
| return 0; |
| |
| set_end: |
| ctf_next_destroy (i); |
| *it = NULL; |
| return ECTF_NEXT_END; |
| } |
| |
| /* ctf_hash, used for fixed-size maps from const char * -> ctf_id_t without |
| removal. This is a straight cast of a hashtab. */ |
| |
| ctf_hash_t * |
| ctf_hash_create (unsigned long nelems, ctf_hash_fun hash_fun, |
| ctf_hash_eq_fun eq_fun) |
| { |
| return (ctf_hash_t *) htab_create_alloc (nelems, (htab_hash) hash_fun, |
| eq_fun, free, xcalloc, free); |
| } |
| |
| uint32_t |
| ctf_hash_size (const ctf_hash_t *hp) |
| { |
| return htab_elements ((struct htab *) hp); |
| } |
| |
| int |
| ctf_hash_insert_type (ctf_hash_t *hp, ctf_dict_t *fp, uint32_t type, |
| uint32_t name) |
| { |
| const char *str = ctf_strraw (fp, name); |
| |
| if (type == 0) |
| return EINVAL; |
| |
| if (str == NULL |
| && CTF_NAME_STID (name) == CTF_STRTAB_1 |
| && fp->ctf_syn_ext_strtab == NULL |
| && fp->ctf_str[CTF_NAME_STID (name)].cts_strs == NULL) |
| return ECTF_STRTAB; |
| |
| if (str == NULL) |
| return ECTF_BADNAME; |
| |
| if (str[0] == '\0') |
| return 0; /* Just ignore empty strings on behalf of caller. */ |
| |
| if (ctf_hashtab_insert ((struct htab *) hp, (char *) str, |
| (void *) (ptrdiff_t) type, NULL, NULL) != NULL) |
| return 0; |
| return errno; |
| } |
| |
| /* if the key is already in the hash, override the previous definition with |
| this new official definition. If the key is not present, then call |
| ctf_hash_insert_type and hash it in. */ |
| int |
| ctf_hash_define_type (ctf_hash_t *hp, ctf_dict_t *fp, uint32_t type, |
| uint32_t name) |
| { |
| /* This matches the semantics of ctf_hash_insert_type in this |
| implementation anyway. */ |
| |
| return ctf_hash_insert_type (hp, fp, type, name); |
| } |
| |
| ctf_id_t |
| ctf_hash_lookup_type (ctf_hash_t *hp, ctf_dict_t *fp __attribute__ ((__unused__)), |
| const char *key) |
| { |
| ctf_helem_t **slot; |
| |
| slot = ctf_hashtab_lookup ((struct htab *) hp, key, NO_INSERT); |
| |
| if (slot) |
| return (ctf_id_t) (uintptr_t) ((*slot)->value); |
| |
| return 0; |
| } |
| |
| void |
| ctf_hash_destroy (ctf_hash_t *hp) |
| { |
| if (hp != NULL) |
| htab_delete ((struct htab *) hp); |
| } |