improve dynamic arrays
This commit is contained in:
@@ -161,28 +161,37 @@ size_t arr_cap(void *this);
|
|||||||
|
|
||||||
#define arr_pop(THIS) (arr_shrink(THIS, 1), THIS[arr_len(THIS)])
|
#define arr_pop(THIS) (arr_shrink(THIS, 1), THIS[arr_len(THIS)])
|
||||||
|
|
||||||
|
|
||||||
/* Compile-time assert that works in expression contexts */
|
|
||||||
#define STATIC_ASSERT(expr) ((void)sizeof(char[(expr) ? 1 : -1]))
|
#define STATIC_ASSERT(expr) ((void)sizeof(char[(expr) ? 1 : -1]))
|
||||||
|
|
||||||
|
|
||||||
bool dyn_array_contains_func(void *this, uint8_t *value);
|
bool dyn_array_contains_func(void *this, uint8_t *value);
|
||||||
|
|
||||||
/*
|
|
||||||
arr_contains(THIS, TYPE, VALUE)
|
|
||||||
|
|
||||||
- SIZE CHECK: compile-time check that sizeof(*THIS) == sizeof(TYPE)
|
|
||||||
- TYPE is the type of the value to search for
|
|
||||||
- VALUE is stored in a compound literal of TYPE
|
|
||||||
- Pure C99, no extensions needed
|
|
||||||
- Callers with typeof support can make wrapper macros
|
|
||||||
*/
|
|
||||||
#define arr_contains(THIS, ...)\
|
#define arr_contains(THIS, ...)\
|
||||||
(\
|
(\
|
||||||
STATIC_ASSERT(sizeof(*(THIS)) == sizeof(*(__VA_ARGS__))),\
|
STATIC_ASSERT(sizeof(*(THIS)) == sizeof(*(__VA_ARGS__))),\
|
||||||
dyn_array_contains_func((THIS), (uint8_t*)(__VA_ARGS__))\
|
dyn_array_contains_func((THIS), (uint8_t*)(__VA_ARGS__))\
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Comparison function: returns true if a equals b, false otherwise
|
||||||
|
typedef bool (*dyn_array_eq_fn)(const void *a, const void *b);
|
||||||
|
|
||||||
|
bool dyn_array_contains_cmp_func(void *this, uint8_t *value, dyn_array_eq_fn eq);
|
||||||
|
|
||||||
|
#define arr_contains_cmp(THIS, EQ_FN, ...)\
|
||||||
|
(\
|
||||||
|
STATIC_ASSERT(sizeof(*(THIS)) == sizeof(*(__VA_ARGS__))),\
|
||||||
|
dyn_array_contains_cmp_func((THIS), (uint8_t*)(__VA_ARGS__), (EQ_FN))\
|
||||||
|
)
|
||||||
|
|
||||||
|
// Comparison function for sorting: returns -1 if a < b, 0 if a == b, 1 if a > b
|
||||||
|
typedef int (*dyn_array_cmp_fn)(const void *a, const void *b);
|
||||||
|
|
||||||
|
void dyn_array_insert_sorted_func(void **this_ptr, const void *value, dyn_array_cmp_fn cmp, const char *file, int line);
|
||||||
|
|
||||||
|
#define arr_insert_sorted(THIS, VALUE, CMP_FN) do { \
|
||||||
|
typeof((THIS)[0]) _tmp_value = (VALUE); \
|
||||||
|
dyn_array_insert_sorted_func((void**)&(THIS), &_tmp_value, (CMP_FN), __FILE__, __LINE__); \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
// CLI /////////////////////////////////////////////////////////////////////////
|
// CLI /////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#define CLI_UNIQUE1 __macro_internal_34bba35b8b9b20a75f9881e3795630e25d36e620d9c9741e2e9141ba82ec6ef7__
|
#define CLI_UNIQUE1 __macro_internal_34bba35b8b9b20a75f9881e3795630e25d36e620d9c9741e2e9141ba82ec6ef7__
|
||||||
@@ -343,4 +352,5 @@ void allocator_reset(allocator_t this) {
|
|||||||
#include "string_builder.c"
|
#include "string_builder.c"
|
||||||
|
|
||||||
#endif // CIG_IMPL
|
#endif // CIG_IMPL
|
||||||
|
|
||||||
#endif // CIG_H
|
#endif // CIG_H
|
||||||
|
|||||||
+50
-32
@@ -1,38 +1,7 @@
|
|||||||
#include "cig.h"
|
#include "cig.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
typedef struct dyn_array_create_non_crashing_func_args {
|
|
||||||
allocator_t allocator;
|
|
||||||
size_t itemsize;
|
|
||||||
size_t initial_capacity;
|
|
||||||
const char *file;
|
|
||||||
size_t line;
|
|
||||||
} dyn_array_create_non_crashing_func_args_t;
|
|
||||||
|
|
||||||
|
|
||||||
static inline void *todo_remove_create_func(dyn_array_create_non_crashing_func_args_t args);
|
|
||||||
void *dyn_array_create_func(dyn_array_create_func_args_t args) {
|
void *dyn_array_create_func(dyn_array_create_func_args_t args) {
|
||||||
void *bytes = todo_remove_create_func(
|
|
||||||
(dyn_array_create_non_crashing_func_args_t) {
|
|
||||||
.allocator=args.allocator,
|
|
||||||
.itemsize=args.itemsize,
|
|
||||||
.initial_capacity=args.initial_capacity,
|
|
||||||
.file=args.file,
|
|
||||||
.line=args.line
|
|
||||||
}
|
|
||||||
);
|
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void *todo_remove_grow_func(void *this, size_t n_new_items, const char *file, int line);
|
|
||||||
void *dyn_array_grow_func(void *this, size_t n_new_items, const char *file, int line) {
|
|
||||||
void *bytes = todo_remove_grow_func(this, n_new_items, file, line);
|
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
// These functions are from when there were non-crashing versions of these
|
|
||||||
// allocating functions.
|
|
||||||
static inline void *todo_remove_create_func(dyn_array_create_non_crashing_func_args_t args) {
|
|
||||||
dyn_array_header_t *header = allocator_alloc_func(
|
dyn_array_header_t *header = allocator_alloc_func(
|
||||||
args.allocator,
|
args.allocator,
|
||||||
sizeof(dyn_array_header_t) + args.itemsize * args.initial_capacity,
|
sizeof(dyn_array_header_t) + args.itemsize * args.initial_capacity,
|
||||||
@@ -46,7 +15,7 @@ static inline void *todo_remove_create_func(dyn_array_create_non_crashing_func_a
|
|||||||
return &header->bytes;
|
return &header->bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void *todo_remove_grow_func(void *this, size_t n_new_items, const char *file, int line) {
|
void *dyn_array_grow_func(void *this, size_t n_new_items, const char *file, int line) {
|
||||||
dyn_array_header_t *header = PTR_FROM_FIELD_PTR(dyn_array_header_t, bytes, this);
|
dyn_array_header_t *header = PTR_FROM_FIELD_PTR(dyn_array_header_t, bytes, this);
|
||||||
size_t new_size = header->n_items + n_new_items;
|
size_t new_size = header->n_items + n_new_items;
|
||||||
|
|
||||||
@@ -69,6 +38,7 @@ static inline void *todo_remove_grow_func(void *this, size_t n_new_items, const
|
|||||||
return &header->bytes;
|
return &header->bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void dyn_array_shrink_func(void *this, size_t n_items_to_remove, const char *file, int line) {
|
void dyn_array_shrink_func(void *this, size_t n_items_to_remove, const char *file, int line) {
|
||||||
if (arr_len(this) < n_items_to_remove) {
|
if (arr_len(this) < n_items_to_remove) {
|
||||||
fprintf(
|
fprintf(
|
||||||
@@ -112,3 +82,51 @@ bool dyn_array_contains_func(void *this, uint8_t *value) {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool dyn_array_contains_cmp_func(void *this, uint8_t *value, dyn_array_eq_fn eq) {
|
||||||
|
dyn_array_header_t *header = PTR_FROM_FIELD_PTR(dyn_array_header_t, bytes, this);
|
||||||
|
size_t itemsize = header->itemsize;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < header->n_items; i++) {
|
||||||
|
void *element = &header->bytes[i * itemsize];
|
||||||
|
if (eq(element, value)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dyn_array_insert_sorted_func(void **this_ptr, const void *value, dyn_array_cmp_fn cmp, const char *file, int line) {
|
||||||
|
void *this = *this_ptr;
|
||||||
|
dyn_array_header_t *header = PTR_FROM_FIELD_PTR(dyn_array_header_t, bytes, this);
|
||||||
|
|
||||||
|
// First append the value
|
||||||
|
this = dyn_array_grow_func(this, 1, file, line);
|
||||||
|
*this_ptr = this;
|
||||||
|
header = PTR_FROM_FIELD_PTR(dyn_array_header_t, bytes, this);
|
||||||
|
|
||||||
|
size_t itemsize = header->itemsize;
|
||||||
|
size_t n_items = header->n_items;
|
||||||
|
|
||||||
|
// Copy value to the end
|
||||||
|
memcpy(&header->bytes[(n_items - 1) * itemsize], value, itemsize);
|
||||||
|
|
||||||
|
// Bubble it into the correct position from the back
|
||||||
|
size_t pos = n_items - 1;
|
||||||
|
while (pos > 0) {
|
||||||
|
void *a = &header->bytes[(pos - 1) * itemsize];
|
||||||
|
void *b = &header->bytes[pos * itemsize];
|
||||||
|
|
||||||
|
if (cmp(a, b) <= 0) {
|
||||||
|
break; // Already in correct position
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap using a temporary buffer
|
||||||
|
uint8_t temp[itemsize];
|
||||||
|
memcpy(temp, a, itemsize);
|
||||||
|
memcpy(a, b, itemsize);
|
||||||
|
memcpy(b, temp, itemsize);
|
||||||
|
|
||||||
|
pos--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+16
-16
@@ -186,15 +186,15 @@ Test(arena_allocator, dyn_array_resize_pattern) {
|
|||||||
allocator_t arena = arena_allocator_create(backing, 10 * KB);
|
allocator_t arena = arena_allocator_create(backing, 10 * KB);
|
||||||
allocator_reset(arena);
|
allocator_reset(arena);
|
||||||
|
|
||||||
int *arr = dyn_array_create(arena, int);
|
int *arr = make_arr(arena, int);
|
||||||
|
|
||||||
// Add 100 elements, forcing multiple resizes
|
// Add 100 elements, forcing multiple resizes
|
||||||
for (int i = 0; i < 100; i++) {
|
for (int i = 0; i < 100; i++) {
|
||||||
dyn_array_append(arr, i * 3);
|
arr_append(arr, i * 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify all elements
|
// Verify all elements
|
||||||
cr_assert_eq(dyn_array_length(arr), 100);
|
cr_assert_eq(arr_len(arr), 100);
|
||||||
for (int i = 0; i < 100; i++) {
|
for (int i = 0; i < 100; i++) {
|
||||||
cr_assert_eq(arr[i], i * 3, "Expected arr[%d] = %d, got %d", i, i * 3, arr[i]);
|
cr_assert_eq(arr[i], i * 3, "Expected arr[%d] = %d, got %d", i, i * 3, arr[i]);
|
||||||
}
|
}
|
||||||
@@ -207,32 +207,32 @@ Test(arena_allocator, multiple_arrays_resize) {
|
|||||||
allocator_t arena = arena_allocator_create(backing, 20 * KB);
|
allocator_t arena = arena_allocator_create(backing, 20 * KB);
|
||||||
allocator_reset(arena);
|
allocator_reset(arena);
|
||||||
|
|
||||||
int *arr1 = dyn_array_create(arena, int);
|
int *arr1 = make_arr(arena, int);
|
||||||
int *arr2 = dyn_array_create(arena, int);
|
int *arr2 = make_arr(arena, int);
|
||||||
|
|
||||||
// Add to arr1
|
// Add to arr1
|
||||||
for (int i = 0; i < 50; i++) {
|
for (int i = 0; i < 50; i++) {
|
||||||
dyn_array_append(arr1, i);
|
arr_append(arr1, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add to arr2
|
// Add to arr2
|
||||||
for (int i = 0; i < 30; i++) {
|
for (int i = 0; i < 30; i++) {
|
||||||
dyn_array_append(arr2, i * 2);
|
arr_append(arr2, i * 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add more to arr1
|
// Add more to arr1
|
||||||
for (int i = 50; i < 100; i++) {
|
for (int i = 50; i < 100; i++) {
|
||||||
dyn_array_append(arr1, i);
|
arr_append(arr1, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify arr1
|
// Verify arr1
|
||||||
cr_assert_eq(dyn_array_length(arr1), 100);
|
cr_assert_eq(arr_len(arr1), 100);
|
||||||
for (int i = 0; i < 100; i++) {
|
for (int i = 0; i < 100; i++) {
|
||||||
cr_assert_eq(arr1[i], i);
|
cr_assert_eq(arr1[i], i);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify arr2
|
// Verify arr2
|
||||||
cr_assert_eq(dyn_array_length(arr2), 30);
|
cr_assert_eq(arr_len(arr2), 30);
|
||||||
for (int i = 0; i < 30; i++) {
|
for (int i = 0; i < 30; i++) {
|
||||||
cr_assert_eq(arr2[i], i * 2);
|
cr_assert_eq(arr2[i], i * 2);
|
||||||
}
|
}
|
||||||
@@ -267,21 +267,21 @@ Test(arena_allocator, nested_structures_with_resize) {
|
|||||||
allocator_reset(arena);
|
allocator_reset(arena);
|
||||||
|
|
||||||
// Create array of int pointers (like dyn_array of dyn_arrays)
|
// Create array of int pointers (like dyn_array of dyn_arrays)
|
||||||
int **rows = dyn_array_create(arena, int*);
|
int **rows = make_arr(arena, int*);
|
||||||
|
|
||||||
// Add 5 rows
|
// Add 5 rows
|
||||||
for (int i = 0; i < 5; i++) {
|
for (int i = 0; i < 5; i++) {
|
||||||
int *row = dyn_array_create(arena, int);
|
int *row = make_arr(arena, int);
|
||||||
for (int j = 0; j < 10; j++) {
|
for (int j = 0; j < 10; j++) {
|
||||||
dyn_array_append(row, i * 10 + j);
|
arr_append(row, i * 10 + j);
|
||||||
}
|
}
|
||||||
dyn_array_append(rows, row);
|
arr_append(rows, row);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify data
|
// Verify data
|
||||||
cr_assert_eq(dyn_array_length(rows), 5);
|
cr_assert_eq(arr_len(rows), 5);
|
||||||
for (int i = 0; i < 5; i++) {
|
for (int i = 0; i < 5; i++) {
|
||||||
cr_assert_eq(dyn_array_length(rows[i]), 10);
|
cr_assert_eq(arr_len(rows[i]), 10);
|
||||||
for (int j = 0; j < 10; j++) {
|
for (int j = 0; j < 10; j++) {
|
||||||
cr_assert_eq(rows[i][j], i * 10 + j);
|
cr_assert_eq(rows[i][j], i * 10 + j);
|
||||||
}
|
}
|
||||||
|
|||||||
+227
-2
@@ -1,9 +1,23 @@
|
|||||||
#include <criterion/criterion.h>
|
#include <criterion/criterion.h>
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
#include "cig.h"
|
#include "cig.h"
|
||||||
|
|
||||||
|
static int int_cmp(const void *a, const void *b) {
|
||||||
|
int ia = *(const int*)a;
|
||||||
|
int ib = *(const int*)b;
|
||||||
|
if (ia < ib) return -1;
|
||||||
|
if (ia > ib) return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool int_eq(const void *a, const void *b) {
|
||||||
|
return *(const int*)a == *(const int*)b;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int reverse_cmp(const void *a, const void *b) {
|
||||||
|
return -int_cmp(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
Test(dynamic_arrays, append) {
|
Test(dynamic_arrays, append) {
|
||||||
with_borrow(alloc) {
|
with_borrow(alloc) {
|
||||||
int *numbers = make_arr(alloc, int);
|
int *numbers = make_arr(alloc, int);
|
||||||
@@ -68,3 +82,214 @@ Test(dynamic_arrays, contains) {
|
|||||||
cr_assert_eq(arr_len(numbers), 100);
|
cr_assert_eq(arr_len(numbers), 100);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Test(dynamic_arrays, insert_sorted_ascending) {
|
||||||
|
with_borrow(alloc) {
|
||||||
|
int *arr = make_arr(alloc, int, .initial_capacity = 5);
|
||||||
|
|
||||||
|
// Insert in random order
|
||||||
|
arr_insert_sorted(arr, 5, int_cmp);
|
||||||
|
arr_insert_sorted(arr, 2, int_cmp);
|
||||||
|
arr_insert_sorted(arr, 8, int_cmp);
|
||||||
|
arr_insert_sorted(arr, 1, int_cmp);
|
||||||
|
arr_insert_sorted(arr, 9, int_cmp);
|
||||||
|
arr_insert_sorted(arr, 3, int_cmp);
|
||||||
|
|
||||||
|
// Verify sorted order
|
||||||
|
cr_assert_eq(arr_len(arr), 6);
|
||||||
|
cr_assert_eq(arr[0], 1);
|
||||||
|
cr_assert_eq(arr[1], 2);
|
||||||
|
cr_assert_eq(arr[2], 3);
|
||||||
|
cr_assert_eq(arr[3], 5);
|
||||||
|
cr_assert_eq(arr[4], 8);
|
||||||
|
cr_assert_eq(arr[5], 9);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Test(dynamic_arrays, insert_sorted_descending) {
|
||||||
|
with_borrow(alloc) {
|
||||||
|
int *arr = make_arr(alloc, int, .initial_capacity = 5);
|
||||||
|
|
||||||
|
// Insert in random order with reverse comparison
|
||||||
|
arr_insert_sorted(arr, 5, reverse_cmp);
|
||||||
|
arr_insert_sorted(arr, 2, reverse_cmp);
|
||||||
|
arr_insert_sorted(arr, 8, reverse_cmp);
|
||||||
|
arr_insert_sorted(arr, 1, reverse_cmp);
|
||||||
|
|
||||||
|
// Verify descending order
|
||||||
|
cr_assert_eq(arr_len(arr), 4);
|
||||||
|
cr_assert_eq(arr[0], 8);
|
||||||
|
cr_assert_eq(arr[1], 5);
|
||||||
|
cr_assert_eq(arr[2], 2);
|
||||||
|
cr_assert_eq(arr[3], 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Test(dynamic_arrays, insert_sorted_duplicates) {
|
||||||
|
with_borrow(alloc) {
|
||||||
|
int *arr = make_arr(alloc, int, .initial_capacity = 5);
|
||||||
|
|
||||||
|
// Insert duplicates
|
||||||
|
arr_insert_sorted(arr, 5, int_cmp);
|
||||||
|
arr_insert_sorted(arr, 3, int_cmp);
|
||||||
|
arr_insert_sorted(arr, 5, int_cmp);
|
||||||
|
arr_insert_sorted(arr, 3, int_cmp);
|
||||||
|
arr_insert_sorted(arr, 5, int_cmp);
|
||||||
|
|
||||||
|
// Verify all elements are present
|
||||||
|
cr_assert_eq(arr_len(arr), 5);
|
||||||
|
cr_assert_eq(arr[0], 3);
|
||||||
|
cr_assert_eq(arr[1], 3);
|
||||||
|
cr_assert_eq(arr[2], 5);
|
||||||
|
cr_assert_eq(arr[3], 5);
|
||||||
|
cr_assert_eq(arr[4], 5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Test(dynamic_arrays, contains_found) {
|
||||||
|
with_borrow(alloc) {
|
||||||
|
int *arr = make_arr(alloc, int, .initial_capacity = 5);
|
||||||
|
arr_append(arr, 10);
|
||||||
|
arr_append(arr, 20);
|
||||||
|
arr_append(arr, 30);
|
||||||
|
|
||||||
|
cr_assert(arr_contains(arr, &(int){10}));
|
||||||
|
cr_assert(arr_contains(arr, &(int){20}));
|
||||||
|
cr_assert(arr_contains(arr, &(int){30}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Test(dynamic_arrays, contains_not_found) {
|
||||||
|
with_borrow(alloc) {
|
||||||
|
int *arr = make_arr(alloc, int, .initial_capacity = 5);
|
||||||
|
arr_append(arr, 10);
|
||||||
|
arr_append(arr, 20);
|
||||||
|
arr_append(arr, 30);
|
||||||
|
|
||||||
|
cr_assert_not(arr_contains(arr, &(int){5}));
|
||||||
|
cr_assert_not(arr_contains(arr, &(int){15}));
|
||||||
|
cr_assert_not(arr_contains(arr, &(int){99}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Test(dynamic_arrays, contains_empty) {
|
||||||
|
with_borrow(alloc) {
|
||||||
|
int *arr = make_arr(alloc, int, .initial_capacity = 5);
|
||||||
|
|
||||||
|
cr_assert_not(arr_contains(arr, &(int){10}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Test(dynamic_arrays, insert_sorted_single_element) {
|
||||||
|
with_borrow(alloc) {
|
||||||
|
int *arr = make_arr(alloc, int, .initial_capacity = 5);
|
||||||
|
|
||||||
|
arr_insert_sorted(arr, 42, int_cmp);
|
||||||
|
|
||||||
|
cr_assert_eq(arr_len(arr), 1);
|
||||||
|
cr_assert_eq(arr[0], 42);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Test(dynamic_arrays, insert_sorted_already_sorted) {
|
||||||
|
with_borrow(alloc) {
|
||||||
|
int *arr = make_arr(alloc, int, .initial_capacity = 5);
|
||||||
|
|
||||||
|
// Insert in already sorted order
|
||||||
|
arr_insert_sorted(arr, 1, int_cmp);
|
||||||
|
arr_insert_sorted(arr, 2, int_cmp);
|
||||||
|
arr_insert_sorted(arr, 3, int_cmp);
|
||||||
|
arr_insert_sorted(arr, 4, int_cmp);
|
||||||
|
|
||||||
|
// Verify order maintained
|
||||||
|
cr_assert_eq(arr_len(arr), 4);
|
||||||
|
for (size_t i = 0; i < arr_len(arr); i++) {
|
||||||
|
cr_assert_eq(arr[i], (int)(i + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Test(dynamic_arrays, insert_sorted_reverse_order) {
|
||||||
|
with_borrow(alloc) {
|
||||||
|
int *arr = make_arr(alloc, int, .initial_capacity = 5);
|
||||||
|
|
||||||
|
// Insert in reverse order (worst case for bubble sort)
|
||||||
|
arr_insert_sorted(arr, 5, int_cmp);
|
||||||
|
arr_insert_sorted(arr, 4, int_cmp);
|
||||||
|
arr_insert_sorted(arr, 3, int_cmp);
|
||||||
|
arr_insert_sorted(arr, 2, int_cmp);
|
||||||
|
arr_insert_sorted(arr, 1, int_cmp);
|
||||||
|
|
||||||
|
// Verify correct ascending order
|
||||||
|
cr_assert_eq(arr_len(arr), 5);
|
||||||
|
for (size_t i = 0; i < arr_len(arr); i++) {
|
||||||
|
cr_assert_eq(arr[i], (int)(i + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Test(dynamic_arrays, contains_cmp_found) {
|
||||||
|
with_borrow(alloc) {
|
||||||
|
int *arr = make_arr(alloc, int, .initial_capacity = 5);
|
||||||
|
arr_append(arr, 10);
|
||||||
|
arr_append(arr, 20);
|
||||||
|
arr_append(arr, 30);
|
||||||
|
|
||||||
|
cr_assert(arr_contains_cmp(arr, int_eq, &(int){10}));
|
||||||
|
cr_assert(arr_contains_cmp(arr, int_eq, &(int){20}));
|
||||||
|
cr_assert(arr_contains_cmp(arr, int_eq, &(int){30}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Test(dynamic_arrays, contains_cmp_not_found) {
|
||||||
|
with_borrow(alloc) {
|
||||||
|
int *arr = make_arr(alloc, int, .initial_capacity = 5);
|
||||||
|
arr_append(arr, 10);
|
||||||
|
arr_append(arr, 20);
|
||||||
|
arr_append(arr, 30);
|
||||||
|
|
||||||
|
cr_assert_not(arr_contains_cmp(arr, int_eq, &(int){5}));
|
||||||
|
cr_assert_not(arr_contains_cmp(arr, int_eq, &(int){15}));
|
||||||
|
cr_assert_not(arr_contains_cmp(arr, int_eq, &(int){99}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Test(dynamic_arrays, contains_cmp_empty) {
|
||||||
|
with_borrow(alloc) {
|
||||||
|
int *arr = make_arr(alloc, int, .initial_capacity = 5);
|
||||||
|
|
||||||
|
cr_assert_not(arr_contains_cmp(arr, int_eq, &(int){10}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test using arr_contains_cmp for a simple key-value map
|
||||||
|
typedef struct {
|
||||||
|
int key;
|
||||||
|
const char *value;
|
||||||
|
} kv_pair_t;
|
||||||
|
|
||||||
|
static bool kv_eq_by_key(const void *a, const void *b) {
|
||||||
|
const kv_pair_t *pa = a;
|
||||||
|
const kv_pair_t *pb = b;
|
||||||
|
return pa->key == pb->key;
|
||||||
|
}
|
||||||
|
|
||||||
|
Test(dynamic_arrays, contains_cmp_key_value_map) {
|
||||||
|
with_borrow(alloc) {
|
||||||
|
kv_pair_t *map = make_arr(alloc, kv_pair_t, .initial_capacity = 10);
|
||||||
|
|
||||||
|
// Add some key-value pairs
|
||||||
|
arr_append(map, ((kv_pair_t){.key = 1, .value = "one"}));
|
||||||
|
arr_append(map, ((kv_pair_t){.key = 5, .value = "five"}));
|
||||||
|
arr_append(map, ((kv_pair_t){.key = 10, .value = "ten"}));
|
||||||
|
|
||||||
|
// Check if keys exist
|
||||||
|
cr_assert(arr_contains_cmp(map, kv_eq_by_key, &(kv_pair_t){.key = 1}));
|
||||||
|
cr_assert(arr_contains_cmp(map, kv_eq_by_key, &(kv_pair_t){.key = 5}));
|
||||||
|
cr_assert(arr_contains_cmp(map, kv_eq_by_key, &(kv_pair_t){.key = 10}));
|
||||||
|
|
||||||
|
// Check for non-existent keys
|
||||||
|
cr_assert_not(arr_contains_cmp(map, kv_eq_by_key, &(kv_pair_t){.key = 2}));
|
||||||
|
cr_assert_not(arr_contains_cmp(map, kv_eq_by_key, &(kv_pair_t){.key = 99}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user