From aaad35adc56c2184092d90014b51a4ea6d18b246 Mon Sep 17 00:00:00 2001 From: epochryphon Date: Wed, 20 May 2026 19:44:04 -0500 Subject: [PATCH] public API solo dev done, awaiting other ideas --- .../marigold_dynamic_array.h | 410 +++++++++++++----- 1 file changed, 309 insertions(+), 101 deletions(-) diff --git a/source_code/MODULE_marigold_dynamic_array/marigold_dynamic_array.h b/source_code/MODULE_marigold_dynamic_array/marigold_dynamic_array.h index 8b57c8e..9ef04a2 100644 --- a/source_code/MODULE_marigold_dynamic_array/marigold_dynamic_array.h +++ b/source_code/MODULE_marigold_dynamic_array/marigold_dynamic_array.h @@ -1,45 +1,87 @@ #ifndef MARIGOLD_DYNAMIC_ARRAY_H #define MARIGOLD_DYNAMIC_ARRAY_H +#include #include #include #include -#include -typedef struct dynamic_array_struct +typedef enum +dynamic_array_flag_enum +{ + dynamic_array_multithread_safe = 1 << 0, + dynamic_array_read_only = 1 << 1, + dynamic_array_certainly_sorted = 1 << 2, + dynamic_array_marked_for_free = 1 << 3, + dynamic_array_debug_has_function_call_rolled_over = 1 << 4, + dynamic_array_debug_has_owner_count_rolled_over = 1 << 5, + dynamic_array_debug_has_been_resized = 1 << 6, + dynamic_array_debug_has_been_resized_rolled_over = 1 << 7 +} +dynamic_array_flag_enum; + +typedef enum +dynamic_array_growth_rate_enum +{ + dynamic_array_growth_static = 0, + dynamic_array_growth_linear = 1, + dynamic_array_growth_double = 2, + dynamic_array_growth_triple = 3, + dynamic_array_growth_quadruple = 4, + dynamic_array_growth_quintuple = 5 +} +dynamic_array_growth_rate_enum; + +typedef struct +dynamic_array_struct { size_t item_size; size_t current_capacity; size_t current_occupancy; void* data_pointer; - int (*comparator)(const void*, const void*); + int (*comparator)(const void *, const void *); + void (*item_free)(const void *); pthread_mutex_t* mutex_lock; unsigned short owner_count; - unsigned short function_call_count; - unsigned short growth_steps; - unsigned char growth_factor; - uint8_t flags; // is_multithread_safe, is_read_only, is_certainly_sorted, has_owner_count_rolled_over, has_function_call_count_rolled_over, has_growth_steps_rolled_over, has_been_resized, is_array_full (occupancy == capacity) -} dynamic_array_struct; + uint8_t growth_strategy; + uint8_t flags; +#ifdef MARIGOLD_DEBUG + uint8_t debug_flags; + uint32_t debug_resize_count; + uint32_t debug_function_call_count; + uint32_t debug_last_error_code; + uint32_t debug_allocation_failures; + +#endif // MARIGOLD_DEBUG. +} +dynamic_array_struct; /** - * @brief Create a new dynamic array, it then internally does all the initalization. + * @brief Create a new dynamic array, it then internally does all the + * initialization. * * Creates a dynamic array with the specified item size and starting capacity. - * The growth_factor determines how the array expands when capacity is exceeded. + * The growth_strategy determines how the array expands when capacity is + * exceeded. + * + * If is_multithread_safe is true, the internal mutex is created and initialized + * here. * * @param item_size Size of each element in bytes. * @param starting_capacity Initial number of elements to allocate space for. - * @param growth_factor Growth strategy: 0 = no growth. 1 = linear (+starting_capacity), - * 2+ = multiplier (2 = 2x, 3 = 3x, etc.) - * @param comparator Function to compare two elements. May be NULL if not needed. + * @param growth_strategy Growth strategy enum. + * @param comparator Function to compare two elements. May be NULL if not + * needed. * @param is_multithread_safe If true, enables thread-safe operations. * @return Pointer to the initialized dynamic_array, or NULL on failure. */ -dynamic_array_struct* dynamic_array_create(size_t item_size, - size_t starting_capacity, - unsigned char growth_factor, - int (*comparator)(const void*, const void*), - bool is_multithread_safe); +dynamic_array_struct* +dynamic_array_create(size_t item_size, + size_t starting_capacity, + dynamic_array_growth_rate_enum growth_rate, + int (*comparator)(const void*, const void*), + void (*item_free)(const void *), + bool is_multithread_safe); /** * @brief Explicitly destroy the dynamic array and free its memory. @@ -48,10 +90,37 @@ dynamic_array_struct* dynamic_array_create(size_t item_size, * and the struct itself. Use this for explicit cleanup. For reference-counted * shared ownership, prefer dynamic_array_release(). * + * If the array is thread-safe, the internal mutex is destroyed and freed here. + * * @param array_to_destroy is the dynamic array being deallocated/destroyed. - * @return true on successful destory, false on failure. + * @return true on successful destroy, false on failure. */ -bool dynamic_array_destroy(dynamic_array_struct* array_to_destroy); +bool +dynamic_array_destroy(dynamic_array_struct* array_to_destroy); + +/** + * @brief Set or clear a specific flag on the dynamic array. + * + * @param array Pointer to the dynamic_array. + * @param flag The flag to modify. + * @param value true to set the flag, false to clear it. + */ +void +dynamic_array_set_flag(dynamic_array_struct* array, + dynamic_array_flag_enum flag, + bool value); + +/** + * @brief Get a specific flag on the dynamic array. + * + * @param array Pointer to the dynamic_array. + * @param flag The flag to get. + * + * @return value of flag as bool (true or false) + */ +bool +dynamic_array_get_flag_value(const dynamic_array_struct* array, + dynamic_array_flag_enum flag); /** * @brief Get the number of elements currently in the array. @@ -59,7 +128,8 @@ bool dynamic_array_destroy(dynamic_array_struct* array_to_destroy); * @param array Pointer to the dynamic_array. * @return Number of elements (current_occupancy). */ -size_t dynamic_array_get_occupancy(const dynamic_array_struct* array); +size_t +dynamic_array_get_occupancy(const dynamic_array_struct* array); /** * @brief Get the total capacity of the array. @@ -67,7 +137,8 @@ size_t dynamic_array_get_occupancy(const dynamic_array_struct* array); * @param array Pointer to the dynamic_array. * @return Total capacity (current_capacity). */ -size_t dynamic_array_get_capacity(const dynamic_array_struct* array); +size_t +dynamic_array_get_capacity(const dynamic_array_struct* array); /** * @brief Get a pointer to the element at the specified index. @@ -76,8 +147,9 @@ size_t dynamic_array_get_capacity(const dynamic_array_struct* array); * @param index Index of the element to access. * @return Pointer to the element, or NULL if index is out of bounds. */ -void* dynamic_array_get_pointer_to_index(dynamic_array_struct* array, - size_t index); +void* +dynamic_array_get_pointer_to_index(dynamic_array_struct* array, + size_t index); /** * @brief Get a const pointer to the element at the specified index. @@ -86,8 +158,9 @@ void* dynamic_array_get_pointer_to_index(dynamic_array_struct* array, * @param index Index of the element to access. * @return Const pointer to the element, or NULL if index is out of bounds. */ -const void* dynamic_array_get_const_pointer_to_index(const dynamic_array_struct* array, - size_t index); +const void* +dynamic_array_get_const_pointer_to_index(const dynamic_array_struct* array, + size_t index); /** * @brief Get a direct pointer to the underlying memory block. @@ -98,7 +171,8 @@ const void* dynamic_array_get_const_pointer_to_index(const dynamic_array_struct* * @param array Pointer to the dynamic_array. * @return Pointer to the raw memory block, or NULL if empty. */ -void* dynamic_array_get_raw_data_pointer(const dynamic_array_struct* array); +void* +dynamic_array_get_raw_data_pointer(const dynamic_array_struct* array); /** * @brief Append an element to the end of the array. @@ -109,8 +183,8 @@ void* dynamic_array_get_raw_data_pointer(const dynamic_array_struct* array); * @param element Pointer to the element to append. * @return true on success, false on allocation failure. */ -bool dynamic_array_append(dynamic_array_struct* array, - const void* element); +bool +dynamic_array_append(dynamic_array_struct* array, const void* element); /** * @brief Insert an element at the specified index. @@ -123,9 +197,10 @@ bool dynamic_array_append(dynamic_array_struct* array, * @param element Pointer to the element to insert. * @return true on success, false if index is out of bounds or allocation fails. */ -bool dynamic_array_insert_at(dynamic_array_struct* array, - size_t index, - const void* element); +bool +dynamic_array_insert_at(dynamic_array_struct* array, + size_t index, + const void* element); /** * @brief Replace the element at the specified index. @@ -135,9 +210,10 @@ bool dynamic_array_insert_at(dynamic_array_struct* array, * @param new_element Pointer to the new element data. * @return true on success, false if index is out of bounds. */ -bool dynamic_array_set_item(dynamic_array_struct* array, - size_t index, - const void* new_element); +bool +dynamic_array_set_item(dynamic_array_struct* array, + size_t index, + const void* new_element); /** * @brief Swap the elements at two indices. @@ -147,40 +223,47 @@ bool dynamic_array_set_item(dynamic_array_struct* array, * @param index_b Second index. * @return true on success, false if either index is out of bounds. */ -bool dynamic_array_swap(dynamic_array_struct* array, - size_t index_a, - size_t index_b); +bool +dynamic_array_swap(dynamic_array_struct* array, + size_t index_a, + size_t index_b); /** * @brief Remove the last element from the array. * * Decrements current_occupancy. Does not free the memory block. + * If item_free is set, it will be called on the removed element. * * @param array Pointer to the dynamic_array. * @return true on success, false if array is empty. */ -bool dynamic_array_pop(dynamic_array_struct* array); +bool +dynamic_array_pop(dynamic_array_struct* array); /** * @brief Remove an element at the specified index. * * Shifts subsequent elements down to fill the gap. + * If item_free is set, it will be called on the removed element. * * @param array Pointer to the dynamic_array. * @param index Index of the element to remove. * @return true on success, false if index is out of bounds. */ -bool dynamic_array_remove(dynamic_array_struct* array, - size_t index); +bool +dynamic_array_remove(dynamic_array_struct* array, + size_t index); /** * @brief Clear all elements from the array. * * Sets current_occupancy to 0. Does not free the memory block. + * If item_free is set, it will be called on all removed elements. * * @param array Pointer to the dynamic_array. */ -void dynamic_array_clear(dynamic_array_struct* array); +void +dynamic_array_clear(dynamic_array_struct* array); /** * @brief Check if the array is empty. @@ -188,29 +271,19 @@ void dynamic_array_clear(dynamic_array_struct* array); * @param array Pointer to the dynamic_array. * @return true if current_occupancy is 0, false otherwise. */ -bool dynamic_array_is_empty(const dynamic_array_struct* array); +bool +dynamic_array_is_empty(const dynamic_array_struct* array); /** - * @brief Reserve additional capacity without changing size. + * @brief Reserve additional capacity without changing elements. * * @param array Pointer to the dynamic_array. * @param new_capacity Desired capacity. * @return true on success, false on allocation failure. */ -bool dynamic_array_increase_capacity(dynamic_array_struct* array, - size_t new_capacity); - -/** - * @brief Resize the array capacity to the specified value. - * - * Can grow or shrink the underlying allocation. Occupancy remains unchanged. - * - * @param array Pointer to the dynamic_array. - * @param new_capacity Desired capacity. - * @return true on success, false on allocation failure or if new_capacity < occupancy. - */ -bool dynamic_array_resize(dynamic_array_struct* array, - size_t new_capacity); +bool +dynamic_array_increase_capacity(dynamic_array_struct* array, + size_t new_capacity); /** * @brief Shrink the array capacity to match current occupancy. @@ -220,7 +293,8 @@ bool dynamic_array_resize(dynamic_array_struct* array, * @param array Pointer to the dynamic_array. * @return true on success, false on allocation failure. */ -bool dynamic_array_shrink_to_fit(dynamic_array_struct* array); +bool +dynamic_array_shrink_to_fit(dynamic_array_struct* array); /** * @brief Increment the owner count for shared ownership. @@ -228,7 +302,8 @@ bool dynamic_array_shrink_to_fit(dynamic_array_struct* array); * @param array Pointer to the dynamic_array. * @return true on success, false if owner_count would overflow. */ -bool dynamic_array_acquire(dynamic_array_struct* array); +bool +dynamic_array_acquire(dynamic_array_struct* array); /** * @brief Decrement the owner count for shared ownership. @@ -238,7 +313,8 @@ bool dynamic_array_acquire(dynamic_array_struct* array); * @param array Pointer to the dynamic_array. * @return true if owner_count > 0 after decrement, false if destroyed. */ -bool dynamic_array_release(dynamic_array_struct* array); +bool +dynamic_array_release(dynamic_array_struct* array); /** * @brief Get the current owner count. @@ -246,7 +322,8 @@ bool dynamic_array_release(dynamic_array_struct* array); * @param array Pointer to the dynamic_array. * @return Current owner_count value. */ -unsigned short dynamic_array_get_owner_count(const dynamic_array_struct* array); +unsigned short +dynamic_array_get_owner_count(const dynamic_array_struct* array); /** * @brief Check if the array is thread-safe. @@ -254,31 +331,75 @@ unsigned short dynamic_array_get_owner_count(const dynamic_array_struct* array); * @param array Pointer to the dynamic_array. * @return true if is_multithread_safe is set, false otherwise. */ -bool dynamic_array_is_thread_safe(const dynamic_array_struct* array); +bool +dynamic_array_is_thread_safe(const dynamic_array_struct* array); + +/** + * @brief Creates a shallow copy of the array. + * + * The new array will have the same capacity and occupancy, but the elements + * themselves are not copied (pointers are copied). + * + * @param array_to_copy Pointer to the array being copied. + * @return Pointer to the new shallow copy, or NULL on failure. + */ +dynamic_array_struct* +dynamic_array_shallow_copy(const dynamic_array_struct* array_to_copy); /** * @brief Creates a deep copy of the array. * + * Uses the provided item_copy function to duplicate each element. + * * @param array_to_copy Pointer to the array being copied. + * @param item_copy Function pointer to copy a single element. * @return Pointer to the new deep copy, or NULL on failure. */ -dynamic_array_struct* dynamic_array_clone(const dynamic_array_struct* array_to_copy); +dynamic_array_struct* +dynamic_array_deep_copy(const dynamic_array_struct* array_to_copy, + void* (*item_copy)(const void* item)); /** - * @brief Creates a deep copy of a slice of an array. + * @brief Creates a shallow copy of a slice of an array. * * @param array_to_copy Pointer to the array being copied/sliced. * @param starting_point Index to start the slice at. * @param ending_point Index to end the slice at. - * @param is_start_inclusive Boolean indicator of if the starting index should be included. - * @param is_end_inclusive Boolean indicator of if the ending index should be included. + * @param is_start_inclusive Boolean indicator of if the starting index should + * be included. + * @param is_end_inclusive Boolean indicator of if the ending index should + * be included. + * @return Pointer to the new shallow copy of the slice, or NULL on failure. + */ +dynamic_array_struct* +dynamic_array_shallow_copy_slice(const dynamic_array_struct* array_to_copy, + size_t starting_point, + size_t ending_point, + bool is_start_inclusive, + bool is_end_inclusive); + +/** + * @brief Creates a deep copy of a slice of an array. + * + * Uses the provided item_copy function to duplicate each element in the slice. + * + * @param array_to_copy Pointer to the array being copied/sliced. + * @param item_copy Function pointer to copy a single element. + * @param starting_point Index to start the slice at. + * @param ending_point Index to end the slice at. + * @param is_start_inclusive Boolean indicator of if the starting index should + * be included. + * @param is_end_inclusive Boolean indicator of if the ending index should + * be included. * @return Pointer to the new deep copy of the slice, or NULL on failure. */ -dynamic_array_struct* dynamic_array_clone_slice(const dynamic_array_struct* array_to_copy, - size_t starting_point, - size_t ending_point, - bool is_start_inclusive, - bool is_end_inclusive); +dynamic_array_struct* +dynamic_array_deep_copy_slice(const dynamic_array_struct* array_to_copy, + void* (*item_copy)(const void* item), + size_t starting_point, + size_t ending_point, + bool is_start_inclusive, + bool is_end_inclusive); /** * @brief Modifies the array in place so that it is sorted. @@ -286,8 +407,9 @@ dynamic_array_struct* dynamic_array_clone_slice(const dynamic_array_struct* arra * @param array_to_sort Pointer to the array to be sorted. * @param should_reverse_sort If true, inverts the order of the array. */ -void dynamic_array_sort_in_place(dynamic_array_struct* array_to_sort, - bool should_reverse_sort); +void +dynamic_array_sort_in_place(dynamic_array_struct* array_to_sort, + bool should_reverse_sort); /** * @brief Sorts a slice of the array in place. @@ -295,33 +417,38 @@ void dynamic_array_sort_in_place(dynamic_array_struct* array_to_sort, * @param array_to_sort Pointer to the array to be sorted. * @param starting_point Index to start the slice at. * @param ending_point Index to end the slice at. - * @param is_start_inclusive Boolean indicator of if the starting index should be included. - * @param is_end_inclusive Boolean indicator of if the ending index should be included. + * @param is_start_inclusive Boolean indicator of if the starting index should + * be included. + * @param is_end_inclusive Boolean indicator of if the ending index should + * be included. * @param should_reverse_sort If true, inverts the order of the slice. */ -void dynamic_array_sort_slice(dynamic_array_struct* array_to_sort, - size_t starting_point, - size_t ending_point, - bool is_start_inclusive, - bool is_end_inclusive, - bool should_reverse_sort); +void +dynamic_array_sort_slice(dynamic_array_struct* array_to_sort, + size_t starting_point, + size_t ending_point, + bool is_start_inclusive, + bool is_end_inclusive, + bool should_reverse_sort); /** - * @brief Creates a new sorted copy of the array without modifying the original. + * @brief Creates a new sorted shallow copy of the array without modifying the original. * * @param array_to_sort Pointer to the array to be sorted. * @param should_reverse_sort If true, inverts the order of the copy. * @return Pointer to the new sorted array, or NULL on failure. */ -dynamic_array_struct* dynamic_array_get_sorted_copy(dynamic_array_struct* array_to_sort, - bool should_reverse_sort); +dynamic_array_struct* +dynamic_array_get_sorted_copy(const dynamic_array_struct* array, + bool should_reverse_sort); /** * @brief Reverses the order of elements in the array in place. * * @param array_to_reverse Pointer to the array to be reversed. */ -void dynamic_array_reverse(dynamic_array_struct* array_to_reverse); +void +dynamic_array_reverse(dynamic_array_struct* array_to_reverse); /** * @brief Check if the array contains a specific value. @@ -332,8 +459,9 @@ void dynamic_array_reverse(dynamic_array_struct* array_to_reverse); * @param pointer_of_value_to_find Pointer to the value to find. * @return true if the value is found, false otherwise. */ -bool dynamic_array_contains(dynamic_array_struct* array_to_search, - void* pointer_of_value_to_find); +bool +dynamic_array_contains(const dynamic_array_struct* array_to_search, + const void* pointer_of_value_to_find); /** * @brief Find the first index of a value in the array. @@ -344,8 +472,9 @@ bool dynamic_array_contains(dynamic_array_struct* array_to_search, * @param pointer_of_value_to_find Pointer to the value to find. * @return Index of the first occurrence, or SIZE_MAX if not found. */ -size_t dynamic_array_find_first_index_of(dynamic_array_struct* array_to_search, - void* pointer_of_value_to_find); +size_t +dynamic_array_find_first_index_of(const dynamic_array_struct* array_to_search, + const void* pointer_of_value_to_find); /** * @brief Find the last index of a value in the array. @@ -356,8 +485,9 @@ size_t dynamic_array_find_first_index_of(dynamic_array_struct* array_to_search, * @param pointer_of_value_to_find Pointer to the value to find. * @return Index of the last occurrence, or SIZE_MAX if not found. */ -size_t dynamic_array_find_final_index_of(dynamic_array_struct* array_to_search, - void* pointer_of_value_to_find); +size_t +dynamic_array_find_final_index_of(const dynamic_array_struct* array_to_search, + const void* pointer_of_value_to_find); /** * @brief Set or change the comparator function used for sorting and searching. @@ -365,22 +495,100 @@ size_t dynamic_array_find_final_index_of(dynamic_array_struct* array_to_search, * @param array Pointer to the dynamic_array. * @param comparator Function to compare two elements. */ -void dynamic_array_set_comparator_function(dynamic_array_struct* array, - int (*comparator)(const void*, const void*)); +void +dynamic_array_set_comparator_function(dynamic_array_struct *array, + int (*comparator)(const void*, const void*)); /** * @brief Copies the element at the specified index into a user-provided buffer. * - * Performs bounds checking and uses memcpy to safely extract the element's data. - * Useful when you need a standalone copy of an element rather than a temporary pointer. + * Performs bounds checking and uses memcpy to safely extract the element's + * data. Useful when you need a standalone copy of an element rather than a + * temporary pointer. * * @param array Pointer to the dynamic_array. * @param index Index of the element to retrieve. - * @param out_buffer Pointer to a buffer large enough to hold one element (at least item_size bytes). - * @return true on success, false if index is out of bounds or out_buffer is NULL. + * @param out_buffer Pointer to a buffer large enough to hold one element (at + * least item_size bytes). + * @return true on success, false if index is out of bounds or out_buffer is + * NULL. */ -bool dynamic_array_get_item(const dynamic_array_struct* array, - size_t index, - void* out_buffer); +bool +dynamic_array_get_item(const dynamic_array_struct* array, + size_t index, + void* out_buffer); + +/** + * @brief Iterates over a specific range of the array. + * + * Calls the callback function for each element in the specified range. + * + * @param array Pointer to the dynamic_array. + * @param callback Function to call for each element. + * @param starting_index Index to start iteration from. + * @param ending_index Index to end iteration at. + * @param is_start_inclusive If true, includes the starting_index in iteration. + * @param is_end_inclusive If true, includes the ending_index in iteration. + */ +void +dynamic_array_iterate_over(dynamic_array_struct* array, + void (*callback)(void* element, void* user_data), + size_t starting_index, + size_t ending_index, + bool is_start_inclusive, + bool is_end_inclusive); + +/** + * @brief Iterates over every element in the array. + * + * Calls the callback function for each element in the array. + * + * @param array Pointer to the dynamic_array. + * @param callback Function to call for each element. + */ +void +dynamic_array_foreach(dynamic_array_struct* array, + void (*callback)(void* element, void* user_data)); + +/** + * @brief Performs a binary search on the array. + * + * If the array is not marked as sorted, a temporary sorted copy is created. + * For repeated searches on unsorted data, sort the array in-place first for + * better performance. + * + * @param array Pointer to the dynamic_array. + * @param value Pointer to the value to search for. + * @return Index of the element if found, or SIZE_MAX if not found. + */ +size_t +dynamic_array_binary_search(const dynamic_array_struct* array, + const void* value); + +#ifdef MARIGOLD_DEBUG +/** + * @brief Prints debug statistics about the array to stdout. + * + * @param array Pointer to the dynamic_array. + */ +void dynamic_array_debug_print_stats(const dynamic_array_struct* array); + +/** + * @brief Dumps the contents of the array to stdout. + * + * @param array Pointer to the dynamic_array. + */ +void dynamic_array_debug_dump_array(const dynamic_array_struct* array); + +/** + * @brief Validates the internal integrity of the array. + * + * Checks for consistency in capacity, occupancy, and pointers. + * + * @param array Pointer to the dynamic_array. + * @return true if the array is valid, false otherwise. + */ +bool dynamic_array_validate_integrity(const dynamic_array_struct* array); +#endif /* MARIGOLD_DEBUG */ #endif /* MARIGOLD_DYNAMIC_ARRAY_H */