util: implement iterator for hash tables and sets (#4639)

* Implement iterators for hash tables.
* Implement iterator over set.
* Constify get-size() of hash tables.
* Mention iterators in documentation.
This commit is contained in:
Rot127 2024-09-24 13:36:54 +00:00 committed by GitHub
parent ef8e303710
commit 35731483dd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 543 additions and 1 deletions

View file

@ -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

View file

@ -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
@ -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 /* <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);

View file

@ -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;
@ -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
}

View file

@ -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
@ -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 /* <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;
}

View file

@ -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);
@ -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 /* <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);
}

View file

@ -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',

View file

@ -3,6 +3,8 @@
#include "minunit.h"
#include <sdb.h>
#include <rz_util/rz_iterator.h>
#include <rz_util/rz_set.h>
#include <rz_util/ht_uu.h>
#include <rz_util/ht_up.h>
#include <rz_util/ht_pp.h>
@ -10,6 +12,7 @@
#include <rz_util/ht_sp.h>
#include <rz_util/ht_su.h>
#include <rz_util/ht_ss.h>
#include <rz_util/rz_str.h>
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;
}