librz/util: add rz_vector_contains() and rz_vector_insert_sorted() (#5892)

* Add rz_vector_contains().
* Add rz_vector_insert_sorted().
* Pointer arithmetic fix for Windows.
This commit is contained in:
Rot127
2026-02-11 02:45:12 +08:00
committed by GitHub
parent 9140a66666
commit d7111d056c
3 changed files with 139 additions and 0 deletions
+5
View File
@@ -48,6 +48,7 @@ typedef struct rz_vector_t {
size_t len;
size_t capacity;
size_t elem_size;
bool reverse_sorted;
RzVectorFree free;
void *free_user;
} RzVector;
@@ -142,6 +143,8 @@ RZ_API void *rz_vector_insert(RzVector *vec, size_t index, void *x);
// insert count values of size vec->elem_size into vec starting at the given index.
RZ_API void *rz_vector_insert_range(RzVector *vec, size_t index, RZ_NULLABLE void *first, size_t count);
RZ_API void *rz_vector_insert_sorted(RZ_NONNULL RzVector *vec, RZ_NONNULL void *elem, RzVectorComparator cmp, void *user);
// like rz_vector_remove_at for the last element
RZ_API void rz_vector_pop(RzVector *vec, void *into);
@@ -154,6 +157,8 @@ RZ_API void *rz_vector_push(RzVector *vec, void *x);
// like rz_vector_insert for the beginning of vec
RZ_API void *rz_vector_push_front(RzVector *vec, void *x);
RZ_API bool rz_vector_contains(const RZ_NONNULL RzVector *vec, const RZ_NONNULL void *elem);
/**
* \brief Swap two elements of the vector
* \param index_a index of the first element to swap
+65
View File
@@ -36,6 +36,7 @@
RZ_API void rz_vector_init(RzVector *vec, size_t elem_size, RzVectorFree free, void *free_user) {
rz_return_if_fail(vec);
vec->a = NULL;
vec->reverse_sorted = false;
vec->capacity = vec->len = 0;
vec->elem_size = elem_size;
vec->free = free;
@@ -255,6 +256,41 @@ RZ_API void *rz_vector_insert_range(RzVector *vec, size_t index, RZ_NULLABLE voi
return p;
}
/**
* \brief Inserts an element into a sorted vector keeping the order.
* NOTE: This function assumes the vector is already sorted!
* If it isn't the final position of the element is undefined.
*
* \param vec A sorted vector to insert the element into.
* \param elem Pointer to the element to insert into the vector.
* \param cmp The comparator for the elements.
* \param user The user data passed to the comparator.
*
* \return Pointer to the position in the vector where the element was placed.
* Or NULL in case of failure.
*/
RZ_API void *rz_vector_insert_sorted(RZ_NONNULL RzVector *vec, RZ_NONNULL void *elem, RzVectorComparator cmp, void *user) {
rz_return_val_if_fail(vec && elem, NULL);
if (rz_vector_empty(vec)) {
return rz_vector_push(vec, elem);
}
size_t i = vec->reverse_sorted ? rz_vector_len(vec) - 1 : 0;
int inc = vec->reverse_sorted ? -1 : 1;
do {
void *velem = ((char *)vec->a) + (vec->elem_size * i);
if (cmp(velem, elem, user) >= 0) {
return rz_vector_insert(vec, vec->reverse_sorted ? i + 1 : i, elem);
}
if (i == 0 && inc == -1) {
// Overflow is undefined. So lets not depend on it.
break;
}
i += inc;
} while (i >= 0 && i < rz_vector_len(vec));
return vec->reverse_sorted ? rz_vector_push_front(vec, elem) : rz_vector_push(vec, elem);
}
RZ_API void rz_vector_pop(RzVector *vec, void *into) {
if (rz_vector_empty(vec)) {
return;
@@ -289,6 +325,26 @@ RZ_API void *rz_vector_push_front(RzVector *vec, void *x) {
return rz_vector_insert(vec, 0, x);
}
/**
* \brief Checks if the given element is in the vector.
*
* \param vec The vector to search in.
* \param elem Pointer to the element to search.
*
* \return True if the vector contains the element, false otherwise.
*/
RZ_API bool rz_vector_contains(const RZ_NONNULL RzVector *vec, const RZ_NONNULL void *elem) {
rz_return_val_if_fail(vec && elem, false);
for (size_t i = 0; i < vec->len; i++) {
// Casts to make Windows happy.
char *elem_v = ((char *)vec->a) + (vec->elem_size * i);
if (memcmp(elem_v, (char *)elem, vec->elem_size) == 0) {
return true;
}
}
return false;
}
RZ_API bool rz_vector_swap(RzVector *vec, size_t index_a, size_t index_b) {
rz_return_val_if_fail(vec && index_a < vec->len && index_b < vec->len, false);
ut8 *tmp = malloc(vec->elem_size);
@@ -378,6 +434,7 @@ static void vector_quick_sort(void *a, size_t elem_size, size_t len, RzVectorCom
*/
RZ_API void rz_vector_sort(RzVector *vec, RzVectorComparator cmp, bool reverse, void *user) {
rz_return_if_fail(vec && cmp);
vec->reverse_sorted = reverse;
if (rz_vector_empty(vec)) {
return;
}
@@ -438,6 +495,14 @@ RZ_API void rz_pvector_free(RzPVector *vec) {
free(vec);
}
/**
* \brief Checks if a the pointer \p x is in the vector.
*
* \param vec The vector to search in.
* \param x The pointer to search.
*
* \return Returns the pointer to the \p x pointer in the vector if found. NULL otherwise.
*/
RZ_API void **rz_pvector_contains(RzPVector *vec, const void *x) {
rz_return_val_if_fail(vec, NULL);
size_t i;
+69
View File
@@ -241,6 +241,7 @@ static bool test_vector_sort(void) {
mu_assert_streq(rz_vector_index_ptr(v, 1), "abb", "sorted strings");
mu_assert_streq(rz_vector_index_ptr(v, 2), "caa", "sorted strings");
mu_assert_streq(rz_vector_index_ptr(v, 3), "ccc", "sorted strings");
mu_assert_false(v->reverse_sorted, "Flag not set.");
// do dec sort
num = 55;
@@ -250,6 +251,55 @@ static bool test_vector_sort(void) {
mu_assert_streq(rz_vector_index_ptr(v, 1), "caa", "sorted strings");
mu_assert_streq(rz_vector_index_ptr(v, 2), "abb", "sorted strings");
mu_assert_streq(rz_vector_index_ptr(v, 3), "abb", "sorted strings");
mu_assert_true(v->reverse_sorted, "Flag not set.");
rz_vector_free(v);
mu_end;
}
static int uint_cmp(ut64 *a, ut64 *b, void *user) {
if (*a > *b) {
return 1;
} else if (*a < *b) {
return -1;
}
return 0;
}
static bool test_vector_insert_sorted(void) {
RzVector *v = rz_vector_new(sizeof(ut64), NULL, NULL);
ut64 n[] = { 0, 1, 2, 3, 4, 5 };
rz_vector_insert_sorted(v, &n[1], (RzVectorComparator)uint_cmp, NULL);
mu_assert_eq(*(ut64 *)rz_vector_index_ptr(v, 0), 1, "Insert failed");
rz_vector_insert_sorted(v, &n[0], (RzVectorComparator)uint_cmp, NULL);
mu_assert_eq(*(ut64 *)rz_vector_index_ptr(v, 0), 0, "Insert failed");
mu_assert_eq(*(ut64 *)rz_vector_index_ptr(v, 1), 1, "Insert failed");
rz_vector_push(v, &n[2]);
rz_vector_push(v, &n[4]);
rz_vector_sort(v, (RzVectorComparator)uint_cmp, true, NULL);
rz_vector_insert_sorted(v, &n[3], (RzVectorComparator)uint_cmp, NULL);
rz_vector_insert_sorted(v, &n[5], (RzVectorComparator)uint_cmp, NULL);
size_t i = 5;
ut64 *it = NULL;
rz_vector_foreach (v, it) {
mu_assert_eq(*it, n[i--], "Compare failed");
}
rz_vector_sort(v, (RzVectorComparator)uint_cmp, false, NULL);
rz_vector_remove_at(v, 5, NULL);
rz_vector_remove_at(v, 1, NULL);
rz_vector_remove_at(v, 0, NULL);
rz_vector_insert_sorted(v, &n[5], (RzVectorComparator)uint_cmp, NULL);
rz_vector_insert_sorted(v, &n[0], (RzVectorComparator)uint_cmp, NULL);
rz_vector_insert_sorted(v, &n[1], (RzVectorComparator)uint_cmp, NULL);
i = 0;
rz_vector_foreach (v, it) {
mu_assert_eq(*it, n[i++], "Compare failed");
}
rz_vector_free(v);
mu_end;
@@ -653,6 +703,23 @@ static bool test_vector_push_front(void) {
mu_end;
}
static bool test_vector_contains(void) {
RzVector v;
init_test_vector(&v, 6, 0, NULL, NULL);
ut64 a = 0;
ut64 b = 5;
ut64 c = 6;
mu_assert_true(rz_vector_contains(&v, &a), "Should contain");
mu_assert_true(rz_vector_contains(&v, &b), "Should contain");
mu_assert_false(rz_vector_contains(&v, &c), "Should not contain");
rz_vector_pop(&v, NULL);
mu_assert_false(rz_vector_contains(&v, &b), "Should contain");
rz_vector_pop_front(&v, NULL);
mu_assert_false(rz_vector_contains(&v, &a), "Should contain");
rz_vector_clear(&v);
mu_end;
}
static bool test_vector_swap(void) {
RzVector v;
init_test_vector(&v, 3, 0, NULL, NULL);
@@ -1494,10 +1561,12 @@ static int all_tests(void) {
mu_run_test(test_vector_remove_range);
mu_run_test(test_vector_insert);
mu_run_test(test_vector_insert_range);
mu_run_test(test_vector_insert_sorted);
mu_run_test(test_vector_pop);
mu_run_test(test_vector_pop_front);
mu_run_test(test_vector_push);
mu_run_test(test_vector_push_front);
mu_run_test(test_vector_contains);
mu_run_test(test_vector_swap);
mu_run_test(test_vector_reserve);
mu_run_test(test_vector_shrink);