From fbed1b21d6eff2fc2d8d0889d09aee5bd35bc414 Mon Sep 17 00:00:00 2001 From: Lucien Cartier-Tilet Date: Mon, 26 Oct 2020 20:18:34 +0100 Subject: [PATCH] Better error handling, inline functions, new PERR Functions that end up calling a `malloc' or `realloc' return the new struct `Result' which indicates if the memory allocation or reallocation worked or not. If not, it holds a message, otherwise it holds the value the function is meant to return (can be a null pointer if the function returned a `void' before). The library will no longer forcibly exit the program on such error in case it can be recovered by the code calling it. The modifications described above lead to vector-creating functions returning a pointer to the newly created vector, and the function for deleting vectors also frees the vector. This commit also inlines some functions to make the compiled code a bit lighter and calls to these functions possibly a bit faster. `PDEB' is redefined following the new `PCOMMON' macro that is also used by `PERR' for desplaying debug error messages. Some lines are also modified to avoid potential memory leaks in case of a memory (re)allocation. --- src/vector.c | 96 +++++++++++++++++++++++++++++++--------------------- src/vector.h | 68 +++++++++++++++++++++++++------------ 2 files changed, 104 insertions(+), 60 deletions(-) diff --git a/src/vector.c b/src/vector.c index 9c84f8f..aa2e89b 100644 --- a/src/vector.c +++ b/src/vector.c @@ -1,28 +1,52 @@ #include "vector.h" -Vector vec_new(Destructor destructor) +static Result vec_warn_error(Result error) { - Vector self; - self.length = 0; - self.capacity = INITIAL_CAPACITY; - self.offset = sizeof(void *); - self.elements = (void *)malloc(self.offset * INITIAL_CAPACITY); - self.destroy = destructor; - return self; + PERR("%s", error.result.message); + return error; } -Vector vec_with_capacity(Destructor const t_destructor, size_t const t_capacity) +Result vec_new(Destructor destructor) { - Vector self = vec_new(t_destructor); - free(self.elements); - self.elements = (void **)malloc(self.offset * t_capacity); - self.capacity = t_capacity; - return self; + Vector *self = NULL; + self = (Vector *)malloc(sizeof(Vector)); + if (!self) { /* Error handling */ + return vec_warn_error((Result){ + .error = true, + .result = {.message = "Could not allocate memory for Vector structure"}}); + } + (*self).length = 0; + (*self).capacity = INITIAL_CAPACITY; + *(size_t *)&self->offset = sizeof(void *); /* weird syntax due to constness */ + (*self).elements = (void *)malloc(self->offset * INITIAL_CAPACITY); + if (!self->elements) { /* Error handling */ + free(self); + return vec_warn_error((Result){ + .error = true, + .result = {.message = "Could not allocate memory for Vector’s array"}}); + } + (*self).destroy = destructor; + return (Result){.error = false, .result.value = self}; } -void *vec_at(Vector const *const self, size_t const t_index) +Result vec_with_capacity(Destructor const t_destructor, size_t const t_capacity) { - return self->elements[t_index]; + Result vec = vec_new(t_destructor); + if (vec.error) { /* Error handling */ + return vec; + } + Vector *self = vec.result.value; + free((*self).elements); + (*self).elements = (void **)malloc(self->offset * t_capacity); + if (!self->elements) { /* Error handling */ + free(self); + return vec_warn_error((Result){ + .error = true, + .result = {.message = "Could not allocate memory for Vector’s array"}}); + } + (*self).capacity = t_capacity; + vec.result.value = self; + return vec; } void *vec_safe_at(Vector const *const self, size_t const t_index) @@ -38,37 +62,28 @@ static void vec_maybe_destroy_element(Vector const *self, size_t const t_index) } } -void *vec_last(Vector const *const self) -{ - return vec_at(self, vec_length(self) - 1); -} - -size_t vec_length(Vector const *const self) -{ - return self->length; -} - -size_t vec_capacity(Vector const *const self) -{ - return self->capacity; -} - -static void vec_realloc(Vector *const self) +static Result vec_realloc(Vector *const self) { self->capacity *= 2; self->elements = realloc(self->elements, sizeof(void *) * vec_capacity(self)); if (!self->elements) { - PDEB("Could not reallocate Vector’s memory, aborting...", NULL); - exit(ERR_MEM_ALLOC); + return (Result){ + .error = true, + .result = {.message = "Could not reallocate Vector’s array"}}; } + return (Result){.error = false, .result = {.value = NULL}}; } -void vec_push(Vector *const self, void const *const t_element) +Result vec_push(Vector *const self, void *const t_element) { if (vec_length(self) >= vec_capacity(self)) { - vec_realloc(self); + Result res_realloc = vec_realloc(self); + if (res_realloc.error) { + return res_realloc; + } } self->elements[(*self).length++] = t_element; + return (Result){.error = false, .result = {.value = NULL}}; } void vec_pop(Vector *const self) @@ -80,14 +95,16 @@ void vec_pop(Vector *const self) --(*self).length; } -void vec_shrink_to_fit(Vector *const self) +Result vec_shrink_to_fit(Vector *const self) { self->capacity = self->length; self->elements = realloc(self->elements, sizeof(void *) * vec_capacity(self)); if (!self->elements) { - PDEB("Could not reallocate Vector’s memory, aborting...", NULL); - exit(ERR_MEM_ALLOC); + return (Result){ + .error = true, + .result = {.message = "Could not reallocate Vector’s memory"}}; } + return (Result){.error = false, .result = {.value = NULL}}; } void vec_delete(Vector *const self) @@ -98,4 +115,5 @@ void vec_delete(Vector *const self) } } free(self->elements); + free(self); } diff --git a/src/vector.h b/src/vector.h index bacae7c..09d6f30 100644 --- a/src/vector.h +++ b/src/vector.h @@ -1,6 +1,7 @@ #ifndef VECTOR_H #define VECTOR_H +#include #include #include #include @@ -20,37 +21,62 @@ #define UNINPLEMENTED printf("%s:%d: Not yet implemented", __FILE__, __LINE__) #define INITIAL_CAPACITY 4 -#define PDEB(format, ...) \ - if (DEBUG) { \ - fprintf(stderr, \ - GREEN "%s:%d\t" NO_COLOR format "\n", \ - __FILE__, \ - __LINE__, \ - __VA_ARGS__); \ - } +#define PCOMMON(color, format, ...) \ + fprintf(stderr, \ + "%s%s:%d%s\t" format "\n", \ + color, \ + __FILE__, \ + __LINE__, \ + NO_COLOR, \ + __VA_ARGS__) +#define PDEB(format, ...) PCOMMON(GREEN, format, __VA_ARGS__) +#define PERR(format, ...) PCOMMON(RED, format, __VA_ARGS__) /* Destructor typedef */ typedef void (*Destructor)(void *element); typedef struct Vector_s { - size_t length; - size_t capacity; - size_t offset; - void ** elements; - Destructor destroy; + size_t length; + size_t capacity; + const size_t offset; + void ** elements; + Destructor destroy; } Vector; -Vector vec_new(Destructor destructor); -Vector vec_with_capacity(Destructor const destructor, size_t const capacity); +typedef struct Result_s { + union { + const char *message; + void * value; + } result; + bool error; +} Result; -void * vec_at(Vector const *const self, size_t const index); +Result vec_new(Destructor destructor); +Result vec_with_capacity(Destructor const destructor, size_t const capacity); +Result vec_push(Vector *const self, void *const element); +Result vec_shrink_to_fit(Vector *const self); void * vec_safe_at(Vector const *const self, size_t const index); -void * vec_last(Vector const *const self); -size_t vec_length(Vector const *const self); -size_t vec_capacity(Vector const *const self); -void vec_push(Vector *const self, void const *const element); void vec_pop(Vector *const self); -void vec_shrink_to_fit(Vector *const self); void vec_delete(Vector *const self); +inline size_t vec_length(Vector const *const self) +{ + return self->length; +} + +inline size_t vec_capacity(Vector const *const self) +{ + return self->capacity; +} + +inline void *vec_at(Vector const *const self, size_t const index) +{ + return self->elements[index]; +} + +inline void *vec_last(Vector const *const self) +{ + return vec_at(self, vec_length(self) - 1); +} + #endif /* VECTOR_H */