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

Implement iterator for hash tables and sets #4639

Merged
merged 10 commits into from
Sep 24, 2024
Merged
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
3 changes: 3 additions & 0 deletions DEVELOPERS.md
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,9 @@ rz_core_wrap.cxx:32103:61: error: assigning to 'RzDebugReasonType' from incompat
int sum = 0; // set sum to 0
```

* If you want to iterate over values of your struct, implement `RzIterator *mystruct_as_iter()` and `RzIterator *mystruct_as_iter_mut()` for them.
See `rz_iterator.h` for details about the iterator.

* If you need bitmaps, do not shift and OR the bits manually on `ut32`. Use bit vectors from `rz_bitvector.h` instead.

### Shell Scripts
Expand Down
33 changes: 33 additions & 0 deletions librz/include/rz_util/ht_inc.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
// SPDX-FileCopyrightText: 2024 pelijah
// SPDX-License-Identifier: BSD-3-Clause

#include <rz_util/rz_iterator.h>

#ifndef HT_TYPE
#error HT_TYPE should be defined before including this header
#endif
Expand Down Expand Up @@ -166,6 +168,22 @@ typedef struct Ht_(t) {
}
HtName_(Ht);

typedef struct Ht_(iter_mut_t) {
HtName_(Ht) *ht; ///< The hash table to iterate over.
ut32 ti; ///< Table index
ut32 bi; ///< Bucket index
HT_(Kv) *kv; ///< Current Key-Value-pair.
}
HT_(IterMutState);

typedef struct Ht_(iter_t) {
const HtName_(Ht) *ht; ///< The hash table to iterate over.
ut32 ti; ///< Table index
ut32 bi; ///< Bucket index
const HT_(Kv) *kv; ///< Current Key-Value-pair.
}
HT_(IterState);

// Create a new Ht with the provided Options
RZ_API RZ_OWN HtName_(Ht) *Ht_(new_opt)(RZ_NONNULL HT_(Options) *opt);
// Create a new Ht with the provided Options and initial size
Expand All @@ -190,6 +208,21 @@ RZ_API VALUE_TYPE Ht_(find)(RZ_NONNULL HtName_(Ht) *ht, const KEY_TYPE key, RZ_N
// NOTE: cb can delete the current element, but it should be avoided
RZ_API void Ht_(foreach)(RZ_NONNULL HtName_(Ht) *ht, RZ_NONNULL HT_(ForeachCallback) cb, RZ_NULLABLE void *user);

RZ_API ut32 Ht_(size)(const RZ_NONNULL HtName_(Ht) *ht);

RZ_API RZ_BORROW HT_(Kv) *Ht_(find_kv)(RZ_NONNULL HtName_(Ht) *ht, const KEY_TYPE key, RZ_NULLABLE bool *found);
RZ_API bool Ht_(insert_kv)(RZ_NONNULL HtName_(Ht) *ht, RZ_NONNULL HT_(Kv) *kv, bool update);
RZ_API HtRetCode Ht_(insert_kv_ex)(RZ_NONNULL HtName_(Ht) *ht, RZ_NONNULL HT_(Kv) *kv, bool update, RZ_OUT RZ_NULLABLE HT_(Kv) **out_kv);

RZ_API RZ_OWN HT_(IterMutState) *Ht_(new_iter_mut_state)(RZ_NONNULL HtName_(Ht) *ht);
RZ_API RZ_OWN HT_(IterState) *Ht_(new_iter_state)(const RZ_NONNULL HtName_(Ht) *ht);
RZ_API void Ht_(free_iter_mut_state)(RZ_NULLABLE HT_(IterMutState) *state);
RZ_API void Ht_(free_iter_state)(RZ_NULLABLE HT_(IterState) *state);

RZ_API RZ_BORROW VALUE_TYPE *Ht_(iter_next_mut)(RzIterator *it);
RZ_API const VALUE_TYPE *Ht_(iter_next)(RzIterator *it);
RZ_API const KEY_TYPE *Ht_(iter_next_key)(RzIterator *it);

RZ_API RZ_OWN RzIterator /* <HtName_(Ht)> */ *Ht_(as_iter_mut)(RZ_NONNULL HtName_(Ht) *ht);
RZ_API RZ_OWN RzIterator /* <HtName_(Ht)> */ *Ht_(as_iter)(const RZ_NONNULL HtName_(Ht) *ht);
RZ_API RZ_OWN RzIterator /* <HtName_(Ht)> */ *Ht_(as_iter_keys)(const RZ_NONNULL HtName_(Ht) *ht);
4 changes: 4 additions & 0 deletions librz/include/rz_util/rz_set.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ RZ_API void rz_set_s_free(RZ_NULLABLE RzSetS *set);
RZ_API void rz_set_s_add(RZ_NONNULL RzSetS *set, const char *str);
RZ_API bool rz_set_s_contains(RZ_NONNULL RzSetS *set, const char *str);
RZ_API void rz_set_s_delete(RZ_NONNULL RzSetS *set, const char *str);
RZ_API ut32 rz_set_s_size(const RZ_NONNULL RzSetS *set);
RZ_API RZ_OWN RzPVector /*<char *>*/ *rz_set_s_to_vector(RZ_NONNULL RzSetS *set);
RZ_API RzIterator /* <RzSetS> */ *rz_set_s_as_iter(const RZ_NONNULL RzSetS *set);

typedef HtUP RzSetU;

Expand All @@ -29,6 +31,8 @@ RZ_API void rz_set_u_free(RZ_NULLABLE RzSetU *set);
RZ_API void rz_set_u_add(RZ_NONNULL RzSetU *set, ut64 u);
RZ_API bool rz_set_u_contains(RZ_NONNULL RzSetU *set, ut64 u);
RZ_API void rz_set_u_delete(RZ_NONNULL RzSetU *set, ut64 u);
RZ_API ut32 rz_set_u_size(const RZ_NONNULL RzSetU *set);
RZ_API RzIterator /* <RzSetU> */ *rz_set_u_as_iter(const RZ_NONNULL RzSetU *set);

#ifdef __cplusplus
}
Expand Down
192 changes: 192 additions & 0 deletions librz/util/ht/ht_inc.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
// SPDX-FileCopyrightText: 2024 pelijah
// SPDX-License-Identifier: BSD-3-Clause

#include <rz_util/rz_log.h>
#include <rz_util/rz_assert.h>
#include <rz_util/rz_iterator.h>
#include <rz_util/rz_str.h>

#define LOAD_FACTOR 1
Expand Down Expand Up @@ -512,3 +514,193 @@ RZ_API void Ht_(foreach)(RZ_NONNULL HtName_(Ht) *ht, RZ_NONNULL HT_(ForeachCallb
}
}
}

/**
* \brief Returns the number of elements stored in the hash map \p ht.
*
* \param ht The hash map.
*
* \return The number of elements saved in the hash map.
*/
RZ_API ut32 Ht_(size)(const RZ_NONNULL HtName_(Ht) *ht) {
rz_return_val_if_fail(ht, 0);
return ht->count;
}

/**
* \brief Advances an RzIterator over a hashtable to the next value and returns it.
*
* \param it The next value or NULL if iteration terminated. The value is mutable.
*/
RZ_API RZ_BORROW VALUE_TYPE *Ht_(iter_next_mut)(RzIterator *it) {
rz_return_val_if_fail(it, NULL);

HT_(IterMutState) *state = it->u;
if (state->ti >= state->ht->size) {
// Iteration is done. No elements left to select.
return NULL;
}
// Iterate over tables until a table with an element is found.
for (; state->ti < state->ht->size; state->ti++) {
if (state->ht->table[state->ti].count == 0) {
// Table has no elements. Check next table.
continue;
}
if (state->bi < state->ht->table[state->ti].count) {
// Table has elements, select the element.
state->kv = &state->ht->table[state->ti].arr[state->bi];
// For the next iteration, increment bucket index to the following element.
state->bi++;
return &state->kv->value;
}
// Reset bucket index to first bucket.
state->bi = 0;
// Go to next table
}
// Iteration is done. No elements left to select.
return NULL;
}

/**
* \brief Advances an RzIterator over a hash table to the next value and returns it.
*
* \param it The next value as immutable or NULL if iteration terminated.
*/
RZ_API const VALUE_TYPE *Ht_(iter_next)(RzIterator *it) {
rz_return_val_if_fail(it, NULL);

HT_(IterState) *state = it->u;
if (state->ti >= state->ht->size) {
// Iteration is done. No elements left to select.
return NULL;
}
// Iterate over tables until a table with an element is found.
for (; state->ti < state->ht->size; state->ti++) {
if (state->ht->table[state->ti].count == 0) {
// Table has no elements. Check next table.
continue;
}
if (state->bi < state->ht->table[state->ti].count) {
// Table has elements, select the element.
state->kv = &state->ht->table[state->ti].arr[state->bi];
// For the next iteration, increment bucket index to the following element.
state->bi++;
return (const VALUE_TYPE *)&state->kv->value;
}
// Reset bucket index to first bucket.
state->bi = 0;
// Go to next table
}
// Iteration is done. No elements left to select.
return NULL;
}

/**
* \brief Advances an RzIterator over a hash table to the next key in
* and returns it.
*
* \param it The next key as immutable or NULL if iteration terminated.
*/
RZ_API const KEY_TYPE *Ht_(iter_next_key)(RzIterator *it) {
rz_return_val_if_fail(it, NULL);

HT_(IterState) *state = it->u;
if (state->ti >= state->ht->size) {
// Iteration is done. No elements left to select.
return NULL;
}
// Iterate over tables until a table with an element is found.
for (; state->ti < state->ht->size; state->ti++) {
if (state->ht->table[state->ti].count == 0) {
// Table has no elements. Check next table.
continue;
}
if (state->bi < state->ht->table[state->ti].count) {
// Table has elements, select the element.
state->kv = &state->ht->table[state->ti].arr[state->bi];
// For the next iteration, increment bucket index to the following element.
state->bi++;
return (const KEY_TYPE *)&state->kv->key;
}
// Reset bucket index to first bucket.
state->bi = 0;
// Go to next table
}
// Iteration is done. No elements left to select.
return NULL;
}

RZ_API RZ_OWN HT_(IterMutState) *Ht_(new_iter_mut_state)(RZ_NONNULL HtName_(Ht) *ht) {
rz_return_val_if_fail(ht, NULL);
HT_(IterMutState) *state = RZ_NEW0(HT_(IterMutState));
rz_return_val_if_fail(state, NULL);
Rot127 marked this conversation as resolved.
Show resolved Hide resolved
state->ht = ht;
return state;
}

RZ_API RZ_OWN HT_(IterState) *Ht_(new_iter_state)(const RZ_NONNULL HtName_(Ht) *ht) {
rz_return_val_if_fail(ht, NULL);
HT_(IterState) *state = RZ_NEW0(HT_(IterState));
rz_return_val_if_fail(state, NULL);
state->ht = ht;
return state;
}

RZ_API void Ht_(free_iter_mut_state)(RZ_NULLABLE HT_(IterMutState) *state) {
free(state);
}

RZ_API void Ht_(free_iter_state)(RZ_NULLABLE HT_(IterState) *state) {
free(state);
}

/**
* \brief Returns an iterator over the hash table \p ht. The iterator yields mutable values.
*
* \param ht The hash table to create the iterator for.
*
* \return The iterator over the hash table values or NULL in case of failure.
*/
RZ_API RZ_OWN RzIterator /* <HtName_(Ht)> */ *Ht_(as_iter_mut)(RZ_NONNULL HtName_(Ht) *ht) {
rz_return_val_if_fail(ht, NULL);
HT_(IterMutState) *state = Ht_(new_iter_mut_state)(ht);
if (!state) {
RZ_LOG_ERROR("Could not allocate a new ht_iter state.\n");
return NULL;
}

RzIterator *iter = rz_iterator_new((rz_iterator_next_cb)Ht_(iter_next_mut), NULL, (rz_iterator_free_cb)Ht_(free_iter_mut_state), state);
return iter;
}

/**
* \brief Returns an iterator over the hash table \p ht. The iterator yields immutable values.
*
* \param ht The hash table to create the iterator for.
*
* \return The iterator over the hash table values or NULL in case of failure.
*/
RZ_API RZ_OWN RzIterator /* <HtName_(Ht)> */ *Ht_(as_iter)(const RZ_NONNULL HtName_(Ht) *ht) {
rz_return_val_if_fail(ht, NULL);
HT_(IterState) *state = Ht_(new_iter_state)(ht);
rz_return_val_if_fail(state, NULL);

RzIterator *iter = rz_iterator_new((rz_iterator_next_cb)Ht_(iter_next), NULL, (rz_iterator_free_cb)Ht_(free_iter_state), state);
return iter;
}

/**
* \brief Returns an iterator over the hash table \p ht. The iterator yields immutable keys.
*
* \param ht The hash table to create the iterator for.
*
* \return The iterator over the hash table keys or NULL in case of failure.
*/
RZ_API RZ_OWN RzIterator /* <HtName_(Ht)> */ *Ht_(as_iter_keys)(const RZ_NONNULL HtName_(Ht) *ht) {
rz_return_val_if_fail(ht, NULL);
HT_(IterState) *state = Ht_(new_iter_state)(ht);
rz_return_val_if_fail(state, NULL);

RzIterator *iter = rz_iterator_new((rz_iterator_next_cb)Ht_(iter_next_key), NULL, (rz_iterator_free_cb)Ht_(free_iter_state), state);
return iter;
}
36 changes: 36 additions & 0 deletions librz/util/set.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,16 @@ RZ_API void rz_set_s_delete(RZ_NONNULL RzSetS *set, const char *str) {
ht_sp_delete(set, str);
}

/**
* \brief Returns an iterator over the \p set with immutable elements.
*
* \return Iterator yielding immutable elements.
*/
RZ_API RzIterator /* <RzSetS> */ *rz_set_s_as_iter(const RZ_NONNULL RzSetS *set) {
rz_return_val_if_fail(set, NULL);
return ht_sp_as_iter_keys((const HtSP *)set);
}

static bool push_to_pvector(void *user, const char *k, RZ_UNUSED const void *v) {
RzPVector *vec = (RzPVector *)user;
return !!rz_pvector_push(vec, (void *)k);
Expand Down Expand Up @@ -65,6 +75,14 @@ RZ_API void rz_set_s_free(RZ_NULLABLE RzSetS *set) {
ht_sp_free((HtSP *)set);
}

/**
* \brief Return number of elements saved in the set.
*/
RZ_API ut32 rz_set_s_size(const RZ_NONNULL RzSetS *set) {
rz_return_val_if_fail(set, 0);
return ht_sp_size((HtSP *)set);
}

/**
* \brief Create a new hash set with ut64 as elements.
*/
Expand Down Expand Up @@ -99,3 +117,21 @@ RZ_API void rz_set_u_delete(RZ_NONNULL RzSetU *set, ut64 u) {
RZ_API void rz_set_u_free(RZ_NULLABLE RzSetU *set) {
ht_up_free((HtUP *)set);
}

/**
* \brief Return number of elements saved in the set.
*/
RZ_API ut32 rz_set_u_size(const RZ_NONNULL RzSetU *set) {
rz_return_val_if_fail(set, 0);
return ht_up_size((HtUP *)set);
}

/**
* \brief Returns an iterator over the \p set with immutable elements.
*
* \return Iterator yielding immutable elements.
*/
RZ_API RzIterator /* <RzSetU> */ *rz_set_u_as_iter(const RZ_NONNULL RzSetU *set) {
rz_return_val_if_fail(set, NULL);
return ht_up_as_iter_keys((const HtUP *)set);
}
2 changes: 1 addition & 1 deletion test/unit/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ if get_option('enable_tests')
'graph',
'hash',
'hex',
'ht',
'id_storage',
'idpool',
'idstorage',
Expand Down Expand Up @@ -81,7 +82,6 @@ if get_option('enable_tests')
'rz_test',
'sdb_array',
'sdb_diff',
'sdb_hash',
'sdb_sdb',
'sdb_util',
'serialize_analysis',
Expand Down
Loading
Loading