Compare commits

..

12 Commits

Author SHA1 Message Date
4238d73749 style: update formatting and alignment in dynamic array files
All checks were successful
CI / build (push) Successful in 10m53s
2026-04-16 11:06:06 -03:00
a8cd5fb7a8 test: add null handling tests for dynamic array functions
Some checks failed
CI / build (pull_request) Successful in 16s
CI / build (push) Has been cancelled
2026-04-15 21:06:35 -03:00
09e1a360fd feat: add null checks to dynamic array macros for improved safety 2026-04-15 20:59:52 -03:00
60f9f88af2 Merge pull request 'fix/fixes-overflow-on-pop' (#3) from fix/fixes-overflow-on-pop into main
All checks were successful
CI / build (push) Successful in 23s
Reviewed-on: http://gitea.hideyoshi.com.br/HideyoshiNakazone/dynamic_array/pulls/3
2026-04-15 23:32:21 +00:00
e5e4d75e2c test: add looping test for dynamic array to verify growth and value integrity
All checks were successful
CI / build (pull_request) Successful in 19s
2026-04-15 20:31:22 -03:00
10251039d5 fix: prevent underflow in array_pop when called on an empty array 2026-04-15 20:28:11 -03:00
8c6e7e035b Merge pull request 'chore: update README to enhance clarity and detail on dynamic array features' (#2) from chore/better-readme into main
All checks were successful
CI / build (push) Successful in 35s
Reviewed-on: http://gitea.hideyoshi.com.br/HideyoshiNakazone/dynamic_array/pulls/2
2026-04-15 23:21:59 +00:00
e48aecf36a chore: update README to enhance clarity and detail on dynamic array features
All checks were successful
CI / build (pull_request) Successful in 17s
2026-04-15 20:16:32 -03:00
2061eeaf4f Merge pull request 'refactor/better-interface' (#1) from refactor/better-interface into main
All checks were successful
CI / build (push) Successful in 19s
Reviewed-on: http://gitea.hideyoshi.com.br/HideyoshiNakazone/dynamic_array/pulls/1
2026-04-15 23:12:01 +00:00
ff26be2e75 refactor: simplify CI configuration and enhance CMake installation rules
All checks were successful
CI / build (pull_request) Successful in 37s
2026-04-15 20:10:09 -03:00
c4e7627d1f refactor: update CMake configuration and add clang-format file
Some checks failed
CI / build (pull_request) Failing after 1m24s
2026-04-15 18:27:39 -03:00
7940c4a86e feat: adds build directory to gitignore 2026-04-14 14:30:34 -03:00
8 changed files with 354 additions and 517 deletions

22
.clang-format Normal file
View File

@@ -0,0 +1,22 @@
BasedOnStyle: LLVM
IndentWidth: 4
TabWidth: 4
UseTab: Never
ColumnLimit: 100
BreakBeforeBraces: Attach
AllowShortIfStatementsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
PointerAlignment: Left
SpaceBeforeParens: ControlStatements
SortIncludes: true
IndentCaseLabels: true
AlignEscapedNewlines: Right

View File

@@ -16,9 +16,7 @@ jobs:
submodules: recursive submodules: recursive
- name: Cache and install APT packages - name: Cache and install APT packages
uses: awalsh128/cache-apt-pkgs-action@v1 run: sudo apt update && sudo apt install -y cmake build-essential
with:
packages: cmake build-essential
- name: Configure project - name: Configure project
run: make configure run: make configure
@@ -34,8 +32,4 @@ jobs:
- name: Verify installation - name: Verify installation
run: | run: |
ls -la ${{ runner.temp }}/install/lib/
ls -la ${{ runner.temp }}/install/include/ ls -la ${{ runner.temp }}/install/include/

5
.gitignore vendored
View File

@@ -93,4 +93,7 @@ CMakeUserPresets.json
# IDE # IDE
.idea/ .idea/
.vscode/ .vscode/
# Build directories
build/

116
README.md
View File

@@ -9,21 +9,22 @@ A simple, educational C implementation of a dynamic array (vector-like) data str
This library provides a basic dynamic array implementation in C, similar to C++'s `std::vector` or Python's lists. It demonstrates fundamental concepts in systems programming: This library provides a basic dynamic array implementation in C, similar to C++'s `std::vector` or Python's lists. It demonstrates fundamental concepts in systems programming:
- **Memory management** — allocation, reallocation, deallocation - **Memory management** — allocation, reallocation, deallocation
- **Generic programming** — using `void*` pointers - **Generic programming** — type-agnostic macros that work with any pointer type
- **Type-safe macros** — for type casting and operations
- **Unit testing** — with the Unity C testing framework - **Unit testing** — with the Unity C testing framework
## ✨ Features ## ✨ Features
- **Dynamic resizing** — Automatically grows as elements are added - **Header-only** — just include `dynamic_array.h`, no compilation step needed
- **Type-safe operations** — Macros for working with typed elements - **Dynamic resizing** — automatically grows as elements are added
- **Simple API** — Easy-to-use functions for common operations - **Type-safe** — macros operate on typed pointers; direct indexing is fully typed
- **Simple API** — a small set of macros for common operations
## 📁 Project Structure ## 📁 Project Structure
``` ```
test-meson/ dynamic_array/
├── src/ ├── src/
│ ├── dynamic_array.c # Implementation │ ├── dynamic_array.h # Header-only implementation (public API)
│ ├── dynamic_array.h # Public API header
│ ├── test_dynamic_array.c # Unit tests │ ├── test_dynamic_array.c # Unit tests
│ └── CMakeLists.txt # Build configuration │ └── CMakeLists.txt # Build configuration
├── submodules/ ├── submodules/
@@ -33,6 +34,7 @@ test-meson/
├── Makefile # Build automation ├── Makefile # Build automation
└── README.md # This file └── README.md # This file
``` ```
## 🔧 Prerequisites ## 🔧 Prerequisites
**Linux-only project** **Linux-only project**
@@ -41,6 +43,7 @@ test-meson/
- **C compiler** (GCC recommended, Clang supported) - **C compiler** (GCC recommended, Clang supported)
- **Make** (optional, but recommended) - **Make** (optional, but recommended)
- **build-essential** (on Ubuntu/Debian): `sudo apt-get install build-essential` - **build-essential** (on Ubuntu/Debian): `sudo apt-get install build-essential`
## 🔨 Building ## 🔨 Building
### Using the Makefile (Recommended) ### Using the Makefile (Recommended)
@@ -63,6 +66,7 @@ ctest --output-on-failure
cmake --install . cmake --install .
cd .. && rm -rf build cd .. && rm -rf build
``` ```
## 💡 Usage ## 💡 Usage
### Including the Library ### Including the Library
@@ -70,72 +74,70 @@ cd .. && rm -rf build
```c ```c
#include "dynamic_array.h" #include "dynamic_array.h"
// Create an array of integers // Create a typed array (works with any pointer type)
ArrayCreateOptions opts = { int *arr = NULL;
.initial_size = 10, array_create(arr);
.element_size = sizeof(int)
};
Array *my_array = array_create(&opts);
// Add elements // Add elements
int value = 42; array_push_value(arr, 10);
array_set_value(my_array, 0, &value); array_push_value(arr, 20);
array_push_value(arr, 30);
// Retrieve elements // Access elements directly via indexing
int *retrieved = (int*)array_get_value(my_array, 0); int first = arr[0]; // 10
// Using type-safe macros // Remove the last element
int result = array_set_value_as(int, my_array, 1, 100); array_pop(arr);
// Get size and capacity // Inspect size and capacity via the header
size_t size = array_get_size(my_array); const ArrayHeader *header = array_get_header(arr);
size_t capacity = array_get_capacity(my_array); size_t size = header->size; // current number of elements
size_t capacity = header->capacity; // allocated capacity
// Cleanup // Cleanup
array_deconstructor(&my_array); array_destroy(arr); // frees memory and sets arr to NULL
``` ```
## 📖 API Reference ## 📖 API Reference
### Structures ### Structures
#### `Array` #### `ArrayHeader`
Stored immediately before the array data in memory. Retrieve it with `array_get_header`.
```c ```c
typedef struct { typedef struct {
void *value; // Pointer to element data size_t size; // Current number of elements
size_t element_size; // Size of each element size_t capacity; // Allocated capacity
size_t size; // Current number of elements } ArrayHeader;
size_t capacity; // Allocated capacity
} Array;
``` ```
#### `ArrayCreateOptions` Memory layout:
```c
typedef struct {
int initial_size; // Initial capacity
size_t element_size; // Size of each element type
} ArrayCreateOptions;
``` ```
[ ArrayHeader | elem 0 | elem 1 | ... ]
### Functions
pointer returned by array_create (and all macros)
| Function | Description | ```
|----------|-------------|
| `Array *array_create(const ArrayCreateOptions *options)` | Creates a new dynamic array |
| `void array_deconstructor(Array **pp_array)` | Frees allocated memory and sets pointer to NULL |
| `void array_resize(Array *p_array, size_t new_size)` | Resizes the array's capacity |
| `void *array_get_value(const Array *p_array, size_t index)` | Retrieves a pointer to the element at index |
| `void array_set_value(Array *p_array, size_t index, const void *value)` | Sets the element at index |
| `size_t array_get_size(const Array *p_array)` | Returns the current number of elements |
| `size_t array_get_capacity(const Array *p_array)` | Returns the allocated capacity |
### Macros ### Macros
| Macro | Description | | Macro | Description |
|-------|-------------| |-------|-------------|
| `array_get_value_as(type, arr, idx)` | Type-safe retrieval of elements | | `array_create(arr)` | Allocates a new array; sets `arr` to point to the first element |
| `array_set_value_as(type, arr, idx, value)` | Type-safe setting of elements (with type checking) | | `array_destroy(arr)` | Frees the array and sets `arr` to `NULL` |
| `array_get_header(arr)` | Returns a pointer to the `ArrayHeader` for `arr` |
| `array_push_value(arr, value)` | Appends `value`; grows the array automatically if needed |
| `array_pop(arr)` | Removes the last element by decrementing `size` |
### Constants
| Constant | Default | Description |
|----------|---------|-------------|
| `DYNAMIC_ARRAY_DEFAULT_ARRAY_SIZE` | `10` | Initial capacity allocated by `array_create` |
| `DYNAMIC_ARRAY_CAPACITY_FACTOR` | `2` | Growth multiplier applied on resize |
## ✅ Testing ## ✅ Testing
Tests are written using the [Unity](http://www.throwtheswitch.org/unity) C testing framework. Tests are written using the [Unity](http://www.throwtheswitch.org/unity) C testing framework.
@@ -149,6 +151,7 @@ Or directly with ctest:
```bash ```bash
cd build && ctest --output-on-failure cd build && ctest --output-on-failure
``` ```
## 📦 Installation ## 📦 Installation
### Default Installation ### Default Installation
@@ -158,7 +161,6 @@ make install
``` ```
This installs to `~/.local/bin/dynamic_array/`: This installs to `~/.local/bin/dynamic_array/`:
- **Library:** `lib/libdynamic_array.a`
- **Header:** `include/dynamic_array.h` - **Header:** `include/dynamic_array.h`
### Custom Installation Directory ### Custom Installation Directory
@@ -173,20 +175,26 @@ Or set it as an environment variable:
export INSTALL_PREFIX=$HOME/.local export INSTALL_PREFIX=$HOME/.local
make install make install
``` ```
## ⚠️ Known Issues & Limitations ## ⚠️ Known Issues & Limitations
- ⚠️ No bounds checking in release builds - ⚠️ No bounds checking
- ⚠️ `array_pop` does not return the removed element (use `arr[header->size - 1]` before popping)
- ⚠️ Simple reallocation strategy (not optimized for performance) - ⚠️ Simple reallocation strategy (not optimized for performance)
- ⚠️ Not thread-safe - ⚠️ Not thread-safe
- ⚠️ Limited error handling - ⚠️ `realloc` failure is not propagated to the caller
- ⚠️ Educational purpose only — use established libraries for production - ⚠️ Educational purpose only — use established libraries for production
## 🚀 Future Improvements ## 🚀 Future Improvements
- [ ] Return value from `array_pop`
- [ ] Propagate allocation failures
- [ ] Add more comprehensive error handling - [ ] Add more comprehensive error handling
- [ ] Implement shrinking functionality - [ ] Implement shrinking / `array_reserve`
- [ ] Add iterator support - [ ] Add iterator support
- [ ] Optimize memory allocation strategy - [ ] Optimize memory allocation strategy
- [ ] Add more test coverage - [ ] Add more test coverage
## 📄 License ## 📄 License
This project is for educational purposes. This project is for educational purposes.

View File

@@ -1,7 +1,8 @@
include(GNUInstallDirs)
# Library Configuration # Library Configuration
add_library(dynamic_array add_library(dynamic_array INTERFACE)
dynamic_array.c
dynamic_array.h)
target_include_directories(dynamic_array INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) target_include_directories(dynamic_array INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
set_target_properties(dynamic_array PROPERTIES PUBLIC_HEADER dynamic_array.h) set_target_properties(dynamic_array PROPERTIES PUBLIC_HEADER dynamic_array.h)
@@ -20,5 +21,4 @@ install(TARGETS dynamic_array
add_executable(test_dynamic_array test_dynamic_array.c) add_executable(test_dynamic_array test_dynamic_array.c)
target_link_libraries(test_dynamic_array PRIVATE dynamic_array unity::framework) target_link_libraries(test_dynamic_array PRIVATE dynamic_array unity::framework)
add_test(NAME test_dynamic_array COMMAND test_dynamic_array) add_test(NAME test_dynamic_array COMMAND test_dynamic_array)

View File

@@ -1,71 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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;
}

View File

@@ -1,65 +1,86 @@
#ifndef DYNAMIC_ARRAY_H #ifndef DYNAMIC_ARRAY_H
#define DYNAMIC_ARRAY_H #define DYNAMIC_ARRAY_H
#define DYNAMIC_ARRAY_VERSION_MAJOR 0 #define DYNAMIC_ARRAY_VERSION_MAJOR 0
#define DYNAMIC_ARRAY_VERSION_MINOR 1 #define DYNAMIC_ARRAY_VERSION_MINOR 1
#define DYNAMIC_ARRAY_VERSION_BUILD 0 #define DYNAMIC_ARRAY_VERSION_BUILD 0
#define DYNAMIC_ARRAY_VERSION ((DYNAMIC_ARRAY_VERSION_MAJOR << 16) | (DYNAMIC_ARRAY_VERSION_MINOR << 8) | DYNAMIC_ARRAY_VERSION_BUILD) #define DYNAMIC_ARRAY_VERSION \
((DYNAMIC_ARRAY_VERSION_MAJOR << 16) | (DYNAMIC_ARRAY_VERSION_MINOR << 8) | \
DYNAMIC_ARRAY_VERSION_BUILD)
#include <stddef.h> #include <stddef.h>
#include <stdlib.h>
#define DYNAMIC_ARRAY_DEFAULT_ARRAY_SIZE 10 #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 { typedef struct {
void *value;
size_t element_size;
size_t size; size_t size;
size_t capacity; size_t capacity;
} Array; } ArrayHeader;
#define array_create(arr) \
do { \
ArrayHeader* header = \
malloc(sizeof(*(arr)) * DYNAMIC_ARRAY_DEFAULT_ARRAY_SIZE + sizeof(ArrayHeader)); \
if (header == NULL) { \
abort(); \
} \
header->size = 0; \
header->capacity = DYNAMIC_ARRAY_DEFAULT_ARRAY_SIZE; \
\
(arr) = (void*)(header + 1); \
} while (0)
typedef struct { #define array_get_header(arr) ((arr) ? ((ArrayHeader*)(arr) - 1) : NULL)
int initial_size;
size_t element_size;
} ArrayCreateOptions;
#define array_destroy(arr) \
do { \
if ((arr) == NULL) { \
break; \
} \
free(array_get_header(arr)); \
(arr) = NULL; \
} while (0)
#define DYNAMIC_ARRAY_DEFAULT_ARRAY_CREATE_OPTIONS \ #define array_push_value(arr, value) \
(ArrayCreateOptions){ DYNAMIC_ARRAY_DEFAULT_ARRAY_SIZE, sizeof(int) } do { \
if ((arr) == NULL) { \
array_create(arr); \
} \
ArrayHeader* header = array_get_header(arr); \
if (header->size >= 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) { \
abort(); \
} \
header = new_header; \
header->capacity = new_capacity; \
(arr) = (void*)(header + 1); \
} \
(arr)[header->size] = (value); \
header->size++; \
} while (0)
Array *array_create(const ArrayCreateOptions *options); #define array_pop(arr) \
do { \
if ((arr) == NULL) { \
break; \
} \
ArrayHeader* header = array_get_header(arr); \
if (header->size == 0) { \
break; \
} \
header->size--; \
} while (0)
#define array_size(arr) ((arr) ? array_get_header(arr)->size : 0)
void array_deconstructor(Array **pp_array); #define array_capacity(arr) ((arr) ? array_get_header(arr)->capacity : 0)
void array_resize(Array *p_array, size_t new_size);
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)
#endif #endif

View File

@@ -2,394 +2,254 @@
#include "dynamic_array.h" #include "dynamic_array.h"
void setUp(void) {
void setUp() {
} }
void tearDown() { void tearDown(void) {
} }
void test_array_create_without_options(void) { void test_array_create_without_options(void) {
const Array *array = array_create(NULL); int* arr = NULL;
TEST_ASSERT_EQUAL(DYNAMIC_ARRAY_DEFAULT_ARRAY_SIZE, array->capacity); 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_destroy(void) {
void test_array_create_with_options(void) { int* arr = NULL;
const int EXPECTED_CAPACITY = 20; array_create(arr);
array_destroy(arr);
const Array *array = array_create(&(ArrayCreateOptions){ TEST_ASSERT_NULL(arr);
.initial_size = EXPECTED_CAPACITY
});
TEST_ASSERT_EQUAL(EXPECTED_CAPACITY, array->capacity);
} }
void test_array_size_initial(void) {
int* arr = NULL;
array_create(arr);
void test_array_deconstruct(void) { TEST_ASSERT_EQUAL(0, array_get_header(arr)->size);
Array *array = array_create(NULL);
array_deconstructor(&array); array_destroy(arr);
TEST_ASSERT_NULL(array);
} }
void test_array_push_value(void) {
int* arr = NULL;
array_create(arr);
void test_array_resize_expand(void) { array_push_value(arr, 42);
Array *array = array_create(NULL);
const size_t ORIGINAL_CAPACITY = array->capacity;
const int NEW_CAPACITY = 30;
array_resize(array, NEW_CAPACITY); TEST_ASSERT_EQUAL(42, arr[0]);
TEST_ASSERT_EQUAL(1, array_get_header(arr)->size);
TEST_ASSERT_EQUAL(NEW_CAPACITY, array->capacity); array_destroy(arr);
TEST_ASSERT_GREATER_THAN(ORIGINAL_CAPACITY, array->capacity);
array_deconstructor(&array);
} }
void test_array_push_multiple_values(void) {
int* arr = NULL;
array_create(arr);
void test_array_resize_shrink(void) { array_push_value(arr, 10);
Array *array = array_create(&(ArrayCreateOptions){.initial_size = 50, .element_size = sizeof(int)}); array_push_value(arr, 20);
const int NEW_CAPACITY = 20; array_push_value(arr, 30);
array_resize(array, NEW_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);
TEST_ASSERT_EQUAL(NEW_CAPACITY, array->capacity); array_destroy(arr);
array_deconstructor(&array);
} }
void test_array_pop_decrements_size(void) {
int* arr = NULL;
array_create(arr);
void test_array_set_value_within_capacity(void) { array_push_value(arr, 42);
Array *p_array = array_create(NULL); array_push_value(arr, 99);
const int INDEX = 5; array_pop(arr);
const int EXPECTED_VALUE = 42;
array_set_value(p_array, INDEX, &EXPECTED_VALUE); TEST_ASSERT_EQUAL(1, array_get_header(arr)->size);
const int VALUE = *(int*)array_get_value(p_array, INDEX); array_destroy(arr);
TEST_ASSERT_EQUAL(EXPECTED_VALUE, VALUE);
array_deconstructor(&p_array);
} }
void test_array_pop_on_empty_is_noop(void) {
int* arr = NULL;
array_create(arr);
void test_array_set_value_beyond_capacity(void) { array_push_value(arr, 99);
Array *p_array = array_create(NULL); TEST_ASSERT_EQUAL(1, array_get_header(arr)->size);
const int INDEX = 50; TEST_ASSERT_EQUAL(99, arr[0]);
const int EXPECTED_VALUE = 99;
const size_t ORIGINAL_CAPACITY = p_array->capacity;
array_set_value(p_array, INDEX, &EXPECTED_VALUE); array_pop(arr);
TEST_ASSERT_EQUAL(0, array_get_header(arr)->size);
const int VALUE = *(int*)array_get_value(p_array, INDEX); array_pop(arr); // should not underflow
TEST_ASSERT_EQUAL(0, array_get_header(arr)->size);
TEST_ASSERT_GREATER_THAN(ORIGINAL_CAPACITY, p_array->capacity); array_destroy(arr);
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);
}
void test_array_get_capacity_default(void) { 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_destroy(arr);
array_deconstructor(&array);
} }
void test_array_size_capacity_relationship(void) {
int* arr = NULL;
array_create(arr);
void test_array_get_capacity_custom_size(void) { array_push_value(arr, 1);
const int EXPECTED_CAPACITY = 25; array_push_value(arr, 2);
Array *array = array_create(&(ArrayCreateOptions){.initial_size = EXPECTED_CAPACITY}); array_push_value(arr, 3);
const size_t capacity = array_get_capacity(array); const ArrayHeader* header = array_get_header(arr);
TEST_ASSERT_EQUAL(3, header->size);
TEST_ASSERT_LESS_OR_EQUAL(header->capacity, header->size);
TEST_ASSERT_EQUAL(EXPECTED_CAPACITY, capacity); array_destroy(arr);
array_deconstructor(&array);
} }
void test_array_push_beyond_capacity(void) {
int* arr = NULL;
array_create(arr);
void test_array_get_capacity_after_resize(void) { const int num_elements = DYNAMIC_ARRAY_DEFAULT_ARRAY_SIZE * 2;
Array *array = array_create(NULL); for (int i = 0; i < num_elements; i++) {
const size_t ORIGINAL_CAPACITY = array_get_capacity(array); array_push_value(arr, i);
const size_t NEW_CAPACITY = 50;
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);
} }
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);
int main(void) 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);
}
void test_array_looping(void) {
int* arr = NULL;
array_create(arr);
const int num_elements = DYNAMIC_ARRAY_DEFAULT_ARRAY_SIZE * 2;
for (int i = 0; i < num_elements; i++) {
array_push_value(arr, i);
}
const ArrayHeader* header = array_get_header(arr);
TEST_ASSERT_EQUAL(num_elements, header->size);
for (int i = 0; i < header->size; i++) {
TEST_ASSERT_EQUAL(i, arr[i]);
}
}
/* --- array_get_header branch: NULL arr → returns NULL --- */
void test_array_get_header_on_null_returns_null(void) {
int* arr = NULL;
TEST_ASSERT_NULL(array_get_header(arr));
}
/* --- array_destroy branch: NULL arr → no-op, does not crash --- */
void test_array_destroy_on_null_is_noop(void) {
int* arr = NULL;
array_destroy(arr);
TEST_ASSERT_NULL(arr);
}
/* --- array_push_value branch: NULL arr → auto-creates the array --- */
void test_array_push_value_on_null_auto_creates(void) {
int* arr = NULL;
array_push_value(arr, 7);
TEST_ASSERT_NOT_NULL(arr);
TEST_ASSERT_EQUAL(7, arr[0]);
TEST_ASSERT_EQUAL(1, array_get_header(arr)->size);
array_destroy(arr);
}
/* --- array_pop branch: NULL arr → no-op, does not crash --- */
void test_array_pop_on_null_is_noop(void) {
int* arr = NULL;
array_pop(arr);
TEST_ASSERT_NULL(arr);
}
/* --- array_size branch: NULL arr → 0 --- */
void test_array_size_on_null_returns_zero(void) {
int* arr = NULL;
TEST_ASSERT_EQUAL(0, array_size(arr));
}
/* --- array_size branch: non-NULL arr → current size --- */
void test_array_size_returns_current_size(void) {
int* arr = NULL;
array_create(arr);
array_push_value(arr, 1);
array_push_value(arr, 2);
TEST_ASSERT_EQUAL(2, array_size(arr));
array_destroy(arr);
}
/* --- array_capacity branch: NULL arr → 0 --- */
void test_array_capacity_on_null_returns_zero(void) {
int* arr = NULL;
TEST_ASSERT_EQUAL(0, array_capacity(arr));
}
/* --- array_capacity branch: non-NULL arr → current capacity --- */
void test_array_capacity_returns_current_capacity(void) {
int* arr = NULL;
array_create(arr);
TEST_ASSERT_EQUAL(DYNAMIC_ARRAY_DEFAULT_ARRAY_SIZE, array_capacity(arr));
array_destroy(arr);
}
int main(void) {
UNITY_BEGIN(); UNITY_BEGIN();
RUN_TEST(test_array_create_without_options); RUN_TEST(test_array_create_without_options);
RUN_TEST(test_array_create_with_options); RUN_TEST(test_array_destroy);
RUN_TEST(test_array_deconstruct); RUN_TEST(test_array_size_initial);
RUN_TEST(test_array_resize_expand); RUN_TEST(test_array_push_value);
RUN_TEST(test_array_resize_shrink); RUN_TEST(test_array_push_multiple_values);
RUN_TEST(test_array_set_value_within_capacity); RUN_TEST(test_array_pop_decrements_size);
RUN_TEST(test_array_set_value_beyond_capacity); RUN_TEST(test_array_pop_on_empty_is_noop);
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_get_capacity_default); RUN_TEST(test_array_get_capacity_default);
RUN_TEST(test_array_get_capacity_custom_size); RUN_TEST(test_array_size_capacity_relationship);
RUN_TEST(test_array_get_capacity_after_resize); RUN_TEST(test_array_push_beyond_capacity);
RUN_TEST(test_array_get_size_initial); RUN_TEST(test_array_values_correct_after_growth);
RUN_TEST(test_array_get_size_after_single_set); RUN_TEST(test_array_looping);
RUN_TEST(test_array_get_size_after_multiple_sets);
RUN_TEST(test_array_get_size_with_gap_in_indices); RUN_TEST(test_array_get_header_on_null_returns_null);
RUN_TEST(test_array_get_size_capacity_relationship); RUN_TEST(test_array_destroy_on_null_is_noop);
RUN_TEST(test_array_get_size_after_resize_beyond_capacity); RUN_TEST(test_array_push_value_on_null_auto_creates);
RUN_TEST(test_array_get_value_as); RUN_TEST(test_array_pop_on_null_is_noop);
RUN_TEST(test_array_get_value_as_invalid_type); RUN_TEST(test_array_size_on_null_returns_zero);
RUN_TEST(test_array_set_value_as); RUN_TEST(test_array_size_returns_current_size);
RUN_TEST(test_array_set_value_as_invalid_type); RUN_TEST(test_array_capacity_on_null_returns_zero);
RUN_TEST(test_adding_elements); RUN_TEST(test_array_capacity_returns_current_capacity);
return UNITY_END(); return UNITY_END();
} }