diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3776d2e --- /dev/null +++ b/Makefile @@ -0,0 +1,31 @@ +CC = gcc +CFLAGS = -Wall -Wextra -Werror -pedantic-errors -fstack-protector-strong \ + -fsanitize=address -fsanitize=undefined -fstack-clash-protection \ + -Wwrite-strings -std=c99 +LDFLAGS = -fsanitize=address -fsanitize=undefined + +SRC_DIR = src +OBJ_DIR = obj + +TARGET = usage +LIB_OBJS = $(OBJ_DIR)/vector.o +PROG_OBJS = $(OBJ_DIR)/usage.o + +.PHONY: all clean + +all: $(TARGET) + +$(TARGET): $(PROG_OBJS) $(LIB_OBJS) + $(CC) $(CFLAGS) -o $@ $^ + +$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(OBJ_DIR) + $(CC) $(CFLAGS) -c -o $@ $< + +$(OBJ_DIR)/%.o: %.c | $(OBJ_DIR) + $(CC) $(CFLAGS) -c -o $@ $< + +$(OBJ_DIR): + mkdir -p $(OBJ_DIR) + +clean: + rm -rf $(OBJ_DIR) $(TARGET) diff --git a/src/vector.c b/src/vector.c index 53433b2..dadcc94 100644 --- a/src/vector.c +++ b/src/vector.c @@ -1,26 +1,26 @@ -#include // for snprintf -#include // for malloc, realloc and free -#include // for memcpy +#include +#include +#include #include "vector.h" // Internal method to increase vector size -static Result vector_resize(Vector *vector); +static VectorResult vector_resize(Vector *vector); /** * vector_new * @size: initial number of elements * @data_size: size of each element in bytes * - * Returns a Result data type containing a new vector + * Returns a VectorResult data type containing a new vector */ -Result vector_new(size_t size, size_t data_size) { - Result result = {0}; +VectorResult vector_new(size_t size, size_t data_size) { + VectorResult result = {0}; // Allocate a new vector Vector *vector = malloc(sizeof(Vector)); if (vector == NULL) { - result.status = 1; + result.status = VECTOR_ERR_ALLOCATE; snprintf((char *)result.message, RESULT_MSG_SIZE, "Failed to allocate memory for vector"); return result; @@ -32,13 +32,13 @@ Result vector_new(size_t size, size_t data_size) { vector->data_size = data_size; vector->elements = calloc(size, data_size); if (vector->elements == NULL) { - result.status = 1; + result.status = VECTOR_ERR_ALLOCATE; snprintf((char *)result.message, RESULT_MSG_SIZE, "Failed to allocate memory for vector elements"); return result; } - result.status = 0; + result.status = VECTOR_OK; snprintf((char *)result.message, RESULT_MSG_SIZE, "Vector successfully created"); result.value.vector = vector; @@ -51,17 +51,17 @@ Result vector_new(size_t size, size_t data_size) { * * Increases the size of @vector * - * Returns a Result data type containing the status + * Returns a VectorResult data type containing the status */ -Result vector_resize(Vector *vector) { - Result result = {0}; +VectorResult vector_resize(Vector *vector) { + VectorResult result = {0}; size_t old_capacity = vector->capacity; vector->capacity = (old_capacity > 0 ? ((old_capacity * 3) / 2) : 1); // Check for stack overflow errors if (vector->capacity > SIZE_MAX / vector->data_size) { - result.status = 1; + result.status = VECTOR_ERR_OVERFLOW; snprintf((char *)result.message, RESULT_MSG_SIZE, "Exceeded maximum size while resizing vector"); return result; @@ -69,7 +69,7 @@ Result vector_resize(Vector *vector) { void *new_elements = realloc(vector->elements, (vector->capacity * vector->data_size)); if (new_elements == NULL) { - result.status = 1; + result.status = VECTOR_ERR_ALLOCATE; snprintf((char *)result.message, RESULT_MSG_SIZE, "Failed to reallocate memory for vector"); return result; @@ -77,7 +77,7 @@ Result vector_resize(Vector *vector) { vector->elements = new_elements; - result.status = 0; + result.status = VECTOR_OK; snprintf((char *)result.message, RESULT_MSG_SIZE, "Vector successfully resized"); return result; @@ -86,17 +86,17 @@ Result vector_resize(Vector *vector) { /** * vector_push * @vector: a non-null vector - * @value: a generic value to add on the vector + * @value: a generic value to add to the vector * * Adds @value at the end of @vector * - * Returns a Result data type containing the status + * Returns a VectorResult data type containing the status */ -Result vector_push(Vector *vector, void *value) { - Result result = {0}; +VectorResult vector_push(Vector *vector, void *value) { + VectorResult result = {0}; if (vector == NULL || value == NULL) { - result.status = 1; + result.status = VECTOR_ERR_INVALID; snprintf((char *)result.message, RESULT_MSG_SIZE, "Invalid vector or value"); return result; @@ -105,7 +105,7 @@ Result vector_push(Vector *vector, void *value) { // Check whether vector has enough space available if (vector->capacity == vector->count) { result = vector_resize(vector); - if (result.status != 0) { + if (result.status != VECTOR_OK) { return result; } } @@ -129,43 +129,159 @@ Result vector_push(Vector *vector, void *value) { // Increase elements count vector->count++; - result.status = 0; + result.status = VECTOR_OK; snprintf((char *)result.message, RESULT_MSG_SIZE, "Value successfully added"); return result; } +/** + * vector_set + * @vector: a non-null vector + * @index: a non-negative integer representing the position to write into + * @value: a generic value to add to the vector + * + * Writes @value at @index + * + * Returns a VectorResult data type + */ +VectorResult vector_set(Vector *vector, size_t index, void *value) { + VectorResult result = {0}; + + if (vector == NULL || value == NULL) { + result.status = VECTOR_ERR_INVALID; + snprintf((char *)result.message, RESULT_MSG_SIZE, "Invalid vector or value"); + + return result; + } + + if (index >= vector->count) { + result.status = VECTOR_ERR_OVERFLOW; + snprintf((char *)result.message, RESULT_MSG_SIZE, "Index out of bounds"); + + return result; + } + + uint8_t *destination_addr = (uint8_t *)vector->elements + (index * vector->data_size); + + // Append @value to the data structure according to its data type + if (vector->data_size == sizeof(int)) { + *(int*)destination_addr = *(int*)value; + } else if (vector->data_size == sizeof(long)) { + *(long*)destination_addr = *(long*)value; + } else if (vector->data_size == sizeof(double)) { + *(double*)destination_addr = *(double*)value; + } else if (vector->data_size == sizeof(float)) { + *(float*)destination_addr = *(float*)value; + } else { + memcpy(destination_addr, value, vector->data_size); + } + + result.status = VECTOR_OK; + snprintf((char *)result.message, RESULT_MSG_SIZE, "Value successfully set"); + + return result; +} + /** * vector_get * @vector: a non-null vector * @index: a non-negative integer representing the position of an element * - * Returns a Result data type containing the element at position @index if present + * Returns a VectorResult data type containing the element at position @index if present */ -Result vector_get(Vector *vector, size_t index) { - Result result = {0}; +VectorResult vector_get(Vector *vector, size_t index) { + VectorResult result = {0}; if (vector == NULL) { - result.status = 1; + result.status = VECTOR_ERR_INVALID; snprintf((char *)result.message, RESULT_MSG_SIZE, "Invalid vector"); return result; } if (index >= vector->count) { - result.status = 1; + result.status = VECTOR_ERR_OVERFLOW; snprintf((char *)result.message, RESULT_MSG_SIZE, "Index out of bounds"); return result; } - result.status = 0; + result.status = VECTOR_OK; snprintf((char *)result.message, RESULT_MSG_SIZE, "Value successfully retrieved"); - result.value.element = (uint8_t*)vector->elements + (index * vector->data_size); + result.value.element = (uint8_t *)vector->elements + (index * vector->data_size); return result; } +/** + * vector_pop + * @vector: a non-null vector + * + * Logically extract an element from the vector by following the LIFO policy. + * This method does NOT de-allocate memory + * + * Returns a VectorResult data type + */ +VectorResult vector_pop(Vector *vector) { + VectorResult result = {0}; + + if (vector == NULL) { + result.status = VECTOR_ERR_INVALID; + snprintf((char *)result.message, RESULT_MSG_SIZE, "Invalid vector"); + + return result; + } + + if (vector->count == 0) { + result.status = VECTOR_ERR_UNDERFLOW; + snprintf((char *)result.message, RESULT_MSG_SIZE, "Vector is empty"); + + return result; + } + + // Pop an element from the vector + const size_t index = (vector->count - 1); + VectorResult popped_res = vector_get(vector, index); + + if (popped_res.status != VECTOR_OK) { + return popped_res; + } + + vector->count--; + + result.status = VECTOR_OK; + snprintf((char *)result.message, RESULT_MSG_SIZE, "Value successfully popped"); + result.value.element = popped_res.value.element; + + return result; +} + +/** + * vector_clear + * @vector: a non-null vector + * + * Resets the vector to an empty state without de-allocating memory + * + * Returns a VectorResult data type + */ +VectorResult vector_clear(Vector *vector) { + VectorResult result = {0}; + + if (vector == NULL) { + result.status = VECTOR_ERR_INVALID; + snprintf((char *)result.message, RESULT_MSG_SIZE, "Invalid vector"); + + return result; + } + + vector->count = 0; + + result.status = VECTOR_OK; + snprintf((char *)result.message, RESULT_MSG_SIZE, "Vector successfully cleared"); + + return result; +} /** * vector_free @@ -173,17 +289,17 @@ Result vector_get(Vector *vector, size_t index) { * * Deletes the vector and all its elements from the memory * - * Returns a Result data type + * Returns a VectorResult data type */ -Result vector_free(Vector *vector) { - Result result = {0}; +VectorResult vector_free(Vector *vector) { + VectorResult result = {0}; if (vector != NULL) { free(vector->elements); free(vector); } - result.status = 0; + result.status = VECTOR_OK; snprintf((char *)result.message, RESULT_MSG_SIZE, "Vector successfully deleted"); return result; diff --git a/src/vector.h b/src/vector.h index 00a2cf8..624f2b6 100644 --- a/src/vector.h +++ b/src/vector.h @@ -3,8 +3,8 @@ #define RESULT_MSG_SIZE 64 -#include // for uint8_t -#include // for size_t +#include +#include // Vector data type typedef struct { @@ -14,24 +14,46 @@ typedef struct { void *elements; } Vector; -// Result data type +// Result status codes +typedef enum { + VECTOR_OK = 0x0, + VECTOR_ERR_ALLOCATE, + VECTOR_ERR_OVERFLOW, + VECTOR_ERR_UNDERFLOW, + VECTOR_ERR_INVALID +} VectorStatus; + +// Wrapper data type for vector APIs typedef struct { - uint8_t status; + VectorStatus status; uint8_t message[RESULT_MSG_SIZE]; union { Vector *vector; void *element; } value; -} Result; +} VectorResult; #ifdef __cplusplus extern "C" { #endif -Result vector_new(size_t size, size_t data_size); -Result vector_push(Vector *vector, void *value); -Result vector_get(Vector *vector, size_t index); -Result vector_free(Vector *vector); +// public APIs +VectorResult vector_new(size_t size, size_t data_size); +VectorResult vector_push(Vector *vector, void *value); +VectorResult vector_set(Vector *vector, size_t index, void *value); +VectorResult vector_get(Vector *vector, size_t index); +VectorResult vector_pop(Vector *vector); +VectorResult vector_clear(Vector *vector); +VectorResult vector_free(Vector *vector); + +// Inline methods +static inline size_t vector_size(const Vector *vector) { + return vector ? vector->count : 0; +} + +static inline size_t vector_capacity(const Vector *vector) { + return vector ? vector->capacity : 0; +} #ifdef __cplusplus } diff --git a/usage.c b/usage.c new file mode 100644 index 0000000..cf48443 --- /dev/null +++ b/usage.c @@ -0,0 +1,65 @@ +#include +#include "src/vector.h" + +int main(void) { + // Create a vector of 5 integers + VectorResult res = vector_new(5, sizeof(int)); + if (res.status != VECTOR_OK) { + printf("Error while creating vector: %s\n", res.message); + + return 1; + } + + Vector *vector = res.value.vector; + + // Push some values + for (int idx = 0; idx <= 5; idx++) { + VectorResult add_res = vector_push(vector, &idx); + if (add_res.status != VECTOR_OK) { + printf("Error while adding elements: %s\n", add_res.message); + + return 1; + } + } + + // Print vector size and capacity + printf("Vector size: %zu\n", vector_size(vector)); + printf("Vector capacity: %zu\n", vector_capacity(vector)); + + // Print the whole vector + for (size_t idx = 0; idx < vector_size(vector); idx++) { + VectorResult get_res = vector_get(vector, idx); + if (get_res.status == VECTOR_OK) { + int val = *(int *)get_res.value.element; + printf("vec[%zu] = %d\n", idx, val); + } + } + + // Set an element at index 2 + int new_val = 0xABABA; + VectorResult set_res = vector_set(vector, 2, &new_val); + if (set_res.status == VECTOR_OK) { + printf("vec[2] updated to %d\n", new_val); + } + + // Pop last element + VectorResult pop_res = vector_pop(vector); + if (pop_res.status == VECTOR_OK) { + int val = *(int *)pop_res.value.element; + printf("Popped value: %d\n", val); + } + + // Clear vector + VectorResult clear_res = vector_clear(vector); + if (clear_res.status == VECTOR_OK) { + printf("Vector cleared. New size is: %zu\n", vector_size(vector)); + } + + // Free vector + VectorResult free_res = vector_free(vector); + if (free_res.status != VECTOR_OK) { + printf("Error while freeing the vector: %s\n", free_res.message); + } + + return 0; +}