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.
This commit is contained in:
Lucien Cartier-Tilet 2020-10-26 20:18:34 +01:00
parent 193bf1d75f
commit fbed1b21d6
Signed by: phundrak
GPG Key ID: BD7789E705CB8DCA
2 changed files with 104 additions and 60 deletions

View File

@ -1,28 +1,52 @@
#include "vector.h" #include "vector.h"
Vector vec_new(Destructor destructor) static Result vec_warn_error(Result error)
{ {
Vector self; PERR("%s", error.result.message);
self.length = 0; return error;
self.capacity = INITIAL_CAPACITY;
self.offset = sizeof(void *);
self.elements = (void *)malloc(self.offset * INITIAL_CAPACITY);
self.destroy = destructor;
return self;
} }
Vector vec_with_capacity(Destructor const t_destructor, size_t const t_capacity) Result vec_new(Destructor destructor)
{ {
Vector self = vec_new(t_destructor); Vector *self = NULL;
free(self.elements); self = (Vector *)malloc(sizeof(Vector));
self.elements = (void **)malloc(self.offset * t_capacity); if (!self) { /* Error handling */
self.capacity = t_capacity; return vec_warn_error((Result){
return self; .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 Vectors 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 Vectors array"}});
}
(*self).capacity = t_capacity;
vec.result.value = self;
return vec;
} }
void *vec_safe_at(Vector const *const self, size_t const t_index) 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) static Result vec_realloc(Vector *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)
{ {
self->capacity *= 2; self->capacity *= 2;
self->elements = realloc(self->elements, sizeof(void *) * vec_capacity(self)); self->elements = realloc(self->elements, sizeof(void *) * vec_capacity(self));
if (!self->elements) { if (!self->elements) {
PDEB("Could not reallocate Vectors memory, aborting...", NULL); return (Result){
exit(ERR_MEM_ALLOC); .error = true,
.result = {.message = "Could not reallocate Vectors 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)) { 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; self->elements[(*self).length++] = t_element;
return (Result){.error = false, .result = {.value = NULL}};
} }
void vec_pop(Vector *const self) void vec_pop(Vector *const self)
@ -80,14 +95,16 @@ void vec_pop(Vector *const self)
--(*self).length; --(*self).length;
} }
void vec_shrink_to_fit(Vector *const self) Result vec_shrink_to_fit(Vector *const self)
{ {
self->capacity = self->length; self->capacity = self->length;
self->elements = realloc(self->elements, sizeof(void *) * vec_capacity(self)); self->elements = realloc(self->elements, sizeof(void *) * vec_capacity(self));
if (!self->elements) { if (!self->elements) {
PDEB("Could not reallocate Vectors memory, aborting...", NULL); return (Result){
exit(ERR_MEM_ALLOC); .error = true,
.result = {.message = "Could not reallocate Vectors memory"}};
} }
return (Result){.error = false, .result = {.value = NULL}};
} }
void vec_delete(Vector *const self) void vec_delete(Vector *const self)
@ -98,4 +115,5 @@ void vec_delete(Vector *const self)
} }
} }
free(self->elements); free(self->elements);
free(self);
} }

View File

@ -1,6 +1,7 @@
#ifndef VECTOR_H #ifndef VECTOR_H
#define VECTOR_H #define VECTOR_H
#include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -20,14 +21,16 @@
#define UNINPLEMENTED printf("%s:%d: Not yet implemented", __FILE__, __LINE__) #define UNINPLEMENTED printf("%s:%d: Not yet implemented", __FILE__, __LINE__)
#define INITIAL_CAPACITY 4 #define INITIAL_CAPACITY 4
#define PDEB(format, ...) \ #define PCOMMON(color, format, ...) \
if (DEBUG) { \
fprintf(stderr, \ fprintf(stderr, \
GREEN "%s:%d\t" NO_COLOR format "\n", \ "%s%s:%d%s\t" format "\n", \
color, \
__FILE__, \ __FILE__, \
__LINE__, \ __LINE__, \
__VA_ARGS__); \ NO_COLOR, \
} __VA_ARGS__)
#define PDEB(format, ...) PCOMMON(GREEN, format, __VA_ARGS__)
#define PERR(format, ...) PCOMMON(RED, format, __VA_ARGS__)
/* Destructor typedef */ /* Destructor typedef */
typedef void (*Destructor)(void *element); typedef void (*Destructor)(void *element);
@ -35,22 +38,45 @@ typedef void (*Destructor)(void *element);
typedef struct Vector_s { typedef struct Vector_s {
size_t length; size_t length;
size_t capacity; size_t capacity;
size_t offset; const size_t offset;
void ** elements; void ** elements;
Destructor destroy; Destructor destroy;
} Vector; } Vector;
Vector vec_new(Destructor destructor); typedef struct Result_s {
Vector vec_with_capacity(Destructor const destructor, size_t const capacity); 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_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_pop(Vector *const self);
void vec_shrink_to_fit(Vector *const self);
void vec_delete(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 */ #endif /* VECTOR_H */