Compare commits

...

12 Commits

14 changed files with 1424 additions and 264 deletions

View File

@@ -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.

View File

@@ -1,3 +1,4 @@
# marigold_dynamic_array # marigold_vector
dynamic_array implementation in C99 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.)

View File

@@ -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);

View File

@@ -1,248 +0,0 @@
#ifndef MARIGOLD_DYNAMIC_ARRAY_H
#define MARIGOLD_DYNAMIC_ARRAY_H
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <pthread.h>
typedef struct dynamic_array
{
size_t item_size;
size_t starting_capacity;
size_t current_capacity;
size_t current_size;
void* memory_block_pointer;
int (*comparator)(const void*, const void*);
pthread_mutex_t* mutex_lock;
unsigned short owner_count;
unsigned short growth_steps;
unsigned char growth_factor;
bool is_multithread_safe;
bool is_read_only;
bool is_certainly_sorted;
} dynamic_array;
/**
* @brief Create a new dynamic array, it then internally does all the initalization.
*
* Creates a dynamic array with the specified item size and starting capacity.
* The growth_factor determines how the array expands when capacity is exceeded.
*
* @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 = linear (+starting_capacity),
* 1 = 1.5x, 2+ = multiplier (2 = 2x, 3 = 3x, etc.)
* @param is_multithread_safe If true, enables thread-safe operations.
* @return Pointer to the initialized dynamic_array, or NULL on failure.
*/
dynamic_array* dynamic_array_create(size_t item_size,
unsigned int starting_capacity,
unsigned char growth_factor,
int (*comparator)(const void*, const void*),
bool is_multithread_safe);
/**
* @brief This function checks to make sure the owner count 1 or 0
* then deallocates what needs to be deallocated, before destorying it.
*
* @param array_to_destroy is the dynamic array being deallocated/destroyed.
* @return true on successful destory, false on failure.
*/
bool dynamic_array_destroy(dynamic_array* array_to_destroy);
/**
* @brief Get the number of elements currently in the array.
*
* @param array Pointer to the dynamic_array.
* @return Number of elements (current_size).
*/
unsigned int dynamic_array_get_occupancy(const dynamic_array* array);
/**
* @brief Get the total capacity of the array.
*
* @param array Pointer to the dynamic_array.
* @return Total capacity (current_capacity).
*/
unsigned int dynamic_array_get_capacity(const dynamic_array* 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(const dynamic_array* array,
const unsigned int 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* array,
const unsigned int index);
/**
* @brief Append an element to the end of the array.
*
* Automatically resizes if current_size 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* array,
const void* element);
/**
* @brief Remove the last element from the array.
*
* Decrements current_size. Does not free the memory block.
*
* @param array Pointer to the dynamic_array.
* @return true on success, false if array is empty.
*/
bool dynamic_array_pop(dynamic_array* array);
/**
* @brief Remove an element at the specified index.
*
* Shifts subsequent elements down to fill the gap.
*
* @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* array,
const unsigned int index);
/**
* @brief Clear all elements from the array.
*
* Sets current_size to 0. Does not free the memory block.
*
* @param array Pointer to the dynamic_array.
*/
void dynamic_array_clear(dynamic_array* array);
/**
* @brief Check if the array is empty.
*
* @param array Pointer to the dynamic_array.
* @return true if current_size is 0, false otherwise.
*/
bool dynamic_array_is_empty(const dynamic_array* array);
/**
* @brief Reserve additional capacity without changing size.
*
* @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* array,
const unsigned int new_capacity);
/**
* @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* 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* 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* 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* array);
/**
* @brief Creates a deep copy of the array.
*
* @param array_to_copy Pointer to the array being copied.
*
* @return returns the new deep copy as a pointer.
*/
dynamic_array* dynamic_array_clone(const dynamic_array* array_to_copy);
/**
* @brief Creates a deep copy of a slice of an array
*
* @param array_to_copy_and_slice 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 in the slice.
* @param is_end_inclusive Boolean indicator of if the ending index should be
* included in the slice.
*
* @return returns the new deep copy of the array as a pointer to a new array.
*/
dynamic_array* dynamic_array_clone_slice(const dynamic_array* array_to_copy_and_slice,
const size_t starting_point,
const size_t ending_point,
const bool is_start_inclusive,
const bool is_end_inclusive);
/**
* @brief Modifies the list so that it is sorted, and can invert the sorting function.
*
* @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* array_to_sort,
const bool should_reverse_sort);
void dynamic_array_sort_slice(dynamic_array* array_to_sort,
const size_t starting_point,
const size_t ending_point,
const bool is_start_inclusive,
const bool is_end_inclusive,
const bool should_reverse_sort);
dynamic_array* dynamic_array_get_sorted_copy(dynamic_array* array_to_sort,
const bool should_reverse_sort);
void dynamic_array_reverse(dynamic_array* array_to_reverse);
bool dynamic_array_contains(dynamic_array* array_to_search,
void* pointer_of_value_to_find);
size_t dynamic_array_find_first_index_of(dynamic_array* array_to_search,
void* pointer_of_value_to_find);
size_t dynamic_array_find_final_index_of(dynamic_array* array_to_search,
void* pointer_of_value_to_find);
void dynamic_array_set_comparator_function(dynamic_array* array,
int (*comparator)(const void*, const void*));
#endif /* MARIGOLD_DYNAMIC_ARRAY_H */

View File

@@ -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 */

View File

@@ -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;
}

View File

@@ -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 */

View 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 */

View 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 */

View File

@@ -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 = init_dynamic_array(); vector_struct* vector = vector_create(sizeof(int),
500,
vector_growth_double,
NULL,
NULL,
true);
return 0; return EXIT_SUCCESS;
} }