1
0
Fork 0
C_lib/vector/vector.c

245 lines
8.8 KiB
C

/*
* C_lib Copyright (C) 2021 Wael Karram.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/* This file contains the function implementations for a vector. */
/* Include guard + includes. */
#ifndef VECTOR_C
#define VECTOR_C
#include "vector.h"
#endif /* VECTOR_C */
/* Function implementations. */
/* This function initializes a new empty vector, returns NULL on failure. */
static inline vector_t* initialize_vector() {
/* Local variables. */
vector_t *vector;
/* Attempt to allocate. */
vector = (vector_t*) malloc(sizeof(vector_t));
/* Check the allocation. */
if (vector == NULL)
return NULL;
/* Set the initial values. */
vector->power = INITIAL_POWER_VECTOR_C;
vector->length = powl(GROWTH_CONSTANT_FACTOR, INITIAL_POWER_VECTOR_C);
vector->element_size = sizeof(void*); /* For better portability. */
vector->last_element = (size_t) LAST_ELEMENT_EMPTY_INDEX;
/* Attempt to allocate the array. */
vector->array = malloc(vector->length * sizeof(void*));
/* Check the allocation. */
if (vector->array == NULL) {
free(vector);
return NULL;
}
/* TODO: Added as a precaution, check if it is practically useful or not. */
/* Set all to NULL. */
memset(vector->array, NULL, vector->length * sizeof(void*));
/* Return the pointer. */
return vector;
}
/* This function appends a value to a vector, user should verify that size of data is compatible! */
/* Note that if a failure code is returned, then the original vector wasn't changed whatsoever. */
/* Returns SUCCESS_CODE_VECTOR_C on success and FAILURE_CODE_VECTOR_C on failure. */
/* Undefined behavior for NULL vectors. */
static inline int append_to_vector(const vector_t* const vector, const void* const data) {
/* Check if the vector has enough space. */
if (check_size_change_vector(vector, THRESHOLD_GROW_VECTOR)) {
/* Grow the vector. */
if (!grow_vector(vector))
return FAILURE_CODE_VECTOR_C;
}
/* Add the value to the end. */ /* TODO: check!! */
*((vector->array) + (((vector->last_element == (size_t) LAST_ELEMENT_EMPTY_INDEX) ? 0 : vector->last_element) * vector->element_size)) = data;
/* Increment the internal index. */
vector->last_element = vector->last_element + 1L;
/* Return success. */
return SUCCESS_CODE_VECTOR_C;
}
/* This function prepends a value to a vector, user should verify that size of data is compatible! */
/* Note that if a failure code is returned, then the original vector wasn't changed whatsoever. */
/* Returns SUCCESS_CODE_VECTOR_C on success and FAILURE_CODE_VECTOR_C on failure. */
/* Undefined behavior for NULL vectors. */
static inline int prepend_to_vector(const vector_t* const vector, const void* const data) {
/* Check if the vector has enough space. */
if (check_size_change_vector(vector, THRESHOLD_GROW_VECTOR)) {
/* Grow the vector. */
if (!grow_vector(vector))
return FAILURE_CODE_VECTOR_C;
}
/* Push the vector by 1. */
push_vector(vector, 1L);
/* Prepend. */
*(vector->array) = data;
/* Increment the internal index. */
vector->last_element = vector->last_element + 1L;
/* Return success. */
return SUCCESS_CODE_VECTOR_C;
}
/* This function reads from the vector at the given index. */
/* Returns NULL on error and sets error to the error code. */
/* NULL can also be a perfectly fine value to store and retrieve. */
/* Undefined behavior for NULL vectors. */
static inline void* read_from_vector(const vector_t* const vector, const size_t index, const int *error) {
/* Check if read index is within bounds. */
if (vector->last_element < index) {
/* Index out of bounds. */
*error = INDEX_NOT_FOUND_CODE_VECTOR_C;
return NULL;
}
/* Read and return the pointer. */
return (vector->array + (index * vector->element_size));
}
/* This function reads from the vector at the given index. */
/* Returns NULL on error and sets error to the error code. */
/* NULL can also be a perfectly fine value to store and retrieve. */
/* Undefined behavior for NULL vectors. */
/* Disallows writes beyond last_index + 1. */
static inline int write_to_vector(const vector_t* const vector, const size_t index, const void* const data) {
/* Check if write index is within bounds. */
if ((vector->length <= index) || ((vector->last_element + 1L) < index))
return INDEX_NOT_FOUND_CODE_VECTOR_C;
/* Check if the write is over-writing or not. */
/* Check for an append call. */
if (index == (vector->last_element + 1L))
return append_to_vector(vector, data);
/* Overwriting call! */
*(vector->array + (index * vector->element_size)) = data;
vector->last_element = vector->last_element + 1L;
/* Check if the vector needs to be grown. */
if (check_size_change_vector(vector, THRESHOLD_GROW_VECTOR_C))
return (grow_vector(vector)) ? SUCCESS_CODE_VECTOR_C : WRITE_SUCCESS_VECTOR_GROW_FAIL_VECTOR_C;
/* Done. */
return SUCCESS_CODE_VECTOR_C;
}
/* Internal functions. */
/* This function checks if the vector needs to be grown, shrunk or kept as is. */
inline int check_size_change_vector(const vector_t* const vector, const double threshold) {
/* Calculate the threshold and length. */
size_t current_size = vector->last_element;
size_t current_threshold = (size_t)((vector->length) * threshold);
/* Check if it needs to be grown. */
if (current_size >= current_threshold)
return CODE_GROW_VECTOR_C;
/* Calculate the new threshold for shrinking the vector. */
current_threshold = (size_t)((vector->length) * (THRESHOLD_WHOLE_VECTOR_C - threshold));
/* Return according to if it needs to be shrunk or kept at the same size. */
return (current_size <= current_threshold) ? CODE_SHRINK_VECTOR_C : CODE_KEEP_VECTOR_C;
}
/* This function grows a vector, doesn't change it on failure. */
/* Returns SUCCESS_CODE_VECTOR_C on success and FAILURE_CODE_VECTOR_C on failure. */
inline int grow_vector(const vector_t* const vector) {
/* Local variables. */
size_t size;
void* array;
/* Calculate the new size. */
size = ((vector->length * vector->element_size) * GROWTH_CONSTANT_VECTOR_C);
/* Attempt to reallocate, and return the result. */
array = realloc(vector->array, size);
/* Check if the allocation succeeded. */
if (array != NULL) {
/* Put the new pointer in. */
vector->array = array;
/* Update the length. */
vector->length = size;
/* Return success. */
return SUCCESS_CODE_VECTOR_C;
}
/* Allocation failed. */
return FAILURE_CODE_VECTOR_C;
}
/* This function shrinks a vector, doesn't change it on failure. */
/* Note that if the vector cannot be shrunk according to the growth factor, data loss may occure. */
/* Returns SUCCESS_CODE_VECTOR_C on success and FAILURE_CODE_VECTOR_C on failure. */
inline int shrink_vector(const vector_t* const vector) {
/* Local variables. */
size_t size;
void* array;
/* Calculate the new size. */
size = ((vector->length * vector->element_size) / GROWTH_CONSTANT_VECTOR_C);
/* Attempt to reallocate, and return the result. */
array = realloc(vector->array, size);
/* Check if the allocation succeeded. */
if (array != NULL) {
/* Put the new pointer in. */
vector->array = array;
/* Update the length. */
vector->length = size;
/* Return success. */
return SUCCESS_CODE_VECTOR_C;
}
/* Allocation failed. */
return FAILURE_CODE_VECTOR_C;
}
/* This function compacts a vector by pushing all the NULLs to the end. */
/* Try to avoid calling it unless needed as it has an O(n^2) runtime complexity. */
inline void compact_vector(const vector_t* const vector) {
/* Loop and push. */
void *pointer = vector->array;
size_t i, j;
/* TODO: Check this logic and pointer arithmetic! */
for (i = 0; i < vector->length; i++)
for (j = 0; j < vector->length - 1; j++)
if (*(pointer + (j * vector->element_size)) == NULL) {
/* Current pointer is NULL, swap with the next one. */
*(pointer + (j * vector->element_size)) = *(pointer + (j * (vector->element_size + 1)));
*(pointer + (j * (vector->element_size + 1))) = NULL;
}
}
/* This function creates a "gap" (of NULL) at the start of the vector, doesn't do any sanity checking! */
inline void push_vector(const vector_t* const vector, const size_t gap_length) {
/* Local variables. */
size_t i;
/* Push from the end to the start. */
for (i = vector->last_element; i >= 0L; i--)
*((i + gap_length ) * vector->element_size) = *(i * vector->element_size);
/* Put NULL in the start of the vector. */
for (i = 0L; i < gap_length; i++)
*(i * vector->element_size) = NULL;
}