Compare commits
7 Commits
23dea850c4
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| fddd21064d | |||
| e142468c81 | |||
| df76edc97a | |||
| 76247fec68 | |||
| 826de732d4 | |||
| 5f429f38da | |||
| bb555e34e3 |
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.
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,10 +1,75 @@
|
|||||||
|
#ifndef INTERNAL_MARIGOLD_VECTOR_H
|
||||||
|
#define INTERNAL_MARIGOLD_VECTOR_H
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Destroy a dynamic array and free its memory.
|
#include <pthread.h>
|
||||||
*
|
#include <stdbool.h>
|
||||||
* Frees the memory block and the array structure itself.
|
#include <stdint.h>
|
||||||
* Sets owner_count to 0 before freeing.
|
#include <stdlib.h>
|
||||||
*
|
|
||||||
* @param array Pointer to the dynamic_array to destroy.
|
typedef enum
|
||||||
*/
|
vector_flag_enum
|
||||||
void dynamic_array_destroy(dynamic_array* array);
|
{
|
||||||
|
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 */
|
||||||
|
|||||||
@@ -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 */
|
||||||
|
|||||||
@@ -1,60 +1,7 @@
|
|||||||
#ifndef MARIGOLD_VECTOR_H
|
#ifndef MARIGOLD_VECTOR_H
|
||||||
#define MARIGOLD_VECTOR_H
|
#define MARIGOLD_VECTOR_H
|
||||||
|
|
||||||
#include <pthread.h>
|
#include "internal/internal_marigold_vector.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;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Create a new dynamic vector, it then internally does all the
|
* @brief Create a new dynamic vector, it then internally does all the
|
||||||
@@ -206,13 +153,13 @@ vector_insert_at(vector_struct* vector,
|
|||||||
* @brief Replace the element at the specified index.
|
* @brief Replace the element at the specified index.
|
||||||
*
|
*
|
||||||
* @param vector Pointer to the vector.
|
* @param vector Pointer to the vector.
|
||||||
* @param index Index of the element to replace.
|
* @param index_to_set Index of the element to replace.
|
||||||
* @param new_element Pointer to the new element data.
|
* @param new_element Pointer to the new element data.
|
||||||
* @return true on success, false if index is out of bounds.
|
* @return true on success, false if index is out of bounds.
|
||||||
*/
|
*/
|
||||||
bool
|
bool
|
||||||
vector_set_item(vector_struct* vector,
|
vector_set_item(vector_struct* vector,
|
||||||
size_t index,
|
size_t index_to_set,
|
||||||
const void* new_element);
|
const void* new_element);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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