From 70638a9b46108e5a11860957cb85b69ed9b96a10 Mon Sep 17 00:00:00 2001 From: SoraKatadzuma Date: Wed, 12 Aug 2026 19:47:59 -0500 Subject: [PATCH] Reworked, no version bump --- chinookfile | 3 + include/malunal/allocators.h | 470 +++++++++----------------- include/malunal/allocators/arena.h | 128 +------- sources/allocator.c | 441 +++++++------------------ sources/arena.c | 258 ++++++++------- sources/internal.h | 75 +++++ sources/libc.c | 109 +++++++ sources/linux.c | 132 ++++++++ sources/win32.c | 134 ++++++++ tests/allocator.c | 2 +- tests/arena_allocator.c | 507 +++++++++++------------------ tests/libc_allocator.c | 147 +++------ tests/platform_allocator.c | 147 +++------ 13 files changed, 1170 insertions(+), 1383 deletions(-) create mode 100644 sources/internal.h create mode 100644 sources/libc.c create mode 100644 sources/linux.c create mode 100644 sources/win32.c diff --git a/chinookfile b/chinookfile index 51f01ee..12ff9f2 100644 --- a/chinookfile +++ b/chinookfile @@ -19,6 +19,9 @@ targets: - malunal.types srcs: - ./sources/allocator.c + - ./sources/libc.c + - ./sources/linux.c + - ./sources/win32.c - ./sources/arena.c tests: diff --git a/include/malunal/allocators.h b/include/malunal/allocators.h index a04f934..c277836 100644 --- a/include/malunal/allocators.h +++ b/include/malunal/allocators.h @@ -24,13 +24,34 @@ */ #define NULL_ADDRESS ((void*)0) + +// Assuring that ALLOCATOR_SIZE is correct. +#ifdef ALLOCATOR_SIZE +#undef ALLOCATOR_SIZE +#endif /* ALLOCATOR_SIZE */ + +/** + * @def ALLOCATOR_SIZE + * @brief Defines the size of allocators such that it can exist on the stack + * without the need to heap allocate it. + * @details This is defined based on the number of bytes of a pointer for the + * system and the number of members within the internal allocator + * implementation. It is statically asserted during compile to assure + * it is not wrong. + */ +#define ALLOCATOR_SIZE sizeof(malunal_size_t) * 7 + + /** * @brief Defines the basis of all allocators. - * @details You can create new allocators simply by providing your own virtual - * function table, an optional upstream allocator, and the allocator - * context. + * @details This is a stack allocated opaque pointer to the internals. It is not + * meant to be extensible outside of this library, as this library + * should be capable of providing any and all possibly needed allocator + * and allocation strategies required. */ -typedef struct Allocator allocator_t; +typedef struct { + malunal_uint8_t __opaque[ALLOCATOR_SIZE]; +} allocator_t; /** * @brief A pointer to a mutable allocator. @@ -52,20 +73,20 @@ typedef const allocator_t* allocator_iptr_t; * @details These are extremely useful for debugging an allocator or catching * runtime issues that can be fixed. */ -typedef enum AllocatorException { +typedef enum { /** * @brief No exception was thrown. * @details Some allocator functions only return an exception. This indicates * for those functions that the operation was successful. */ - ALLOCATOR_EXCEPTION_SUCCESS, + ALLOCATION_ERROR_SUCCESS, /** * @brief The generic allocator exception. * @details If the allocator does not know what to throw, or in test cases, * this may be thrown. */ - ALLOCATOR_EXCEPTION_FAILURE, + ALLOCATION_ERROR_FAILURE, /** * @brief The allocator instance was null. @@ -73,7 +94,7 @@ typedef enum AllocatorException { * large object. So, when the reference is expected to be non-null, * which is always, this will be thrown. */ - ALLOCATOR_EXCEPTION_NULL_ALLOCATOR, + ALLOCATION_ERROR_NULL_ALLOCATOR, /** * @brief The allocator upstream was null. @@ -81,7 +102,7 @@ typedef enum AllocatorException { * to pointer to a valid allocator. For those allocators that expect * an upstream, and do not receive it, this will be thrown. */ - ALLOCATOR_EXCEPTION_NULL_UPSTREAM, + ALLOCATION_ERROR_NULL_UPSTREAM, /** * @brief The allocator context was null. @@ -89,14 +110,14 @@ typedef enum AllocatorException { * to point to a valid address. For those allocators that expect a * context, and do not receive it, this will be thrown. */ - ALLOCATOR_EXCEPTION_NULL_CONTEXT, + ALLOCATION_ERROR_NULL_CONTEXT, /** * @brief The allocator ran out of memory. * @details By nature of memory being a limited resource, if the system or the * allocator run out of memory this will be thrown. */ - ALLOCATOR_EXCEPTION_OUT_OF_MEMORY, + ALLOCATION_ERROR_OUT_OF_MEMORY, /** * @brief The address being released does not belong to the allocator. @@ -104,7 +125,7 @@ typedef enum AllocatorException { * itself. Most allocators can check if it was allocated from its * memory pool, and if it wasn't this will be thrown. */ - ALLOCATOR_EXCEPTION_NOT_MY_ADDRESS, + ALLOCATION_ERROR_NOT_MY_ADDRESS, /** * @brief The allocator has determined that memory is being leaked. @@ -112,91 +133,8 @@ typedef enum AllocatorException { * some memory is still in use upon release or finalization. If this * happens, the allocator may throw this exception. */ - ALLOCATOR_EXCEPTION_LEAKY_MEMORY, -} allocator_exception_t; - - -/** - * @brief Packs together the information needed to initialize an allocator. - * @details By using a struct like this, we can easily indicate what parameters - * are being passed into the function. - */ -typedef struct AllocatorInitializeRequest { - /** - * @brief The allocator to initialize. - * @details There is a sort of double dispatch methodology with allocators. - * The primary @c allocator_initialize function will simply call into - * the provided allocators virtual function table, passing this data - * to it. This makes it possible for the virtual function to interact - * with the entirety of the allocator as if inherited. - */ - allocator_mptr_t allocator; - - /** - * @brief The upstream allocator for allocator being initialized. - * @details This allows the allocator to get large memory allocations from an - * upstream allocator, such that it can partition those larger memory - * blocks. - */ - allocator_mptr_t upstream; - - /** - * @brief The size to initialize the allocator to. - * @details It's useful for an allocator to be initialized to some initial - * size so that it can immediately begin to hand out allocated - * addresses. - */ - malunal_size_t size; -} allocator_initialize_req_t; - -/** - * @brief A type definition for a pointer to a function that can initialize - * some allocator instance. - * @param request Contains the information necessary to fulfill the request. - * @returns @c ALLOCATOR_EXCEPTION_SUCCESS if the request could be fulfilled; - * otherwise, any other exception value. - */ -typedef allocator_exception_t -(*allocator_initialize_pfn_t)( - allocator_initialize_req_t request -); - -/** - * @brief A type definition for a pointer to a function that can finalize some - * allocator instance. - * @param allocator The allocator to finalize. - * @returns @c ALLOCATOR_EXCEPTION_SUCCESS if the allocator could be finalized; - * otherwise, any other exception value. - */ -typedef allocator_exception_t -(*allocator_finalize_pfn_t)( - allocator_mptr_t allocator -); - - -/** - * @brief Packs together the information needed to acquire some memory. - * @details By using a struct like this, we can easily indicate what parameters - * are being passed into the function. - */ -typedef struct AllocatorAcquireRequest { - /** - * @brief The allocator to use to acquire the memory. - * @details There is a sort of double dispatch methodology with allocators. - * The primary @c allocator_acquire function will simply call into - * the provided allocators virtual function table, passing this data - * to it. This makes it possible for the virtual function to interact - * with the entirety of the allocator as if inherited. - */ - allocator_mptr_t allocator; - - /** - * @brief The size of the allocation to acquire. - * @details Allocators need this to obtain a memory block. Allocators may also - * use it to perform bounds checking, update internals, or otherwise. - */ - malunal_size_t size; -} allocator_acquire_req_t; + ALLOCATION_ERROR_LEAKY_MEMORY, +} allocation_error_t; /** * @brief Represents the result of an acquisition request. @@ -204,11 +142,11 @@ typedef struct AllocatorAcquireRequest { * exception. Which part is put into the result is indicated by the * @c threw tag. */ -typedef struct AllocatorAcquireResult { +typedef struct { /** - * @brief Indicates whether the result contains an allocation or exception. - * @details Allocators will set this to true when an exception has occurred, - * such as when there is no more memory to allocate from. + * @brief Indicates whether the result contains an allocation or an error. + * @details Allocators will set this to true when an error has occurred, such + * as when there is no more memory to allocate from. */ malunal_bool_t threw; @@ -225,248 +163,145 @@ typedef struct AllocatorAcquireResult { * @details If the allocator unsuccessfully fulfilled the request, this will * contain the reason for failure. */ - allocator_exception_t exception; + allocation_error_t error; }; -} allocator_acquire_res_t; +} allocation_result_t; /** - * @brief A type definition of a pointer to a function that can acquire some - * newly allocated memory address. - * @param request Contains the information necessary to fulfill the request. - * @returns The result of acquisition which indicates if the function threw or - * if the request was fulfilled along with the exception or allocation. + * @brief Gets the upstream allocator of the provided allocator. + * @param allocator A pointer to the allocator. + * @param out A pointer to where to store the upstream allocator. + * @returns An error if the allocator could not provide the upstream allocator; + * otherwise, @c ALLOCATION_ERROR_SUCCESS. */ -typedef allocator_acquire_res_t -(*allocator_acquire_pfn_t)( - allocator_acquire_req_t request +allocation_error_t +allocator_upstream( + allocator_mptr_t allocator, + allocator_mptr_t* out ); - /** - * @brief Packs together the information needed to reacquire some memory. - * @details By using a struct like this, we can easily indicate what parameters - * are being passed into the function. + * @brief Gets the number of bytes allocated by the allocator. + * @param allocator A pointer to the allocator. + * @param out A pointer to where to store the allocated count. + * @returns An error if the allocator could not provide the number of allocated + * bytes; otherwise, @c ALLOCATION_ERROR_SUCCESS. */ -typedef struct AllocatorReacquireRequest { - /** - * @brief The allocator to use to reacquire the memory. - * @details There is a sort of double dispatch methodology with allocators. - * The primary @c allocator_acquire function will simply call into - * the provided allocators virtual function table, passing this data - * to it. This makes it possible for the virtual function to interact - * with the entirety of the allocator as if inherited. - */ - allocator_mptr_t allocator; - - /** - * @brief The address of the previous allocation. - * @details The memory at this address will typically be released after its - * contents are copied to the new address, but some allocators will - * not fully release it, only soft release it. - */ - malunal_mptr_t prev; - - /** - * @brief The size of the allocation to release. - * @details Allocators need this to release a memory block. Allocators may - * also use it to perform bounds checking, update internals, or - * otherwise. - */ - malunal_size_t oldsz; - - /** - * @brief The size of the allocation to reacquire. - * @details Allocators need this to obtain a memory block. Allocators may also - * use it to perform bounds checking, update internals, or otherwise. - */ - malunal_size_t newsz; -} allocator_reacquire_req_t; - -/** - * @brief A type definition of a pointer to a function that can reacquire some - * newly allocated memory address. - * @param request Contains the information necessary to fulfill the request. - * @returns The result of reacquisition which indicates if the function threw or - * if the request was fulfilled along with the exception or allocation. - */ -typedef allocator_acquire_res_t -(*allocator_reacquire_pfn_t)( - allocator_reacquire_req_t request +allocation_error_t +allocator_allocated_bytes( + allocator_mptr_t allocator, + malunal_size_t* out ); - /** - * @brief Packs together the information needed to release some memory. - * @details By using a struct like this, we can easily indicate what parameters - * are being passed into the function. + * @brief Gets the number of bytes reserved by the allocator. + * @param allocator A pointer to the allocator. + * @param out A pointer to where to store the reserved count. + * @returns An error if the allocator could not provide the number of reserved + * bytes; otherwise, @c ALLOCATION_ERROR_SUCCESS. */ -typedef struct AllocatorReleaseRequest { - /** - * @brief The allocator to use to release the memory. - * @details There is a sort of double dispatch methodology with allocators. - * The primary @c allocator_release function will simply call into - * the provided allocators virtual function table, passing this data - * to it. This makes it possible for the virtual function to interact - * with the entirety of the allocator as if inherited. - */ - allocator_mptr_t allocator; - - /** - * @brief The address of the allocation to release. - * @details The address can be null, as most allocators will simply ignore it. - * However, when the address is not null, it should be aligned unless - * an allocator explicitly requires an address to be unaligned. - */ - malunal_mptr_t address; - - /** - * @brief The size of the allocation to release. - * @details Allocators need this to release a memory block. Allocators may - * also use it to perform bounds checking, update internals, or - * otherwise. - */ - malunal_size_t size; -} allocator_release_req_t; - -/** - * @brief A type definition of a pointer to a function that can release some - * previously allocated memory address. - * @param request - */ -typedef allocator_exception_t -(*allocator_release_pfn_t)( - allocator_release_req_t request +allocation_error_t +allocator_reserved_bytes( + allocator_mptr_t allocator, + malunal_size_t* out ); +/** + * @brief Gets the number of times @c acquires was called on the allocator. + * @param allocator A pointer to the allocator. + * @param out A pointer to where to store the count. + * @returns An error if the allocator could not provide the number of times that + * @c acquire was called; otherwise, @c ALLOCATION_ERROR_SUCCESS. + */ +allocation_error_t +allocator_acquires_count( + allocator_mptr_t allocator, + malunal_size_t* out +); /** - * @brief Defines the basis of all allocators. - * @details You can create new allocators simply by providing your own virtual - * function table, an optional upstream allocator, and the allocator - * context. + * @brief Gets the number of times @c release was called on the allocator. + * @param allocator A pointer to the allocator. + * @param out A pointer to where to store the count. + * @returns An error if the allocator could not provide the number of times that + * @c release was called; otherwise, @c ALLOCATION_ERROR_SUCCESS. */ -struct Allocator { - /** - * @brief The virtual function table for the allocator. - * @details Contains the pointers to the functions that make this allocator - * struct effectively an interface. - */ - struct { - /** - * @brief The initialization funciton for a given allocator. - * @details This is responsible for initializing an allocator, which may - * include acquiring some memory from an upstream allocator or - * otherwise. - */ - const allocator_initialize_pfn_t initialize; +allocation_error_t +allocator_releases_count( + allocator_mptr_t allocator, + malunal_size_t* out +); - /** - * @brief The finalization function for a given allocator. - * @details This is responsible for finalizing an allocator, which may - * include releasing some memory to an upstream allocator or - * otherwise. - */ - const allocator_finalize_pfn_t finalize; - - /** - * @brief The acquisition function for a given allocator. - * @details This is responsible for acquiring a new valid memory address for - * the calling code to use. - */ - const allocator_acquire_pfn_t acquire; - - /** - * @brief The reacquisition function for a given allocator. - * @details This is responsible for acquiring a new valid memory address for - * the calling code, copying the old contents over to the new address - * automatically for the caller. - */ - const allocator_reacquire_pfn_t reacquire; - - /** - * @brief The release function for a given allocator. - * @details This is responsible for releasing a previously allocated memory - * address. The caller is expected to destroy anything within the - * valid address. - */ - const allocator_release_pfn_t release; - } const vtable; - - /** - * @brief The upstream allocator to this allocator. - * @details The purpose of an upstream allocator is to provide this allocator - * the ability to obtain other memory pages or blocks that it plans - * to further partition. - */ - allocator_mptr_t upstream; - - /** - * @brief The context data for the allocator. - * @details This is passed to the allocator implementation through the vtable - * functions for the ability of the implementation to access its own - * data in the virtual functions. - */ - malunal_mptr_t context; - - /** - * @brief How much of the reserved memory has been allocated. - * @details This is a piece of diagnostics that might be useful to know how - * much memory is being allocated by this allocator. - */ - malunal_size_t allocated; - - /** - * @brief How much memory was reserved by this allocator. - * @details This is a piece of diagnostics that might be useful to know how - * much memory is being reserved by this allocator. - */ - malunal_size_t reserved; - - /** - * @brief How many times this allocator was acquired from. - * @details This is a piece of diagnostics that might be useful to know how - * often this allocator is being used. - */ - malunal_size_t acquires; - - /** - * @brief How many times this allocator was released from. - * @details This is a piece of diagnostics that might be useful to know how - * often this allocator is being used. - */ - malunal_size_t releases; -}; +/** + * @brief Executes an initialize request on an allocator. + * @param allocator A pointer to the allocator to initialize. + * @param upstream A pointer to the upstream for the allocator. + * @param capacity The amount of memory to initially reserve. + * @returns An error if the allocator could not be initialized properly; + * otherwise, @c ALLOCATION_ERROR_SUCCESS. + */ +allocation_error_t +allocator_initialize( + allocator_mptr_t allocator, + allocator_mptr_t upstream, + malunal_size_t capacity +); +/** + * @brief Executes a finalize request on an allocator. + * @param allocator A pointer to the allocator to finalize. + * @returns An error if the allocator could not be finalized properly; + * otherwise, @c ALLOCATION_ERROR_SUCCESS. + */ +allocation_error_t +allocator_finalize( + allocator_mptr_t allocator +); /** * @brief Executes an acquisition request on an allocator. - * @details This simplifies the execution of an allocator's virtual function - * table entries, specifically @c acquire. - * @param request The acquisition request being made. - * @returns The result of the acquisition request to the allocator. + * @param allocator A pointer to the allocator to use for acquisition. + * @param size The size of the allocation to acquire from the allocator. + * @returns The result of acquisition which indicates if the function threw or + * if the request was fulfilled along with the error or allocation. */ -allocator_acquire_res_t -allocator_acquire(allocator_acquire_req_t request); +allocation_result_t +allocator_acquire( + allocator_mptr_t allocator, + malunal_size_t size +); /** * @brief Executes a reacquisition request on an allocator. - * @details This simplifies the execution of an allocator's virtual function - * table entries, specifically @c reacquire. - * @param request The reacquisition request being made. - * @returns The result of the reacquisition request to the allocator. + * @param allocator A pointer to the allocator to use for reacquisition. + * @param address A pointer to the memory to reacquire from the allocator. + * @param oldsize The size of the old allocation to release. + * @param newsize The size of the new allocation to acquire. + * @returns The result of reacquisition which indicates if the function threw or + * if the request was fulfilled along with the error or allocation. */ -allocator_acquire_res_t -allocator_reacquire(allocator_reacquire_req_t request); +allocation_result_t +allocator_reacquire( + allocator_mptr_t allocator, + malunal_mptr_t address, + malunal_size_t oldsize, + malunal_size_t newsize +); /** * @brief Executes a release request on an allocator. - * @details This simplifies the execution of an allocator's virtual function - * table entries, specifically @c release. - * @param request The release request being made. - * @returns The result of the release request to the allocator. + * @param allocator A pointer to the allocator to use to release the memory. + * @param address A pointer to the memory to release from the allocator. + * @param size The size of the allocation to release. + * @returns An error if the allocator could not release the memory back to the + * allocator; otherwise, @c ALLOCATION_ERROR_SUCCESS. */ -allocator_exception_t -allocator_release(allocator_release_req_t request); +allocation_error_t +allocator_release( + allocator_mptr_t allocator, + malunal_mptr_t address, + malunal_size_t size +); /** * @brief Rounds the given size up to the nearest multiple of @c alignment. @@ -541,7 +376,7 @@ linux_allocator(); #elif MALUNAL_PLATFORM_WIN32 /** * @brief Provides a windows specific allocator. - * @details Thisis the windows specific allocator. It uses @c VirtualAlloc and + * @details This is the windows specific allocator. It uses @c VirtualAlloc and * @c VirtualFree to acquire, reacquire, and release memory. * @returns An allocator that works specifically with windows. */ @@ -549,4 +384,13 @@ allocator_t win32_allocator(); #endif /* Platform specific allocators */ +/** + * @brief Provides an arena allocator. + * @details This will provide appropriate arena allocator functions when the + * instance is created. + * @returns An allocator instance that is populated to be an arena allocator. + */ +allocator_t +arena_allocator(); + #endif /* MALUNAL_ALLOCATORS_HEADER */ diff --git a/include/malunal/allocators/arena.h b/include/malunal/allocators/arena.h index 981822a..121a2e5 100644 --- a/include/malunal/allocators/arena.h +++ b/include/malunal/allocators/arena.h @@ -10,122 +10,6 @@ #ifndef MALUNAL_ALLOCATORS_ARENA_HEADER #define MALUNAL_ALLOCATORS_ARENA_HEADER -/** - * @def ARENA_DEFAULT_PAGE_SIZE - * @brief Defines the default page size for the arena in terms of bytes. - * @details This should be, but is not required to be, a multiple of 4KiB. This - * is because, on most systems the memory page size will be 4KiB. - */ -#ifndef ARENA_DEFAULT_PAGE_SIZE -#define ARENA_DEFAULT_PAGE_SIZE 8192 -#endif /* ARENA_DEFAULT_PAGE_SIZE */ - - -/** - * @brief Defines an arena allocator page. - * @details Pages are large blocks of memory typically acquired from the - * operating system, but may be acquired from any upstream allocator. - * These pages may be subdivided by another allocator if the arena is - * used as an upstream itself. - */ -typedef struct ArenaAllocatorPage arena_page_t; - -/** - * @brief A pointer to a mutable arena page. - * @details This is provided to simplify type declarations for functions - * requiring arena pages that are meant to be mutable. - */ -typedef arena_page_t* arena_page_mptr_t; - -/** - * @brief A pointer to a immutable arena page. - * @details This is provided to simplify type declarations for functions - * requiring arena pages that are meant to be immutable. - */ -typedef const arena_page_t* arena_page_iptr_t; - - -struct ArenaAllocatorPage { - /** - * @brief A pointer to the next page. - * @details This is a linked list of pages making it possible for the arena - * to easily add new pages and find available memory blocks. - */ - arena_page_mptr_t next; - - /** - * @brief How much of this page is already been used. - * @details This helps the arena know if it can fit the allocation request it - * has into this page, acquiring a new page if necessary. - */ - malunal_uint32_t used; - - /** - * @brief The full size of this page. - * @details This helps the arena know if it can fit the allocation request it - * has into this page, acquiring a new page if necessary. - */ - malunal_uint32_t size; - - /** - * @brief The acquired page data. - * @details This is where the arena allocator will retrieve a memory block - * address from when fulfilling an acquisition request. - */ - malunal_int8_t data[]; -}; - -/** - * @brief Initializes the given arena. - * @details The arena is initialized by using the upstream allocator to obtain - * pages to fill the arena with. - * @param arena The arena to initialize. - * @param upstream The allocator to use to acquire arena pages. - * @param size The initial amount of memory to acquire for the arena. - */ -allocator_t -arena_allocator( - allocator_mptr_t upstream, - malunal_size_t size -); - -/** - * @brief Arena implementation for memory acquisition. - * @details This will find the next available memory location within the pages - * of the arena and reserve the required memory for the acquisition - * request. - * @param request The request to an arena allocator to acquire the specified - * amount of memory listed in the request. - * @returns An object containing either the address acquired or an exception if - * the allocator threw. - */ -allocator_acquire_res_t -arena_acquire(allocator_acquire_req_t request); - -/** - * @brief Arena implementation for memory re-acquisition. - * @details This works exactly the same as @c arena_acquire but will copy the - * contents of the previous block to the new block. - * @param request The request to an arena allocator to reacquire the specified - * amount of memory listed in the request, releasing the old memory. - * @returns An object containing either the address acquired or an exception if - * the allocator threw. - */ -allocator_acquire_res_t -arena_reacquire(allocator_reacquire_req_t request); - -/** - * @brief Arena implementation for memory release. - * @details For an arena, this will not do anything internally. This is because - * arenas are like push allocators. They are there so a bunch of data - * can be dumped in it and released all at once. - * @param request The request to an arena allocator to release the specified - * amount of memory listed in the request. - * @returns An exception if the memory could not be released. - */ -allocator_exception_t -arena_release(allocator_release_req_t request); - /** * @brief Resets the entire arena without freeing its pages. * @details This will iterate through each of the pages acquired for the arena @@ -134,17 +18,7 @@ arena_release(allocator_release_req_t request); * @param arena The arena to reset. * @returns An exception if the arena could not be reset. */ -allocator_exception_t +allocation_error_t arena_reset(allocator_mptr_t arena); -/** - * @brief Frees the entire arena. - * @details This will iterate through each of the pages acquired for the arena, - * releasing them back to the upstream. - * @param arena The arena to free. - * @returns An exception if the arena could not be freed. - */ -allocator_exception_t -arena_free(allocator_mptr_t arena); - #endif /* MALUNAL_ALLOCATORS_ARENA_HEADER */ diff --git a/sources/allocator.c b/sources/allocator.c index 6ffaa37..854d477 100644 --- a/sources/allocator.c +++ b/sources/allocator.c @@ -1,56 +1,130 @@ #include #include -#include "malunal/allocators.h" -#include "malunal/assert.h" - -#if MALUNAL_PLATFORM_LINUX -# include -# include -#elif MALUNAL_PLATFORM_WIN32 -# include -#endif /* Platform headers */ +#include "internal.h" -allocator_acquire_res_t -allocator_acquire(allocator_acquire_req_t request) { - if (request.allocator == NULL_ADDRESS) - return (allocator_acquire_res_t) { - .threw = 1, - .exception = ALLOCATOR_EXCEPTION_NULL_ALLOCATOR - }; +allocation_error_t +allocator_upstream( + allocator_mptr_t allocator, + allocator_mptr_t* out +) { + impl_mptr_t implementation = (impl_mptr_t)allocator; + if (implementation == NULL_ADDRESS) + return ALLOCATION_ERROR_NULL_ALLOCATOR; + *out = implementation->upstream; + return ALLOCATION_ERROR_SUCCESS; +} - return request.allocator->vtable.acquire != NULL_ADDRESS - ? request.allocator->vtable.acquire(request) - : (allocator_acquire_res_t) { - .threw = 0, - .address = NULL_ADDRESS +allocation_error_t +allocator_allocated_bytes( + allocator_mptr_t allocator, + malunal_size_t* out +) { + impl_mptr_t implementation = (impl_mptr_t)allocator; + if (implementation == NULL_ADDRESS) + return ALLOCATION_ERROR_NULL_ALLOCATOR; + *out = implementation->allocated; + return ALLOCATION_ERROR_SUCCESS; +} + +allocation_error_t +allocator_reserved_bytes( + allocator_mptr_t allocator, + malunal_size_t* out +) { + impl_mptr_t implementation = (impl_mptr_t)allocator; + if (implementation == NULL_ADDRESS) + return ALLOCATION_ERROR_NULL_ALLOCATOR; + *out = implementation->reserved; + return ALLOCATION_ERROR_SUCCESS; +} + +allocation_error_t +allocator_acquires_count( + allocator_mptr_t allocator, + malunal_size_t* out +) { + impl_mptr_t implementation = (impl_mptr_t)allocator; + if (implementation == NULL_ADDRESS) + return ALLOCATION_ERROR_NULL_ALLOCATOR; + *out = implementation->acquires; + return ALLOCATION_ERROR_SUCCESS; +} + +allocation_error_t +allocator_releases_count( + allocator_mptr_t allocator, + malunal_size_t* out +) { + impl_mptr_t implementation = (impl_mptr_t)allocator; + if (implementation == NULL_ADDRESS) + return ALLOCATION_ERROR_NULL_ALLOCATOR; + *out = implementation->releases; + return ALLOCATION_ERROR_SUCCESS; +} + +allocation_error_t +allocator_initialize( + allocator_mptr_t allocator, + allocator_mptr_t upstream, + malunal_size_t capacity +) { + impl_mptr_t implementation = (impl_mptr_t)allocator; + return implementation != NULL_ADDRESS + ? implementation->vtable->initialize(allocator, upstream, capacity) + : ALLOCATION_ERROR_NULL_ALLOCATOR; +} + +allocation_error_t +allocator_finalize( + allocator_mptr_t allocator +) { + impl_mptr_t implementation = (impl_mptr_t)allocator; + return implementation != NULL_ADDRESS + ? implementation->vtable->finalize(allocator) + : ALLOCATION_ERROR_NULL_ALLOCATOR; +} + +allocation_result_t +allocator_acquire( + allocator_mptr_t allocator, + malunal_size_t size +) { + impl_mptr_t implementation = (impl_mptr_t)allocator; + return implementation != NULL_ADDRESS + ? implementation->vtable->acquire(allocator, size) + : (allocation_result_t) { + .threw = 1, + .error = ALLOCATION_ERROR_NULL_ALLOCATOR }; } -allocator_acquire_res_t -allocator_reacquire(allocator_reacquire_req_t request) { - if (request.allocator == NULL_ADDRESS) - return (allocator_acquire_res_t) { - .threw = 1, - .exception = ALLOCATOR_EXCEPTION_NULL_ALLOCATOR - }; - - return request.allocator->vtable.reacquire != NULL_ADDRESS - ? request.allocator->vtable.reacquire(request) - : (allocator_acquire_res_t) { - .threw = 0, - .address = NULL_ADDRESS +allocation_result_t +allocator_reacquire( + allocator_mptr_t allocator, + malunal_mptr_t address, + malunal_size_t oldsize, + malunal_size_t newsize +) { + impl_mptr_t implementation = (impl_mptr_t)allocator; + return implementation != NULL_ADDRESS + ? implementation->vtable->reacquire(allocator, address, oldsize, newsize) + : (allocation_result_t) { + .threw = 1, + .error = ALLOCATION_ERROR_NULL_ALLOCATOR }; } -allocator_exception_t -allocator_release(allocator_release_req_t request) { - if (request.allocator == NULL_ADDRESS) - return ALLOCATOR_EXCEPTION_NULL_ALLOCATOR; - - return request.allocator->vtable.release != NULL_ADDRESS - ? request.allocator->vtable.release(request) - : ALLOCATOR_EXCEPTION_SUCCESS; +allocation_error_t +allocator_release( + allocator_mptr_t allocator, + malunal_mptr_t address, + malunal_size_t size +) { + impl_mptr_t implementation = (impl_mptr_t)allocator; + return implementation != NULL_ADDRESS + ? implementation->vtable->release(allocator, address, size) + : ALLOCATION_ERROR_NULL_ALLOCATOR; } malunal_size_t @@ -66,111 +140,6 @@ align_to_page(malunal_size_t size) { return align_to(size, platform_page_size()); } -static -malunal_size_t -g_platform_page_size = 0; - -malunal_size_t -platform_page_size() { - if (g_platform_page_size != 0) - return g_platform_page_size; - -#if MALUNAL_PLATFORM_LINUX - g_platform_page_size = sysconf(_SC_PAGESIZE); -#elif MALUNAL_PLATFORM_WIN32 - SYSTEM_INFO si; - GetSystemInfo(&si); - g_platform_page_size = si.dwPageSize; -#endif - - return g_platform_page_size; -} - - -static -allocator_acquire_res_t -libc_acquire(allocator_acquire_req_t request); - -static -allocator_acquire_res_t -libc_reacquire(allocator_reacquire_req_t request); - -static -allocator_exception_t -libc_release(allocator_release_req_t request); - - -static -allocator_acquire_res_t -libc_acquire(allocator_acquire_req_t request) { - allocator_acquire_res_t result; - - malunal_mptr_t addr = malloc(request.size); - if (addr == NULL_ADDRESS) { - return (allocator_acquire_res_t) { - .threw = 1, - .exception = ALLOCATOR_EXCEPTION_OUT_OF_MEMORY - }; - } - - request.allocator->allocated += request.size; - request.allocator->reserved += request.size; - request.allocator->acquires += 1; - return (allocator_acquire_res_t) { - .threw = 0, - .address = addr - }; -} - -static -allocator_acquire_res_t -libc_reacquire(allocator_reacquire_req_t request) { - allocator_acquire_res_t result = - libc_acquire((allocator_acquire_req_t) { - .allocator = request.allocator, - .size = request.newsz - }); - - if (result.threw) - return result; - - result.address = memcpy( - result.address, - request.prev, - request.oldsz - ); - - libc_release((allocator_release_req_t) { - .allocator = request.allocator, - .address = request.prev, - .size = request.oldsz - }); - - return result; -} - -static -allocator_exception_t -libc_release(allocator_release_req_t request) { - free(request.address); - request.allocator->allocated -= request.size; - request.allocator->reserved -= request.size; - request.allocator->releases += 1; - return ALLOCATOR_EXCEPTION_SUCCESS; -} - -allocator_t -libc_allocator() { - return (allocator_t) { - .vtable = { - .acquire = &libc_acquire, - .reacquire = &libc_reacquire, - .release = &libc_release - }, - .context = NULL_ADDRESS - }; -} - allocator_t platform_allocator() { #if MALUNAL_PLATFORM_LINUX @@ -179,181 +148,3 @@ platform_allocator() { return win32_allocator(); #endif /* Platform specific allocators */ } - -#if MALUNAL_PLATFORM_LINUX -static -allocator_acquire_res_t -linux_acquire(allocator_acquire_req_t request); - -static -allocator_acquire_res_t -linux_reacquire(allocator_reacquire_req_t request); - -static -allocator_exception_t -linux_release(allocator_release_req_t request); - - -static -allocator_acquire_res_t -linux_acquire(allocator_acquire_req_t request) { - const malunal_int32_t memops = PROT_READ | PROT_WRITE; - const malunal_int32_t memprms = MAP_PRIVATE | MAP_ANONYMOUS; - - allocator_acquire_res_t result; - - // TODO: - malunal_size_t size = request.size; - malunal_mptr_t ptr = mmap(0, size, memops, memprms, -1, 0); - if (ptr == MAP_FAILED || ptr == NULL_ADDRESS) { - result = (allocator_acquire_res_t) { - .threw = 1, - .exception = ALLOCATOR_EXCEPTION_OUT_OF_MEMORY - }; - } else { - result = (allocator_acquire_res_t) { - .threw = 0, - .address = ptr - }; - } - - return result; -} - -static -allocator_acquire_res_t -linux_reacquire(allocator_reacquire_req_t request) { - allocator_acquire_res_t result = - linux_acquire((allocator_acquire_req_t) { - .allocator = request.allocator, - .size = request.newsz - }); - - if (result.threw) - return result; - - result.address = memcpy( - result.address, - request.prev, - request.oldsz - ); - - linux_release((allocator_release_req_t) { - .allocator = request.allocator, - .address = request.prev, - .size = request.oldsz - }); - - return result; -} - -static -allocator_exception_t -linux_release(allocator_release_req_t request) { - munmap(request.address, request.size); - return ALLOCATOR_EXCEPTION_SUCCESS; -} - -allocator_t -linux_allocator() { - return (allocator_t) { - .vtable = { - .acquire = &linux_acquire, - .reacquire = &linux_reacquire, - .release = &linux_release - }, - .context = NULL_ADDRESS - }; -} -#elif MALUNAL_PLATFORM_WIN32 -static -allocator_acquire_res_t -win32_acquire(allocator_acquire_req_t request); - -static -allocator_acquire_res_t -win32_reacquire(allocator_reacquire_req_t request); - -static -allocator_exception_t -win32_release(allocator_release_req_t request); - -static -allocator_acquire_res_t -win32_acquire(allocator_acquire_req_t request) { - const malunal_int32_t memops = MEM_COMMIT | MEM_RESERVE; - const malunal_int32_t pageops = PAGE_READWRITE; - - malunal_mptr_t addr = VirtualAlloc( - NULL_ADDRESS, - request.size, - memops, - pageops - ); - - if (addr == NULL_ADDRESS) { - result = (allocator_acquire_res_t) { - .threw = 1, - .exception = ALLOCATOR_EXCEPTION_OUT_OF_MEMORY - }; - } else { - result = (allocator_acquire_res_t) { - .threw = 0, - .address = addr - }; - } - - return result; -} - -static -allocator_acquire_res_t -win32_reacquire(allocator_reacquire_req_t request) { - allocator_acquire_res_t result = - win32_acquire((allocator_acquire_req_t) { - .allocator = request.allocator, - .size = request.newsz - }); - - if (result.threw) - return result; - - result.address = memcpy( - result.address, - request.prev, - request.oldsz - ); - - win32_release((allocator_release_req_t) { - .allocator = request.allocator, - .address = request.prev, - .size = request.oldsz - }); - - return result; -} - -static -allocator_exception_t -win32_release(allocator_release_req_t request) { - VirtualFree( - request.address, - request.size, - MEM_RELEASE - ); - - return ALLOCATOR_EXCEPTION_SUCCESS; -} - -allocator_t -win32_allocator() { - return (allocator_t) { - .vtable = { - .acquire = &win32_acquire, - .reacquire = &win32_reacquire, - .release = &win32_release - }, - .context = NULL_ADDRESS - }; -} -#endif /* Platform specific allocators */ diff --git a/sources/arena.c b/sources/arena.c index 5598b79..a1b4702 100644 --- a/sources/arena.c +++ b/sources/arena.c @@ -1,180 +1,198 @@ #include -#include "malunal/allocators/arena.h" -#include "malunal/assert.h" +#include "internal.h" + +typedef struct ArenaPage page_t; +typedef const page_t* page_iptr_t; +typedef page_t* page_mptr_t; + +struct ArenaPage { + page_mptr_t next; + malunal_uint32_t used; + malunal_uint32_t size; + malunal_int8_t data[]; +}; -allocator_acquire_res_t -create_page(allocator_acquire_req_t request) { - if (request.allocator == NULL_ADDRESS) { - return (allocator_acquire_res_t) { - .threw = 1, - .exception = ALLOCATOR_EXCEPTION_NULL_UPSTREAM - }; - } - - allocator_acquire_res_t result = - allocator_acquire(request); +allocation_result_t +create_page( + allocator_mptr_t allocator, + malunal_size_t size +) { + malunal_size_t rounded = align_to_page(size); + allocation_result_t result = allocator_acquire(allocator, rounded); if (result.threw) return result; - arena_page_mptr_t page = result.address; - page->size = request.size; - page->used = sizeof(arena_page_t); + page_mptr_t page = result.address; + page->size = rounded; + page->used = sizeof(page_t); page->next = NULL_ADDRESS; return result; } -allocator_exception_t +allocation_error_t delete_page( - allocator_mptr_t allocator, - arena_page_mptr_t page + allocator_mptr_t allocator, + page_mptr_t page ) { if (allocator == NULL_ADDRESS) - return ALLOCATOR_EXCEPTION_NULL_UPSTREAM; + return ALLOCATION_ERROR_NULL_UPSTREAM; if (page == NULL_ADDRESS) - return ALLOCATOR_EXCEPTION_FAILURE; + return ALLOCATION_ERROR_FAILURE; - delete_page(allocator, page->next); - return allocator_release((allocator_release_req_t) { - .allocator = allocator, - .address = page, - .size = page->size - }); + allocation_error_t err = delete_page(allocator, page->next); + return err == ALLOCATION_ERROR_SUCCESS + ? allocator_release(allocator, page, page->size) + : err; } -allocator_acquire_res_t -page_acquire(allocator_acquire_req_t request) { - arena_page_mptr_t page = request.allocator->context; - if (page == NULL_ADDRESS) { - return (allocator_acquire_res_t) { - .threw = 1, - .exception = ALLOCATOR_EXCEPTION_NULL_CONTEXT - }; - } - - malunal_uint32_t offset = page->used - sizeof(arena_page_t); +allocation_result_t +page_acquire( + page_mptr_t page, + malunal_size_t size +) { + malunal_uint32_t offset = page->used - sizeof(page_t); malunal_mptr_t result = page->data + offset; - page->used += request.size; - return (allocator_acquire_res_t) { + page->used += size; + return (allocation_result_t) { .threw = 0, .address = result }; } -allocator_t -arena_allocator( +static +allocation_error_t +arena_initialize( + impl_mptr_t allocator, allocator_mptr_t upstream, - malunal_size_t size + malunal_size_t capacity ) { - allocator_acquire_req_t request = - (allocator_acquire_req_t) { - .allocator = upstream, - .size = platform_page_size() - }; + if (upstream == NULL_ADDRESS) + return ALLOCATION_ERROR_NULL_UPSTREAM; - allocator_acquire_res_t result = create_page(request); - arena_page_mptr_t currpage = - !result.threw ? result.address : NULL_ADDRESS; + allocation_result_t result = create_page(upstream, capacity); + page_mptr_t currpage = !result.threw ? result.address : NULL_ADDRESS; - malunal_size_t reserved = 0; - if (currpage != NULL_ADDRESS) { - reserved += currpage->size; - while (reserved > size) { - result = create_page(request); - if (result.threw) - break; - currpage = currpage->next = result.address; - reserved += currpage->size; - } - } - - return (allocator_t) { - .vtable = { - .acquire = (allocator_acquire_pfn_t)&arena_acquire, - .reacquire = (allocator_reacquire_pfn_t)&arena_reacquire, - .release = (allocator_release_pfn_t)&arena_release - }, - .upstream = upstream, - .context = currpage - }; + allocator->upstream = upstream; + allocator->context = currpage; + allocator->allocated = currpage->used; + allocator->reserved = currpage->size; + return ALLOCATION_ERROR_SUCCESS; } -allocator_acquire_res_t -arena_acquire(allocator_acquire_req_t request) { - if (request.allocator == NULL_ADDRESS) { - return (allocator_acquire_res_t) { - .threw = 1, - .exception = ALLOCATOR_EXCEPTION_NULL_ALLOCATOR - }; - } +static +allocation_error_t +arena_finalize(impl_mptr_t allocator) { + delete_page(allocator->upstream, allocator->context); +} - if (request.size > platform_page_size()) { - return (allocator_acquire_res_t) { - .threw = 1, - .exception = ALLOCATOR_EXCEPTION_OUT_OF_MEMORY +static +allocation_result_t +arena_acquire( + impl_mptr_t allocator, + malunal_size_t size +) { + if (allocator->context == NULL_ADDRESS) + return (allocation_result_t) { + .threw = 1, + .error = ALLOCATION_ERROR_NULL_CONTEXT }; - } - arena_page_mptr_t page = request.allocator->context; - while (page != NULL_ADDRESS) { + struct ArenaPage* page = allocator->context; + if (size > page->size) + return (allocation_result_t) { + .threw = 1, + .error = ALLOCATION_ERROR_OUT_OF_MEMORY + }; + + while (page->next != NULL_ADDRESS) { malunal_uint32_t remaining = page->size - page->used; - if (request.size <= remaining) - return page_acquire(request); + if (size <= remaining) + return page_acquire(page, size); page = page->next; continue; } - allocator_acquire_res_t result = - create_page((allocator_acquire_req_t) { - .allocator = request.allocator->upstream, - .size = platform_page_size() - }); + allocation_result_t result = + create_page(allocator->upstream, page->size); page = page->next = !result.threw ? result.address : NULL_ADDRESS; return page != NULL_ADDRESS - ? page_acquire(request) - : (allocator_acquire_res_t) { - .threw = 1, - .exception = ALLOCATOR_EXCEPTION_OUT_OF_MEMORY + ? page_acquire(page, size) + : (allocation_result_t) { + .threw = 1, + .error = ALLOCATION_ERROR_OUT_OF_MEMORY }; } -allocator_acquire_res_t -arena_reacquire(allocator_reacquire_req_t request) { - return arena_acquire((allocator_acquire_req_t) { - .allocator = request.allocator, - .size = request.newsz - }); +static +allocation_result_t +arena_reacquire( + impl_mptr_t allocator, + malunal_mptr_t address, + malunal_size_t oldsize, + malunal_size_t newsize +) { + MALUNAL_UNUSED(address); + MALUNAL_UNUSED(oldsize); + return arena_acquire(allocator, newsize); } -allocator_exception_t -arena_release(allocator_release_req_t request) { - if (request.allocator == NULL_ADDRESS) - return ALLOCATOR_EXCEPTION_NULL_ALLOCATOR; - - if (request.allocator->context == NULL_ADDRESS) - return ALLOCATOR_EXCEPTION_NULL_CONTEXT; +allocation_error_t +arena_release( + impl_mptr_t allocator, + malunal_mptr_t address, + malunal_size_t size +) { + MALUNAL_UNUSED(allocator); + MALUNAL_UNUSED(address); + MALUNAL_UNUSED(size); } -allocator_exception_t +allocation_error_t arena_reset(allocator_mptr_t arena) { if (arena == NULL_ADDRESS) - return ALLOCATOR_EXCEPTION_NULL_ALLOCATOR; + return ALLOCATION_ERROR_NULL_ALLOCATOR; - arena_page_mptr_t page = arena->context; + impl_mptr_t impl = (impl_mptr_t)arena; + page_mptr_t page = impl->context; while (page != NULL_ADDRESS) { - page->used = 0; + page->used = sizeof(page_t); page = page->next; } } -allocator_exception_t -arena_free(allocator_mptr_t arena) { - if (arena == NULL_ADDRESS) - return ALLOCATOR_EXCEPTION_NULL_ALLOCATOR; - delete_page(arena->upstream, arena->context); +static const +virtual_table_t arena_vtable = { + .initialize = (allocator_initialize_pfn_t)&arena_initialize, + .finalize = (allocator_finalize_pfn_t)&arena_finalize, + .acquire = (allocator_acquire_pfn_t)&arena_acquire, + .reacquire = (allocator_reacquire_pfn_t)&arena_reacquire, + .release = (allocator_release_pfn_t)&arena_release +}; + +allocator_t +arena_allocator() { + implementation_t implementation = { + .vtable = &arena_vtable, + .upstream = NULL_ADDRESS, + .context = NULL_ADDRESS, + .allocated = 0, + .reserved = 0, + .acquires = 0, + .releases = 0 + }; + + allocator_t result; + memcpy( + &result, + &implementation, + sizeof(implementation_t) + ); + + return result; } diff --git a/sources/internal.h b/sources/internal.h new file mode 100644 index 0000000..31d808a --- /dev/null +++ b/sources/internal.h @@ -0,0 +1,75 @@ +/** + * @file internal.h + * @brief This header contains the definition of the internal allocator + * implementation and function pointers for the allocator virtual + * function table. + * @author John Christman (sorakatadzuma@gmail.com) + * @copyright Malunal Studios, LLC. + */ +#include "malunal/allocators.h" + +#ifndef MALUNAL_ALLOCATORS_INTERNAL_HEADER +#define MALUNAL_ALLOCATORS_INTERNAL_HEADER + +typedef allocation_error_t +(*allocator_initialize_pfn_t)( + allocator_mptr_t allocator, + allocator_mptr_t upstream, + malunal_size_t capacity +); + +typedef allocation_error_t +(*allocator_finalize_pfn_t)( + allocator_mptr_t allocator +); + +typedef allocation_result_t +(*allocator_acquire_pfn_t)( + allocator_mptr_t allocator, + malunal_size_t size +); + +typedef allocation_result_t +(*allocator_reacquire_pfn_t)( + allocator_mptr_t allocator, + malunal_mptr_t address, + malunal_size_t oldsize, + malunal_size_t newsize +); + +typedef allocation_error_t +(*allocator_release_pfn_t)( + allocator_mptr_t allocator, + malunal_mptr_t address, + malunal_size_t size +); + + +typedef struct { + allocator_initialize_pfn_t const initialize; + allocator_finalize_pfn_t const finalize; + allocator_acquire_pfn_t const acquire; + allocator_reacquire_pfn_t const reacquire; + allocator_release_pfn_t const release; +} virtual_table_t; + +typedef virtual_table_t* vtable_mptr_t; +typedef const virtual_table_t* vtable_iptr_t; + + +typedef struct { + vtable_iptr_t const vtable; + + allocator_mptr_t upstream; + malunal_mptr_t context; + malunal_size_t allocated; + malunal_size_t reserved; + malunal_size_t acquires; + malunal_size_t releases; +} implementation_t; + +typedef implementation_t* impl_mptr_t; +typedef const implementation_t* impl_iptr_t; + + +#endif /* MALUNAL_ALLOCATORS_INTERNAL_HEADER */ diff --git a/sources/libc.c b/sources/libc.c new file mode 100644 index 0000000..0a2685c --- /dev/null +++ b/sources/libc.c @@ -0,0 +1,109 @@ +#include +#include +#include "internal.h" + + +static +allocation_error_t +libc_initialize( + impl_mptr_t allocator, + allocator_mptr_t upstream, + malunal_size_t capacity +) { + MALUNAL_UNUSED(allocator); + MALUNAL_UNUSED(upstream); + MALUNAL_UNUSED(capacity); + return ALLOCATION_ERROR_SUCCESS; +} + +static +allocation_error_t +libc_finalize(impl_mptr_t allocator) { + MALUNAL_UNUSED(allocator); + return ALLOCATION_ERROR_SUCCESS; +} + +static +allocation_result_t +libc_acquire( + impl_mptr_t allocator, + malunal_size_t size +) { + malunal_mptr_t addr = malloc(size); + if (addr == NULL_ADDRESS) { + return (allocation_result_t) { + .threw = 1, + .error = ALLOCATION_ERROR_OUT_OF_MEMORY + }; + } + + allocator->allocated += size; + allocator->reserved += size; + allocator->acquires += 1; + return (allocation_result_t) { + .threw = 0, + .address = addr + }; +} + +static +allocation_error_t +libc_release( + impl_mptr_t allocator, + malunal_mptr_t address, + malunal_size_t size +) { + free(address); + + allocator->allocated -= size; + allocator->reserved -= size; + allocator->releases += 1; + return ALLOCATION_ERROR_SUCCESS; +} + +static +allocation_result_t +libc_reacquire( + impl_mptr_t allocator, + malunal_mptr_t address, + malunal_size_t oldsize, + malunal_size_t newsize +) { + allocation_result_t result = libc_acquire(allocator, newsize); + if (result.threw) + return result; + + result.address = memcpy( result.address, address, oldsize); + libc_release(allocator, address, oldsize); + return result; +} + +static const +virtual_table_t libc_vtable = { + .initialize = (allocator_initialize_pfn_t)&libc_initialize, + .finalize = (allocator_finalize_pfn_t)&libc_finalize, + .acquire = (allocator_acquire_pfn_t)&libc_acquire, + .reacquire = (allocator_reacquire_pfn_t)&libc_reacquire, + .release = (allocator_release_pfn_t)&libc_release +}; + +allocator_t +libc_allocator() { + implementation_t implementation = { + .vtable = &libc_vtable, + .upstream = NULL_ADDRESS, + .context = NULL_ADDRESS, + .allocated = 0, + .reserved = 0, + .acquires = 0, + .releases = 0 + }; + + allocator_t result; + memcpy( + &result, + &implementation, + sizeof(implementation_t) + ); + return result; +} diff --git a/sources/linux.c b/sources/linux.c new file mode 100644 index 0000000..01896c3 --- /dev/null +++ b/sources/linux.c @@ -0,0 +1,132 @@ +#include "malunal/config.h" + +#if MALUNAL_PLATFORM_LINUX +#include +#include +#include +#include +#include "internal.h" + + +static +malunal_size_t +g_platform_page_size = 0; + +malunal_size_t +platform_page_size() { + return g_platform_page_size == 0 + ? g_platform_page_size = sysconf(_SC_PAGE_SIZE) + : g_platform_page_size; +} + + +static +allocation_error_t +linux_initialize( + impl_mptr_t allocator, + allocator_mptr_t upstream, + malunal_size_t capacity +) { + MALUNAL_UNUSED(allocator); + MALUNAL_UNUSED(upstream); + MALUNAL_UNUSED(capacity); + return ALLOCATION_ERROR_SUCCESS; +} + +static +allocation_error_t +linux_finalize(impl_mptr_t allocator) { + MALUNAL_UNUSED(allocator); + return ALLOCATION_ERROR_SUCCESS; +} + +static +allocation_result_t +linux_acquire( + impl_mptr_t allocator, + malunal_size_t size +) { + MALUNAL_UNUSED(allocator); + + const malunal_int32_t memops = PROT_READ | PROT_WRITE; + const malunal_int32_t memprms = MAP_PRIVATE | MAP_ANONYMOUS; + + malunal_mptr_t ptr = mmap(0, size, memops, memprms, -1, 0); + if (ptr == MAP_FAILED || ptr == NULL_ADDRESS) + return (allocation_result_t) { + .threw = 1, + .error = ALLOCATION_ERROR_OUT_OF_MEMORY + }; + + allocator->allocated += size; + allocator->reserved += size; + allocator->acquires += 1; + return (allocation_result_t) { + .threw = 0, + .address = ptr + }; +} + +static +allocation_error_t +linux_release( + impl_mptr_t allocator, + malunal_mptr_t address, + malunal_size_t size +) { + MALUNAL_UNUSED(allocator); + munmap(address, size); + allocator->allocated -= size; + allocator->reserved -= size; + allocator->releases += 1; + return ALLOCATION_ERROR_SUCCESS; +} + +static +allocation_result_t +linux_reacquire( + impl_mptr_t allocator, + malunal_mptr_t address, + malunal_size_t oldsize, + malunal_size_t newsize +) { + allocation_result_t result = linux_acquire(allocator, newsize); + if (result.threw) + return result; + + result.address = memcpy(result.address, address, oldsize); + linux_release(allocator, address, oldsize); + return result; +} + +static const +virtual_table_t linux_vtable = { + .initialize = (allocator_initialize_pfn_t)&linux_initialize, + .finalize = (allocator_finalize_pfn_t)&linux_finalize, + .acquire = (allocator_acquire_pfn_t)&linux_acquire, + .reacquire = (allocator_reacquire_pfn_t)&linux_reacquire, + .release = (allocator_release_pfn_t)&linux_release +}; + +allocator_t +linux_allocator() { + implementation_t implementation = { + .vtable = &linux_vtable, + .upstream = NULL_ADDRESS, + .context = NULL_ADDRESS, + .allocated = 0, + .reserved = 0, + .acquires = 0, + .releases = 0 + }; + + allocator_t result; + memcpy( + &result, + &implementation, + sizeof(implementation_t) + ); + + return result; +} +#endif /* MALUNAL_PLATFORM_LINUX */ diff --git a/sources/win32.c b/sources/win32.c new file mode 100644 index 0000000..ea05ec9 --- /dev/null +++ b/sources/win32.c @@ -0,0 +1,134 @@ +#include "malunal/config.h" + +#if MALUNAL_PLATFORM_WIN32 +#include +#include +#include +#include "internal.h" + + +static +malunal_size_t +g_platform_page_size = 0; + +malunal_size_t +platform_page_size() { + if (g_platform_page_size != 0) + return g_platform_page_size; + + SYSTEM_INFO si; + GetSystemInfo(&si); + g_platform_page_size = si.dwPageSize; + return g_platform_page_size; +} + +static +allocation_error_t +win32_initialize( + impl_mptr_t allocator, + allocator_mptr_t upstream, + malunal_size_t capacity +) { + MALUNAL_UNUSED(allocator); + MALUNAL_UNUSED(upstream); + MALUNAL_UNUSED(capacity); + return ALLOCATION_ERROR_SUCCESS; +} + +static +allocation_error_t +win32_finalize(impl_mptr_t allocator) { + MALUNAL_UNUSED(allocator); + return ALLOCATION_ERROR_SUCCESS; +} + +static +allocation_result_t +win32_acquire( + impl_mptr_t allocator, + malunal_size_t size +) { + MALUNAL_UNUSED(allocator); + + const malunal_int32_t memops = MEM_COMMIT | MEM_RESERVE; + const malunal_int32_t pageops = PAGE_READWRITE; + + malunal_mptr_t addr = VirtualAlloc(NULL_ADDRESS, size, memops, pageops); + if (addr == NULL_ADDRESS) + return (allocation_result_t) { + .threw = 1, + .error = ALLOCATION_ERROR_OUT_OF_MEMORY + }; + + allocator->allocated += size; + allocator->reserved += size; + allocator->acquires += 1; + return (allocation_result_t) { + .threw = 0, + .address = addr + }; +} + +static +allocation_error_t +win32_release( + impl_mptr_t allocator, + malunal_mptr_t address, + malunal_size_t size +) { + MALUNAL_UNUSED(allocator); + VirtualFree(address, size, MEM_RELEASE); + allocator->allocated -= size; + allocator->reserved -= size; + allocator->releases += 1; + return ALLOCATION_ERROR_SUCCESS; +} + +static +allocation_result_t +win32_reacquire( + impl_mptr_t allocator, + malunal_mptr_t address, + malunal_size_t oldsize, + malunal_size_t newsize +) { + allocation_result_t result = win32_acquire(allocator, newsize); + if (result.threw) + return result; + + result.address = memcpy(result.address, address, oldsize); + win32_release(allocator, address, oldsize); + return result; +} + +static const +virtual_table_t win32_vtable = { + .initialize = (allocator_initialize_pfn_t)&win32_initialize, + .finalize = (allocator_finalize_pfn_t)&win32_finalize, + .acquire = (allocator_acquire_pfn_t)&win32_acquire, + .reacquire = (allocator_reacquire_pfn_t)&win32_reacquire, + .release = (allocator_release_pfn_t)&win32_release +}; + +allocator_t +win32_allocator() { + implementation_t implementation = { + .vtable = &win32_vtable, + .upstream = NULL_ADDRESS, + .context = NULL_ADDRESS, + .allocated = 0, + .reserved = 0, + .acquires = 0, + .releases = 0 + }; + + allocator_t result; + memcpy( + &result, + &implementation, + sizeof(implementation_t) + ); + + return result; +} +#endif /* MALUNAL_PLATFORM_WIN32 */ diff --git a/tests/allocator.c b/tests/allocator.c index 9a08438..a874289 100644 --- a/tests/allocator.c +++ b/tests/allocator.c @@ -4,7 +4,7 @@ extern int test_arena_allocator(); int main(void) { return !( - test_libc_allocator() && + test_libc_allocator() && test_platform_allocator() && test_arena_allocator() ); diff --git a/tests/arena_allocator.c b/tests/arena_allocator.c index 15fe08d..017cb2f 100644 --- a/tests/arena_allocator.c +++ b/tests/arena_allocator.c @@ -1,104 +1,115 @@ #include #include "malunal/allocators/arena.h" -const malunal_size_t page_bytes = 4096; -const malunal_size_t init_bytes = sizeof(malunal_int32_t) * 4; -const malunal_size_t final_bytes = sizeof(malunal_int32_t) * 8; +#define assert_equals_result(condition) \ + if (!(condition)) { \ + fprintf( \ + stderr, \ + format, \ + __FILE__, \ + __LINE__, \ + MALUNAL_TOSTRING(condition) \ + ); \ + return (allocation_result_t) { \ + .threw = 1, \ + .error = ALLOCATION_ERROR_FAILURE \ + }; \ + } + +#define assert_equals(condition) \ + if (!(condition)) { \ + fprintf( \ + stderr, \ + format, \ + __FILE__, \ + __LINE__, \ + MALUNAL_TOSTRING(condition) \ + ); \ + return 0; \ + } + + +const malunal_cstr_t format = "[arena_allocator(%s:%d)]: %s failed\n"; + +const malunal_size_t page_bytes = 4096; +const malunal_size_t pre_alloc = 16; +const malunal_size_t init_bytes = sizeof(malunal_int32_t) * 4; +const malunal_size_t final_bytes = sizeof(malunal_int32_t) * 8; +const malunal_size_t init_with_pre = pre_alloc + init_bytes; +const malunal_size_t all_bytes = pre_alloc + init_bytes + final_bytes; static -allocator_acquire_res_t -test_arena_acquires(allocator_mptr_t arena) { - allocator_acquire_res_t result = - allocator_acquire((allocator_acquire_req_t) { - .allocator = arena, - .size = init_bytes - }); +allocation_error_t +test_arena_initialize( + allocator_mptr_t arena, + allocator_mptr_t upstream +) { + assert_equals(allocator_initialize(arena, upstream, page_bytes) == ALLOCATION_ERROR_SUCCESS); + malunal_size_t count; + assert_equals(allocator_reserved_bytes(upstream, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == page_bytes); + + assert_equals(allocator_allocated_bytes(upstream, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == page_bytes); + + assert_equals(allocator_acquires_count(upstream, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == 1); + + assert_equals(allocator_releases_count(upstream, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == 0); + + // Arena checks. + assert_equals(allocator_reserved_bytes(arena, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == page_bytes); + + assert_equals(allocator_allocated_bytes(arena, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == pre_alloc); + + assert_equals(allocator_acquires_count(arena, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == 0); + + assert_equals(allocator_releases_count(arena, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == 0); + return ALLOCATION_ERROR_SUCCESS; +} + +static +allocation_result_t +test_arena_acquires(allocator_mptr_t arena) { + allocation_result_t result = allocator_acquire(arena, init_bytes); if (result.threw) return result; // Upstream checks. - if (arena->upstream->reserved != page_bytes) { - const malunal_cstr_t format = - "[arena_allocator]: arena->upstream->reserved != %d, actual == %d"; - fprintf(stderr, format, page_bytes, arena->upstream->reserved); - return (allocator_acquire_res_t) { - .threw = 1, - .exception = ALLOCATOR_EXCEPTION_FAILURE - }; - } + allocator_mptr_t upstream; + assert_equals_result(allocator_upstream(arena, &upstream) == ALLOCATION_ERROR_SUCCESS); - if (arena->upstream->allocated != page_bytes) { - const malunal_cstr_t format = - "[arena_allocator]: arena->upstream->allocated != %d, actual == %d"; - fprintf(stderr, format, page_bytes, arena->upstream->allocated); - return (allocator_acquire_res_t) { - .threw = 1, - .exception = ALLOCATOR_EXCEPTION_FAILURE - }; - } + malunal_size_t count; + assert_equals_result(allocator_reserved_bytes(upstream, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals_result(count == page_bytes); - if (arena->upstream->acquires != 1) { - const malunal_cstr_t format = - "[arena_allocator]: arena->upstream->acquires != 1, actual == %d"; - fprintf(stderr, format, arena->upstream->acquires); - return (allocator_acquire_res_t) { - .threw = 1, - .exception = ALLOCATOR_EXCEPTION_FAILURE - }; - } + assert_equals_result(allocator_allocated_bytes(upstream, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals_result(count == page_bytes); - if (arena->upstream->releases != 0) { - const malunal_cstr_t format = - "[arena_allocator]: arena->upstream->releases != 0, actual == %d"; - fprintf(stderr, format, arena->upstream->releases); - return (allocator_acquire_res_t) { - .threw = 1, - .exception = ALLOCATOR_EXCEPTION_FAILURE - }; - } + assert_equals_result(allocator_acquires_count(upstream, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals_result(count == 1); + + assert_equals_result(allocator_releases_count(upstream, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals_result(count == 0); // Arena checks. - if (arena->reserved != page_bytes) { - const malunal_cstr_t format = - "[arena_allocator]: arena->reserved != %d, actual == %d"; - fprintf(stderr, format, page_bytes, arena->reserved); - return (allocator_acquire_res_t) { - .threw = 1, - .exception = ALLOCATOR_EXCEPTION_FAILURE - }; - } + assert_equals_result(allocator_reserved_bytes(arena, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals_result(count == page_bytes); - if (arena->allocated != init_bytes) { - const malunal_cstr_t format = - "[arena_allocator]: arena->allocated != %d, actual == %d"; - fprintf(stderr, format, init_bytes, arena->allocated); - return (allocator_acquire_res_t) { - .threw = 1, - .exception = ALLOCATOR_EXCEPTION_FAILURE - }; - } + assert_equals_result(allocator_allocated_bytes(arena, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals_result(count == init_with_pre); - if (arena->acquires != 1) { - const malunal_cstr_t format = - "[arena_allocator]: arena->acquires != 1, actual == %d"; - fprintf(stderr, format, arena->acquires); - return (allocator_acquire_res_t) { - .threw = 1, - .exception = ALLOCATOR_EXCEPTION_FAILURE - }; - } - - if (arena->releases != 0) { - const malunal_cstr_t format = - "[arena_allocator]: arena->releases != 0, actual == %d"; - fprintf(stderr, format, arena->releases); - return (allocator_acquire_res_t) { - .threw = 1, - .exception = ALLOCATOR_EXCEPTION_FAILURE - }; - } + assert_equals_result(allocator_acquires_count(arena, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals_result(count == 1); + assert_equals_result(allocator_releases_count(arena, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals_result(count == 0); return result; } @@ -114,77 +125,45 @@ test_arena_reacquire( for (index = 0; index < 4; index++) vector[index] = index + 1; - allocator_acquire_res_t result = - allocator_reacquire((allocator_reacquire_req_t) { - .allocator = arena, - .prev = result.address, - .oldsz = init_bytes, - .newsz = final_bytes - }); + allocation_result_t result = allocator_reacquire( + arena, + address, + init_bytes, + final_bytes + ); if (result.threw) - return result.exception; + return result.error; - // Upstream checks. - if (arena->upstream->reserved != page_bytes) { - const malunal_cstr_t format = - "[arena_allocator]: arena->upstream->reserved != %d, actual == %d"; - fprintf(stderr, format, page_bytes, arena->upstream->reserved); - return 1; - } + allocator_mptr_t upstream; + assert_equals(allocator_upstream(arena, &upstream) == ALLOCATION_ERROR_SUCCESS); - if (arena->upstream->allocated != page_bytes) { - const malunal_cstr_t format = - "[arena_allocator]: arena->upstream->allocated != %d, actual == %d"; - fprintf(stderr, format, page_bytes, arena->upstream->allocated); - return 1; - } + malunal_size_t count; + assert_equals(allocator_reserved_bytes(upstream, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == page_bytes); - if (arena->upstream->acquires != 1) { - const malunal_cstr_t format = - "[arena_allocator]: arena->upstream->acquires != 1, actual == %d"; - fprintf(stderr, format, arena->upstream->acquires); - return 1; - } + assert_equals(allocator_allocated_bytes(upstream, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == page_bytes); - if (arena->upstream->releases != 0) { - const malunal_cstr_t format = - "[arena_allocator]: arena->upstream->releases != 0, actual == %d"; - fprintf(stderr, format, arena->upstream->releases); - return 1; - } + assert_equals(allocator_acquires_count(upstream, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == 1); + + assert_equals(allocator_releases_count(upstream, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == 0); // Arena checks. - if (arena->reserved != page_bytes) { - const malunal_cstr_t format = - "[arena_allocator]: arena->reserved != %d, actual == %d"; - fprintf(stderr, format, page_bytes, arena->reserved); - return 1; - } + assert_equals(allocator_reserved_bytes(arena, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == page_bytes); - malunal_int32_t all_bytes = init_bytes + final_bytes; - if (arena->allocated != all_bytes) { - const malunal_cstr_t format = - "[arena_allocator]: arena->allocated != %d, actual == %d"; - fprintf(stderr, format, all_bytes, arena->allocated); - return 1; - } + assert_equals(allocator_allocated_bytes(arena, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == all_bytes); - if (arena->acquires != 2) { - const malunal_cstr_t format = - "[arena_allocator]: arena->acquires != 2, actual == %d"; - fprintf(stderr, format, arena->acquires); - return 1; - } + assert_equals(allocator_acquires_count(arena, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == 2); - if (arena->releases != 1) { - const malunal_cstr_t format = - "[arena_allocator]: arena->releases != 1, actual == %d"; - fprintf(stderr, format, arena->releases); - return 1; - } - - return ALLOCATOR_EXCEPTION_SUCCESS; + assert_equals(allocator_releases_count(arena, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == 1); + return ALLOCATION_ERROR_SUCCESS; } static int @@ -200,220 +179,126 @@ test_arena_release( if (vector[index] != index + 1) return 1; - allocator_exception_t relexc = - allocator_release((allocator_release_req_t) { - .allocator = arena, - .address = address, - .size = final_bytes - }); + allocation_error_t relexc = + allocator_release(arena, address, final_bytes); - if (relexc != ALLOCATOR_EXCEPTION_SUCCESS) + if (relexc != ALLOCATION_ERROR_SUCCESS) return relexc; - // Upstream checks. - if (arena->upstream->reserved != page_bytes) { - const malunal_cstr_t format = - "[arena_allocator]: arena->upstream->reserved != %d, actual == %d"; - fprintf(stderr, format, page_bytes, arena->upstream->reserved); - return 1; - } + allocator_mptr_t upstream; + assert_equals(allocator_upstream(arena, &upstream) == ALLOCATION_ERROR_SUCCESS); - if (arena->upstream->allocated != page_bytes) { - const malunal_cstr_t format = - "[arena_allocator]: arena->upstream->allocated != %d, actual == %d"; - fprintf(stderr, format, page_bytes, arena->upstream->allocated); - return 1; - } + malunal_size_t count; + assert_equals(allocator_reserved_bytes(upstream, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == page_bytes); - if (arena->upstream->acquires != 1) { - const malunal_cstr_t format = - "[arena_allocator]: arena->upstream->acquires != 1, actual == %d"; - fprintf(stderr, format, arena->upstream->acquires); - return 1; - } + assert_equals(allocator_allocated_bytes(upstream, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == page_bytes); - if (arena->upstream->releases != 0) { - const malunal_cstr_t format = - "[arena_allocator]: arena->upstream->releases != 0, actual == %d"; - fprintf(stderr, format, arena->upstream->releases); - return 1; - } + assert_equals(allocator_acquires_count(upstream, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == 1); + + assert_equals(allocator_releases_count(upstream, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == 0); // Arena checks. - if (arena->reserved != page_bytes) { - const malunal_cstr_t format = - "[arena_allocator]: arena->reserved != %d, actual == %d"; - fprintf(stderr, format, page_bytes, arena->reserved); - return 1; - } + assert_equals(allocator_reserved_bytes(arena, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == page_bytes); - malunal_int32_t all_bytes = init_bytes + final_bytes; - if (arena->allocated != all_bytes) { - const malunal_cstr_t format = - "[arena_allocator]: arena->allocated != %d, actual == %d"; - fprintf(stderr, format, all_bytes, arena->allocated); - return 1; - } + assert_equals(allocator_allocated_bytes(arena, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == all_bytes); - if (arena->acquires != 2) { - const malunal_cstr_t format = - "[arena_allocator]: arena->acquires != 2, actual == %d"; - fprintf(stderr, format, arena->acquires); - return 1; - } + assert_equals(allocator_acquires_count(arena, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == 2); - if (arena->releases != 2) { - const malunal_cstr_t format = - "[arena_allocator]: arena->releases != 2, actual == %d"; - fprintf(stderr, format, arena->releases); - return 1; - } - - return ALLOCATOR_EXCEPTION_SUCCESS; + assert_equals(allocator_releases_count(arena, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == 2); + return ALLOCATION_ERROR_SUCCESS; } static int test_arena_reset(allocator_mptr_t arena) { arena_reset(arena); - // Upstream checks. - if (arena->upstream->reserved != page_bytes) { - const malunal_cstr_t format = - "[arena_allocator]: arena->upstream->reserved != %d, actual == %d"; - fprintf(stderr, format, page_bytes, arena->upstream->reserved); - return 1; - } + allocator_mptr_t upstream; + assert_equals(allocator_upstream(arena, &upstream) == ALLOCATION_ERROR_SUCCESS); - if (arena->upstream->allocated != page_bytes) { - const malunal_cstr_t format = - "[arena_allocator]: arena->upstream->allocated != %d, actual == %d"; - fprintf(stderr, format, page_bytes, arena->upstream->allocated); - return 1; - } + malunal_size_t count; + assert_equals(allocator_reserved_bytes(upstream, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == page_bytes); - if (arena->upstream->acquires != 1) { - const malunal_cstr_t format = - "[arena_allocator]: arena->upstream->acquires != 1, actual == %d"; - fprintf(stderr, format, arena->upstream->acquires); - return 1; - } + assert_equals(allocator_allocated_bytes(upstream, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == page_bytes); - if (arena->upstream->releases != 0) { - const malunal_cstr_t format = - "[arena_allocator]: arena->upstream->releases != 0, actual == %d"; - fprintf(stderr, format, arena->upstream->releases); - return 1; - } + assert_equals(allocator_acquires_count(upstream, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == 1); + + assert_equals(allocator_releases_count(upstream, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == 0); // Arena checks. - if (arena->reserved != page_bytes) { - const malunal_cstr_t format = - "[arena_allocator]: arena->reserved != %d, actual == %d"; - fprintf(stderr, format, page_bytes, arena->reserved); - return 1; - } + assert_equals(allocator_reserved_bytes(arena, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == page_bytes); - if (arena->allocated != 0) { - const malunal_cstr_t format = - "[arena_allocator]: arena->allocated != 0, actual == %d"; - fprintf(stderr, format, arena->allocated); - return 1; - } + assert_equals(allocator_allocated_bytes(arena, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == 0); - if (arena->acquires != 2) { - const malunal_cstr_t format = - "[arena_allocator]: arena->acquires != 2, actual == %d"; - fprintf(stderr, format, arena->acquires); - return 1; - } + assert_equals(allocator_acquires_count(arena, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == 2); - if (arena->releases != 2) { - const malunal_cstr_t format = - "[arena_allocator]: arena->releases != 2, actual == %d"; - fprintf(stderr, format, arena->releases); - return 1; - } - - return ALLOCATOR_EXCEPTION_SUCCESS; + assert_equals(allocator_releases_count(arena, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == 2); + return ALLOCATION_ERROR_SUCCESS; } static int -test_arena_free(allocator_mptr_t arena) { - arena_free(arena); +test_arena_finalize(allocator_mptr_t arena) { + allocator_finalize(arena); - // Upstream checks. - if (arena->upstream->reserved != 0) { - const malunal_cstr_t format = - "[arena_allocator]: arena->upstream->reserved != 0, actual == %d"; - fprintf(stderr, format, arena->upstream->reserved); - return 1; - } + allocator_mptr_t upstream; + assert_equals(allocator_upstream(arena, &upstream) == ALLOCATION_ERROR_SUCCESS); - if (arena->upstream->allocated != 0) { - const malunal_cstr_t format = - "[arena_allocator]: arena->upstream->allocated != %d, actual == %d"; - fprintf(stderr, format, arena->upstream->allocated); - return 1; - } + malunal_size_t count; + assert_equals(allocator_reserved_bytes(upstream, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == 0); - if (arena->upstream->acquires != 1) { - const malunal_cstr_t format = - "[arena_allocator]: arena->upstream->acquires != 1, actual == %d"; - fprintf(stderr, format, arena->upstream->acquires); - return 1; - } + assert_equals(allocator_allocated_bytes(upstream, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == 0); - if (arena->upstream->releases != 1) { - const malunal_cstr_t format = - "[arena_allocator]: arena->upstream->releases != 1, actual == %d"; - fprintf(stderr, format, arena->upstream->releases); - return 1; - } + assert_equals(allocator_acquires_count(upstream, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == 1); + + assert_equals(allocator_releases_count(upstream, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == 1); // Arena checks. - if (arena->reserved != 0) { - const malunal_cstr_t format = - "[arena_allocator]: arena->reserved != %d, actual == %d"; - fprintf(stderr, format, arena->reserved); - return 1; - } + assert_equals(allocator_reserved_bytes(arena, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == 0); - if (arena->allocated != 0) { - const malunal_cstr_t format = - "[arena_allocator]: arena->allocated != 0, actual == %d"; - fprintf(stderr, format, arena->allocated); - return 1; - } + assert_equals(allocator_allocated_bytes(arena, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == 0); - if (arena->acquires != 2) { - const malunal_cstr_t format = - "[arena_allocator]: arena->acquires != 2, actual == %d"; - fprintf(stderr, format, arena->acquires); - return 1; - } + assert_equals(allocator_acquires_count(arena, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == 2); - if (arena->releases != 2) { - const malunal_cstr_t format = - "[arena_allocator]: arena->releases != 2, actual == %d"; - fprintf(stderr, format, arena->releases); - return 1; - } - - return ALLOCATOR_EXCEPTION_SUCCESS; + assert_equals(allocator_releases_count(arena, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == 2); + return ALLOCATION_ERROR_SUCCESS; } int test_arena_allocator() { allocator_t upstream = platform_allocator(); - allocator_t arena = arena_allocator(&upstream, page_bytes); + allocator_t arena = arena_allocator(); + assert_equals(test_arena_initialize(&arena, &upstream) == ALLOCATION_ERROR_SUCCESS); - allocator_acquire_res_t result = + allocation_result_t result = test_arena_acquires(&arena); return !( !result.threw && - test_arena_reacquire(result.address, &arena) && - test_arena_release(result.address, &arena) && + test_arena_reacquire(&arena, result.address) && + test_arena_release(&arena, result.address) && test_arena_reset(&arena) && - test_arena_free(&arena) + test_arena_finalize(&arena) ); } diff --git a/tests/libc_allocator.c b/tests/libc_allocator.c index 4293fd1..dcc8cc3 100644 --- a/tests/libc_allocator.c +++ b/tests/libc_allocator.c @@ -1,49 +1,43 @@ #include #include "malunal/allocators.h" +#define assert_equals(condition) \ + do { \ + if ((condition)) break; \ + const malunal_cstr_t format = \ + "[libc_allocator(%s:%d)]: " \ + MALUNAL_TOSTRING(condition) \ + " failed\n"; \ + fprintf(stderr, format, __FILE__, __LINE__); \ + return 0; \ + } while (0) + int test_libc_allocator() { const malunal_size_t init_bytes = sizeof(malunal_int32_t) * 4; const malunal_size_t final_bytes = sizeof(malunal_int32_t) * 8; - allocator_acquire_res_t result; - allocator_t allocator = libc_allocator(); + allocation_result_t result; - result = allocator_acquire((allocator_acquire_req_t) { - .allocator = &allocator, - .size = init_bytes - }); + allocator_t libc_alloc = libc_allocator(); + allocator_mptr_t allocator = &libc_alloc; + result = allocator_acquire(allocator, init_bytes); if (result.threw) - return result.exception; + return result.error; - if (allocator.reserved != init_bytes) { - const malunal_cstr_t format = - "[libc_allocator]: allocator.reserved != %d, actual == %d"; - fprintf(stderr, format, init_bytes, allocator.reserved); - return 1; - } + malunal_size_t count; + assert_equals(allocator_reserved_bytes(allocator, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == init_bytes); - if (allocator.allocated != init_bytes) { - const malunal_cstr_t format = - "[libc_allocator]: allocator.allocated != %d, actual == %d"; - fprintf(stderr, format, init_bytes, allocator.allocated); - return 1; - } + assert_equals(allocator_allocated_bytes(allocator, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == init_bytes); - if (allocator.acquires != 1) { - const malunal_cstr_t format = - "[libc_allocator]: allocator.acquires != 1, actual == %d"; - fprintf(stderr, format, allocator.acquires); - return 1; - } + assert_equals(allocator_acquires_count(allocator, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == 1); - if (allocator.releases != 0) { - const malunal_cstr_t format = - "[libc_allocator]: allocator.releases != 0, actual == %d"; - fprintf(stderr, format, allocator.releases); - return 1; - } + assert_equals(allocator_releases_count(allocator, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == 0); malunal_int32_t index; malunal_int32_t* vector; @@ -52,83 +46,50 @@ int test_libc_allocator() { for (index = 0; index < 4; index++) vector[index] = index + 1; - result = allocator_reacquire((allocator_reacquire_req_t) { - .allocator = &allocator, - .prev = result.address, - .oldsz = init_bytes, - .newsz = final_bytes - }); + result = allocator_reacquire( + allocator, + result.address, + init_bytes, + final_bytes + ); if (result.threw) - return result.exception; + return result.error; - if (allocator.reserved != final_bytes) { - const malunal_cstr_t format = - "[libc_allocator]: allocator.reserved != %d, actual == %d"; - fprintf(stderr, format, final_bytes, allocator.reserved); - return 1; - } + assert_equals(allocator_reserved_bytes(allocator, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == final_bytes); - if (allocator.allocated != final_bytes) { - const malunal_cstr_t format = - "[libc_allocator]: allocator.allocator != %d, actual == %d"; - fprintf(stderr, format, final_bytes, allocator.reserved); - return 1; - } + assert_equals(allocator_allocated_bytes(allocator, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == final_bytes); - if (allocator.acquires != 2) { - const malunal_cstr_t format = - "[libc_allocator]: allocator.acquires != 2, actual == %d"; - fprintf(stderr, format, allocator.acquires); - return 1; - } + assert_equals(allocator_acquires_count(allocator, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == 2); - if (allocator.releases != 1) { - const malunal_cstr_t format = - "[libc_allocator]: allocator.releases != 1, actual == %d"; - fprintf(stderr, format, allocator.releases); - return 1; - } + assert_equals(allocator_releases_count(allocator, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == 1); vector = result.address; for (index = 0; index < 4; index++) if (vector[index] != index + 1) return 1; - allocator_exception_t relexc = - allocator_release((allocator_release_req_t) { - .allocator = &allocator, - .address = result.address, - .size = final_bytes - }); + allocation_error_t relexc = allocator_release( + allocator, + result.address, + final_bytes + ); - if (allocator.reserved != 0) { - const malunal_cstr_t format = - "[libc_allocator]: allocator.reserved != 0, actual == %d"; - fprintf(stderr, format, allocator.reserved); - return 1; - } + assert_equals(allocator_reserved_bytes(allocator, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == 0); - if (allocator.allocated != 0) { - const malunal_cstr_t format = - "[libc_allocator]: allocator.allocator != 0, actual == %d"; - fprintf(stderr, format, allocator.reserved); - return 1; - } + assert_equals(allocator_allocated_bytes(allocator, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == 0); - if (allocator.acquires != 2) { - const malunal_cstr_t format = - "[libc_allocator]: allocator.acquires != 2, actual == %d"; - fprintf(stderr, format, allocator.acquires); - return 1; - } + assert_equals(allocator_acquires_count(allocator, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == 2); - if (allocator.releases != 2) { - const malunal_cstr_t format = - "[libc_allocator]: allocator.releases != 2, actual == %d"; - fprintf(stderr, format, allocator.releases); - return 1; - } + assert_equals(allocator_releases_count(allocator, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == 2); - return relexc == ALLOCATOR_EXCEPTION_SUCCESS; + return relexc == ALLOCATION_ERROR_SUCCESS; } diff --git a/tests/platform_allocator.c b/tests/platform_allocator.c index 8d1a041..4b3daff 100644 --- a/tests/platform_allocator.c +++ b/tests/platform_allocator.c @@ -1,49 +1,43 @@ #include #include "malunal/allocators.h" +#define assert_equals(condition) \ + do { \ + if ((condition)) break; \ + const malunal_cstr_t format = \ + "[platform_allocator(%s:%d)]: " \ + MALUNAL_TOSTRING(condition) \ + " failed\n"; \ + fprintf(stderr, format, __FILE__, __LINE__); \ + return 0; \ + } while (0) + int test_platform_allocator() { const malunal_size_t init_bytes = sizeof(malunal_int32_t) * 4; const malunal_size_t final_bytes = sizeof(malunal_int32_t) * 8; - allocator_acquire_res_t result; - allocator_t allocator = platform_allocator(); + allocation_result_t result; - result = allocator_acquire((allocator_acquire_req_t) { - .allocator = &allocator, - .size = init_bytes - }); + allocator_t plat_alloc = platform_allocator(); + allocator_mptr_t allocator = &plat_alloc; + result = allocator_acquire(allocator, init_bytes); if (result.threw) - return result.exception; + return result.error; - if (allocator.reserved != init_bytes) { - const malunal_cstr_t format = - "[platform_allocator]: allocator.reserved != %d, actual == %d"; - fprintf(stderr, format, init_bytes, allocator.reserved); - return 1; - } + malunal_size_t count; + assert_equals(allocator_reserved_bytes(allocator, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == init_bytes); - if (allocator.allocated != init_bytes) { - const malunal_cstr_t format = - "[platform_allocator]: allocator.allocated != %d, actual == %d"; - fprintf(stderr, format, init_bytes, allocator.allocated); - return 1; - } + assert_equals(allocator_allocated_bytes(allocator, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == init_bytes); - if (allocator.acquires != 1) { - const malunal_cstr_t format = - "[platform_allocator]: allocator.acquires != 1, actual == %d"; - fprintf(stderr, format, allocator.acquires); - return 1; - } + assert_equals(allocator_acquires_count(allocator, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == 1); - if (allocator.releases != 0) { - const malunal_cstr_t format = - "[platform_allocator]: allocator.releases != 0, actual == %d"; - fprintf(stderr, format, allocator.releases); - return 1; - } + assert_equals(allocator_releases_count(allocator, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == 0); malunal_int32_t index; malunal_int32_t* vector; @@ -52,83 +46,50 @@ int test_platform_allocator() { for (index = 0; index < 4; index++) vector[index] = index + 1; - result = allocator_reacquire((allocator_reacquire_req_t) { - .allocator = &allocator, - .prev = result.address, - .oldsz = init_bytes, - .newsz = final_bytes - }); + result = allocator_reacquire( + allocator, + result.address, + init_bytes, + final_bytes + ); if (result.threw) - return result.exception; + return result.error; - if (allocator.reserved != final_bytes) { - const malunal_cstr_t format = - "[platform_allocator]: allocator.reserved != %d, actual == %d"; - fprintf(stderr, format, final_bytes, allocator.reserved); - return 1; - } + assert_equals(allocator_reserved_bytes(allocator, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == final_bytes); - if (allocator.allocated != final_bytes) { - const malunal_cstr_t format = - "[platform_allocator]: allocator.allocator != %d, actual == %d"; - fprintf(stderr, format, final_bytes, allocator.reserved); - return 1; - } + assert_equals(allocator_allocated_bytes(allocator, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == final_bytes); - if (allocator.acquires != 2) { - const malunal_cstr_t format = - "[platform_allocator]: allocator.acquires != 2, actual == %d"; - fprintf(stderr, format, allocator.acquires); - return 1; - } + assert_equals(allocator_acquires_count(allocator, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == 2); - if (allocator.releases != 1) { - const malunal_cstr_t format = - "[platform_allocator]: allocator.releases != 1, actual == %d"; - fprintf(stderr, format, allocator.releases); - return 1; - } + assert_equals(allocator_releases_count(allocator, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == 1); vector = result.address; for (index = 0; index < 4; index++) if (vector[index] != index + 1) return 1; - allocator_exception_t relexc = - allocator_release((allocator_release_req_t) { - .allocator = &allocator, - .address = result.address, - .size = final_bytes - }); + allocation_error_t relexc = allocator_release( + allocator, + result.address, + final_bytes + ); - if (allocator.reserved != 0) { - const malunal_cstr_t format = - "[platform_allocator]: allocator.reserved != 0, actual == %d"; - fprintf(stderr, format, allocator.reserved); - return 1; - } + assert_equals(allocator_reserved_bytes(allocator, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == 0); - if (allocator.allocated != 0) { - const malunal_cstr_t format = - "[platform_allocator]: allocator.allocator != 0, actual == %d"; - fprintf(stderr, format, allocator.reserved); - return 1; - } + assert_equals(allocator_allocated_bytes(allocator, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == 0); - if (allocator.acquires != 2) { - const malunal_cstr_t format = - "[platform_allocator]: allocator.acquires != 2, actual == %d"; - fprintf(stderr, format, allocator.acquires); - return 1; - } + assert_equals(allocator_acquires_count(allocator, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == 2); - if (allocator.releases != 2) { - const malunal_cstr_t format = - "[platform_allocator]: allocator.releases != 2, actual == %d"; - fprintf(stderr, format, allocator.releases); - return 1; - } + assert_equals(allocator_releases_count(allocator, &count) == ALLOCATION_ERROR_SUCCESS); + assert_equals(count == 2); - return relexc == ALLOCATOR_EXCEPTION_SUCCESS; + return relexc == ALLOCATION_ERROR_SUCCESS; }