diff options
Diffstat (limited to 'src/malloc.c')
-rw-r--r-- | src/malloc.c | 185 |
1 files changed, 180 insertions, 5 deletions
diff --git a/src/malloc.c b/src/malloc.c index 82a6f54..f7a2e95 100644 --- a/src/malloc.c +++ b/src/malloc.c @@ -19,6 +19,8 @@ #include <slibc-alloc.h> #include <strings.h> #include <sys/mman.h> +#include <unistd.h> +#include <errno.h> @@ -40,12 +42,19 @@ void* malloc(size_t size) { /* TODO implement implementation of malloc */ char* ptr; + size_t full_size; + if (size == 0) return NULL; - ptr = mmap(NULL, sizeof(size_t) + size, (PROT_READ | PROT_WRITE), + if (__builtin_uaddl_overflow(2 * sizeof(size_t), size, &full_size)) + return errno = ENOMEM, NULL; + + ptr = mmap(NULL, full_size, (PROT_READ | PROT_WRITE), (MAP_PRIVATE | MAP_ANONYMOUS), -1, 0); - *(size_t*)ptr = size; - return ptr + sizeof(size_t); + + ((size_t*)ptr)[0] = size; + ((size_t*)ptr)[1] = 0; + return ptr + 2 * sizeof(size_t); } @@ -68,9 +77,16 @@ void* malloc(size_t size) */ void* calloc(size_t elem_count, size_t elem_size) { - void* ptr = malloc(elem_count * elem_size); + void* ptr; + size_t size; + + if (__builtin_umull_overflow(elem_count, elem_size, &size)) + return errno = ENOMEM, NULL; + + ptr = malloc(size); if (ptr != NULL) - explicit_bzero(ptr, elem_count * elem_size); + explicit_bzero(ptr, size); + return ptr; } @@ -132,3 +148,162 @@ void cfree(void* ptr, ...) fast_free(ptr); } + +/** + * Variant of `malloc` that returns an address with a + * specified alignment. + * + * It is unspecified how the function works. This implemention + * will allocate a bit of extra memory and shift the returned + * pointer so that it is aligned. + * + * As a GNU-compliant slibc extension, memory allocated + * with this function can be freed with `free`. + * + * @param boundary The alignment. + * @param size The number of bytes to allocated. + * @return Pointer to the beginning of the new allocation. + * If `size` is zero, this function will either return + * `NULL` (that is what this implement does) or return + * a unique pointer that can later be freed with `free`. + * `NULL` is returned on error, and `errno` is set to + * indicate the error. + * + * @throws ENOMEM The process cannot allocate more memory. + * @throws EINVAL If `boundary` is not a power of two. + */ +void* memalign(size_t boundary, size_t size) +{ + char* ptr; + size_t full_size; + size_t address; + size_t shift = 0; + + if (!boundary || (__builtin_ffsl(boundary) != boundary)) + return errno = EINVAL, NULL; + if (__builtin_uaddl_overflow(boundary - 1, size, &full_size)) + return errno = ENOMEM, NULL; + + ptr = malloc(full_size); + if (ptr == NULL) + return NULL; + + address = (size_t)ptr; + if (address % boundary != 0) + { + shift = boundary - (address % boundary); + ptr += shift; + *(size_t*)(ptr - sizeof(size_t)) = shift; + } + + return ptr; +} + + +/** + * `posix_memalign(p, b, n)` is equivalent to + * `(*p = memalign(b, n), *p ? 0 : errno)`, except + * `boundary` must also be a multiple of `sizeof(void*)`, + * and `errno` is unspecified. + * + * As a GNU-compliant slibc extension, memory allocated + * with this function can be freed with `free`. + * + * @param ptrptr Output parameter for the allocated memory. + * @param boundary The alignment. + * @param size The number of bytes to allocated. + * @return Zero on success, a value for `errno` on error. + * + * @throws ENOMEM The process cannot allocate more memory. + * @throws EINVAL If `boundary` is not a power-of-two multiple of `sizeof(void*)`. + */ +int posix_memalign(void** ptrptr, size_t boundary, size_t size) +{ + if (boundary < sizeof(void*)) + return EINVAL; + *ptrptr = memalign(boundary, size); + return *ptrptr ? 0 : errno; +} + + +/** + * `valloc(n)` is equivalent to `memalign(sysconf(_SC_PAGESIZE), n)`. + * + * As a GNU-compliant slibc extension, memory allocated + * with this function can be freed with `free`. + * + * @param size The number of bytes to allocated. + * @return Pointer to the beginning of the new allocation. + * If `size` is zero, this function will either return + * `NULL` (that is what this implement does) or return + * a unique pointer that can later be freed with `free`. + * `NULL` is returned on error, and `errno` is set to + * indicate the error. + * + * @throws ENOMEM The process cannot allocate more memory. + */ +void* valloc(size_t size) +{ + return memalign((size_t)sysconf(_SC_PAGESIZE), size); +} + + +/** + * This function works like `valloc`, except the allocation size, + * including auxiliary space, is rounded up to the next multiple + * of the page size. + * + * @param size The number of bytes to allocated. + * @return Pointer to the beginning of the new allocation. + * If `size` is zero, this function will either return + * `NULL` (that is what this implement does) or return + * a unique pointer that can later be freed with `free`. + * `NULL` is returned on error, and `errno` is set to + * indicate the error. + * + * @throws ENOMEM The process cannot allocate more memory. + */ +void* pvalloc(size_t size) +{ + size_t boundary = (size_t)sysconf(_SC_PAGESIZE); + size_t full_size = 2 * sizeof(size_t) + boundary - 1 + size; + size_t rounding = 0; + + if (full_size % boundary != 0) + rounding = boundary - (full_size % boundary); + + if (__builtin_uaddl_overflow(size, rounding, &full_size)) + return errno = ENOMEM, NULL; + + return memalign(boundary, full_size); +} + + +/** + * This function is identical to `memalign`, + * except it can be freed with `free`. + * + * Variant of `malloc` that returns an address with a + * specified alignment. + * + * It is unspecified how the function works. This implemention + * will allocate a bit of extra memory and shift the returned + * pointer so that it is aligned. + * + * @param boundary The alignment. + * @param size The number of bytes to allocated. + * @return Pointer to the beginning of the new allocation. + * If `size` is zero, this function will either return + * `NULL` (that is what this implement does) or return + * a unique pointer that can later be freed with `free`. + * `NULL` is returned on error, and `errno` is set to + * indicate the error. + * + * @throws ENOMEM The process cannot allocate more memory. + * @throws EINVAL If `boundary` is not a power of two. + */ +void* aligned_alloc(size_t boundary, size_t size) +{ + return memalign(boundary, size); +} + |