diff options
-rw-r--r-- | include/slibc-alloc.h | 81 | ||||
-rw-r--r-- | src/slibc-alloc.c | 129 |
2 files changed, 208 insertions, 2 deletions
diff --git a/include/slibc-alloc.h b/include/slibc-alloc.h index 3fe31e0..d745dee 100644 --- a/include/slibc-alloc.h +++ b/include/slibc-alloc.h @@ -29,7 +29,7 @@ /** * Configurations for `extalloc`. - * There are independent of each other, and + * They are independent of each other, and * multiple can be selected by using bitwise or * between them. */ @@ -50,7 +50,7 @@ enum extalloc_mode /** * Configurations for `rememalign`. - * There are independent of each other, and + * They are independent of each other, and * multiple can be selected by using bitwise or * between them. */ @@ -76,6 +76,33 @@ enum rememalign_mode /** + * Configurations for `falloc`. + * They are independent of each other, and + * multiple can be selected by using bitwise or + * between them. + */ +enum falloc_mode + { + /** + * Clear disowned memory. + */ + FALLOC_CLEAR = 1, + + /** + * Initialise new memory. + */ + FALLOC_INIT = 2, + + /** + * If a new allocation is created, copy the data + * from the old allocation over to the new allocation. + */ + FALLOC_MEMCPY = 4, + + }; + + +/** * This function is identical to `free`, except it is guaranteed not to * override the memory segment with zeroes before freeing the allocation. * @@ -263,6 +290,56 @@ void* naive_realloc(void*, size_t, size_t) /* sic! we limit ourself to ASCII */ void* naive_extalloc(void*, size_t) /* sic! we limit ourself to ASCII */ __GCC_ONLY(__attribute__((__nonnull__, __warn_unused_result__))); +/** + * Allocates, deallocates, or reallocates memory without + * bookkeeping. The created allocation may not be inspecifed, + * deallocated or reallocated with any other function than + * this function. + * + * If `new_size` is zero and `ptr` is `NULL`, + * nothing happens, but errno is set zero and `NULL` is returned. + * If `new_size` is zero and `ptr` is not `NULL`, + * `ptr` is deallocated, errno is set zero, and `NULL` is returned. + * `ptr`'s content will be clear if `old_size` is not zero + * and `mode & FALLOC_CLEAR`, but if `old_size` is zero and + * and `mode & FALLOC_CLEAR`, `errno` is set to `EINVAL`. + * If `new_size` is not zero, and `ptr` is not `NULL`, + * `ptr` is reallocated. If `mode & FALLOC_CLEAR` the old allocation + * is cleared if a new allocation is created, or if the allocation + * is shrinked, the disowned area is cleared. However, if `old_size` + * is zero an `mode & FALLOC_CLEAR` errno is set to `EINVAL` and + * `NULL` is returned. If a new allocation is created, the first + * `old_size` bytes is copied from the old allocation into the new + * allocation. If `mode & FALLOC_INIT`, all newly available space + * will be initialised with zeroes. + * If neither `new_size` nor `new_size` is zero, and `ptr` is `NULL`, + * `errno` is set to `EINVAL` and `NULL` is returned. + * Otherwise (if `new_size` is non-zero, `old_size` is zero, and + * `ptr` is `NULL ), a new allocation is created. + * + * @param ptr The old pointer, `NULL` if a new shall be created. + * @param ptrshift Pointer that is used to keep track of the pointers + * shift for alignment. `NULL` if the shift shall not + * be tracked. If this is the case, `falloc` cannot + * be used to reallocate or deallocate an allocation, + * unless the pointer is unaligned (`alignment <= 0`). + * @param alignment The aligment of both the new and old pointer, zero + * or one if it should not be aligned. + * @param old_size The old allocation size, zero if a new shall be created. + * @param new_size The new allocation size, zero if it shall be freed. + * @param mode `FALLOC_CLEAR`, `FALLOC_INIT` or `FALLOC_MEMCPY`, or + * both or neither. + * @return The new pointer, or the old pointer if it was reallocated + * without creating a new allocation. `NULL` is returned + * if `new_size` (errno is set to zero) is zero, or on error + * (errno is set to describe the error.) + * + * @throws 0 `new_size` is zero. + * @throws EINVAL The arguments are invalid. + * @throws ENOMEM The process cannot allocate more memory. + */ +void* falloc(void*, size_t*, size_t, size_t, size_t, enum falloc_mode); + /** * This macro calls `fast_free` and then sets the pointer to `NULL`, diff --git a/src/slibc-alloc.c b/src/slibc-alloc.c index 2ec324a..4d2d0a6 100644 --- a/src/slibc-alloc.c +++ b/src/slibc-alloc.c @@ -18,6 +18,7 @@ #include <slibc-alloc.h> #include <stdlib.h> #include <stddef.h> +#include <string.h> #include <strings.h> #include <errno.h> /* TODO #include <sys/mman.h> */ @@ -378,3 +379,131 @@ void* naive_extalloc(void* ptr, size_t size) (void) ptr, (void) size; } + + +/** + * Allocates, deallocates, or reallocates memory without + * bookkeeping. The created allocation may not be inspecifed, + * deallocated or reallocated with any other function than + * this function. + * + * If `new_size` is zero and `ptr` is `NULL`, + * nothing happens, but `errno` is set to zero and `NULL` + * is returned. + * If `new_size` is non-zero, `old_size` is zero, and `ptr` + * is not `NULL` or if `new_size` and `old_size` is non-zero, + * and `ptr` is `NULL`, `errno` is set to `EINVAL` and `NULL` + * is returned. + * If `new_size` and `old_size` is zero and `ptr` is not `NULL`, + * `errno` is set to `EINVAL` and `NULL` is returned. + * If `new_size` is zero, `old_size` is non-zero, and `ptr` + * is not `NULL`, `ptr` is deallocated, and `NULL` is returned + * with `errno` set to zero. The memory cleared before it is + * deallocated if `mode & FALLOC_CLEAR`. + * If `new_size` is non-zero, `old_size` is zero, and `ptr` is + * `NULL`, a new allocation is created of `new_size` bytes. + * It will be zero-initialised if `mode & FALLOC_INIT`. + * If `new_size` and `old_size` is non-zero and `ptr` is not + * `NULL`, `ptr` is reallocated. if the allocation is shrunk, + * the disowned area is cleared if `mode & FALLOC_CLEAR`. + * Newly available memory is zero-initialised if + * `mode & FALLOC_INIT`. If a new allocation is required, + * the data from the old allocation is only copied over to + * the new allocation if `mode & FALLOC_MEMCPY`. If + * `(mode & FALLOC_INIT) && !(mode & FALLOC_MEMCPY)`, the + * entire allocation will be cleared. + * + * @param ptr The old pointer, `NULL` if a new shall be created. + * @param ptrshift Pointer that is used to keep track of the pointers + * shift for alignment. `NULL` if the shift shall not + * be tracked. If this is the case, `falloc` cannot + * be used to reallocate or deallocate an allocation, + * unless the pointer is unaligned (`alignment <= 0`). + * @param alignment The aligment of both the new and old pointer, zero + * or one if it should not be aligned. + * @param old_size The old allocation size, zero if a new shall be created. + * @param new_size The new allocation size, zero if it shall be freed. + * @param mode `FALLOC_CLEAR`, `FALLOC_INIT` or `FALLOC_MEMCPY`, or + * both or neither. + * @return The new pointer, or the old pointer if it was reallocated + * without creating a new allocation. `NULL` is returned + * if `new_size` (errno is set to zero) is zero, or on error + * (errno is set to describe the error.) + * + * @throws 0 `new_size` is zero. + * @throws EINVAL The arguments are invalid. + * @throws ENOMEM The process cannot allocate more memory. + */ +void* falloc(void* ptr, size_t* ptrshift, size_t alignment, + size_t old_size, size_t new_size, enum falloc_mode mode) +{ + void* new_ptr = NULL; + size_t shift = 0; + + if (mode & (enum falloc_mode)~(FALLOC_CLEAR | FALLOC_INIT | FALLOC_MEMCPY)) + return errno = EINVAL, NULL; + + alignment = alignment ? alignment : 1; + + if (new_size && old_size && ptr) + { + shift = ptrshift == NULL ? *ptrshift : 0; + if ((alignment > 1) && (ptrshift == NULL)) + return errno = EINVAL, NULL; + if ((mode & FALLOC_CLEAR) && (old_size > new_size)) + explicit_bzero(ptr + new_size, old_size - new_size); + new_ptr = falloc_extalloc(ptr - shift, old_size + shift, new_size + shift); + if ((new_ptr == NULL) && (errno == 0)) + { + new_ptr = falloc_malloc(new_size + alignment - 1); + if (new_ptr != NULL) + { + if ((size_t)new_ptr % alignment) + shift = alignment - ((size_t)new_ptr % alignment); + if (ptrshift != NULL) + *ptrshift = shift; + new_ptr = (void*)((char*)new_ptr + shift); + if (mode & FALLOC_MEMCPY) + memcpy(new_ptr, ptr, old_size); + } + } + } + else if (new_size && (old_size || ptr)) + return errno = EINVAL, NULL; + else if (new_size) + new_ptr = falloc_malloc(new_size); + else if (old_size && ptr) + { + shift = ptrshift == NULL ? *ptrshift : 0; + if ((alignment > 1) && (ptrshift == NULL)) + return errno = EINVAL, NULL; + if (mode & FALLOC_CLEAR) + explicit_bzero(ptr, old_size); + falloc_free(ptr - shift); + return errno = 0, NULL; + } + else if (old_size || !ptr) + return errno = 0, NULL; + else + return errno = EINVAL, NULL; + + if (new_ptr != NULL) + { + if ((new_ptr != ptr) && (ptr != NULL)) + { + if (mode & FALLOC_CLEAR) + explicit_bzero(ptr, old_size); + falloc_free(ptr - shift); + } + if (mode & FALLOC_INIT) + { + if (!(mode & FALLOC_MEMCPY)) + old_size = 0; + if (new_size > old_size) + bzero(new_ptr + old_size, new_size - old_size); + } + } + + return errno = 0, new_ptr; +} + |