Make TRY's autocleanup infra simpler and more versatile
This commit is contained in:
parent
7345815424
commit
d5ac622d44
2 changed files with 75 additions and 40 deletions
44
cmaybe.h
44
cmaybe.h
|
@ -22,13 +22,14 @@
|
|||
struct __free_list {
|
||||
struct __free_list *next;
|
||||
void *ptr;
|
||||
void (*freer)(void*);
|
||||
};
|
||||
#define ENABLE_TRY() \
|
||||
struct __free_list *__allocations = NULL, *__free_list_node;\
|
||||
void *__allocation, *__old_allocation;\
|
||||
void *__allocation;\
|
||||
jmp_buf __try_fail_jmp_buf;\
|
||||
if (setjmp(__try_fail_jmp_buf)) {\
|
||||
TRY_FREE_ALL();\
|
||||
TRY_FAIL_HANDLE();\
|
||||
RETURN_NOTHING();\
|
||||
}
|
||||
#define TRY_TYPE(name) MAYBE(name) __try_tmp_##name
|
||||
|
@ -40,42 +41,41 @@ struct __free_list {
|
|||
__try_tmp_##name.value\
|
||||
)
|
||||
|
||||
#define TRY_MEMALLOC(allocator, ...) (\
|
||||
#define HANDLE_ON_TRY_FAIL(freer_func, pointer) (\
|
||||
__allocation = pointer,\
|
||||
__free_list_node = malloc(sizeof(struct __free_list)),\
|
||||
(__free_list_node == NULL ? longjmp(__try_fail_jmp_buf, 1) : 0),\
|
||||
__allocation = allocator(__VA_ARGS__),\
|
||||
(__allocation == NULL ? (\
|
||||
free(__free_list_node),\
|
||||
(__free_list_node == NULL ? (\
|
||||
free(__allocation),\
|
||||
longjmp(__try_fail_jmp_buf, 1)\
|
||||
) : (\
|
||||
__free_list_node->next = __allocations,\
|
||||
__free_list_node->ptr = __allocation,\
|
||||
__free_list_node->freer = freer_func,\
|
||||
__allocations = __free_list_node\
|
||||
)),\
|
||||
__allocation\
|
||||
)
|
||||
#define TRY_MALLOC(size) TRY_MEMALLOC(malloc, size)
|
||||
#define FREE_ON_TRY_FAIL(pointer) HANDLE_ON_TRY_FAIL(free, pointer)
|
||||
|
||||
static void __remove_free_list(struct __free_list **head, void *ptr, void(*free)(void*)) {
|
||||
static void __remove_free_list(struct __free_list **head, void *ptr, void (*free)(void*)) {
|
||||
struct __free_list **current = head;
|
||||
while (*current != NULL) {
|
||||
if (current[0]->ptr == ptr) {
|
||||
struct __free_list *deleted = *current;
|
||||
*current = current[0]->next;
|
||||
*current = deleted->next;
|
||||
free(deleted);
|
||||
break;
|
||||
}
|
||||
current = ¤t[0]->next;
|
||||
}
|
||||
}
|
||||
#define TRY_FREE(allocation) (\
|
||||
free(allocation),\
|
||||
__remove_free_list(&__allocations, allocation, free)\
|
||||
)
|
||||
#define TRY_FREE_ALL() \
|
||||
|
||||
#define TRY_HOF_REMOVE(allocation) __remove_free_list(&__allocations, allocation, free)
|
||||
#define TRY_FAIL_HANDLE() \
|
||||
while (__allocations != NULL) {\
|
||||
struct __free_list *current = __allocations;\
|
||||
free(current->ptr);\
|
||||
void (*freer)(void*) = current->freer;\
|
||||
freer(current->ptr);\
|
||||
__allocations = current->next;\
|
||||
free(current);\
|
||||
}
|
||||
|
@ -90,16 +90,6 @@ static void __replace_free_list(struct __free_list **head, void *from, void *to)
|
|||
current = ¤t[0]->next;
|
||||
}
|
||||
}
|
||||
#define TRY_MEMREALLOC(reallocator, old, ...) (\
|
||||
__old_allocation = old,\
|
||||
__allocation = reallocator(old, __VA_ARGS__),\
|
||||
(__allocation == NULL ? (\
|
||||
longjmp(__try_fail_jmp_buf, 1)\
|
||||
) : \
|
||||
__replace_free_list(&__allocations, __old_allocation, __allocation)\
|
||||
),\
|
||||
__allocation\
|
||||
)
|
||||
#define TRY_REALLOC(old, size) TRY_MEMREALLOC(realloc, old, size)
|
||||
#define TRY_HOF_REPLACE(old_ptr, new_ptr) __replace_free_list(&__allocations, old_ptr, new_ptr)
|
||||
|
||||
#endif
|
||||
|
|
|
@ -21,6 +21,26 @@ MAYBE(voidptr) strchr_maybe(const void *s, int c) {
|
|||
}
|
||||
}
|
||||
|
||||
MAYBE(voidptr) malloc_maybe(size_t size) {
|
||||
ENABLE_RETURN(voidptr);
|
||||
void *allocation = malloc(size);
|
||||
if(allocation == NULL && size != 0) {
|
||||
RETURN_NOTHING();
|
||||
} else {
|
||||
RETURN_VALUE(allocation);
|
||||
}
|
||||
}
|
||||
|
||||
MAYBE(voidptr) realloc_maybe(void *old, size_t size) {
|
||||
ENABLE_RETURN(voidptr);
|
||||
void *allocation = realloc(old, size);
|
||||
if(allocation == NULL && size != 0) {
|
||||
RETURN_NOTHING();
|
||||
} else {
|
||||
RETURN_VALUE(allocation);
|
||||
}
|
||||
}
|
||||
|
||||
MAYBE_TYPE(intmax_t, intmax_t);
|
||||
|
||||
MAYBE(intmax_t) str2int(const char *string) {
|
||||
|
@ -43,15 +63,44 @@ struct intmax_t_array {
|
|||
|
||||
MAYBE_TYPE(intmax_t_array, struct intmax_t_array);
|
||||
|
||||
MAYBE(intmax_t_array) create_array(void) {
|
||||
ENABLE_RETURN(intmax_t_array);
|
||||
ENABLE_TRY();
|
||||
TRY_TYPE(voidptr);
|
||||
|
||||
struct intmax_t_array array;
|
||||
array.length = 0;
|
||||
array.alloc_size = sizeof(intmax_t);
|
||||
array.data = TRY(voidptr, malloc_maybe(array.alloc_size));
|
||||
RETURN_VALUE(array);
|
||||
}
|
||||
|
||||
MAYBE(intmax_t_array) resize_array(struct intmax_t_array array, size_t length) {
|
||||
ENABLE_RETURN(intmax_t_array);
|
||||
ENABLE_TRY();
|
||||
TRY_TYPE(voidptr);
|
||||
|
||||
struct intmax_t_array resized_array = {array.data, length, array.alloc_size};
|
||||
|
||||
if(resized_array.alloc_size / sizeof(intmax_t) < resized_array.length) {
|
||||
if(SIZE_MAX / 2 < resized_array.alloc_size) {
|
||||
RETURN_NOTHING();
|
||||
}
|
||||
resized_array.alloc_size *= 2;
|
||||
resized_array.data = TRY(voidptr, realloc_maybe(resized_array.data, resized_array.alloc_size));
|
||||
}
|
||||
|
||||
RETURN_VALUE(resized_array);
|
||||
}
|
||||
|
||||
MAYBE(intmax_t_array) convert_to_numbers(char *line) {
|
||||
ENABLE_RETURN(intmax_t_array);
|
||||
ENABLE_TRY();
|
||||
TRY_TYPE(intmax_t);
|
||||
TRY_TYPE(intmax_t_array);
|
||||
|
||||
struct intmax_t_array numbers;
|
||||
numbers.length = 0;
|
||||
numbers.alloc_size = 8 * sizeof(intmax_t);
|
||||
numbers.data = TRY_MALLOC(numbers.alloc_size * sizeof(intmax_t));
|
||||
struct intmax_t_array numbers = TRY(intmax_t_array, create_array());
|
||||
FREE_ON_TRY_FAIL(numbers.data);
|
||||
|
||||
char *strtok_saveptr = NULL;
|
||||
for (;;) {
|
||||
|
@ -69,15 +118,11 @@ MAYBE(intmax_t_array) convert_to_numbers(char *line) {
|
|||
intmax_t num = TRY(intmax_t, str2int(num_str));
|
||||
|
||||
size_t index = numbers.length;
|
||||
numbers.length++;
|
||||
if(numbers.alloc_size / sizeof(intmax_t) < numbers.length) {
|
||||
if(SIZE_MAX / 2 < numbers.alloc_size) {
|
||||
TRY_FREE(numbers.data);
|
||||
RETURN_NOTHING();
|
||||
}
|
||||
numbers.alloc_size *= 2;
|
||||
numbers.data = TRY_REALLOC(numbers.data, numbers.alloc_size);
|
||||
}
|
||||
|
||||
void *old_ptr = numbers.data;
|
||||
numbers = TRY(intmax_t_array, resize_array(numbers, numbers.length + 1));
|
||||
TRY_HOF_REPLACE(old_ptr, numbers.data);
|
||||
|
||||
numbers.data[index] = num;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue