diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bb5e641..432bb67 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: - name: Install dependencies run: | sudo apt-get update - sudo apt-get install -y gcc make libcriterion-dev valgrind + sudo apt-get install -y gcc make libcriterion-dev valgrind zig - name: Build and run tests run: make test diff --git a/buffer_allocator.c b/buffer_allocator.c index 2f0b259..1422e86 100644 --- a/buffer_allocator.c +++ b/buffer_allocator.c @@ -1,13 +1,11 @@ #include "cig.h" static void *buffer_allocator_alloc(buffer_allocator_t *this, size_t bytes) { - size_t address_of_new_data = (size_t) &this->data[this->size]; - size_t offset = address_of_new_data % MAX_ALIGN; - size_t new_size = this->size + offset + bytes; + bytes = (bytes + MAX_ALIGN - 1) / MAX_ALIGN * MAX_ALIGN; + size_t new_size = this->size + bytes; if (new_size > this->capacity) { return NULL; } - this->size += offset; void *ptr = &this->data[this->size]; this->size = new_size; return ptr; diff --git a/cig.h b/cig.h index 5781605..15a8a5f 100644 --- a/cig.h +++ b/cig.h @@ -13,8 +13,7 @@ typedef union any_align { char c; int i; long l; long long ll; float f; double d #define KB (1024) #define MB (KB * KB) #define GB (KB * KB * KB) -#define OFFSET(STRUCT, FIELD) ((size_t) (&((STRUCT*) NULL)->FIELD)) -#define PTR_FROM_FIELD_PTR(STRUCT, FIELD, PTR) ((STRUCT *) (((char *) PTR) - OFFSET(STRUCT, FIELD))) +#define PTR_FROM_FIELD_PTR(STRUCT, FIELD, PTR) ((STRUCT *) (((char *) PTR) - offsetof(STRUCT, FIELD))) // Contains all operations an allocator can do. Similar interface to sdtlibs // malloc, realloc and free. @@ -144,12 +143,11 @@ typedef struct dyn_array_create_func_args { } dyn_array_create_func_args_t; void *dyn_array_create_func(dyn_array_create_func_args_t args); -#define make_arr(ALLOCATOR, TYPE, ...) ((TYPE *)dyn_array_create_func((dyn_array_create_func_args_t){ \ - .allocator = ALLOCATOR, \ +#define make_arr(TYPE, ...) ((TYPE *)dyn_array_create_func((dyn_array_create_func_args_t){ \ .itemsize = sizeof(TYPE), \ .file = __FILE__, \ .line = __LINE__, \ - __VA_ARGS__ \ + .allocator = __VA_ARGS__, \ })) // Always reassign the array. if multiple variables reference the same growing @@ -180,13 +178,13 @@ size_t arr_cap(void *this); #define arr_pop(THIS) (arr_shrink(THIS, 1), THIS[arr_len(THIS)]) -void dyn_array_bounds_check_func(void *this, size_t index, const char *file, int line); +void dyn_array_bounds_check_func(void *this, size_t index, const char *file, int line, bool print_error); #define arr_get(THIS, INDEX) \ - (dyn_array_bounds_check_func(THIS, INDEX, __FILE__, __LINE__), (THIS)[INDEX]) + (dyn_array_bounds_check_func(THIS, INDEX, __FILE__, __LINE__, true), (THIS)[INDEX]) #define arr_set(THIS, INDEX, VALUE) \ - (dyn_array_bounds_check_func(THIS, INDEX, __FILE__, __LINE__), (THIS)[INDEX] = (VALUE)) + (dyn_array_bounds_check_func(THIS, INDEX, __FILE__, __LINE__, true), (THIS)[INDEX] = (VALUE)) #define STATIC_ASSERT(expr) ((void)sizeof(char[(expr) ? 1 : -1])) @@ -212,13 +210,6 @@ bool dyn_array_contains_cmp_func(void *this, uint8_t *value, dyn_array_eq_fn eq) // 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 ///////////////////////////////////////////////////////////////////////// #define CLI_UNIQUE1 __macro_internal_34bba35b8b9b20a75f9881e3795630e25d36e620d9c9741e2e9141ba82ec6ef7__ diff --git a/dyn_array.c b/dyn_array.c index c5ece80..1a1ae4e 100644 --- a/dyn_array.c +++ b/dyn_array.c @@ -96,52 +96,20 @@ bool dyn_array_contains_cmp_func(void *this, uint8_t *value, dyn_array_eq_fn eq) return false; } -void dyn_array_bounds_check_func(void *this, size_t index, const char *file, int line) { +void dyn_array_bounds_check_func(void *this, size_t index, const char *file, int line, bool print_error) { size_t len = arr_len(this); - if (index >= len) { - fprintf( - stderr, - "%s:%d: array index %zu out of bounds (length is %zu)\n", - file, - line, - index, - len - ); + if ((index >= len)) { + if (print_error) { + fprintf( + stderr, + "%s:%d: array index %zu out of bounds (length is %zu)\n", + file, + line, + index, + len + ); + } exit(1); } } -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--; - } -} diff --git a/file_io.c b/file_io.c index de2d1a9..a4143b9 100644 --- a/file_io.c +++ b/file_io.c @@ -19,7 +19,7 @@ char *read_entire_file(const char *path, allocator_t allocator) { // Returns dynamic array, of fixed char strings. char **read_all_file_lines(const char *path, allocator_t allocator) { char *contents = read_entire_file(path, allocator); - char **lines = make_arr(allocator, char*); + char **lines = make_arr(char*, allocator); arr_append(lines, contents); bool just_split = false; for (char *c = contents; (*c)!='\0'; c++) { diff --git a/makefile b/makefile index 5a7b0c0..ce3db8c 100644 --- a/makefile +++ b/makefile @@ -1,5 +1,5 @@ -CC := gcc +CC := zig cc CFLAGS := -pedantic -Wall -Wextra -Wno-override-init -O0 -g -fno-omit-frame-pointer -fno-inline LDFLAGS := -lcriterion diff --git a/test_arena_allocator.c b/test_arena_allocator.c index 0ac264e..478c1f7 100644 --- a/test_arena_allocator.c +++ b/test_arena_allocator.c @@ -186,7 +186,7 @@ Test(arena_allocator, dyn_array_resize_pattern) { allocator_t arena = arena_allocator_create(backing, 10 * KB); allocator_reset(arena); - int *arr = make_arr(arena, int); + int *arr = make_arr(int, arena); // Add 100 elements, forcing multiple resizes for (int i = 0; i < 100; i++) { @@ -207,8 +207,8 @@ Test(arena_allocator, multiple_arrays_resize) { allocator_t arena = arena_allocator_create(backing, 20 * KB); allocator_reset(arena); - int *arr1 = make_arr(arena, int); - int *arr2 = make_arr(arena, int); + int *arr1 = make_arr(int, arena); + int *arr2 = make_arr(int, arena); // Add to arr1 for (int i = 0; i < 50; i++) { @@ -267,11 +267,11 @@ Test(arena_allocator, nested_structures_with_resize) { allocator_reset(arena); // Create array of int pointers (like dyn_array of dyn_arrays) - int **rows = make_arr(arena, int*); + int **rows = make_arr(int*, arena); // Add 5 rows for (int i = 0; i < 5; i++) { - int *row = make_arr(arena, int); + int *row = make_arr(int, arena); for (int j = 0; j < 10; j++) { arr_append(row, i * 10 + j); } diff --git a/test_buffer_allocator.c b/test_buffer_allocator.c index cfbc6f1..131d903 100644 --- a/test_buffer_allocator.c +++ b/test_buffer_allocator.c @@ -11,7 +11,7 @@ static void test_buffer_alloc(buffer_allocator_t impl) { for (int i = 0; i < n_ints; i++) { cr_assert_eq(ints[i], i); } - cr_assert_eq(impl.size, sizeof(int)*n_ints); + cr_assert_eq(impl.size, sizeof(int)*n_ints, "%zu != %d", impl.size, n_ints); ints = allocator_alloc(inter, sizeof(int) * n_ints); for (int i = 0; i < n_ints; i++) { ints[i] = i; diff --git a/test_dynamic_array.c b/test_dynamic_array.c index 08a591b..a88d16e 100644 --- a/test_dynamic_array.c +++ b/test_dynamic_array.c @@ -2,25 +2,13 @@ #include #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) { with_borrow(alloc) { - int *numbers = make_arr(alloc, int); + int *numbers = make_arr(int, alloc); arr_append(numbers, 40); arr_append(numbers, 41); arr_append(numbers, 42); @@ -40,7 +28,7 @@ Test(dynamic_arrays, append) { Test(dynamic_arrays, pop) { with_borrow(alloc) { - int *numbers = make_arr(alloc, int); + int *numbers = make_arr(int, alloc); arr_append(numbers, 40); arr_append(numbers, 41); arr_append(numbers, 42); @@ -67,7 +55,7 @@ Test(dynamic_arrays, pop) { Test(dynamic_arrays, contains) { with_borrow(alloc) { - int *numbers = make_arr(alloc, int); + int *numbers = make_arr(int, alloc); arr_append(numbers, 20); cr_expect(arr_contains(numbers, &(int){20})); arr_reset(numbers); @@ -82,72 +70,12 @@ Test(dynamic_arrays, contains) { 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); + int *arr = make_arr(int, alloc, .initial_capacity = 5); arr_append(arr, 10); arr_append(arr, 20); arr_append(arr, 30); @@ -160,7 +88,7 @@ Test(dynamic_arrays, contains_found) { Test(dynamic_arrays, contains_not_found) { with_borrow(alloc) { - int *arr = make_arr(alloc, int, .initial_capacity = 5); + int *arr = make_arr(int, alloc, .initial_capacity = 5); arr_append(arr, 10); arr_append(arr, 20); arr_append(arr, 30); @@ -173,63 +101,16 @@ Test(dynamic_arrays, contains_not_found) { Test(dynamic_arrays, contains_empty) { with_borrow(alloc) { - int *arr = make_arr(alloc, int, .initial_capacity = 5); + int *arr = make_arr(int, alloc, .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); + int *arr = make_arr(int, alloc, .initial_capacity = 5); arr_append(arr, 10); arr_append(arr, 20); arr_append(arr, 30); @@ -242,7 +123,7 @@ Test(dynamic_arrays, contains_cmp_found) { Test(dynamic_arrays, contains_cmp_not_found) { with_borrow(alloc) { - int *arr = make_arr(alloc, int, .initial_capacity = 5); + int *arr = make_arr(int, alloc, .initial_capacity = 5); arr_append(arr, 10); arr_append(arr, 20); arr_append(arr, 30); @@ -255,7 +136,7 @@ Test(dynamic_arrays, contains_cmp_not_found) { Test(dynamic_arrays, contains_cmp_empty) { with_borrow(alloc) { - int *arr = make_arr(alloc, int, .initial_capacity = 5); + int *arr = make_arr(int, alloc, .initial_capacity = 5); cr_assert_not(arr_contains_cmp(arr, int_eq, &(int){10})); } @@ -275,7 +156,7 @@ static bool kv_eq_by_key(const void *a, const void *b) { Test(dynamic_arrays, contains_cmp_key_value_map) { with_borrow(alloc) { - kv_pair_t *map = make_arr(alloc, kv_pair_t, .initial_capacity = 10); + kv_pair_t *map = make_arr(kv_pair_t, alloc, .initial_capacity = 10); // Add some key-value pairs arr_append(map, ((kv_pair_t){.key = 1, .value = "one"})); @@ -295,7 +176,7 @@ Test(dynamic_arrays, contains_cmp_key_value_map) { Test(dynamic_arrays, get_valid_indices) { with_borrow(alloc) { - int *arr = make_arr(alloc, int); + int *arr = make_arr(int, alloc); arr_append(arr, 10); arr_append(arr, 20); arr_append(arr, 30); @@ -310,7 +191,7 @@ Test(dynamic_arrays, get_valid_indices) { Test(dynamic_arrays, set_valid_indices) { with_borrow(alloc) { - int *arr = make_arr(alloc, int); + int *arr = make_arr(int, alloc); arr_append(arr, 10); arr_append(arr, 20); arr_append(arr, 30); @@ -327,36 +208,26 @@ Test(dynamic_arrays, set_valid_indices) { Test(dynamic_arrays, get_out_of_bounds, .exit_code = 1) { with_borrow(alloc) { - int *arr = make_arr(alloc, int); + int *arr = make_arr(int, alloc); arr_append(arr, 10); arr_append(arr, 20); - - (void)arr_get(arr, 2); + dyn_array_bounds_check_func( arr, 2, __FILE__, __LINE__, false); } } Test(dynamic_arrays, set_out_of_bounds, .exit_code = 1) { with_borrow(alloc) { - int *arr = make_arr(alloc, int); + int *arr = make_arr(int, alloc); arr_append(arr, 10); arr_append(arr, 20); - arr_set(arr, 2, 999); + dyn_array_bounds_check_func( arr, 2, __FILE__, __LINE__, false); } } Test(dynamic_arrays, get_empty_array, .exit_code = 1) { with_borrow(alloc) { - int *arr = make_arr(alloc, int); - (void)arr_get(arr, 0); + int *arr = make_arr(int, alloc); + dyn_array_bounds_check_func( arr, 0, __FILE__, __LINE__, false); } } - -Test(dynamic_arrays, set_empty_array, .exit_code = 1) { - with_borrow(alloc) { - int *arr = make_arr(alloc, int); - arr_set(arr, 0, 42); - } -} - - diff --git a/test_macro_magic.c b/test_macro_magic.c index 0961f20..8680019 100644 --- a/test_macro_magic.c +++ b/test_macro_magic.c @@ -14,6 +14,7 @@ typedef struct args { #define ARGS_DEFAULT .animal=cat, .is_true=true #define ARGS(...) ((args_t){ ARGS_DEFAULT, __VA_ARGS__ }) +// NOTE: this is actually an extension, not really supported by the C standard. Test(macro_magic, default_values) { args_t a1 = ARGS(); args_t a2 = ARGS(.animal=dog);