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"
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 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)
@ -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 Vectors memory, aborting...", NULL);
exit(ERR_MEM_ALLOC);
return (Result){
.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)) {
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 Vectors memory, aborting...", NULL);
exit(ERR_MEM_ALLOC);
return (Result){
.error = true,
.result = {.message = "Could not reallocate Vectors 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);
}

View File

@ -1,6 +1,7 @@
#ifndef VECTOR_H
#define VECTOR_H
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
@ -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 */