Compare commits
9 Commits
7de4fbdd4f
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| fddd21064d | |||
| e142468c81 | |||
| df76edc97a | |||
| 76247fec68 | |||
| 826de732d4 | |||
| 5f429f38da | |||
| bb555e34e3 | |||
| 23dea850c4 | |||
| a1fdf5f0c6 |
2
LICENSE
2
LICENSE
@@ -219,7 +219,7 @@ If you develop a new program, and you want it to be of the greatest possible use
|
|||||||
|
|
||||||
To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found.
|
To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
marigold_dynamic_array
|
marigold_vector
|
||||||
Copyright (C) 2026 Emilia Marigold
|
Copyright (C) 2026 Emilia Marigold
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
# marigold_dynamic_array
|
# marigold_vector
|
||||||
|
|
||||||
dynamic_array implementation in C (likely will be C99 or C17) with toggle-able thread-safety via mutex locks.
|
vector datatype (dynamic/static array) implementation in C99 with toggle-able thread-safety via mutex locks.
|
||||||
|
designed to fit within 1 cache line (at or under 64btes on a 64bit system, and at or under 32bytes on a 32bit system.)
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
|
|
||||||
/**
|
|
||||||
* @brief Destroy a dynamic array and free its memory.
|
|
||||||
*
|
|
||||||
* Frees the memory block and the array structure itself.
|
|
||||||
* Sets owner_count to 0 before freeing.
|
|
||||||
*
|
|
||||||
* @param array Pointer to the dynamic_array to destroy.
|
|
||||||
*/
|
|
||||||
void dynamic_array_destroy(dynamic_array* array);
|
|
||||||
@@ -1,594 +0,0 @@
|
|||||||
#ifndef MARIGOLD_DYNAMIC_ARRAY_H
|
|
||||||
#define MARIGOLD_DYNAMIC_ARRAY_H
|
|
||||||
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
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 *);
|
|
||||||
void (*item_free)(const void *);
|
|
||||||
pthread_mutex_t* mutex_lock;
|
|
||||||
unsigned short owner_count;
|
|
||||||
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
|
|
||||||
* initialization.
|
|
||||||
*
|
|
||||||
* Creates a dynamic array with the specified item size and starting capacity.
|
|
||||||
* 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_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,
|
|
||||||
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.
|
|
||||||
*
|
|
||||||
* Checks the owner count. If it is 0 or 1, deallocates the internal memory
|
|
||||||
* 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 destroy, false on failure.
|
|
||||||
*/
|
|
||||||
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.
|
|
||||||
*
|
|
||||||
* @param array Pointer to the dynamic_array.
|
|
||||||
* @return Number of elements (current_occupancy).
|
|
||||||
*/
|
|
||||||
size_t
|
|
||||||
dynamic_array_get_occupancy(const dynamic_array_struct* array);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get the total capacity of the array.
|
|
||||||
*
|
|
||||||
* @param array Pointer to the dynamic_array.
|
|
||||||
* @return Total capacity (current_capacity).
|
|
||||||
*/
|
|
||||||
size_t
|
|
||||||
dynamic_array_get_capacity(const dynamic_array_struct* array);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get a pointer to the element at the specified index.
|
|
||||||
*
|
|
||||||
* @param array Pointer to the dynamic_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);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get a const pointer to the element at the specified index.
|
|
||||||
*
|
|
||||||
* @param array Pointer to the dynamic_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);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get a direct pointer to the underlying memory block.
|
|
||||||
*
|
|
||||||
* Useful for direct iteration or passing to C APIs expecting raw arrays.
|
|
||||||
* Does not perform bounds checking.
|
|
||||||
*
|
|
||||||
* @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);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Append an element to the end of the array.
|
|
||||||
*
|
|
||||||
* Automatically resizes if current_occupancy equals current_capacity.
|
|
||||||
*
|
|
||||||
* @param array Pointer to the dynamic_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);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Insert an element at the specified index.
|
|
||||||
*
|
|
||||||
* Shifts existing elements at and after the index to the right.
|
|
||||||
* Automatically resizes if capacity is exceeded.
|
|
||||||
*
|
|
||||||
* @param array Pointer to the dynamic_array.
|
|
||||||
* @param index Index at which to insert the element.
|
|
||||||
* @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);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Replace the element at the specified index.
|
|
||||||
*
|
|
||||||
* @param array Pointer to the dynamic_array.
|
|
||||||
* @param index Index of the element to replace.
|
|
||||||
* @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);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Swap the elements at two indices.
|
|
||||||
*
|
|
||||||
* @param array Pointer to the dynamic_array.
|
|
||||||
* @param index_a First index.
|
|
||||||
* @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);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @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);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @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);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @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);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Check if the array is empty.
|
|
||||||
*
|
|
||||||
* @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);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @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 Shrink the array capacity to match current occupancy.
|
|
||||||
*
|
|
||||||
* Frees unused memory while keeping all elements intact.
|
|
||||||
*
|
|
||||||
* @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);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Increment the owner count for shared ownership.
|
|
||||||
*
|
|
||||||
* @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);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Decrement the owner count for shared ownership.
|
|
||||||
*
|
|
||||||
* If owner_count reaches 0, the array is automatically destroyed.
|
|
||||||
*
|
|
||||||
* @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);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get the current owner count.
|
|
||||||
*
|
|
||||||
* @param array Pointer to the dynamic_array.
|
|
||||||
* @return Current owner_count value.
|
|
||||||
*/
|
|
||||||
unsigned short
|
|
||||||
dynamic_array_get_owner_count(const dynamic_array_struct* array);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Check if the array is thread-safe.
|
|
||||||
*
|
|
||||||
* @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);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @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_deep_copy(const dynamic_array_struct* array_to_copy,
|
|
||||||
void* (*item_copy)(const void* item));
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @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.
|
|
||||||
* @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_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.
|
|
||||||
*
|
|
||||||
* @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);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Sorts a slice of the array in place.
|
|
||||||
*
|
|
||||||
* @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 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);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @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(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);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Check if the array contains a specific value.
|
|
||||||
*
|
|
||||||
* Requires a valid comparator function to be set.
|
|
||||||
*
|
|
||||||
* @param array_to_search Pointer to the array to search.
|
|
||||||
* @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(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.
|
|
||||||
*
|
|
||||||
* Requires a valid comparator function to be set.
|
|
||||||
*
|
|
||||||
* @param array_to_search Pointer to the 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(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.
|
|
||||||
*
|
|
||||||
* Requires a valid comparator function to be set.
|
|
||||||
*
|
|
||||||
* @param array_to_search Pointer to the 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(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.
|
|
||||||
*
|
|
||||||
* @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*));
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @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.
|
|
||||||
*
|
|
||||||
* @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.
|
|
||||||
*/
|
|
||||||
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 */
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
#ifndef INTERNAL_MARIGOLD_MACROS_H
|
||||||
|
#define INTERNAL_MARIGOLD_MACROS_H
|
||||||
|
|
||||||
|
#define SINGLE_ELEMENT 1
|
||||||
|
#define INDEX_ZERO 0
|
||||||
|
#define BIT_FALSE 0
|
||||||
|
#define EMPTY 0
|
||||||
|
|
||||||
|
#endif /* INTERNAL_MARIGOLD_MACROS_H */
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
#include "internal_marigold_vector.h"
|
||||||
|
|
||||||
|
vector_struct*
|
||||||
|
vector_initialize(vector_struct* new_vector,
|
||||||
|
size_t item_size,
|
||||||
|
size_t starting_capacity,
|
||||||
|
vector_growth_rate_enum growth_rate,
|
||||||
|
int (*comparator)(const void*, const void*),
|
||||||
|
void (*item_free)(const void *),
|
||||||
|
bool is_multithread_safe)
|
||||||
|
{
|
||||||
|
new_vector->item_size = item_size;
|
||||||
|
new_vector->current_capacity = starting_capacity;
|
||||||
|
new_vector->current_occupancy = 0;
|
||||||
|
new_vector->comparator = comparator;
|
||||||
|
new_vector->item_free = item_free;
|
||||||
|
new_vector->growth_strategy = (uint8_t)growth_rate;
|
||||||
|
new_vector->owner_count = 1;
|
||||||
|
|
||||||
|
if (is_multithread_safe)
|
||||||
|
{
|
||||||
|
new_vector->flags |= vector_multithread_safe;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef MARIGOLD_DEBUG
|
||||||
|
new_vector->debug_flags = 0;
|
||||||
|
new_vector->debug_resize_count = 0;
|
||||||
|
new_vector->debug_function_call_count = 0;
|
||||||
|
new_vector->debug_last_error_code = 0;
|
||||||
|
new_vector->debug_allocation_failures = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (starting_capacity > 0)
|
||||||
|
{
|
||||||
|
new_vector->data_pointer = calloc(starting_capacity, item_size);
|
||||||
|
if (!new_vector->data_pointer)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
new_vector->data_pointer = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_multithread_safe)
|
||||||
|
{
|
||||||
|
new_vector->mutex_lock = calloc(1, sizeof(pthread_mutex_t));
|
||||||
|
if (!new_vector->mutex_lock)
|
||||||
|
{
|
||||||
|
free(new_vector->data_pointer);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
pthread_mutex_init(new_vector->mutex_lock, NULL);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
new_vector->mutex_lock = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new_vector;
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
#ifndef INTERNAL_MARIGOLD_VECTOR_H
|
||||||
|
#define INTERNAL_MARIGOLD_VECTOR_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
vector_flag_enum
|
||||||
|
{
|
||||||
|
vector_multithread_safe = 1 << 0,
|
||||||
|
vector_read_only = 1 << 1,
|
||||||
|
vector_certainly_sorted = 1 << 2,
|
||||||
|
vector_marked_for_free = 1 << 3,
|
||||||
|
vector_debug_has_function_call_rolled_over = 1 << 4,
|
||||||
|
vector_debug_has_owner_count_rolled_over = 1 << 5,
|
||||||
|
vector_debug_has_been_resized = 1 << 6,
|
||||||
|
vector_debug_has_been_resized_rolled_over = 1 << 7
|
||||||
|
}
|
||||||
|
vector_flag_enum;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
vector_growth_rate_enum
|
||||||
|
{
|
||||||
|
vector_growth_none = 0,
|
||||||
|
vector_growth_linear = 1,
|
||||||
|
vector_growth_double = 2,
|
||||||
|
vector_growth_triple = 3,
|
||||||
|
vector_growth_quadruple = 4,
|
||||||
|
vector_growth_quintuple = 5
|
||||||
|
}
|
||||||
|
vector_growth_rate_enum;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
vector_struct
|
||||||
|
{
|
||||||
|
size_t item_size;
|
||||||
|
size_t current_capacity;
|
||||||
|
size_t current_occupancy;
|
||||||
|
void* data_pointer;
|
||||||
|
int (*comparator)(const void *, const void *);
|
||||||
|
void (*item_free)(const void *);
|
||||||
|
pthread_mutex_t* mutex_lock;
|
||||||
|
unsigned short owner_count;
|
||||||
|
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. */
|
||||||
|
}
|
||||||
|
vector_struct;
|
||||||
|
|
||||||
|
vector_struct*
|
||||||
|
vector_initialize(vector_struct* new_vector,
|
||||||
|
size_t item_size,
|
||||||
|
size_t starting_capacity,
|
||||||
|
vector_growth_rate_enum growth_rate,
|
||||||
|
int (*comparator)(const void*, const void*),
|
||||||
|
void (*item_free)(const void *),
|
||||||
|
bool is_multithread_safe);
|
||||||
|
|
||||||
|
void
|
||||||
|
vector_free_members();
|
||||||
|
|
||||||
|
bool
|
||||||
|
vector_grow(vector_struct* vector);
|
||||||
|
|
||||||
|
#endif /* INTERNAL_MARIGOLD_VECTOR_H */
|
||||||
697
source_code/MODULE_marigold_vector/marigold_vector.c
Normal file
697
source_code/MODULE_marigold_vector/marigold_vector.c
Normal file
@@ -0,0 +1,697 @@
|
|||||||
|
#include "marigold_vector.h"
|
||||||
|
#include "internal/internal_marigold_macros.h"
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
vector_struct*
|
||||||
|
vector_create(size_t item_size,
|
||||||
|
size_t starting_capacity,
|
||||||
|
vector_growth_rate_enum growth_rate,
|
||||||
|
int (*comparator)(const void*, const void*),
|
||||||
|
void (*item_free)(const void *),
|
||||||
|
bool is_multithread_safe)
|
||||||
|
{
|
||||||
|
vector_struct* new_vector = calloc(SINGLE_ELEMENT,
|
||||||
|
sizeof(vector_struct));
|
||||||
|
|
||||||
|
if (!new_vector)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return vector_initialize(new_vector,
|
||||||
|
item_size,
|
||||||
|
starting_capacity,
|
||||||
|
growth_rate,
|
||||||
|
comparator,
|
||||||
|
item_free,
|
||||||
|
is_multithread_safe);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
vector_destroy(vector_struct* vector_to_destroy)
|
||||||
|
{
|
||||||
|
bool destroyed_successfully = false;
|
||||||
|
vector_free_members(); // actually frees all internal stuff
|
||||||
|
// then free the base container/mutex etc.
|
||||||
|
return destroyed_successfully;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
vector_set_flag(vector_struct* vector,
|
||||||
|
vector_flag_enum flag,
|
||||||
|
bool value)
|
||||||
|
{
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
vector->flags |= flag; // Bitwise or with value.
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
vector->flags &= ~flag; // Bitwise and with inverted value.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
vector_get_flag_value(const vector_struct* vector,
|
||||||
|
vector_flag_enum flag)
|
||||||
|
{
|
||||||
|
if ((vector->flags & flag) != BIT_FALSE)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t
|
||||||
|
vector_get_occupancy(const vector_struct* vector)
|
||||||
|
{
|
||||||
|
return vector->current_occupancy;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t
|
||||||
|
vector_get_capacity(const vector_struct* vector)
|
||||||
|
{
|
||||||
|
return vector->current_capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
void*
|
||||||
|
vector_get_pointer_to_index(vector_struct* vector,
|
||||||
|
size_t index)
|
||||||
|
{
|
||||||
|
return (char*)vector->data_pointer + (index * vector->item_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
const void*
|
||||||
|
vector_get_const_pointer_to_index(const vector_struct* vector,
|
||||||
|
size_t index)
|
||||||
|
{
|
||||||
|
return (const char*)vector->data_pointer + (index * vector->item_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void*
|
||||||
|
vector_get_raw_data_pointer(const vector_struct* vector)
|
||||||
|
{
|
||||||
|
return vector->data_pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
vector_append(vector_struct* vector,
|
||||||
|
const void* element)
|
||||||
|
{
|
||||||
|
const bool is_thread_safe = vector_is_thread_safe(vector);
|
||||||
|
if (is_thread_safe)
|
||||||
|
{ /* if thread safe enabled, locking before vector modification. */
|
||||||
|
pthread_mutex_lock(vector->mutex_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vector->current_occupancy == vector->current_capacity)
|
||||||
|
{
|
||||||
|
if (!vector_grow(vector))
|
||||||
|
{ /* failed to grow vector (probably out of memory)*/
|
||||||
|
if (is_thread_safe)
|
||||||
|
{ /* unlocking mutex during fail case (if it is locked.) */
|
||||||
|
pthread_mutex_unlock(vector->mutex_lock);
|
||||||
|
}
|
||||||
|
return false; /* returns false to show append failed. */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char* data = (char*)vector->data_pointer;
|
||||||
|
size_t offset = vector->current_occupancy * vector->item_size;
|
||||||
|
memcpy(data + offset, element, vector->item_size);
|
||||||
|
vector->current_occupancy++;
|
||||||
|
|
||||||
|
if (is_thread_safe)
|
||||||
|
{ /* unlocking mutex post vector modification if it exists/is locked. */
|
||||||
|
pthread_mutex_unlock(vector->mutex_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true; /* returns true to show append success. */
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
vector_insert_at(vector_struct* vector,
|
||||||
|
size_t index,
|
||||||
|
const void* element)
|
||||||
|
{
|
||||||
|
const bool is_thread_safe = vector_is_thread_safe(vector);
|
||||||
|
if (is_thread_safe)
|
||||||
|
{ /* if thread safe enabled, locking before vector modification. */
|
||||||
|
pthread_mutex_lock(vector->mutex_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index > vector->current_occupancy)
|
||||||
|
{ /* index out of bounds for insertion. */
|
||||||
|
if (is_thread_safe)
|
||||||
|
{ /* unlocking mutex during fail case (if it is locked.) */
|
||||||
|
pthread_mutex_unlock(vector->mutex_lock);
|
||||||
|
}
|
||||||
|
return false; /* returns false to show insert failed. */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vector->current_occupancy == vector->current_capacity)
|
||||||
|
{
|
||||||
|
if (!vector_grow(vector))
|
||||||
|
{ /* failed to grow vector (probably out of memory). */
|
||||||
|
if (is_thread_safe)
|
||||||
|
{ /* unlocking mutex during fail case (if it is locked.) */
|
||||||
|
pthread_mutex_unlock(vector->mutex_lock);
|
||||||
|
}
|
||||||
|
return false; /* returns false to show insert failed. */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char* data = (char*)vector->data_pointer;
|
||||||
|
const size_t element_size = vector->item_size;
|
||||||
|
const size_t bytes_to_shift = (vector->current_occupancy - index) * element_size;
|
||||||
|
const size_t shift_offset = index * element_size;
|
||||||
|
|
||||||
|
if (bytes_to_shift > INDEX_ZERO)
|
||||||
|
{
|
||||||
|
memmove(data + shift_offset + element_size,
|
||||||
|
data + shift_offset,
|
||||||
|
bytes_to_shift);
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(data + shift_offset, element, element_size);
|
||||||
|
vector->current_occupancy++;
|
||||||
|
|
||||||
|
if (is_thread_safe)
|
||||||
|
{ /* unlocking mutex post vector modification if it exists/is locked. */
|
||||||
|
pthread_mutex_unlock(vector->mutex_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true; /* returns true to show insert success. */
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
vector_set_item(vector_struct* vector,
|
||||||
|
size_t index_to_set,
|
||||||
|
const void* new_element)
|
||||||
|
{
|
||||||
|
const bool is_thread_safe = vector_is_thread_safe(vector);
|
||||||
|
if (is_thread_safe)
|
||||||
|
{ /* if thread safe enabled, locking before vector modification. */
|
||||||
|
pthread_mutex_lock(vector->mutex_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index_to_set >= vector->current_occupancy)
|
||||||
|
{ /* index out of bounds for setting. */
|
||||||
|
if (is_thread_safe)
|
||||||
|
{ /* unlocking mutex during fail case if it is locked. */
|
||||||
|
pthread_mutex_unlock(vector->mutex_lock);
|
||||||
|
}
|
||||||
|
return false; /* failed, cannot set a value outside of current values. */
|
||||||
|
}
|
||||||
|
|
||||||
|
char* data = (char*)vector->data_pointer;
|
||||||
|
const size_t element_size = vector->item_size;
|
||||||
|
const size_t offset = index_to_set * element_size;
|
||||||
|
|
||||||
|
if (vector->item_free != NULL)
|
||||||
|
{ /* free the old element if item_free callback is set. */
|
||||||
|
vector->item_free(data + offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(data + offset, new_element, element_size);
|
||||||
|
|
||||||
|
if (is_thread_safe)
|
||||||
|
{ /* unlocking mutex post vector modification if it exists/is locked. */
|
||||||
|
pthread_mutex_unlock(vector->mutex_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true; /* returns true to show set success. */
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
vector_swap(vector_struct* vector,
|
||||||
|
size_t index_a,
|
||||||
|
size_t index_b)
|
||||||
|
{
|
||||||
|
const bool is_thread_safe = vector_is_thread_safe(vector);
|
||||||
|
if (is_thread_safe)
|
||||||
|
{ /* if thread safe enabled, locking before vector modification. */
|
||||||
|
pthread_mutex_lock(vector->mutex_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index_a >= vector->current_occupancy || index_b >= vector->current_occupancy)
|
||||||
|
{ /* index out of bounds for swapping. */
|
||||||
|
if (is_thread_safe)
|
||||||
|
{ /* unlocking mutex during fail case if it is locked. */
|
||||||
|
pthread_mutex_unlock(vector->mutex_lock);
|
||||||
|
}
|
||||||
|
return false; /* returns false to show swap failed. */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index_a == index_b)
|
||||||
|
{ /* same index, nothing to swap. */
|
||||||
|
if (is_thread_safe)
|
||||||
|
{ /* unlocking mutex post vector modification if it exists/is locked. */
|
||||||
|
pthread_mutex_unlock(vector->mutex_lock);
|
||||||
|
}
|
||||||
|
return true; /* returns true to show swap success. */
|
||||||
|
}
|
||||||
|
|
||||||
|
char* data = (char*)vector->data_pointer;
|
||||||
|
const size_t element_size = vector->item_size;
|
||||||
|
const size_t offset_a = index_a * element_size;
|
||||||
|
const size_t offset_b = index_b * element_size;
|
||||||
|
|
||||||
|
void* temp = malloc(element_size);
|
||||||
|
if (!temp)
|
||||||
|
{ /* failed to allocate temporary buffer. */
|
||||||
|
if (is_thread_safe)
|
||||||
|
{ /* unlocking mutex during fail case if it is locked. */
|
||||||
|
pthread_mutex_unlock(vector->mutex_lock);
|
||||||
|
}
|
||||||
|
return false; /* returns false to show swap failed. */
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(temp, data + offset_a, element_size);
|
||||||
|
memcpy(data + offset_a, data + offset_b, element_size);
|
||||||
|
memcpy(data + offset_b, temp, element_size);
|
||||||
|
|
||||||
|
free(temp);
|
||||||
|
|
||||||
|
if (is_thread_safe)
|
||||||
|
{ /* unlocking mutex post vector modification if it exists/is locked. */
|
||||||
|
pthread_mutex_unlock(vector->mutex_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true; /* returns true to show swap success. */
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
vector_remove(vector_struct* vector,
|
||||||
|
size_t index)
|
||||||
|
{
|
||||||
|
const bool is_thread_safe = vector_is_thread_safe(vector);
|
||||||
|
if (is_thread_safe)
|
||||||
|
{ /* if thread safe enabled, locking before vector modification. */
|
||||||
|
pthread_mutex_lock(vector->mutex_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index >= vector->current_occupancy)
|
||||||
|
{ /* index out of bounds for removal. */
|
||||||
|
if (is_thread_safe)
|
||||||
|
{ /* unlocking mutex during fail case if it is locked. */
|
||||||
|
pthread_mutex_unlock(vector->mutex_lock);
|
||||||
|
}
|
||||||
|
return false; /* returns false to show remove failed. */
|
||||||
|
}
|
||||||
|
|
||||||
|
char* data = (char*)vector->data_pointer;
|
||||||
|
const size_t element_size = vector->item_size;
|
||||||
|
const size_t offset = index * element_size;
|
||||||
|
const size_t bytes_to_shift = (vector->current_occupancy - index - 1) * element_size;
|
||||||
|
|
||||||
|
if (vector->item_free != NULL)
|
||||||
|
{ /* free the removed element if item_free callback is set. */
|
||||||
|
vector->item_free(data + offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bytes_to_shift > INDEX_ZERO)
|
||||||
|
{
|
||||||
|
memmove(data + offset,
|
||||||
|
data + offset + element_size,
|
||||||
|
bytes_to_shift);
|
||||||
|
}
|
||||||
|
|
||||||
|
vector->current_occupancy--;
|
||||||
|
|
||||||
|
if (is_thread_safe)
|
||||||
|
{ /* unlocking mutex post vector modification if it exists/is locked. */
|
||||||
|
pthread_mutex_unlock(vector->mutex_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true; /* returns true to show remove success. */
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
vector_clear(vector_struct* vector)
|
||||||
|
{
|
||||||
|
const bool is_thread_safe = vector_is_thread_safe(vector);
|
||||||
|
if (is_thread_safe)
|
||||||
|
{ /* if thread safe enabled, locking before vector modification. */
|
||||||
|
pthread_mutex_lock(vector->mutex_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vector->item_free != NULL)
|
||||||
|
{ /* free all elements if item_free callback is set. */
|
||||||
|
char* data = (char*)vector->data_pointer;
|
||||||
|
const size_t element_size = vector->item_size;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < vector->current_occupancy; i++)
|
||||||
|
{
|
||||||
|
vector->item_free(data + (i * element_size));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vector->current_occupancy = 0;
|
||||||
|
|
||||||
|
if (is_thread_safe)
|
||||||
|
{ /* unlocking mutex post vector modification if it exists/is locked. */
|
||||||
|
pthread_mutex_unlock(vector->mutex_lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
vector_is_empty(const vector_struct* vector)
|
||||||
|
{
|
||||||
|
if (vector->current_occupancy == EMPTY)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
vector_increase_capacity(vector_struct* vector,
|
||||||
|
size_t new_capacity)
|
||||||
|
{
|
||||||
|
const bool is_thread_safe = vector_is_thread_safe(vector);
|
||||||
|
if (is_thread_safe)
|
||||||
|
{ /* if thread safe enabled, locking before vector modification. */
|
||||||
|
pthread_mutex_lock(vector->mutex_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_capacity <= vector->current_capacity)
|
||||||
|
{ /* new capacity not larger than current, nothing to do. */
|
||||||
|
if (is_thread_safe)
|
||||||
|
{ /* unlocking mutex post vector modification if it exists/is locked. */
|
||||||
|
pthread_mutex_unlock(vector->mutex_lock);
|
||||||
|
}
|
||||||
|
return true; /* returns true to show success (no-op). */
|
||||||
|
}
|
||||||
|
|
||||||
|
void* new_data = calloc(new_capacity, vector->item_size);
|
||||||
|
if (!new_data)
|
||||||
|
{ /* failed to allocate new memory. */
|
||||||
|
if (is_thread_safe)
|
||||||
|
{ /* unlocking mutex during fail case if it is locked. */
|
||||||
|
pthread_mutex_unlock(vector->mutex_lock);
|
||||||
|
}
|
||||||
|
return false; /* returns false to show capacity increase failed. */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vector->current_occupancy > 0)
|
||||||
|
{
|
||||||
|
memcpy(new_data,
|
||||||
|
vector->data_pointer,
|
||||||
|
vector->current_occupancy * vector->item_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(vector->data_pointer);
|
||||||
|
vector->data_pointer = new_data;
|
||||||
|
vector->current_capacity = new_capacity;
|
||||||
|
|
||||||
|
if (is_thread_safe)
|
||||||
|
{ /* unlocking mutex post vector modification if it exists/is locked. */
|
||||||
|
pthread_mutex_unlock(vector->mutex_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true; /* returns true to show capacity increase success. */
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
vector_shrink_to_fit(vector_struct* vector)
|
||||||
|
{
|
||||||
|
const bool is_thread_safe = vector_is_thread_safe(vector);
|
||||||
|
if (is_thread_safe)
|
||||||
|
{ /* if thread safe enabled, locking before vector modification. */
|
||||||
|
pthread_mutex_lock(vector->mutex_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vector->current_capacity <= vector->current_occupancy)
|
||||||
|
{ /* capacity already matches or is smaller than occupancy, nothing to do. */
|
||||||
|
if (is_thread_safe)
|
||||||
|
{ /* unlocking mutex post vector modification if it exists/is locked. */
|
||||||
|
pthread_mutex_unlock(vector->mutex_lock);
|
||||||
|
}
|
||||||
|
return true; /* returns true to show success (no-op). */
|
||||||
|
}
|
||||||
|
|
||||||
|
void* new_data = calloc(vector->current_occupancy, vector->item_size);
|
||||||
|
if (!new_data)
|
||||||
|
{ /* failed to allocate new memory. */
|
||||||
|
if (is_thread_safe)
|
||||||
|
{ /* unlocking mutex during fail case if it is locked. */
|
||||||
|
pthread_mutex_unlock(vector->mutex_lock);
|
||||||
|
}
|
||||||
|
return false; /* returns false to show shrink failed. */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vector->current_occupancy > 0)
|
||||||
|
{
|
||||||
|
memcpy(new_data,
|
||||||
|
vector->data_pointer,
|
||||||
|
vector->current_occupancy * vector->item_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(vector->data_pointer);
|
||||||
|
vector->data_pointer = new_data;
|
||||||
|
vector->current_capacity = vector->current_occupancy;
|
||||||
|
|
||||||
|
if (is_thread_safe)
|
||||||
|
{ /* unlocking mutex post vector modification if it exists/is locked. */
|
||||||
|
pthread_mutex_unlock(vector->mutex_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true; /* returns true to show shrink success. */
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
vector_acquire(vector_struct* vector)
|
||||||
|
{
|
||||||
|
const bool is_thread_safe = vector_is_thread_safe(vector);
|
||||||
|
if (is_thread_safe)
|
||||||
|
{ /* if thread safe enabled, locking before vector modification. */
|
||||||
|
pthread_mutex_lock(vector->mutex_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vector->owner_count >= USHRT_MAX)
|
||||||
|
{ /* owner_count would overflow. */
|
||||||
|
if (is_thread_safe)
|
||||||
|
{ /* unlocking mutex during fail case if it is locked. */
|
||||||
|
pthread_mutex_unlock(vector->mutex_lock);
|
||||||
|
}
|
||||||
|
return false; /* returns false to show acquire failed. */
|
||||||
|
}
|
||||||
|
|
||||||
|
vector->owner_count++;
|
||||||
|
|
||||||
|
if (is_thread_safe)
|
||||||
|
{ /* unlocking mutex post vector modification if it exists/is locked. */
|
||||||
|
pthread_mutex_unlock(vector->mutex_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true; /* returns true to show acquire success. */
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
vector_release(vector_struct* vector)
|
||||||
|
{
|
||||||
|
const bool is_thread_safe = vector_is_thread_safe(vector);
|
||||||
|
if (is_thread_safe)
|
||||||
|
{ /* if thread safe enabled, locking before vector modification. */
|
||||||
|
pthread_mutex_lock(vector->mutex_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vector->owner_count == 0)
|
||||||
|
{ /* owner_count already zero, nothing to release. */
|
||||||
|
if (is_thread_safe)
|
||||||
|
{ /* unlocking mutex during fail case if it is locked. */
|
||||||
|
pthread_mutex_unlock(vector->mutex_lock);
|
||||||
|
}
|
||||||
|
return false; /* returns false to show release failed (already released). */
|
||||||
|
}
|
||||||
|
|
||||||
|
vector->owner_count--;
|
||||||
|
|
||||||
|
if (vector->owner_count == 0)
|
||||||
|
{ /* owner_count reached zero, destroy the vector. */
|
||||||
|
return vector_destroy(vector);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_thread_safe)
|
||||||
|
{ /* unlocking mutex post vector modification if it exists/is locked. */
|
||||||
|
pthread_mutex_unlock(vector->mutex_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
return vector->owner_count > 0; /* returns true if owner_count > 0, false if destroyed. */
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned short
|
||||||
|
vector_get_owner_count(const vector_struct* vector)
|
||||||
|
{
|
||||||
|
return vector->owner_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
vector_is_thread_safe(const vector_struct* vector)
|
||||||
|
{
|
||||||
|
if ((vector->flags & vector_multithread_safe) != FALSE)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vector_struct*
|
||||||
|
vector_shallow_copy(const vector_struct* vector_to_copy)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
vector_struct*
|
||||||
|
vector_deep_copy(const vector_struct* vector_to_copy,
|
||||||
|
void* (*item_copy)(const void* item))
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
vector_struct*
|
||||||
|
vector_shallow_copy_slice(const vector_struct* vector_to_copy,
|
||||||
|
size_t starting_point,
|
||||||
|
size_t ending_point,
|
||||||
|
bool is_start_inclusive,
|
||||||
|
bool is_end_inclusive)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
vector_struct*
|
||||||
|
vector_deep_copy_slice(const vector_struct* vector_to_copy,
|
||||||
|
void* (*item_copy)(const void* item),
|
||||||
|
size_t starting_point,
|
||||||
|
size_t ending_point,
|
||||||
|
bool is_start_inclusive,
|
||||||
|
bool is_end_inclusive)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
vector_sort_in_place(vector_struct* vector_to_sort,
|
||||||
|
bool should_reverse_sort)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
vector_sort_slice(vector_struct* vector_to_sort,
|
||||||
|
size_t starting_point,
|
||||||
|
size_t ending_point,
|
||||||
|
bool is_start_inclusive,
|
||||||
|
bool is_end_inclusive,
|
||||||
|
bool should_reverse_sort)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
vector_struct*
|
||||||
|
vector_get_sorted_copy(const vector_struct* vector,
|
||||||
|
bool should_reverse_sort)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
vector_reverse(vector_struct* vector_to_reverse)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
vector_contains(const vector_struct* vector_to_search,
|
||||||
|
const void* pointer_of_value_to_find)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t
|
||||||
|
vector_find_first_index_of(const vector_struct* vector_to_search,
|
||||||
|
const void* pointer_of_value_to_find)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t
|
||||||
|
vector_find_final_index_of(const vector_struct* vector_to_search,
|
||||||
|
const void* pointer_of_value_to_find)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
vector_set_comparator_function(vector_struct *vector,
|
||||||
|
int (*comparator)(const void*, const void*))
|
||||||
|
{
|
||||||
|
vector->comparator = comparator;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
vector_get_item(const vector_struct* vector,
|
||||||
|
size_t index,
|
||||||
|
void* out_buffer)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
vector_iterate_over(vector_struct* vector,
|
||||||
|
void (*callback)(void* element, void* user_data),
|
||||||
|
size_t starting_index,
|
||||||
|
size_t ending_index,
|
||||||
|
bool is_start_inclusive,
|
||||||
|
bool is_end_inclusive)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
vector_foreach(vector_struct* vector,
|
||||||
|
void (*callback)(void* element, void* user_data))
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t
|
||||||
|
vector_binary_search(const vector_struct* vector,
|
||||||
|
const void* value)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef MARIGOLD_DEBUG
|
||||||
|
void
|
||||||
|
vector_debug_print_stats(const vector_struct* vector)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
vector_debug_dump_vector(const vector_struct* vector)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
vector_validate_integrity(const vector_struct* vector)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif /* MARIGOLD_DEBUG */
|
||||||
544
source_code/MODULE_marigold_vector/marigold_vector.h
Normal file
544
source_code/MODULE_marigold_vector/marigold_vector.h
Normal file
@@ -0,0 +1,544 @@
|
|||||||
|
#ifndef MARIGOLD_VECTOR_H
|
||||||
|
#define MARIGOLD_VECTOR_H
|
||||||
|
|
||||||
|
#include "internal/internal_marigold_vector.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create a new dynamic vector, it then internally does all the
|
||||||
|
* initialization.
|
||||||
|
*
|
||||||
|
* Creates a dynamic vector with the specified item size and starting capacity.
|
||||||
|
* The growth_strategy determines how the vector 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_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 vector, or NULL on failure.
|
||||||
|
*/
|
||||||
|
vector_struct*
|
||||||
|
vector_create(size_t item_size,
|
||||||
|
size_t starting_capacity,
|
||||||
|
vector_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 vector and free its memory.
|
||||||
|
*
|
||||||
|
* Checks the owner count. If it is 0 or 1, deallocates the internal memory
|
||||||
|
* and the struct itself. Use this for explicit cleanup. For reference-counted
|
||||||
|
* shared ownership, prefer vector_release().
|
||||||
|
*
|
||||||
|
* If the vector is thread-safe, the internal mutex is destroyed and freed here.
|
||||||
|
*
|
||||||
|
* @param vector_to_destroy is the dynamic vector being deallocated/destroyed.
|
||||||
|
* @return true on successful destroy, false on failure.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
vector_destroy(vector_struct* vector_to_destroy);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set or clear a specific flag on the dynamic vector.
|
||||||
|
*
|
||||||
|
* @param vector Pointer to the vector.
|
||||||
|
* @param flag The flag to modify.
|
||||||
|
* @param value true to set the flag, false to clear it.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
vector_set_flag(vector_struct* vector,
|
||||||
|
vector_flag_enum flag,
|
||||||
|
bool value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get a specific flag on the dynamic vector.
|
||||||
|
*
|
||||||
|
* @param vector Pointer to the vector.
|
||||||
|
* @param flag The flag to get.
|
||||||
|
*
|
||||||
|
* @return value of flag as bool (true or false)
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
vector_get_flag_value(const vector_struct* vector,
|
||||||
|
vector_flag_enum flag);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the number of elements currently in the vector.
|
||||||
|
*
|
||||||
|
* @param vector Pointer to the vector.
|
||||||
|
* @return Number of elements (current_occupancy).
|
||||||
|
*/
|
||||||
|
size_t
|
||||||
|
vector_get_occupancy(const vector_struct* vector);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the total capacity of the vector.
|
||||||
|
*
|
||||||
|
* @param vector Pointer to the vector.
|
||||||
|
* @return Total capacity (current_capacity).
|
||||||
|
*/
|
||||||
|
size_t
|
||||||
|
vector_get_capacity(const vector_struct* vector);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get a pointer to the element at the specified index.
|
||||||
|
*
|
||||||
|
* @param vector Pointer to the vector.
|
||||||
|
* @param index Index of the element to access.
|
||||||
|
* @return Pointer to the element, or NULL if index is out of bounds.
|
||||||
|
*/
|
||||||
|
void*
|
||||||
|
vector_get_pointer_to_index(vector_struct* vector,
|
||||||
|
size_t index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get a const pointer to the element at the specified index.
|
||||||
|
*
|
||||||
|
* @param vector Pointer to the vector.
|
||||||
|
* @param index Index of the element to access.
|
||||||
|
* @return Const pointer to the element, or NULL if index is out of bounds.
|
||||||
|
*/
|
||||||
|
const void*
|
||||||
|
vector_get_const_pointer_to_index(const vector_struct* vector,
|
||||||
|
size_t index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get a direct pointer to the underlying memory block.
|
||||||
|
*
|
||||||
|
* Useful for direct iteration or passing to C APIs expecting raw vectors.
|
||||||
|
* Does not perform bounds checking.
|
||||||
|
*
|
||||||
|
* @param vector Pointer to the vector.
|
||||||
|
* @return Pointer to the raw memory block, or NULL if empty.
|
||||||
|
*/
|
||||||
|
void*
|
||||||
|
vector_get_raw_data_pointer(const vector_struct* vector);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Append an element to the end of the vector.
|
||||||
|
*
|
||||||
|
* Automatically resizes if current_occupancy equals current_capacity.
|
||||||
|
*
|
||||||
|
* @param vector Pointer to the vector.
|
||||||
|
* @param element Pointer to the element to append.
|
||||||
|
* @return true on success, false on allocation failure.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
vector_append(vector_struct* vector, const void* element);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Insert an element at the specified index.
|
||||||
|
*
|
||||||
|
* Shifts existing elements at and after the index to the right.
|
||||||
|
* Automatically resizes if capacity is exceeded.
|
||||||
|
*
|
||||||
|
* @param vector Pointer to the vector.
|
||||||
|
* @param index Index at which to insert the element.
|
||||||
|
* @param element Pointer to the element to insert.
|
||||||
|
* @return true on success, false if index is out of bounds or allocation fails.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
vector_insert_at(vector_struct* vector,
|
||||||
|
size_t index,
|
||||||
|
const void* element);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Replace the element at the specified index.
|
||||||
|
*
|
||||||
|
* @param vector Pointer to the vector.
|
||||||
|
* @param index_to_set Index of the element to replace.
|
||||||
|
* @param new_element Pointer to the new element data.
|
||||||
|
* @return true on success, false if index is out of bounds.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
vector_set_item(vector_struct* vector,
|
||||||
|
size_t index_to_set,
|
||||||
|
const void* new_element);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Swap the elements at two indices.
|
||||||
|
*
|
||||||
|
* @param vector Pointer to the vector.
|
||||||
|
* @param index_a First index.
|
||||||
|
* @param index_b Second index.
|
||||||
|
* @return true on success, false if either index is out of bounds.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
vector_swap(vector_struct* vector,
|
||||||
|
size_t index_a,
|
||||||
|
size_t index_b);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Remove the last element from the vector.
|
||||||
|
*
|
||||||
|
* Decrements current_occupancy. Does not free the memory block.
|
||||||
|
* If item_free is set, it will be called on the removed element.
|
||||||
|
*
|
||||||
|
* @param vector Pointer to the vector.
|
||||||
|
* @return true on success, false if vector is empty.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
vector_pop(vector_struct* vector);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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 vector Pointer to the vector.
|
||||||
|
* @param index Index of the element to remove.
|
||||||
|
* @return true on success, false if index is out of bounds.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
vector_remove(vector_struct* vector,
|
||||||
|
size_t index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Clear all elements from the vector.
|
||||||
|
*
|
||||||
|
* 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 vector Pointer to the vector.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
vector_clear(vector_struct* vector);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check if the vector is empty.
|
||||||
|
*
|
||||||
|
* @param vector Pointer to the vector.
|
||||||
|
* @return true if current_occupancy is 0, false otherwise.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
vector_is_empty(const vector_struct* vector);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reserve additional capacity without changing elements.
|
||||||
|
*
|
||||||
|
* @param vector Pointer to the vector.
|
||||||
|
* @param new_capacity Desired capacity.
|
||||||
|
* @return true on success, false on allocation failure.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
vector_increase_capacity(vector_struct* vector,
|
||||||
|
size_t new_capacity);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Shrink the vector capacity to match current occupancy.
|
||||||
|
*
|
||||||
|
* Frees unused memory while keeping all elements intact.
|
||||||
|
*
|
||||||
|
* @param vector Pointer to the vector.
|
||||||
|
* @return true on success, false on allocation failure.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
vector_shrink_to_fit(vector_struct* vector);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Increment the owner count for shared ownership.
|
||||||
|
*
|
||||||
|
* @param vector Pointer to the vector.
|
||||||
|
* @return true on success, false if owner_count would overflow.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
vector_acquire(vector_struct* vector);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Decrement the owner count for shared ownership.
|
||||||
|
*
|
||||||
|
* If owner_count reaches 0, the vector is automatically destroyed.
|
||||||
|
*
|
||||||
|
* @param vector Pointer to the vector.
|
||||||
|
* @return true if owner_count > 0 after decrement, false if destroyed.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
vector_release(vector_struct* vector);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the current owner count.
|
||||||
|
*
|
||||||
|
* @param vector Pointer to the vector.
|
||||||
|
* @return Current owner_count value.
|
||||||
|
*/
|
||||||
|
unsigned short
|
||||||
|
vector_get_owner_count(const vector_struct* vector);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check if the vector is thread-safe.
|
||||||
|
*
|
||||||
|
* @param vector Pointer to the vector.
|
||||||
|
* @return true if is_multithread_safe is set, false otherwise.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
vector_is_thread_safe(const vector_struct* vector);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Creates a shallow copy of the vector.
|
||||||
|
*
|
||||||
|
* The new vector will have the same capacity and occupancy, but the elements
|
||||||
|
* themselves are not copied (pointers are copied).
|
||||||
|
*
|
||||||
|
* @param vector_to_copy Pointer to the vector being copied.
|
||||||
|
* @return Pointer to the new shallow copy, or NULL on failure.
|
||||||
|
*/
|
||||||
|
vector_struct*
|
||||||
|
vector_shallow_copy(const vector_struct* vector_to_copy);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Creates a deep copy of the vector.
|
||||||
|
*
|
||||||
|
* Uses the provided item_copy function to duplicate each element.
|
||||||
|
*
|
||||||
|
* @param vector_to_copy Pointer to the vector being copied.
|
||||||
|
* @param item_copy Function pointer to copy a single element.
|
||||||
|
* @return Pointer to the new deep copy, or NULL on failure.
|
||||||
|
*/
|
||||||
|
vector_struct*
|
||||||
|
vector_deep_copy(const vector_struct* vector_to_copy,
|
||||||
|
void* (*item_copy)(const void* item));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Creates a shallow copy of a slice of a vector.
|
||||||
|
*
|
||||||
|
* @param vector_to_copy Pointer to the vector 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.
|
||||||
|
* @return Pointer to the new shallow copy of the slice, or NULL on failure.
|
||||||
|
*/
|
||||||
|
vector_struct*
|
||||||
|
vector_shallow_copy_slice(const vector_struct* vector_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 a vector.
|
||||||
|
*
|
||||||
|
* Uses the provided item_copy function to duplicate each element in the slice.
|
||||||
|
*
|
||||||
|
* @param vector_to_copy Pointer to the vector 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.
|
||||||
|
*/
|
||||||
|
vector_struct*
|
||||||
|
vector_deep_copy_slice(const vector_struct* vector_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 vector in place so that it is sorted.
|
||||||
|
*
|
||||||
|
* @param vector_to_sort Pointer to the vector to be sorted.
|
||||||
|
* @param should_reverse_sort If true, inverts the order of the vector.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
vector_sort_in_place(vector_struct* vector_to_sort,
|
||||||
|
bool should_reverse_sort);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sorts a slice of the vector in place.
|
||||||
|
*
|
||||||
|
* @param vector_to_sort Pointer to the vector 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 should_reverse_sort If true, inverts the order of the slice.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
vector_sort_slice(vector_struct* vector_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 shallow copy of the vector without modifying the original.
|
||||||
|
*
|
||||||
|
* @param vector_to_sort Pointer to the vector to be sorted.
|
||||||
|
* @param should_reverse_sort If true, inverts the order of the copy.
|
||||||
|
* @return Pointer to the new sorted vector, or NULL on failure.
|
||||||
|
*/
|
||||||
|
vector_struct*
|
||||||
|
vector_get_sorted_copy(const vector_struct* vector,
|
||||||
|
bool should_reverse_sort);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reverses the order of elements in the vector in place.
|
||||||
|
*
|
||||||
|
* @param vector_to_reverse Pointer to the vector to be reversed.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
vector_reverse(vector_struct* vector_to_reverse);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check if the vector contains a specific value.
|
||||||
|
*
|
||||||
|
* Requires a valid comparator function to be set.
|
||||||
|
*
|
||||||
|
* @param vector_to_search Pointer to the vector to search.
|
||||||
|
* @param pointer_of_value_to_find Pointer to the value to find.
|
||||||
|
* @return true if the value is found, false otherwise.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
vector_contains(const vector_struct* vector_to_search,
|
||||||
|
const void* pointer_of_value_to_find);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Find the first index of a value in the vector.
|
||||||
|
*
|
||||||
|
* Requires a valid comparator function to be set.
|
||||||
|
*
|
||||||
|
* @param vector_to_search Pointer to the vector 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
|
||||||
|
vector_find_first_index_of(const vector_struct* vector_to_search,
|
||||||
|
const void* pointer_of_value_to_find);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Find the last index of a value in the vector.
|
||||||
|
*
|
||||||
|
* Requires a valid comparator function to be set.
|
||||||
|
*
|
||||||
|
* @param vector_to_search Pointer to the vector 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
|
||||||
|
vector_find_final_index_of(const vector_struct* vector_to_search,
|
||||||
|
const void* pointer_of_value_to_find);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set or change the comparator function used for sorting and searching.
|
||||||
|
*
|
||||||
|
* @param vector Pointer to the vector.
|
||||||
|
* @param comparator Function to compare two elements.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
vector_set_comparator_function(vector_struct *vector,
|
||||||
|
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.
|
||||||
|
*
|
||||||
|
* @param vector Pointer to the vector.
|
||||||
|
* @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.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
vector_get_item(const vector_struct* vector,
|
||||||
|
size_t index,
|
||||||
|
void* out_buffer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Iterates over a specific range of the vector.
|
||||||
|
*
|
||||||
|
* Calls the callback function for each element in the specified range.
|
||||||
|
*
|
||||||
|
* @param vector Pointer to the vector.
|
||||||
|
* @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
|
||||||
|
vector_iterate_over(vector_struct* vector,
|
||||||
|
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 vector.
|
||||||
|
*
|
||||||
|
* Calls the callback function for each element in the vector.
|
||||||
|
*
|
||||||
|
* @param vector Pointer to the vector.
|
||||||
|
* @param callback Function to call for each element.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
vector_foreach(vector_struct* vector,
|
||||||
|
void (*callback)(void* element, void* user_data));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Performs a binary search on the vector.
|
||||||
|
*
|
||||||
|
* If the vector is not marked as sorted, a temporary sorted copy is created.
|
||||||
|
* For repeated searches on unsorted data, sort the vector in-place first for
|
||||||
|
* better performance.
|
||||||
|
*
|
||||||
|
* @param vector Pointer to the vector.
|
||||||
|
* @param value Pointer to the value to search for.
|
||||||
|
* @return Index of the element if found, or SIZE_MAX if not found.
|
||||||
|
*/
|
||||||
|
size_t
|
||||||
|
vector_binary_search(const vector_struct* vector,
|
||||||
|
const void* value);
|
||||||
|
|
||||||
|
#ifdef MARIGOLD_DEBUG
|
||||||
|
/**
|
||||||
|
* @brief Prints debug statistics about the vector to stdout.
|
||||||
|
*
|
||||||
|
* @param vector Pointer to the vector.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
vector_debug_print_stats(const vector_struct* vector);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Dumps the contents of the vector to stdout.
|
||||||
|
*
|
||||||
|
* @param vector Pointer to the vector.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
vector_debug_dump_vector(const vector_struct* vector);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Validates the internal integrity of the vector.
|
||||||
|
*
|
||||||
|
* Checks for consistency in capacity, occupancy, and pointers.
|
||||||
|
*
|
||||||
|
* @param vector Pointer to the vector.
|
||||||
|
* @return true if the vector is valid, false otherwise.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
vector_validate_integrity(const vector_struct* vector);
|
||||||
|
#endif /* MARIGOLD_DEBUG */
|
||||||
|
|
||||||
|
#endif /* MARIGOLD_VECTOR_H */
|
||||||
@@ -1,12 +1,42 @@
|
|||||||
|
#include "MODULE_marigold_vector/marigold_vector.h"
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include "MODULE_marigold_dynamic_array/marigold_dynamic_array.h"
|
#include <time.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
_.-''''-..
|
||||||
|
.-' -_ \
|
||||||
|
/ -. \ ,_ , ' ,,-.
|
||||||
|
/ _ \ \ \ '''" /
|
||||||
|
|(q) ) | / \
|
||||||
|
\',_/ / | (|) (|) )
|
||||||
|
\ __' \ == v ==
|
||||||
|
'--..-' " - ^-'"
|
||||||
|
'-__ __)
|
||||||
|
''''''
|
||||||
|
This codebase has been visited by Snailcat
|
||||||
|
Snailcat believes that software should move slow and
|
||||||
|
be stable. He despises constant updates and dreams of
|
||||||
|
software so finished it stops having to move at all.
|
||||||
|
All problems are trivial to solve with enough knowledge/people working on it.
|
||||||
|
Use AGPLv3 license on your project today.
|
||||||
|
|
||||||
|
Move slow, snailcat!
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
printf("Hello, %s!\n", "marigold_dynamic_array");
|
printf("Hello, %s!\n", "marigold_dynamic_array");
|
||||||
printf("Version: %s\n", "26.136.1101");
|
printf("Version: %s\n", "26.136.1101");
|
||||||
|
|
||||||
dynamic_array array = dynamic_array_create();
|
vector_struct* vector = vector_create(sizeof(int),
|
||||||
|
500,
|
||||||
|
vector_growth_double,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
true);
|
||||||
|
|
||||||
return 0;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user