diff --git a/DEVELOPERS.md b/DEVELOPERS.md index e2dad069407..89467bf82d9 100644 --- a/DEVELOPERS.md +++ b/DEVELOPERS.md @@ -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 diff --git a/librz/include/rz_util/ht_inc.h b/librz/include/rz_util/ht_inc.h index 9fc4024f8a0..e5172112c80 100644 --- a/librz/include/rz_util/ht_inc.h +++ b/librz/include/rz_util/ht_inc.h @@ -4,6 +4,8 @@ // SPDX-FileCopyrightText: 2024 pelijah // SPDX-License-Identifier: BSD-3-Clause +#include + #ifndef HT_TYPE #error HT_TYPE should be defined before including this header #endif @@ -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 @@ -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 /* */ *Ht_(as_iter_mut)(RZ_NONNULL HtName_(Ht) *ht); +RZ_API RZ_OWN RzIterator /* */ *Ht_(as_iter)(const RZ_NONNULL HtName_(Ht) *ht); +RZ_API RZ_OWN RzIterator /* */ *Ht_(as_iter_keys)(const RZ_NONNULL HtName_(Ht) *ht); diff --git a/librz/include/rz_util/rz_set.h b/librz/include/rz_util/rz_set.h index d1c7ec5bd55..9a8a4076390 100644 --- a/librz/include/rz_util/rz_set.h +++ b/librz/include/rz_util/rz_set.h @@ -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 /**/ *rz_set_s_to_vector(RZ_NONNULL RzSetS *set); +RZ_API RzIterator /* */ *rz_set_s_as_iter(const RZ_NONNULL RzSetS *set); typedef HtUP RzSetU; @@ -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 /* */ *rz_set_u_as_iter(const RZ_NONNULL RzSetU *set); #ifdef __cplusplus } diff --git a/librz/util/ht/ht_inc.c b/librz/util/ht/ht_inc.c index 8ef1cf9c70c..e436287a331 100644 --- a/librz/util/ht/ht_inc.c +++ b/librz/util/ht/ht_inc.c @@ -4,7 +4,9 @@ // SPDX-FileCopyrightText: 2024 pelijah // SPDX-License-Identifier: BSD-3-Clause +#include #include +#include #include #define LOAD_FACTOR 1 @@ -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); + 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 /* */ *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 /* */ *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 /* */ *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; +} diff --git a/librz/util/set.c b/librz/util/set.c index 7e4452437da..f870bcace46 100644 --- a/librz/util/set.c +++ b/librz/util/set.c @@ -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 /* */ *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); @@ -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. */ @@ -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 /* */ *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); +} diff --git a/test/unit/meson.build b/test/unit/meson.build index 99d44094b20..832c0009737 100644 --- a/test/unit/meson.build +++ b/test/unit/meson.build @@ -54,6 +54,7 @@ if get_option('enable_tests') 'graph', 'hash', 'hex', + 'ht', 'id_storage', 'idpool', 'idstorage', @@ -81,7 +82,6 @@ if get_option('enable_tests') 'rz_test', 'sdb_array', 'sdb_diff', - 'sdb_hash', 'sdb_sdb', 'sdb_util', 'serialize_analysis', diff --git a/test/unit/test_sdb_hash.c b/test/unit/test_ht.c similarity index 66% rename from test/unit/test_sdb_hash.c rename to test/unit/test_ht.c index 3af4f7270a5..004d7192f88 100644 --- a/test/unit/test_sdb_hash.c +++ b/test/unit/test_ht.c @@ -3,6 +3,8 @@ #include "minunit.h" #include +#include +#include #include #include #include @@ -10,6 +12,7 @@ #include #include #include +#include typedef struct _test_struct { char *name; @@ -570,6 +573,272 @@ bool test_insert_update_ex(void) { mu_end; } +bool test_ht_size(void) { + HtUU *ht = ht_uu_new(); + mu_assert_eq(ht_uu_size(ht), 0, "Length wrong."); + ht_uu_insert(ht, 0x5050505, 0x5050505); + ht_uu_insert(ht, 0x5050505, 0x5050505); + ht_uu_insert(ht, 0x6060606, 0x6060606); + ht_uu_insert(ht, 0x7070707, 0x7070707); + ht_uu_insert(ht, 0x7070707, 0x7070707); + mu_assert_eq(ht_uu_size(ht), 3, "Length wrong."); + bool found = false; + ht_uu_find(ht, 0x5050505, &found); + mu_assert_true(found, "Value was not added."); + ht_uu_find(ht, 0x6060606, &found); + mu_assert_true(found, "Value was not added."); + ht_uu_find(ht, 0x7070707, &found); + mu_assert_true(found, "Value was not added."); + + ht_uu_delete(ht, 0x7070707); + ht_uu_find(ht, 0x7070707, &found); + mu_assert_false(found, "Value was not deleted."); + mu_assert_eq(ht_uu_size(ht), 2, "Length wrong."); + + // Double delete + ht_uu_delete(ht, 0x7070707); + ht_uu_find(ht, 0x7070707, &found); + mu_assert_false(found, "Value was not deleted."); + mu_assert_eq(ht_uu_size(ht), 2, "Length wrong."); + ht_uu_free(ht); + mu_end; +} + +bool test_ht_uu_iter(void) { + HtUU *ht = ht_uu_new(); + ut32 icnt = 0; + const ut64 *im_elem; + + RzIterator *it = ht_uu_as_iter(ht); + rz_iterator_foreach(it, im_elem) { + icnt++; + } + rz_iterator_free(it); + mu_assert_eq(icnt, 0, "Wrong number of iterations"); + ht_uu_insert(ht, 0x1010101, 0x1010101); + ht_uu_insert(ht, 0x2020202, 0x2020202); + ht_uu_insert(ht, 0x3030303, 0x3030303); + ht_uu_insert(ht, 0x4040404, 0x4040404); + ht_uu_insert(ht, 0x5050505, 0x5050505); + icnt = 0; + it = ht_uu_as_iter(ht); + rz_iterator_foreach(it, im_elem) { + icnt++; + mu_assert_true( + *im_elem == 0x1010101 || + *im_elem == 0x2020202 || + *im_elem == 0x3030303 || + *im_elem == 0x4040404 || + *im_elem == 0x5050505, + "Value mismtach"); + } + rz_iterator_free(it); + mu_assert_eq(icnt, 5, "Wrong number of iterations"); + icnt = 0; + // Test write of value + ut64 *m_elem; + it = ht_uu_as_iter_mut(ht); + rz_iterator_foreach(it, m_elem) { + icnt++; + if (*m_elem == 0x1010101) { + *m_elem = 0x0; + } + } + rz_iterator_free(it); + mu_assert_eq(icnt, 5, "Wrong number of iterations"); + bool found = false; + ut64 v = ht_uu_find(ht, 0x1010101, &found); + mu_assert_true(found, "Key not in hash map"); + mu_assert_eq(v, 0x0, "Value didn't change."); + ht_uu_free(ht); + mu_end; +} + +bool test_ht_ss_iter(void) { + HtSS *ht = ht_ss_new(HT_STR_CONST, HT_STR_CONST); + ut32 icnt = 0; + const char **im_elem; + + RzIterator *it = ht_ss_as_iter(ht); + rz_iterator_foreach(it, im_elem) { + icnt++; + } + rz_iterator_free(it); + mu_assert_eq(icnt, 0, "Wrong number of iterations"); + + ht_ss_insert(ht, "0x1010101", "0x1010101"); + ht_ss_insert(ht, "0x2020202", "0x2020202"); + ht_ss_insert(ht, "0x3030303", "0x3030303"); + ht_ss_insert(ht, "0x4040404", "0x4040404"); + ht_ss_insert(ht, "0x5050505", "0x5050505"); + icnt = 0; + it = ht_ss_as_iter(ht); + rz_iterator_foreach(it, im_elem) { + icnt++; + mu_assert_true( + RZ_STR_EQ(*im_elem, "0x1010101") || + RZ_STR_EQ(*im_elem, "0x2020202") || + RZ_STR_EQ(*im_elem, "0x3030303") || + RZ_STR_EQ(*im_elem, "0x4040404") || + RZ_STR_EQ(*im_elem, "0x5050505"), + "Value mismtach"); + } + rz_iterator_free(it); + mu_assert_eq(icnt, 5, "Wrong number of iterations"); + icnt = 0; + // Test write of value + char **m_elem; + it = ht_ss_as_iter_mut(ht); + rz_iterator_foreach(it, m_elem) { + icnt++; + if (RZ_STR_EQ(*m_elem, "0x1010101")) { + *m_elem = "0x0"; + } + } + rz_iterator_free(it); + mu_assert_eq(icnt, 5, "Wrong number of iterations"); + bool found = false; + const char *v = ht_ss_find(ht, "0x1010101", &found); + mu_assert_true(found, "Key not in hash map"); + mu_assert_streq(v, "0x0", "Value didn't change."); + ht_ss_free(ht); + mu_end; +} + +bool test_set_u(void) { + RzSetU *set_u = rz_set_u_new(); + rz_set_u_add(set_u, 0x5050505); + rz_set_u_add(set_u, 0x5050505); + rz_set_u_add(set_u, 0x6060606); + rz_set_u_add(set_u, 0x7070707); + rz_set_u_add(set_u, 0x7070707); + mu_assert_eq(rz_set_u_size(set_u), 3, "Length wrong."); + mu_assert_true(rz_set_u_contains(set_u, 0x5050505), "Value was not added."); + mu_assert_true(rz_set_u_contains(set_u, 0x6060606), "Value was not added."); + mu_assert_true(rz_set_u_contains(set_u, 0x7070707), "Value was not added."); + + rz_set_u_delete(set_u, 0x7070707); + mu_assert_false(rz_set_u_contains(set_u, 0x7070707), "Value was not deleted."); + mu_assert_eq(rz_set_u_size(set_u), 2, "Length wrong."); + + // Double delete + rz_set_u_delete(set_u, 0x7070707); + mu_assert_eq(rz_set_u_size(set_u), 2, "Length wrong."); + + size_t x = 0; + const ut64 *im_elem; + RzIterator *it = rz_set_u_as_iter(set_u); + rz_iterator_foreach(it, im_elem) { + x++; + bool matches = *im_elem == 0x5050505 || *im_elem == 0x6060606; + mu_assert_true(matches, "Set contained ill-formed value."); + } + rz_iterator_free(it); + mu_assert_eq(x, 2, "Foreach hasn't iterated the correct number of times."); + + rz_set_u_delete(set_u, 0x6060606); + mu_assert_eq(rz_set_u_size(set_u), 1, "Length wrong."); + rz_set_u_delete(set_u, 0x5050505); + mu_assert_eq(rz_set_u_size(set_u), 0, "Length wrong."); + + it = rz_set_u_as_iter(set_u); + rz_iterator_foreach(it, im_elem) { + mu_assert("Should not be reached.", false); + } + rz_iterator_free(it); + rz_set_u_add(set_u, 0x53e0); + rz_set_u_add(set_u, 0x53bc); + x = 0; + it = rz_set_u_as_iter(set_u); + rz_iterator_foreach(it, im_elem) { + x++; + } + rz_iterator_free(it); + mu_assert_eq(x, 2, "Foreach hasn't iterated the correct number of times."); + rz_set_u_delete(set_u, 0x53e0); + rz_set_u_delete(set_u, 0x53bc); + + rz_set_u_add(set_u, 0); + rz_set_u_add(set_u, 1); + rz_set_u_add(set_u, 2); + rz_set_u_add(set_u, 3); + + // Add an address as key which is far away from the heap addresses. + rz_set_u_add(set_u, 100000000); + mu_assert_true(rz_set_u_contains(set_u, 100000000), "Not contained."); + mu_assert_eq(set_u->count, 5, "count"); + mu_assert_false(rz_set_u_contains(set_u, 6), "should not be here."); + + x = 0; + it = rz_set_u_as_iter(set_u); + rz_iterator_foreach(it, im_elem) { + x++; + } + rz_iterator_free(it); + mu_assert_eq(x, 5, "Foreach hasn't iterated the correct number of times."); + + rz_set_u_free(set_u); + mu_end; +} + +bool test_set_s(void) { + RzSetS *set_s = rz_set_s_new(HT_STR_CONST); + rz_set_s_add(set_s, "0x5050505"); + rz_set_s_add(set_s, "0x5050505"); + rz_set_s_add(set_s, "0x6060606"); + rz_set_s_add(set_s, "0x7070707"); + rz_set_s_add(set_s, "0x7070707"); + mu_assert_eq(rz_set_s_size(set_s), 3, "Length wrong."); + mu_assert_true(rz_set_s_contains(set_s, "0x5050505"), "Value was not added."); + mu_assert_true(rz_set_s_contains(set_s, "0x6060606"), "Value was not added."); + mu_assert_true(rz_set_s_contains(set_s, "0x7070707"), "Value was not added."); + + rz_set_s_delete(set_s, "0x7070707"); + mu_assert_false(rz_set_s_contains(set_s, "0x7070707"), "Value was not deleted."); + mu_assert_eq(rz_set_s_size(set_s), 2, "Length wrong."); + + // Double delete + rz_set_s_delete(set_s, "0x7070707"); + mu_assert_eq(rz_set_s_size(set_s), 2, "Length wrong."); + + size_t x = 0; + const char **im_elem; + + RzIterator *it = rz_set_s_as_iter(set_s); + rz_iterator_foreach(it, im_elem) { + x++; + bool matches = RZ_STR_EQ(*im_elem, "0x5050505") || RZ_STR_EQ(*im_elem, "0x6060606"); + mu_assert_true(matches, "Set contained ill-formed value."); + } + rz_iterator_free(it); + mu_assert_eq(x, 2, "Foreach hasn't iterated the correct number of times."); + + rz_set_s_delete(set_s, "0x6060606"); + mu_assert_eq(rz_set_s_size(set_s), 1, "Length wrong."); + rz_set_s_delete(set_s, "0x5050505"); + mu_assert_eq(rz_set_s_size(set_s), 0, "Length wrong."); + + it = rz_set_s_as_iter(set_s); + rz_iterator_foreach(it, im_elem) { + mu_assert("Should not be reached.", false); + } + rz_iterator_free(it); + rz_set_s_add(set_s, "0x53e0"); + rz_set_s_add(set_s, "0x53bc"); + x = 0; + it = rz_set_s_as_iter(set_s); + rz_iterator_foreach(it, im_elem) { + x++; + } + rz_iterator_free(it); + mu_assert_eq(x, 2, "Foreach hasn't iterated the correct number of times."); + rz_set_s_delete(set_s, "0x53e0"); + rz_set_s_delete(set_s, "0x53bc"); + + rz_set_s_free(set_s); + mu_end; +} + int all_tests() { mu_run_test(test_ht_insert_lookup); mu_run_test(test_ht_update_lookup); @@ -591,6 +860,11 @@ int all_tests() { mu_run_test(test_update_key); mu_run_test(test_ht_pu_ops); mu_run_test(test_insert_update_ex); + mu_run_test(test_ht_size); + mu_run_test(test_ht_uu_iter); + mu_run_test(test_ht_ss_iter); + mu_run_test(test_set_u); + mu_run_test(test_set_s); return tests_passed != tests_run; }