diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..a72933d --- /dev/null +++ b/.clang-format @@ -0,0 +1,22 @@ +BasedOnStyle: LLVM + +IndentWidth: 4 +TabWidth: 4 +UseTab: Never + +ColumnLimit: 100 + +BreakBeforeBraces: Allman + +AllowShortIfStatementsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None + +PointerAlignment: Left + +SpaceBeforeParens: ControlStatements + +SortIncludes: true + +IndentCaseLabels: true + +AlignEscapedNewlines: Left \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0c26c8f..71afb50 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,20 +1,18 @@ # Library Configuration -add_library(dynamic_array - dynamic_array.c - dynamic_array.h) -target_include_directories(dynamic_array INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) -set_target_properties(dynamic_array PROPERTIES PUBLIC_HEADER dynamic_array.h) +include(GNUInstallDirs) - -# Install the library to a specific directory (e.g., 'lib') relative to the install prefix -install(TARGETS dynamic_array - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} # Installs shared libs to /lib - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} # Installs static libs to /lib - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} # Installs associated executables to /bin - PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} # Installs headers to /include +add_library(dynamic_array INTERFACE) +target_include_directories(dynamic_array INTERFACE + $ + $ ) +# Install the exported target and public header +install(TARGETS dynamic_array) +install(FILES dynamic_array.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + + # Testing add_executable(test_dynamic_array test_dynamic_array.c) diff --git a/src/dynamic_array.c b/src/dynamic_array.c deleted file mode 100644 index 653fb1b..0000000 --- a/src/dynamic_array.c +++ /dev/null @@ -1,71 +0,0 @@ -#include -#include -#include - -#include "dynamic_array.h" - - -Array *array_create(const ArrayCreateOptions *options) { - const ArrayCreateOptions opt = options != NULL ? *options : DYNAMIC_ARRAY_DEFAULT_ARRAY_CREATE_OPTIONS; - Array *array = malloc(sizeof(Array)); - - array->capacity = opt.initial_size; - array->value = calloc(array->capacity, opt.element_size); - array->size = 0; - array->element_size = opt.element_size; - - return array; -} - -void array_deconstructor(Array **pp_array) { - if (*pp_array == NULL) return; - - free((*pp_array)->value); - free(*pp_array); - *pp_array = NULL; -} - -void array_resize(Array *p_array, const size_t new_size) { - int *new_ptr = realloc(p_array->value, new_size*p_array->element_size); - if (new_ptr == NULL) { - exit(1); - } - - if (new_size > p_array->capacity) { - memset(new_ptr + p_array->capacity, 0, (new_size - p_array->capacity) * p_array->element_size); - } - - p_array->value = new_ptr; - p_array->capacity = new_size; -} - -void *array_get_value(const Array *p_array, const size_t index) { - if (index > p_array->capacity) { - return NULL; - } - return p_array->value + index * p_array->element_size; -} - -void array_set_value(Array *p_array, const size_t index, const void *value) { - if ((index + 1) > p_array->capacity) { - size_t new_size = p_array->capacity; - while (index >= new_size) { - new_size *= 2; - } - array_resize(p_array, new_size); - } - memcpy( - array_get_value(p_array, index), value, p_array->element_size - ); - if (index >= p_array->size) { - p_array->size = index + 1; - } -} - -size_t array_get_size(const Array *p_array) { - return p_array->size; -} - -size_t array_get_capacity(const Array *p_array) { - return p_array->capacity; -} diff --git a/src/dynamic_array.h b/src/dynamic_array.h index c2736c7..e4cdbfe 100644 --- a/src/dynamic_array.h +++ b/src/dynamic_array.h @@ -7,59 +7,64 @@ #define DYNAMIC_ARRAY_VERSION ((DYNAMIC_ARRAY_VERSION_MAJOR << 16) | (DYNAMIC_ARRAY_VERSION_MINOR << 8) | DYNAMIC_ARRAY_VERSION_BUILD) #include +#include #define DYNAMIC_ARRAY_DEFAULT_ARRAY_SIZE 10 +#define DYNAMIC_ARRAY_CAPACITY_FACTOR 2 + +// [ HEADER | ARRAY ELEMENTS ] +// ↑ +// THIS IS THE POINTER RETURNED BY ALL MACROS typedef struct { - void *value; - size_t element_size; - size_t size; size_t capacity; -} Array; +} ArrayHeader; -typedef struct { - int initial_size; - size_t element_size; -} ArrayCreateOptions; +#define array_create(arr) \ + do { \ + ArrayHeader *header = malloc(sizeof(*arr) * DYNAMIC_ARRAY_DEFAULT_ARRAY_SIZE + sizeof(ArrayHeader)); \ + header->size = 0; \ + header->capacity = DYNAMIC_ARRAY_DEFAULT_ARRAY_SIZE; \ + \ + arr = (void *)(header + 1); \ + } while(0) -#define DYNAMIC_ARRAY_DEFAULT_ARRAY_CREATE_OPTIONS \ - (ArrayCreateOptions){ DYNAMIC_ARRAY_DEFAULT_ARRAY_SIZE, sizeof(int) } - -Array *array_create(const ArrayCreateOptions *options); +#define array_get_header(arr) ((ArrayHeader*)(arr) - 1) -void array_deconstructor(Array **pp_array); +#define array_destroy(arr) \ + do { \ + free(array_get_header(arr)); \ + arr = NULL; \ + } while (0) -void array_resize(Array *p_array, size_t new_size); +#define array_push_value(arr, value) \ + do { \ + ArrayHeader *header = array_get_header(arr); \ + if (header->size + 1 > header->capacity) { \ + size_t new_capacity = header->capacity * DYNAMIC_ARRAY_CAPACITY_FACTOR; \ + ArrayHeader *new_header = realloc(header, sizeof(*arr) * new_capacity + sizeof(ArrayHeader)); \ + if (new_header != NULL) { \ + header = new_header; \ + } \ + header->capacity = new_capacity; \ + arr = (void *)(header + 1); \ + } \ + arr[header->size] = value; \ + header->size++; \ + } while (0) -void *array_get_value(const Array *p_array, size_t index); - - -void array_set_value(Array *p_array, size_t index, const void *value); - - -size_t array_get_size(const Array *p_array); - - -size_t array_get_capacity(const Array *p_array); - - - -#define array_get_value_as(type, arr, idx) \ - (sizeof(type) == (arr)->element_size) ? \ - (type*)array_get_value(arr, idx) : \ - NULL - -#define array_set_value_as(type, arr, idx, value) \ - ((sizeof(type) == (arr)->element_size) ? \ - (array_set_value((arr), (idx), (value)), 0) : \ - -1) +#define array_pop(arr) \ + do { \ + ArrayHeader *header = array_get_header(arr); \ + header->size--; \ + } while (0) #endif \ No newline at end of file diff --git a/src/test_dynamic_array.c b/src/test_dynamic_array.c index 571a481..0f23ea6 100644 --- a/src/test_dynamic_array.c +++ b/src/test_dynamic_array.c @@ -3,361 +3,142 @@ #include "dynamic_array.h" -void setUp() { -} +void setUp(void) {} -void tearDown() { -} +void tearDown(void) {} void test_array_create_without_options(void) { - const Array *array = array_create(NULL); - TEST_ASSERT_EQUAL(DYNAMIC_ARRAY_DEFAULT_ARRAY_SIZE, array->capacity); + int *arr = NULL; + array_create(arr); + + const ArrayHeader *header = array_get_header(arr); + TEST_ASSERT_EQUAL(DYNAMIC_ARRAY_DEFAULT_ARRAY_SIZE, header->capacity); + + array_destroy(arr); } -void test_array_create_with_options(void) { - const int EXPECTED_CAPACITY = 20; - - const Array *array = array_create(&(ArrayCreateOptions){ - .initial_size = EXPECTED_CAPACITY - }); - TEST_ASSERT_EQUAL(EXPECTED_CAPACITY, array->capacity); +void test_array_destroy(void) { + int *arr = NULL; + array_create(arr); + array_destroy(arr); + TEST_ASSERT_NULL(arr); } -void test_array_deconstruct(void) { - Array *array = array_create(NULL); - array_deconstructor(&array); - TEST_ASSERT_NULL(array); +void test_array_size_initial(void) { + int *arr = NULL; + array_create(arr); + + TEST_ASSERT_EQUAL(0, array_get_header(arr)->size); + + array_destroy(arr); } -void test_array_resize_expand(void) { - Array *array = array_create(NULL); - const size_t ORIGINAL_CAPACITY = array->capacity; - const int NEW_CAPACITY = 30; +void test_array_push_value(void) { + int *arr = NULL; + array_create(arr); - array_resize(array, NEW_CAPACITY); + array_push_value(arr, 42); - TEST_ASSERT_EQUAL(NEW_CAPACITY, array->capacity); - TEST_ASSERT_GREATER_THAN(ORIGINAL_CAPACITY, array->capacity); + TEST_ASSERT_EQUAL(42, arr[0]); + TEST_ASSERT_EQUAL(1, array_get_header(arr)->size); - array_deconstructor(&array); + array_destroy(arr); } -void test_array_resize_shrink(void) { - Array *array = array_create(&(ArrayCreateOptions){.initial_size = 50, .element_size = sizeof(int)}); - const int NEW_CAPACITY = 20; +void test_array_push_multiple_values(void) { + int *arr = NULL; + array_create(arr); - array_resize(array, NEW_CAPACITY); + array_push_value(arr, 10); + array_push_value(arr, 20); + array_push_value(arr, 30); - TEST_ASSERT_EQUAL(NEW_CAPACITY, array->capacity); + TEST_ASSERT_EQUAL(10, arr[0]); + TEST_ASSERT_EQUAL(20, arr[1]); + TEST_ASSERT_EQUAL(30, arr[2]); + TEST_ASSERT_EQUAL(3, array_get_header(arr)->size); - array_deconstructor(&array); + array_destroy(arr); } -void test_array_set_value_within_capacity(void) { - Array *p_array = array_create(NULL); - const int INDEX = 5; - const int EXPECTED_VALUE = 42; +void test_array_pop_decrements_size(void) { + int *arr = NULL; + array_create(arr); - array_set_value(p_array, INDEX, &EXPECTED_VALUE); + array_push_value(arr, 42); + array_push_value(arr, 99); + array_pop(arr); - const int VALUE = *(int*)array_get_value(p_array, INDEX); + TEST_ASSERT_EQUAL(1, array_get_header(arr)->size); - TEST_ASSERT_EQUAL(EXPECTED_VALUE, VALUE); - - array_deconstructor(&p_array); -} - - -void test_array_set_value_beyond_capacity(void) { - Array *p_array = array_create(NULL); - const int INDEX = 50; - const int EXPECTED_VALUE = 99; - const size_t ORIGINAL_CAPACITY = p_array->capacity; - - array_set_value(p_array, INDEX, &EXPECTED_VALUE); - - const int VALUE = *(int*)array_get_value(p_array, INDEX); - - TEST_ASSERT_GREATER_THAN(ORIGINAL_CAPACITY, p_array->capacity); - TEST_ASSERT_EQUAL(EXPECTED_VALUE, VALUE); - - array_deconstructor(&p_array); -} - - -void test_array_get_value_valid_index(void) { - Array *p_array = array_create(NULL); - const int INDEX = 3; - const int EXPECTED_VALUE = 77; - - array_set_value(p_array, INDEX, &EXPECTED_VALUE); - const int *result = array_get_value(p_array, INDEX); - - TEST_ASSERT_NOT_NULL(result); - TEST_ASSERT_EQUAL(EXPECTED_VALUE, *result); - - array_deconstructor(&p_array); -} - - -void test_array_get_value_zero_index(void) { - Array *array = array_create(NULL); - const int VALUE = 100; - - array_set_value(array, 0, &VALUE); - const int *result = array_get_value(array, 0); - - TEST_ASSERT_NOT_NULL(result); - TEST_ASSERT_EQUAL(VALUE, *result); - - array_deconstructor(&array); -} - - -void test_array_get_value_beyond_capacity(void) { - Array *array = array_create(NULL); - - const int *result = array_get_value(array, array->capacity + 1); - - TEST_ASSERT_NULL(result); - - array_deconstructor(&array); -} - - -void test_array_multiple_operations(void) { - Array *array = array_create(&(ArrayCreateOptions){.initial_size = 5, .element_size = sizeof(int)}); - - const int VALUE = 42; - - array_set_value(array, 0, &VALUE); - TEST_ASSERT_EQUAL(VALUE, *(int *)array_get_value(array, 0)); - - array_set_value(array, 1, &VALUE); - TEST_ASSERT_EQUAL(VALUE, *(int *)array_get_value(array, 1)); - - array_set_value(array, 2, &VALUE); - TEST_ASSERT_EQUAL(VALUE, *(int *)array_get_value(array, 2)); - - array_set_value(array, 10, &VALUE); - TEST_ASSERT_EQUAL(VALUE, *(int *)array_get_value(array, 10)); - - array_deconstructor(&array); + array_destroy(arr); } void test_array_get_capacity_default(void) { - Array *array = array_create(NULL); + int *arr = NULL; + array_create(arr); - const size_t capacity = array_get_capacity(array); + TEST_ASSERT_EQUAL(DYNAMIC_ARRAY_DEFAULT_ARRAY_SIZE, array_get_header(arr)->capacity); - TEST_ASSERT_EQUAL(DYNAMIC_ARRAY_DEFAULT_ARRAY_SIZE, capacity); - - array_deconstructor(&array); + array_destroy(arr); } -void test_array_get_capacity_custom_size(void) { - const int EXPECTED_CAPACITY = 25; - Array *array = array_create(&(ArrayCreateOptions){.initial_size = EXPECTED_CAPACITY}); +void test_array_size_capacity_relationship(void) { + int *arr = NULL; + array_create(arr); - const size_t capacity = array_get_capacity(array); + array_push_value(arr, 1); + array_push_value(arr, 2); + array_push_value(arr, 3); - TEST_ASSERT_EQUAL(EXPECTED_CAPACITY, capacity); + const ArrayHeader *header = array_get_header(arr); + TEST_ASSERT_EQUAL(3, header->size); + TEST_ASSERT_LESS_OR_EQUAL(header->capacity, header->size); - array_deconstructor(&array); + array_destroy(arr); } -void test_array_get_capacity_after_resize(void) { - Array *array = array_create(NULL); - const size_t ORIGINAL_CAPACITY = array_get_capacity(array); - const size_t NEW_CAPACITY = 50; +void test_array_push_beyond_capacity(void) { + int *arr = NULL; + array_create(arr); - array_resize(array, NEW_CAPACITY); - const size_t capacity = array_get_capacity(array); - - TEST_ASSERT_EQUAL(NEW_CAPACITY, capacity); - TEST_ASSERT_NOT_EQUAL(ORIGINAL_CAPACITY, capacity); - - array_deconstructor(&array); -} - - -void test_array_get_size_initial(void) { - Array *array = array_create(NULL); - - const size_t size = array_get_size(array); - - TEST_ASSERT_EQUAL(0, size); - - array_deconstructor(&array); -} - - -void test_array_get_size_after_single_set(void) { - Array *array = array_create(NULL); - - const int VALUE = 42; - - array_set_value(array, 0, &VALUE); - const size_t size = array_get_size(array); - - TEST_ASSERT_EQUAL(1, size); - - array_deconstructor(&array); -} - - -void test_array_get_size_after_multiple_sets(void) { - Array *array = array_create(NULL); - - const int VALUE = 42; - - array_set_value(array, 0, &VALUE); - array_set_value(array, 1, &VALUE); - array_set_value(array, 2, &VALUE); - const size_t size = array_get_size(array); - - TEST_ASSERT_EQUAL(3, size); - - array_deconstructor(&array); -} - - -void test_array_get_size_with_gap_in_indices(void) { - Array *array = array_create(NULL); - - const int VALUE = 42; - - array_set_value(array, 0, &VALUE); - array_set_value(array, 5, &VALUE); - const size_t size = array_get_size(array); - - TEST_ASSERT_EQUAL(6, size); - - array_deconstructor(&array); -} - - -void test_array_get_size_capacity_relationship(void) { - Array *array = array_create(&(ArrayCreateOptions){.initial_size = 10}); - - const int VALUE = 42; - - array_set_value(array, 0, &VALUE); - array_set_value(array, 1, &VALUE); - array_set_value(array, 2, &VALUE); - - const size_t size = array_get_size(array); - const size_t capacity = array_get_capacity(array); - - TEST_ASSERT_LESS_OR_EQUAL(capacity, size); - TEST_ASSERT_EQUAL(3, size); - TEST_ASSERT_EQUAL(10, capacity); - - array_deconstructor(&array); -} - - -void test_array_get_size_after_resize_beyond_capacity(void) { - Array *array = array_create(&(ArrayCreateOptions){.initial_size = 5, .element_size = sizeof(int)}); - - const int VALUE = 42; - - array_set_value(array, 0, &VALUE); - array_set_value(array, 15, &VALUE); - const size_t size = array_get_size(array); - const size_t capacity = array_get_capacity(array); - - TEST_ASSERT_EQUAL(16, size); - TEST_ASSERT_LESS_OR_EQUAL(capacity, size); - - array_deconstructor(&array); -} - - -void test_array_get_value_as(void) { - Array *p_array = array_create(&(ArrayCreateOptions){ - .initial_size = 5, - .element_size = sizeof(int) - }); - const int VALUE = 42; - - array_set_value(p_array, 0, &VALUE); - - const int *value = array_get_value_as(int, p_array, 0); - - TEST_ASSERT_EQUAL(VALUE, *value); -} - - -void test_array_get_value_as_invalid_type(void) { - Array *p_array = array_create(&(ArrayCreateOptions){ - .initial_size = 5, - .element_size = sizeof(int) - }); - const int VALUE = 42; - - array_set_value(p_array, 0, &VALUE); - - const void *value = array_get_value_as(double, p_array, 0); - - TEST_ASSERT_NULL(value); -} - - -void test_array_set_value_as(void) { - Array *p_array = array_create(&(ArrayCreateOptions){ - .initial_size = 5, - .element_size = sizeof(double) - }); - - const double VALUE = 42; - - const int result = array_set_value_as(double, p_array, 0, &VALUE); - TEST_ASSERT_EQUAL(result, 0); -} - - -void test_array_set_value_as_invalid_type(void) { - Array *p_array = array_create(&(ArrayCreateOptions){ - .initial_size = 5, - .element_size = sizeof(double) - }); - - const int VALUE = 42; - - const int result = array_set_value_as(double, p_array, 0, &VALUE); - TEST_ASSERT_EQUAL(result, 0); -} - - -void test_adding_elements(void) { - typedef struct { - int value; - } Element; - - Array *p_array = array_create(&(ArrayCreateOptions){ - .initial_size = 10, - .element_size = sizeof(Element) - }); - - const int number_of_elements = 1000; - - for (int index = 0; index < number_of_elements; index++) { - Element element = { index }; - const int result = array_set_value_as(Element, p_array, index, &element); - TEST_ASSERT_EQUAL(result, 0); + const int num_elements = DYNAMIC_ARRAY_DEFAULT_ARRAY_SIZE * 2; + for (int i = 0; i < num_elements; i++) { + array_push_value(arr, i); } - TEST_ASSERT_EQUAL(array_get_size(p_array), number_of_elements); + const ArrayHeader *header = array_get_header(arr); + TEST_ASSERT_EQUAL(num_elements, header->size); + TEST_ASSERT_GREATER_THAN(DYNAMIC_ARRAY_DEFAULT_ARRAY_SIZE, header->capacity); + + array_destroy(arr); +} + + +void test_array_values_correct_after_growth(void) { + int *arr = NULL; + array_create(arr); + + const int num_elements = 1000; + for (int i = 0; i < num_elements; i++) { + array_push_value(arr, i); + } + + TEST_ASSERT_EQUAL(num_elements, array_get_header(arr)->size); + for (int i = 0; i < num_elements; i++) { + TEST_ASSERT_EQUAL(i, arr[i]); + } + + array_destroy(arr); } @@ -366,30 +147,15 @@ int main(void) UNITY_BEGIN(); RUN_TEST(test_array_create_without_options); - RUN_TEST(test_array_create_with_options); - RUN_TEST(test_array_deconstruct); - RUN_TEST(test_array_resize_expand); - RUN_TEST(test_array_resize_shrink); - RUN_TEST(test_array_set_value_within_capacity); - RUN_TEST(test_array_set_value_beyond_capacity); - RUN_TEST(test_array_get_value_valid_index); - RUN_TEST(test_array_get_value_zero_index); - RUN_TEST(test_array_get_value_beyond_capacity); - RUN_TEST(test_array_multiple_operations); + RUN_TEST(test_array_destroy); + RUN_TEST(test_array_size_initial); + RUN_TEST(test_array_push_value); + RUN_TEST(test_array_push_multiple_values); + RUN_TEST(test_array_pop_decrements_size); RUN_TEST(test_array_get_capacity_default); - RUN_TEST(test_array_get_capacity_custom_size); - RUN_TEST(test_array_get_capacity_after_resize); - RUN_TEST(test_array_get_size_initial); - RUN_TEST(test_array_get_size_after_single_set); - RUN_TEST(test_array_get_size_after_multiple_sets); - RUN_TEST(test_array_get_size_with_gap_in_indices); - RUN_TEST(test_array_get_size_capacity_relationship); - RUN_TEST(test_array_get_size_after_resize_beyond_capacity); - RUN_TEST(test_array_get_value_as); - RUN_TEST(test_array_get_value_as_invalid_type); - RUN_TEST(test_array_set_value_as); - RUN_TEST(test_array_set_value_as_invalid_type); - RUN_TEST(test_adding_elements); + RUN_TEST(test_array_size_capacity_relationship); + RUN_TEST(test_array_push_beyond_capacity); + RUN_TEST(test_array_values_correct_after_growth); return UNITY_END(); } \ No newline at end of file