From c1c603a250d5f30edbfcd0b4b8d7058b64aa1e43 Mon Sep 17 00:00:00 2001 From: SoraKatadzuma Date: Sat, 22 Aug 2026 02:13:04 -0500 Subject: [PATCH] New allocators, testing framework, overall improvements --- chinookfile | 15 +- include/malunal/allocator.h | 286 ++++++++++++++++++++ include/malunal/allocators.h | 396 --------------------------- include/malunal/allocators/arena.h | 127 ++++++++- include/malunal/allocators/linear.h | 118 ++++++++ include/malunal/allocators/pool.h | 129 +++++++++ include/malunal/allocators/stack.h | 128 +++++++++ sources/allocator.c | 163 ++++-------- sources/arena.c | 400 +++++++++++++++------------- sources/internal.h | 75 ------ sources/libc.c | 124 +++------ sources/linear.c | 139 ++++++++++ sources/linux.c | 116 ++------ sources/pool.c | 248 +++++++++++++++++ sources/stack.c | 215 +++++++++++++++ sources/win32.c | 114 ++------ tests/allocator.c | 11 - tests/allocators.c | 2 + tests/arena_allocator.c | 340 ++++------------------- tests/libc_allocator.c | 102 +------ tests/linear_allocator.c | 47 ++++ tests/platform_allocator.c | 102 +------ tests/pool_allocator.c | 61 +++++ tests/stack_allocator.c | 61 +++++ 24 files changed, 2004 insertions(+), 1515 deletions(-) create mode 100644 include/malunal/allocator.h delete mode 100644 include/malunal/allocators.h create mode 100644 include/malunal/allocators/linear.h create mode 100644 include/malunal/allocators/pool.h create mode 100644 include/malunal/allocators/stack.h delete mode 100644 sources/internal.h create mode 100644 sources/linear.c create mode 100644 sources/pool.c create mode 100644 sources/stack.c delete mode 100644 tests/allocator.c create mode 100644 tests/allocators.c create mode 100644 tests/linear_allocator.c create mode 100644 tests/pool_allocator.c create mode 100644 tests/stack_allocator.c diff --git a/chinookfile b/chinookfile index c20f547..315f447 100644 --- a/chinookfile +++ b/chinookfile @@ -5,6 +5,8 @@ semv: 1.0.0 requires: - remote: git@git.erasit.com:malunal/config branch: v1.0.2 +- remote: git@git.erasit.com:malunal/microtest + branch: v1.0.0 - remote: git@git.erasit.com:malunal/types branch: v1.0.0 @@ -15,22 +17,29 @@ targets: - malunal.config - malunal.types srcs: - - ./sources/allocator.c - ./sources/libc.c - ./sources/linux.c - ./sources/win32.c + - ./sources/linear.c - ./sources/arena.c + - ./sources/stack.c + - ./sources/pool.c + - ./sources/allocator.c tests: -- name: allocator_tests +- name: malunal.allocators.allinone type: program deps: - malunal.allocators + - malunal.microtest srcs: - ./tests/libc_allocator.c - ./tests/platform_allocator.c + - ./tests/linear_allocator.c - ./tests/arena_allocator.c - - ./tests/allocator.c + - ./tests/stack_allocator.c + - ./tests/pool_allocator.c + - ./tests/allocators.c exports: - malunal.allocators diff --git a/include/malunal/allocator.h b/include/malunal/allocator.h new file mode 100644 index 0000000..0ef4a65 --- /dev/null +++ b/include/malunal/allocator.h @@ -0,0 +1,286 @@ +/** + * @file allocators.h + * @brief This header contains the definition of the memory functions and basic + * allocator object. + * @author John Christman (sorakatadzuma@gmail.com) + * @copyright Malunal Studios, LLC. + */ +#include "malunal/config.h" +#include "malunal/types/error.h" +#include "malunal/types/uuid.h" + +#ifndef MALUNAL_ALLOCATOR_HEADER +#define MALUNAL_ALLOCATOR_HEADER + +/** + * @brief Imports an external constant for the UUID of @c allocator_t. + * @details This is needed to properly be able to cast @c allocator_t compliant + * types to an @c allocator_t. + */ +extern +const uuid_t +UUID_ALLOCATOR_T; + +/** + * @brief Imports an external constant for the error domain of @c allocator_t. + * @details This is needed to detect from what domain an error has come from, + * so that error codes can mean different things based on its domain. + */ +extern +const error_domain_t +ERROR_DOMAIN_ALLOCATOR_T; + +/** + * @brief A pointer to a function which will attempt to acquire some memory + * address from the allocator instance, with the size of the buffer + * equal to @c size and the address placed into the location pointed + * to by @c out. + * @param allocator A pointer to the allocator to acquire memory from. + * @param size The size of the buffer to acquire from the allocator. + * @param out A pointer to the location where the buffer pointer + * should be stored if the buffer could be acquired. + * @returns An error code if the allocator could not acquire the memory. + */ +typedef error_t +(*allocator_acquire_pfn_t)( + malunal_mptr_t allocator, + malunal_size_t size, + malunal_mptr_t* out +); + +/** + * @brief A pointer to a function which will attempt to dispose some memory + * address back to the allocator instance, given the @c size of the + * memory buffer at @c address. + * @param allocator A pointer to the allocator to dispose memory to. + * @param address A pointer to the memory to dispose to the allocator. + * @param size The size of the memory buffer to dispose. + * @returns An error code if the allocator could not dispose the memory. + */ +typedef error_t +(*allocator_dispose_pfn_t)( + malunal_mptr_t allocator, + malunal_mptr_t address, + malunal_size_t size +); + +/** + * @brief Defines the allocator virtual function table. + * @details Implementing classes of the allocator interface are expected to + * provide valid pointers to these functions that are implementation + * specific. + */ +typedef struct { + /** + * @brief A pointer to a function which will attempt to acquire some memory + * address from the allocator instance, with the size of the buffer + * equal to @c size and the address placed into the location pointed + * to by @c out. + */ + allocator_acquire_pfn_t const acquire; + + /** + * @brief A pointer to a function which will attempt to dispose some memory + * address back to the allocator instance, given the @c size of the + * memory buffer at @c address. + */ + allocator_dispose_pfn_t const dispose; +} allocator_vtable_t; + + +/** + * @brief Defines the abstract allocator object. + * @details Since this is only an interface, it only contains a pointer to the + * virtual table for itself. Other objects which implement this, may + * come with member variables. + */ +typedef struct { + /** + * @brief A pointer to an immutable allocator virtual function table. + * @details Contains the function pointers to the allocator specific functions + * that make this allocator function as one. + */ + const allocator_vtable_t* vtable; +} allocator_t; + +/** + * @brief A pointer to a mutable allocator. + * @details This is provided to simplify type declarations for functions + * requiring allocators that are meant to be mutable. + */ +typedef allocator_t* allocator_mptr_t; + +/** + * @brief A pointer to a immutable allocator view. + * @details This is provided to simplify type declarations for functions + * requiring allocators that are meant to be immutable. + */ +typedef const allocator_t* allocator_iptr_t; + + +/** + * @brief Defines a set of errors that an allocator may throw. + * @details These are extremely useful for debugging an allocator or catching + * runtime issues that can be fixed. + */ +typedef enum { + /** + * @brief The generic allocator error. + * @details If the allocator does not know what to throw, or in test cases, + * this may be thrown. + */ + ALLOCATOR_ERROR_FAILURE, + + /** + * @brief The allocator instance was null. + * @details Allocator functions take it by reference because it is a rather + * large object. So, when the reference is expected to be non-null, + * which is always, this will be thrown. + */ + ALLOCATOR_ERROR_NULL_ALLOCATOR, + + /** + * @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_ERROR_OUT_OF_MEMORY, + + /** + * @brief The address being released does not belong to the allocator. + * @details Allocators expect any given address to have been allocated by + * itself. Most allocators can check if it was allocated from its + * memory pool, and if it wasn't this will be thrown. + */ + ALLOCATOR_ERROR_NOT_MY_ADDRESS, + + /** + * @brief The allocator has determined that memory is being leaked. + * @details Given the diagnostics within an allocator, the allocator may find + * some memory is still in use upon release or finalization. If this + * happens, the allocator may throw this error. + */ + ALLOCATOR_ERROR_LEAKY_MEMORY, +} allocator_error_t; + + +/** + * @brief Attempts to acquire some memory buffer from the provided allocator. + * @details This will perform some checks before trying to call the virtual + * function table implementation provided by the allocator. If all + * pre-conditions pass, then the virtual function will be called and + * its result passed back to the caller. + * @param allocator A pointer to the allocator to call @c acquire on. + * @param size The size of the memory buffer to acquire. + * @param out The location where to store the acquired buffer. + * @returns An error if the allocator could not acquire a memory buffer for the + * function caller. + */ +error_t +allocator_acquire( + allocator_mptr_t allocator, + malunal_size_t size, + malunal_mptr_t* out +); + +/** + * @brief Attempts to dispose some memory buffer into the provided allocator. + * @details This will perform some checks before trying to call the virtual + * function table implementation provided by the allocator. If all + * pre-conditions pass, then the virtual function will be called and + * its result passed back to the caller. + * @param allocator A pointer to the allocator to call @c dispose on. + * @param address A poitner to the memory buffer to dispose. + * @param size The size of the memory buffer to dispose. + * @returns An error if the allocator could not dispose a memory buffer for the + * function caller. + */ +error_t +allocator_dispose( + 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. + * @param size The current size to round up to the nearest multiple of + * the provided @c alignment. + * @param alignment The alignment to round the size to. + * @returns The rounded size. + */ +malunal_size_t +align_to( + malunal_size_t size, + malunal_size_t alignment +); + +/** + * @brief Rounds the given size up to the nearest page. + * @details This will give you a size that is rounded up to the next multiple of + * the platform page size. For example, if you provide 4000 and the + * platform page size is 4096, then you will get 4096. Likewise, if you + * provided 5000 and the platform page size is 4096, then you will get + * 8192. + * @param size The current size that will be rounded up to the nearest page + * multiple for the platform. + * @returns The rounded page size. + */ +malunal_size_t +align_to_page(malunal_size_t size); + +/** + * @brief Provides the platform page size. + * @details This can be really useful when you plan to allocate a lot of memory. + * Platforms typically round memory requests up to the nearest page and + * give the base address of the virtually allocated block back, but + * placing a guard on the portion of the memory that will not be used. + * By querying the page size for the platform and rounding a memory + * acquisition request up to the nearest page, you can get the full + * block the platform intends to hand back which, significantly helps + * in proper memory tracking. + * @returns The memory page size for the platform. + */ +malunal_size_t +platform_page_size(); + +/** + * @brief Provides the general libc allocator. + * @details This will provide a general purpose allocator backed by the standard + * library. The functions provided are equivalent to @c malloc, + * @c realloc, and @c free. + * @returns An allocator that interfaces with libc. + */ +allocator_mptr_t +libc_allocator(); + +/** + * @brief Provides the platform allocator. + * @details This will provide the appropriate platform allocator depending on + * what operating system this library was compiled for. + * @returns One of: @c linux_allocator or @c win32_allocator. + */ +allocator_mptr_t +platform_allocator(); + +#if MALUNAL_PLATFORM_LINUX +/** + * @brief Provides a linux specific allocator. + * @details This is the linux specific allocator. It uses @c mmap and @c munmap + * to acquire, reacquire, and release memory. + * @returns An allocator that works specifically with linux. + */ +allocator_mptr_t +linux_allocator(); +#elif MALUNAL_PLATFORM_WIN32 +/** + * @brief Provides a windows specific allocator. + * @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. + */ +allocator_mptr_t +win32_allocator(); +#endif /* Platform specific allocators */ + +#endif /* MALUNAL_ALLOCATOR_HEADER */ diff --git a/include/malunal/allocators.h b/include/malunal/allocators.h deleted file mode 100644 index c277836..0000000 --- a/include/malunal/allocators.h +++ /dev/null @@ -1,396 +0,0 @@ -/** - * @file allocators.h - * @brief This header contains the definition of the memory functions and basic - * allocator object. - * @author John Christman (sorakatadzuma@gmail.com) - * @copyright Malunal Studios, LLC. - */ -#include "malunal/config.h" -#include "malunal/types.h" - -#ifndef MALUNAL_ALLOCATORS_HEADER -#define MALUNAL_ALLOCATORS_HEADER - -// Assuring that NULL_ADDRESS is correct. -#ifdef NULL_ADDRESS -#undef NULL_ADDRESS -#endif /* NULL_ADDRESS */ - -/** - * @def NULL_ADDRESS - * @brief Defines a null address. - * @details This is needed for functions to indicate that an address could not - * acquired. - */ -#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 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 { - malunal_uint8_t __opaque[ALLOCATOR_SIZE]; -} allocator_t; - -/** - * @brief A pointer to a mutable allocator. - * @details This is provided to simplify type declarations for functions - * requiring allocators that are meant to be mutable. - */ -typedef allocator_t* allocator_mptr_t; - -/** - * @brief A pointer to a immutable allocator view. - * @details This is provided to simplify type declarations for functions - * requiring allocators that are meant to be immutable. - */ -typedef const allocator_t* allocator_iptr_t; - - -/** - * @brief Defines a set of exception that an allocator may throw. - * @details These are extremely useful for debugging an allocator or catching - * runtime issues that can be fixed. - */ -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. - */ - 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. - */ - ALLOCATION_ERROR_FAILURE, - - /** - * @brief The allocator instance was null. - * @details Allocator functions take it by reference because it is a rather - * large object. So, when the reference is expected to be non-null, - * which is always, this will be thrown. - */ - ALLOCATION_ERROR_NULL_ALLOCATOR, - - /** - * @brief The allocator upstream was null. - * @details Allocator implementations may expect for the upstream parameter - * to pointer to a valid allocator. For those allocators that expect - * an upstream, and do not receive it, this will be thrown. - */ - ALLOCATION_ERROR_NULL_UPSTREAM, - - /** - * @brief The allocator context was null. - * @details Allocator implementations often expect for the context parameter - * to point to a valid address. For those allocators that expect a - * context, and do not receive it, this will be thrown. - */ - 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. - */ - ALLOCATION_ERROR_OUT_OF_MEMORY, - - /** - * @brief The address being released does not belong to the allocator. - * @details Allocators expect any given address to have been allocated by - * itself. Most allocators can check if it was allocated from its - * memory pool, and if it wasn't this will be thrown. - */ - ALLOCATION_ERROR_NOT_MY_ADDRESS, - - /** - * @brief The allocator has determined that memory is being leaked. - * @details Given the diagnostics within an allocator, the allocator may find - * some memory is still in use upon release or finalization. If this - * happens, the allocator may throw this exception. - */ - ALLOCATION_ERROR_LEAKY_MEMORY, -} allocation_error_t; - -/** - * @brief Represents the result of an acquisition request. - * @details This has two parts to it: a fulfilled acquisition and an possible - * exception. Which part is put into the result is indicated by the - * @c threw tag. - */ -typedef struct { - /** - * @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; - - union { - /** - * @brief The success of fulfilling the acquisition request. - * @details If the allocator sucessfully fulfilled the request, this will - * contain the pointer of the allocation. - */ - malunal_mptr_t address; - - /** - * @brief The failure of fulfilling the acquisition request. - * @details If the allocator unsuccessfully fulfilled the request, this will - * contain the reason for failure. - */ - allocation_error_t error; - }; -} allocation_result_t; - -/** - * @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. - */ -allocation_error_t -allocator_upstream( - allocator_mptr_t allocator, - allocator_mptr_t* out -); - -/** - * @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. - */ -allocation_error_t -allocator_allocated_bytes( - allocator_mptr_t allocator, - malunal_size_t* out -); - -/** - * @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. - */ -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 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. - */ -allocation_error_t -allocator_releases_count( - allocator_mptr_t allocator, - malunal_size_t* out -); - -/** - * @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. - * @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. - */ -allocation_result_t -allocator_acquire( - allocator_mptr_t allocator, - malunal_size_t size -); - -/** - * @brief Executes a reacquisition request on an 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. - */ -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. - * @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. - */ -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. - * @param size The current size to round up to the nearest multiple of - * the provided @c alignment. - * @param alignment The alignment to round the size to. - * @returns The rounded size. - */ -malunal_size_t -align_to( - malunal_size_t size, - malunal_size_t alignment -); - -/** - * @brief Rounds the given size up to the nearest page. - * @details This will give you a size that is rounded up to the next multiple of - * the platform page size. For example, if you provide 4000 and the - * platform page size is 4096, then you will get 4096. Likewise, if you - * provided 5000 and the platform page size is 4096, then you will get - * 8192. - * @param size The current size that will be rounded up to the nearest page - * multiple for the platform. - * @returns The rounded page size. - */ -malunal_size_t -align_to_page(malunal_size_t size); - -/** - * @brief Provides the platform page size. - * @details This can be really useful when you plan to allocate a lot of memory. - * Platforms typically round memory requests up to the nearest page and - * give the base address of the virtually allocated block back, but - * placing a guard on the portion of the memory that will not be used. - * By querying the page size for the platform and rounding a memory - * acquisition request up to the nearest page, you can get the full - * block the platform intends to hand back which, significantly helps - * in proper memory tracking. - * @returns The memory page size for the platform. - */ -malunal_size_t -platform_page_size(); - -/** - * @brief Provides the general libc allocator. - * @details This will provide a general purpose allocator backed by the standard - * library. The functions provided are equivalent to @c malloc, - * @c realloc, and @c free. - * @returns An allocator that interfaces with libc. - */ -allocator_t -libc_allocator(); - -/** - * @brief Provides the platform allocator. - * @details This will provide the appropriate platform allocator depending on - * what operating system this library was compiled for. - * @returns One of: @c linux_allocator or @c win32_allocator. - */ -allocator_t -platform_allocator(); - -#if MALUNAL_PLATFORM_LINUX -/** - * @brief Provides a linux specific allocator. - * @details This is the linux specific allocator. It uses @c mmap and @c munmap - * to acquire, reacquire, and release memory. - * @returns An allocator that works specifically with linux. - */ -allocator_t -linux_allocator(); -#elif MALUNAL_PLATFORM_WIN32 -/** - * @brief Provides a windows specific allocator. - * @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. - */ -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 121a2e5..8fc0e9f 100644 --- a/include/malunal/allocators/arena.h +++ b/include/malunal/allocators/arena.h @@ -5,20 +5,137 @@ * @author John Christman (sorakatadzuma@gmail.com) * @copyright Malunal Studios, LLC. */ -#include "../allocators.h" +#include "../allocator.h" #ifndef MALUNAL_ALLOCATORS_ARENA_HEADER #define MALUNAL_ALLOCATORS_ARENA_HEADER +/** + * @def MALUNAL_ARENA_REGION_SIZE + * @brief The size of the arena regions. + * @details This is to support variable size regions, allowing larger or smaller + * objects to be allocated into the regions. Under the hood these will + * be virtually mapped by the operating system, so a very large set of + * data will be split across physical pages. + */ +#ifndef MALUNAL_ARENA_REGION_SIZE +#define MALUNAL_ARENA_REGION_SIZE 4096 +#endif /* MALUNAL_ARENA_REGION_SIZE */ + + +/** + * @brief Defines a concrete arena allocator. + * @details An arena allocator is a type of large push allocator. It simply + * obtains large regions of memory, rounded up to the next operating + * system page size, and pushes data onto those pages without any + * understanding of how to dispose that data later. + */ +typedef struct { + malunal_size_t __opaque[2]; +} arena_allocator_t; + +/** + * @brief A pointer to a mutable arena allocator. + * @details This is provided to simplify type declarations for functions + * requiring arena allocators that are meant to be mutable. + */ +typedef arena_allocator_t* arena_allocator_mptr_t; + +/** + * @brief A pointer to an immutable arena allocator. + * @details This is provided to simplify type declarations for functions + * requiring arena allocators that are meant to be immutable. + */ +typedef const arena_allocator_t* arena_allocator_iptr_t; + +/** + * @brief Obtains the size of the arena allocator. + * @param allocator The arena allocator to obtain the size from. + * @param out A pointer to where the obtained size will be stored. + * @returns An error if the arena allocator could not obtain the size. + */ +error_t +arena_allocator_size( + arena_allocator_iptr_t allocator, + malunal_uint32_t* out +); + +/** + * @brief Obtains the used count of the arena allocator. + * @param allocator The arena allocator to obtain the used count from. + * @param out A pointer to where the obtained used count will be stored. + * @returns An error if the arena allocator could not obtain the used. + */ +error_t +arena_allocator_used( + arena_allocator_iptr_t allocator, + malunal_uint32_t* out +); + +/** + * @brief Creates an arena instance with the given starting @c capacity, + * placing the result into the @c out variable. + * @param capacity The size of the allocator to start with. + * @param allocator The arena allocator to initialize. + * @returns An error if the arena allocator could not be initialized. + */ +error_t +arena_allocator_init( + malunal_size_t capacity, + arena_allocator_mptr_t allocator +); + +/** + * @brief Acquires some buffer from the arena allocator provided, given the + * @c size and the @c out location to place the buffer. + * @param allocator The arena allocator to acquire from. + * @param size The size of the bufffer to acquire. + * @param out Where to store the buffer. + * @returns An error if the arena allocator could not acquire the buffer. + */ +error_t +arena_allocator_acquire( + arena_allocator_mptr_t allocator, + malunal_size_t size, + malunal_mptr_t* out +); + +/** + * @brief Disposes some buffer into the arena allocator provided, given the + * @c address to dispose and the @c size of the buffer. + * @param allocator The arena allocator to dispose into. + * @param address The address of the buffer to dispose. + * @param size The size of the buffer to dispose. + * @returns An error if the arena allocator could not dispose the buffer. + */ +error_t +arena_allocator_dispose( + arena_allocator_mptr_t allocator, + malunal_mptr_t address, + malunal_size_t size +); + /** * @brief Resets the entire arena without freeing its pages. * @details This will iterate through each of the pages acquired for the arena * setting the usage of each page to zero. This will allow the arena to * overwrite the data within the pages. - * @param arena The arena to reset. - * @returns An exception if the arena could not be reset. + * @param allocator A pointer to the arena allocator to reset. + * @returns An error if the arena allocator could not be reset. */ -allocation_error_t -arena_reset(allocator_mptr_t arena); +error_t +arena_allocator_reset( + arena_allocator_mptr_t allocator +); + +/** + * @brief Frees the memory held by the arena allocator. + * @param allocator A pointer to the arena allocator to free. + * @returns An error if the arena allocator could not be freed. + */ +error_t +arena_allocator_free( + arena_allocator_mptr_t allocator +); #endif /* MALUNAL_ALLOCATORS_ARENA_HEADER */ diff --git a/include/malunal/allocators/linear.h b/include/malunal/allocators/linear.h new file mode 100644 index 0000000..0f41191 --- /dev/null +++ b/include/malunal/allocators/linear.h @@ -0,0 +1,118 @@ +/** + * @file linear.h + * @brief Contains the definition of a linear allocator and all the functions + * needed to utilize it. + * @author John Christman (sorakatadzuma@gmail.com) + * @copyright Malunal Studios, LLC. + */ +#include "../allocator.h" + +#ifndef MALUNAL_ALLOCATORS_LINEAR_HEADER +#define MALUNAL_ALLOCATORS_LINEAR_HEADER + +/** + * @brief Defines a concrete linear allocator. + * @details Linear allocators, also called bump allocators, simply track small + * acquisitions into the buffer that is provided to it. It does this + * by knowing how much memory + */ +typedef struct { + malunal_size_t __opaque[4]; +} linear_allocator_t; + +/** + * @brief A pointer to a mutable linear allocator. + * @details This is provided to simplify type declarations for functions + * requiring linear allocators that are meant to be mutable. + */ +typedef linear_allocator_t* linear_allocator_mptr_t; + +/** + * @brief A pointer to an immutable linear allocator. + * @details This is provided to simplify type declarations for functions + * requiring linear allocators that are meant to be immutable. + */ +typedef const linear_allocator_t* linear_allocator_iptr_t; + +/** + * @brief Obtains the size of the linear allocator. + * @param allocator The linear allocator to obtain the size from. + * @param out A pointer to where the obtained size will be stored. + * @returns An error if the linear allocator could not obtain the size. + */ +error_t +linear_allocator_size( + linear_allocator_iptr_t allocator, + malunal_uint32_t* out +); + +/** + * @brief Obtains the used count of the linear allocator. + * @param allocator The linear allocator to obtain the used count from. + * @param out A pointer to where the obtained used count will be stored. + * @returns An error if the linear allocator could not obtain the used. + */ +error_t +linear_allocator_used( + linear_allocator_iptr_t allocator, + malunal_uint32_t* out +); + +/** + * @brief Initializes a linear allocator. + * @param size The size of the buffer of the linear allocator. + * @param buffer The pointer to the buffer of the linear allocator. + * @param allocator The linear allocator to initialize. + * @returns An error if the linear allocator could not be initialized. + */ +error_t +linear_allocator_init( + malunal_size_t size, + malunal_mptr_t buffer, + linear_allocator_mptr_t allocator +); + +/** + * @brief Acquires some buffer from the linear allocator provided, given the + * @c size and the @c out location to place the buffer. + * @param allocator The linear allocator to acquire from. + * @param size The size of the buffer to acquire. + * @param out Where to store the buffer. + * @returns An error if the linear allocator could not acquire the buffer. + */ +error_t +linear_allocator_acquire( + linear_allocator_mptr_t allocator, + malunal_size_t size, + malunal_mptr_t* out +); + +/** + * @brief Disposes some buffer into the linear allocator provided, given the + * @c address to dispose and the @c size of the buffer. + * @param allocator The linear allocator to dispose into. + * @param address The address of the buffer to dispose. + * @param size The size of the buffer to dispose. + * @returns An error if the linear allocator could not dispose the buffer. + * @remarks Technically, linear allocators cannot dispose buffers that came from + * it because it cannot track free space. However, it will assert that + * the address at least came from itself to assure correctness in usage. + */ +error_t +linear_allocator_dispose( + linear_allocator_mptr_t allocator, + malunal_mptr_t address, + malunal_size_t size +); + +/** + * @brief Resets the linear allocator. + * @param allocator The linear allocator reset. + * @returns An error if the linear allocator could not be reset. + */ +error_t +linear_allocator_reset( + linear_allocator_mptr_t allocator +); + +#endif /* MALUNAL_ALLOCATORS_LINEAR_HEADER */ diff --git a/include/malunal/allocators/pool.h b/include/malunal/allocators/pool.h new file mode 100644 index 0000000..750b70f --- /dev/null +++ b/include/malunal/allocators/pool.h @@ -0,0 +1,129 @@ +/** + * @file pool.h + * @brief Contains the definition of a pool allocator and all the functions + * needed to utilize it. + * @author John Christman + * @copyright Malunal Studios, LLC. + */ +#include "../allocator.h" + +#ifndef MALUNAL_ALLOCATORS_POOL_HEADER +#define MALUNAL_ALLOCATORS_POOL_HEADER + +/** + * @brief Defines a concrete pool allocator. + * @details A pool allocator is a type of free listing allocator. It obtains + * some underlying memory region and divides it up into chunks that + * can individually be acquired and disposed. These chunks must be + * at least the size of the platform pointer. + * @remarks The chunk size, also known as stride, must be a multiple of two. + * The pool allocator will automatically upsize the stride to a multiple + * of two when initializing the pool. + */ +typedef struct { + malunal_size_t __opaque[7]; +} pool_allocator_t; + +/** + * @brief A pointer to a mutable pool allocator. + * @details This is provided to simplify type declarations for functions + * requiring pool allocators that are meant to be mutable. + */ +typedef pool_allocator_t* pool_allocator_mptr_t; + +/** + * @brief A pointer to an immutable pool allocator. + * @details This is provided to simplify type declarations for functions + * requiring pool allocators that are meant to be immutable. + */ +typedef const pool_allocator_t* pool_allocator_iptr_t; + + +/** + * @brief Obtains the stride of the pool allocator. + * @param allocator The pool allocator to obtain the stride from. + * @param out A pointer to where the obtained stride will be stored. + * @returns An error if the pool allocator could not provide the element stride. + */ +error_t +pool_allocator_stride( + pool_allocator_iptr_t allocator, + malunal_size_t* out +); + +/** + * @brief Obtains the count of the pool allocator. + * @param allocator The pool allocator to obtain the count from. + * @param out A pointer to where the obtained count will be stored. + * @returns An error if the pool allocator could not provide the element count. + */ +error_t +pool_allocator_count( + pool_allocator_iptr_t allocator, + malunal_size_t* out +); + +/** + * @brief Obtains the capacity of the pool allocator. + * @param allocator The pool allocator to obtain the capacity of. + * @param out A pointer to where the obtained capacity will be stored. + * @returns An error if the pool allocator could not provide the capacity. + */ +error_t +pool_allocator_capacity( + pool_allocator_iptr_t allocator, + malunal_size_t* out +); + +/** + * @brief Initializes a pool allocator instance with the given @c stride, + * @c capacity, and @c upstream allocator. + * @param stride The size of the chunks of the pool allocator. + * @param capacity The size of the buffer of the pool allocator. + * @param upstream The allocator to obtain the memory buffers from. + * @param allocator The pool allocator to initialize. + * @returns An error if the pool allocator could not be initialized. + */ +error_t +pool_allocator_init( + malunal_size_t stride, + malunal_size_t capacity, + allocator_mptr_t upstream, + pool_allocator_mptr_t allocator +); + +/** + * @brief Acquires a chunk from the pool allocator provided, given the @c out + * location to place the chunk. + * @param out Where to store the acquired chunk. + * @returns An error if the pool allocator could not acquire a chunk. + */ +error_t +pool_allocator_acquire( + pool_allocator_mptr_t allocator, + malunal_mptr_t* out +); + +/** + * @brief Disposes a chunk into the pool allocator provided, given the + * @c address to dispose. + * @param address The address of the buffer to dispose. + * @returns An error if the pool allocator could not dispose a chunk. + */ +error_t +pool_allocator_dispose( + pool_allocator_mptr_t allocator, + malunal_mptr_t address +); + +/** + * @brief Frees the memory held by the pool allocator. + * @param allocator A pointer to the pool allocator to free. + * @returns An error if the pool allocator could not be freed. + */ +error_t +pool_allocator_free( + pool_allocator_mptr_t allocator +); + +#endif /* MALUNAL_ALLOCATORS_POOL_HEADER */ diff --git a/include/malunal/allocators/stack.h b/include/malunal/allocators/stack.h new file mode 100644 index 0000000..fa757eb --- /dev/null +++ b/include/malunal/allocators/stack.h @@ -0,0 +1,128 @@ +/** + * @file stack.h + * @brief Contains the definition of a stack allocator and all the functions + * needed to utilize it. + * @author John Christman (sorakatadzuma@gmail.com) + * @copyright Malunal Studios, LLC. + */ +#include "../allocator.h" + +#ifndef MALUNAL_ALLOCATORS_STACK_HEADER +#define MALUNAL_ALLOCATORS_STACK_HEADER + +/** + * @brief Defines a concrete stack allocator. + * @details A stack allocator is one that acquires and disposes in a first in + * last out manner. This is useful for temporary stacks and some + * container like objects. + * @remarks When disposing into a stack allocator, the address that is being + * disposed must be the last acquired address or the allocator will + * fail to dispose it. + */ +typedef struct { + malunal_size_t __opaque[6]; +} stack_allocator_t; + +/** + * @brief A pointer to a mutable stack allocator. + * @details This is provided to simplify type declarations for functions + * requiring stack allocators that are meant to be mutable. + */ +typedef stack_allocator_t* stack_allocator_mptr_t; + +/** + * @brief A pointer to an immutable stack allocator. + * @details This is provided to simplify type declarations for functions + * requiring stack allocators that are meant to be immutable. + */ +typedef const stack_allocator_t* stack_allocator_iptr_t; + + +/** + * @brief Obtains the stride of the stack allocator. + * @param allocator The stack allocator to obtain the stride from. + * @param out A pointer to where the obtained stride will be stored. + * @returns An error if the stack allocator could not provide the element stride. + */ +error_t +stack_allocator_stride( + stack_allocator_iptr_t allocator, + malunal_size_t* out +); + +/** + * @brief Obtains the count of the stack allocator. + * @param allocator The stack allocator to obtain the count from. + * @param out A pointer to where the obtained count will be stored. + * @returns An error if the stack allocator could not provide the element count. + */ +error_t +stack_allocator_count( + stack_allocator_iptr_t allocator, + malunal_size_t* out +); + +/** + * @brief Obtains the capacity of the stack allocator. + * @param allocator The stack allocator to obtain the capacity of. + * @param out A pointer to where the obtained capacity will be stored. + * @returns An error if the stack allocator could not provide the capacity. + */ +error_t +stack_allocator_capacity( + stack_allocator_iptr_t allocator, + malunal_size_t* out +); + +/** + * @brief Initializes a stack allocator instance with the given @c stride, + * @c capacity, and @c upstream allocator. + * @param stride The size of the chunks of the stack allocator. + * @param capacity The size of the buffer of the stack allocator. + * @param upstream The allocator to obtain the memory buffers from. + * @param allocator The stack allocator to initialize. + * @returns An error if the stack allocator could not be initialized. + */ +error_t +stack_allocator_init( + malunal_size_t stride, + malunal_size_t capacity, + allocator_mptr_t upstream, + stack_allocator_mptr_t allocator +); + +/** + * @brief Acquires a chunk from the stack allocator provided, given the @c out + * location to place the chunk. + * @param out Where to store the acquired chunk. + * @returns An error if the stack allocator could not acquire a chunk. + */ +error_t +stack_allocator_acquire( + stack_allocator_mptr_t allocator, + malunal_mptr_t* out +); + +/** + * @brief Disposes a chunk into the stack allocator provided, given the + * @c address to dispose. + * @param address The address of the buffer to dispose. + * @returns An error if the stack allocator could not dispose a chunk. + */ +error_t +stack_allocator_dispose( + stack_allocator_mptr_t allocator, + malunal_mptr_t address +); + +/** + * @brief Frees the memory held by the stack allocator. + * @param allocator A pointer to the stack allocator to free. + * @returns An error if the stack allocator could not be freed. + */ +error_t +stack_allocator_free( + stack_allocator_mptr_t allocator +); + +#endif /* MALUNAL_ALLOCATORS_STACK_HEADER */ diff --git a/sources/allocator.c b/sources/allocator.c index 854d477..5963102 100644 --- a/sources/allocator.c +++ b/sources/allocator.c @@ -1,130 +1,71 @@ #include #include -#include "internal.h" +#include "malunal/allocator.h" -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; +const uuid_t UUID_ALLOCATOR_T = { + .tl = 0xEB14C3C3, + .tm = 0x5F0E, + .thv = 0x48A4, + .csr = 0x8C, + .csl = 0xFD, + .nbs = { + 0x58, 0x04, 0x13, + 0x19, 0xF9, 0xB2 + } +}; + + +static +malunal_cstr_t +describe(malunal_int32_t code) { + switch (code) { + case ALLOCATOR_ERROR_FAILURE: + return "Generic allocator error"; + case ALLOCATOR_ERROR_NULL_ALLOCATOR: + return "Allocator provided was null"; + case ALLOCATOR_ERROR_OUT_OF_MEMORY: + return "Allocator out of memory"; + case ALLOCATOR_ERROR_NOT_MY_ADDRESS: + return "Address does not belong to allocator"; + case ALLOCATOR_ERROR_LEAKY_MEMORY: + return "Allocator is leaking memory"; + } + return "Unknown allocator error"; } -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; -} +const error_domain_t ERROR_DOMAIN_ALLOCATOR_T = { + .describe = &describe, + .name = "malunal.allocator.error" +}; -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 +error_t allocator_acquire( allocator_mptr_t allocator, - malunal_size_t size + malunal_size_t size, + malunal_mptr_t* out ) { - 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 - }; + return allocator != null + ? allocator->vtable->acquire(allocator, size, out) + : (error_t) { + .domain = &ERROR_DOMAIN_ALLOCATOR_T, + .code = ALLOCATOR_ERROR_NULL_ALLOCATOR + }; } -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 - }; -} - -allocation_error_t -allocator_release( +error_t +allocator_dispose( 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; + return allocator != null + ? allocator->vtable->dispose(allocator, address, size) + : (error_t) { + .domain = &ERROR_DOMAIN_ALLOCATOR_T, + .code = ALLOCATOR_ERROR_NULL_ALLOCATOR + }; } malunal_size_t @@ -140,7 +81,7 @@ align_to_page(malunal_size_t size) { return align_to(size, platform_page_size()); } -allocator_t +allocator_mptr_t platform_allocator() { #if MALUNAL_PLATFORM_LINUX return linux_allocator(); diff --git a/sources/arena.c b/sources/arena.c index a1b4702..93174f9 100644 --- a/sources/arena.c +++ b/sources/arena.c @@ -1,198 +1,238 @@ #include -#include "internal.h" +#include "malunal/allocators/arena.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; +typedef struct { malunal_uint32_t size; - malunal_int8_t data[]; -}; + malunal_uint32_t used; + malunal_mptr_t next; + malunal_uint8_t bytes[]; +} region_t; +typedef region_t* region_mptr_t; +typedef const region_t* region_iptr_t; -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) +typedef struct { + allocator_t allocator; + region_mptr_t context; +} impl_t; + +typedef impl_t* impl_mptr_t; +typedef const impl_t* impl_iptr_t; +_Static_assert( + sizeof(arena_allocator_t) == sizeof(impl_t), + "Arena allocator must be the size of its implementation" +); + +error_t +create_region(region_mptr_t* region) { + allocator_mptr_t allocator = platform_allocator(); + malunal_size_t rounded = align_to_page(MALUNAL_ARENA_REGION_SIZE); + malunal_mptr_t* address = (malunal_mptr_t*)region; + error_t result = allocator_acquire(allocator, rounded, address); + + if (result.domain != null) return result; - page_mptr_t page = result.address; - page->size = rounded; - page->used = sizeof(page_t); - page->next = NULL_ADDRESS; - return result; + (*region)->size = rounded; + (*region)->used = sizeof(region_t); + (*region)->next = null; + return NO_ERROR; } -allocation_error_t -delete_page( - allocator_mptr_t allocator, - page_mptr_t page +error_t +delete_region(region_mptr_t region) { + if (region == null) + return NO_ERROR; + + error_t result = delete_region(region->next); + if (result.domain != null) + return result; + + region->next = null; + allocator_mptr_t allocator = platform_allocator(); + return allocator_dispose(allocator, region, region->size); +} + +error_t +region_acquire( + region_mptr_t region, + malunal_size_t size, + malunal_mptr_t* out ) { - if (allocator == NULL_ADDRESS) - return ALLOCATION_ERROR_NULL_UPSTREAM; - - if (page == NULL_ADDRESS) - return ALLOCATION_ERROR_FAILURE; - - allocation_error_t err = delete_page(allocator, page->next); - return err == ALLOCATION_ERROR_SUCCESS - ? allocator_release(allocator, page, page->size) - : err; -} - -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 += size; - return (allocation_result_t) { - .threw = 0, - .address = result - }; -} - -static -allocation_error_t -arena_initialize( - impl_mptr_t allocator, - allocator_mptr_t upstream, - malunal_size_t capacity -) { - if (upstream == NULL_ADDRESS) - return ALLOCATION_ERROR_NULL_UPSTREAM; - - allocation_result_t result = create_page(upstream, capacity); - page_mptr_t currpage = !result.threw ? result.address : NULL_ADDRESS; - - allocator->upstream = upstream; - allocator->context = currpage; - allocator->allocated = currpage->used; - allocator->reserved = currpage->size; - return ALLOCATION_ERROR_SUCCESS; -} - -static -allocation_error_t -arena_finalize(impl_mptr_t allocator) { - delete_page(allocator->upstream, allocator->context); -} - -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 - }; - - 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 (size <= remaining) - return page_acquire(page, size); - page = page->next; - continue; - } - - 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(page, size) - : (allocation_result_t) { - .threw = 1, - .error = ALLOCATION_ERROR_OUT_OF_MEMORY - }; -} - -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); -} - -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); -} - -allocation_error_t -arena_reset(allocator_mptr_t arena) { - if (arena == NULL_ADDRESS) - return ALLOCATION_ERROR_NULL_ALLOCATOR; - - impl_mptr_t impl = (impl_mptr_t)arena; - page_mptr_t page = impl->context; - while (page != NULL_ADDRESS) { - page->used = sizeof(page_t); - page = page->next; - } + malunal_uint32_t offset = + region->used - sizeof(region_t); + *out = region->bytes + offset; + region->used += size; + return NO_ERROR; } 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_vtable_t arena_allocator_vtable = { + .acquire = (allocator_acquire_pfn_t)&arena_allocator_acquire, + .dispose = (allocator_dispose_pfn_t)&arena_allocator_dispose }; -allocator_t -arena_allocator() { - implementation_t implementation = { - .vtable = &arena_vtable, - .upstream = NULL_ADDRESS, - .context = NULL_ADDRESS, - .allocated = 0, - .reserved = 0, - .acquires = 0, - .releases = 0 - }; +error_t +arena_allocator_size( + arena_allocator_iptr_t allocator, + malunal_uint32_t* out +) { + if (allocator == null) + return (error_t) { + .domain = &ERROR_DOMAIN_ALLOCATOR_T, + .code = ALLOCATOR_ERROR_NULL_ALLOCATOR + }; - allocator_t result; - memcpy( - &result, - &implementation, - sizeof(implementation_t) - ); + impl_iptr_t self = (impl_iptr_t)allocator; + region_iptr_t region = self->context; + malunal_size_t total = 0; + while (region != null) { + total += region->size; + region = region->next; + } - return result; + *out = total; + return NO_ERROR; } + +error_t +arena_allocator_used( + arena_allocator_iptr_t allocator, + malunal_uint32_t* out +) { + if (allocator == null) + return (error_t) { + .domain = &ERROR_DOMAIN_ALLOCATOR_T, + .code = ALLOCATOR_ERROR_NULL_ALLOCATOR + }; + + impl_iptr_t self = (impl_iptr_t)allocator; + region_iptr_t region = self->context; + malunal_size_t total = 0; + while (region != null) { + total += region->used; + region = region->next; + } + + *out = total; + return NO_ERROR; +} + +error_t +arena_allocator_init( + malunal_size_t capacity, + arena_allocator_mptr_t allocator +) { + if (allocator == null) + return (error_t) { + .domain = &ERROR_DOMAIN_ALLOCATOR_T, + .code = ALLOCATOR_ERROR_NULL_ALLOCATOR + }; + + region_mptr_t region = null; + error_t result = create_region(®ion); + if (result.domain != null) + return result; + + impl_mptr_t self = (impl_mptr_t)allocator; + self->allocator = (allocator_t){ &arena_allocator_vtable }; + self->context = region; + return NO_ERROR; +} + +error_t +arena_allocator_acquire( + arena_allocator_mptr_t allocator, + malunal_size_t size, + malunal_mptr_t* out +) { + if (allocator == null) + return (error_t) { + .domain = &ERROR_DOMAIN_ALLOCATOR_T, + .code = ALLOCATOR_ERROR_NULL_ALLOCATOR + }; + + impl_mptr_t self = (impl_mptr_t)allocator; + region_mptr_t region = self->context; + while (region != null) { + if (region->size - region->used >= size) + return region_acquire(region, size, out); + region = region->next; + } + + error_t result = create_region((region_mptr_t*)®ion->next); + return result.domain == null + ? region_acquire(region->next, size, out) + : result; +} + +error_t +arena_allocator_dispose( + arena_allocator_mptr_t allocator, + malunal_mptr_t address, + malunal_size_t size +) { + if (allocator == null) + return (error_t) { + .domain = &ERROR_DOMAIN_ALLOCATOR_T, + .code = ALLOCATOR_ERROR_NULL_ALLOCATOR + }; + + impl_mptr_t self = (impl_mptr_t)allocator; + region_mptr_t reg = self->context; + malunal_uint8_t* addr = address; + malunal_uint8_t* beg = reg->bytes; + malunal_uint8_t* end = reg->bytes + reg->size - sizeof(region_t); + while (true) { + if (addr < beg || addr > end) + return (error_t) { + .domain = &ERROR_DOMAIN_ALLOCATOR_T, + .code = ALLOCATOR_ERROR_NOT_MY_ADDRESS + }; + + reg = reg->next; + if (reg == null) + break; + beg = reg->bytes; + end = reg->bytes + reg->size - sizeof(region_t); + } + return NO_ERROR; +} + +error_t +arena_allocator_reset( + arena_allocator_mptr_t allocator +) { + if (allocator == null) + return (error_t) { + .domain = &ERROR_DOMAIN_ALLOCATOR_T, + .code = ALLOCATOR_ERROR_NULL_ALLOCATOR + }; + + impl_mptr_t self = (impl_mptr_t)allocator; + region_mptr_t region = self->context; + while (region != null) { + region->used = sizeof(region_t); + region = region->next; + } +} + +error_t +arena_allocator_free( + arena_allocator_mptr_t allocator +) { + if (allocator == null) + return (error_t) { + .domain = &ERROR_DOMAIN_ALLOCATOR_T, + .code = ALLOCATOR_ERROR_NULL_ALLOCATOR + }; + + impl_mptr_t self = (impl_mptr_t)allocator; + error_t result = delete_region(self->context); + if (result.domain != null) + return result; + + self->context = null; + return NO_ERROR; +} + diff --git a/sources/internal.h b/sources/internal.h deleted file mode 100644 index 31d808a..0000000 --- a/sources/internal.h +++ /dev/null @@ -1,75 +0,0 @@ -/** - * @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 index 0a2685c..ecca585 100644 --- a/sources/libc.c +++ b/sources/libc.c @@ -1,109 +1,49 @@ #include -#include -#include "internal.h" - +#include "malunal/allocator.h" static -allocation_error_t -libc_initialize( - impl_mptr_t allocator, - allocator_mptr_t upstream, - malunal_size_t capacity +error_t +libc_allocator_acquire( + allocator_mptr_t allocator, + malunal_size_t size, + malunal_mptr_t* out ) { MALUNAL_UNUSED(allocator); - MALUNAL_UNUSED(upstream); - MALUNAL_UNUSED(capacity); - return ALLOCATION_ERROR_SUCCESS; + + *out = malloc(size); + return *out == null + ? (error_t) { + .domain = &ERROR_DOMAIN_ALLOCATOR_T, + .code = ALLOCATOR_ERROR_OUT_OF_MEMORY + } + : NO_ERROR; } static -allocation_error_t -libc_finalize(impl_mptr_t allocator) { +error_t +libc_allocator_dispose( + allocator_mptr_t allocator, + malunal_mptr_t address, + malunal_size_t size +) { 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 -) { + MALUNAL_UNUSED(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; + return NO_ERROR; } 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_vtable_t libc_allocator_vtable = { + .acquire = (allocator_acquire_pfn_t)&libc_allocator_acquire, + .dispose = (allocator_dispose_pfn_t)&libc_allocator_dispose }; -allocator_t -libc_allocator() { - implementation_t implementation = { - .vtable = &libc_vtable, - .upstream = NULL_ADDRESS, - .context = NULL_ADDRESS, - .allocated = 0, - .reserved = 0, - .acquires = 0, - .releases = 0 - }; +static const +allocator_t libc_allocator_instance = { + .vtable = &libc_allocator_vtable +}; - allocator_t result; - memcpy( - &result, - &implementation, - sizeof(implementation_t) - ); - return result; +allocator_mptr_t +libc_allocator() { + return &libc_allocator_instance; } diff --git a/sources/linear.c b/sources/linear.c new file mode 100644 index 0000000..9f1229e --- /dev/null +++ b/sources/linear.c @@ -0,0 +1,139 @@ +#include "malunal/allocators/linear.h" + +typedef struct { + allocator_t allocator; + malunal_size_t size; + malunal_size_t used; + malunal_uint8_t* buffer; +} impl_t; + +typedef impl_t* impl_mptr_t; +typedef const impl_t* impl_iptr_t; +_Static_assert( + sizeof(linear_allocator_t) == sizeof(impl_t), + "Linear allocator must be the size of its implementation" +); + +static const +allocator_vtable_t linear_allocator_vtable = { + .acquire = (allocator_acquire_pfn_t)&linear_allocator_acquire, + .dispose = (allocator_dispose_pfn_t)&linear_allocator_dispose +}; + +error_t +linear_allocator_size( + linear_allocator_iptr_t allocator, + malunal_uint32_t* out +) { + if (allocator == null) + return (error_t) { + .domain = &ERROR_DOMAIN_ALLOCATOR_T, + .code = ALLOCATOR_ERROR_NULL_ALLOCATOR + }; + + impl_mptr_t self = (impl_mptr_t)allocator; + *out = self->size; + return NO_ERROR; +} + +error_t +linear_allocator_used( + linear_allocator_iptr_t allocator, + malunal_uint32_t* out +) { + if (allocator == null) + return (error_t) { + .domain = &ERROR_DOMAIN_ALLOCATOR_T, + .code = ALLOCATOR_ERROR_NULL_ALLOCATOR + }; + + impl_mptr_t self = (impl_mptr_t)allocator; + *out = self->used; + return NO_ERROR; +} + +error_t +linear_allocator_init( + malunal_size_t size, + malunal_mptr_t buffer, + linear_allocator_mptr_t allocator +) { + if (allocator == null) + return (error_t) { + .domain = &ERROR_DOMAIN_ALLOCATOR_T, + .code = ALLOCATOR_ERROR_NULL_ALLOCATOR + }; + + impl_mptr_t self = (impl_mptr_t)allocator; + self->allocator.vtable = &linear_allocator_vtable; + self->buffer = buffer; + self->size = size; + self->used = 0; + return NO_ERROR; +} + +error_t +linear_allocator_acquire( + linear_allocator_mptr_t allocator, + malunal_size_t size, + malunal_mptr_t* out +) { + if (allocator == null) + return (error_t) { + .domain = &ERROR_DOMAIN_ALLOCATOR_T, + .code = ALLOCATOR_ERROR_NULL_ALLOCATOR + }; + + impl_mptr_t self = (impl_mptr_t)allocator; + if (self->used + size > self->size) + return (error_t) { + .domain = &ERROR_DOMAIN_ALLOCATOR_T, + .code = ALLOCATOR_ERROR_OUT_OF_MEMORY + }; + + *out = self->buffer + self->used; + self->used += size; + return NO_ERROR; +} + +error_t +linear_allocator_dispose( + linear_allocator_mptr_t allocator, + malunal_mptr_t address, + malunal_size_t size +) { + if (allocator == null) + return (error_t) { + .domain = &ERROR_DOMAIN_ALLOCATOR_T, + .code = ALLOCATOR_ERROR_NULL_ALLOCATOR + }; + + impl_mptr_t self = (impl_mptr_t)allocator; + + malunal_uint8_t* addr = address; + malunal_uint8_t* beg = self->buffer; + malunal_uint8_t* end = self->buffer + self->size; + if (addr < beg || addr > end) + return (error_t) { + .domain = &ERROR_DOMAIN_ALLOCATOR_T, + .code = ALLOCATOR_ERROR_NOT_MY_ADDRESS + }; + + return NO_ERROR; +} + +error_t +linear_allocator_reset( + linear_allocator_mptr_t allocator +) { + if (allocator == null) + return (error_t) { + .domain = &ERROR_DOMAIN_ALLOCATOR_T, + .code = ALLOCATOR_ERROR_NULL_ALLOCATOR + }; + + impl_mptr_t self = (impl_mptr_t)allocator; + + self->used = 0; + return NO_ERROR; +} diff --git a/sources/linux.c b/sources/linux.c index 01896c3..cc27d44 100644 --- a/sources/linux.c +++ b/sources/linux.c @@ -2,10 +2,9 @@ #if MALUNAL_PLATFORM_LINUX #include -#include #include #include -#include "internal.h" +#include "malunal/allocator.h" static @@ -19,32 +18,12 @@ platform_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 +error_t +linux_allocator_acquire( + allocator_mptr_t allocator, + malunal_size_t size, + malunal_mptr_t* out ) { MALUNAL_UNUSED(allocator); @@ -52,81 +31,42 @@ linux_acquire( 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 + if (ptr == MAP_FAILED || ptr == null) + return (error_t) { + .domain = &ERROR_DOMAIN_ALLOCATOR_T, + .code = ALLOCATOR_ERROR_OUT_OF_MEMORY }; - allocator->allocated += size; - allocator->reserved += size; - allocator->acquires += 1; - return (allocation_result_t) { - .threw = 0, - .address = ptr - }; + *out = ptr; + return NO_ERROR; } static -allocation_error_t -linux_release( - impl_mptr_t allocator, - malunal_mptr_t address, - malunal_size_t size +error_t +linux_allocator_dispose( + allocator_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; + return NO_ERROR; } -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_vtable_t linux_allocator_vtable = { + .acquire = (allocator_acquire_pfn_t)&linux_allocator_acquire, + .dispose = (allocator_dispose_pfn_t)&linux_allocator_dispose }; -allocator_t +static const +allocator_t linux_allocator_instance = { + .vtable = &linux_allocator_vtable +}; + +allocator_mptr_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; + return &linux_allocator_instance; } #endif /* MALUNAL_PLATFORM_LINUX */ diff --git a/sources/pool.c b/sources/pool.c new file mode 100644 index 0000000..2b29691 --- /dev/null +++ b/sources/pool.c @@ -0,0 +1,248 @@ +#include "malunal/allocators/pool.h" + +typedef struct free_t free_t; +typedef free_t* free_mptr_t; +typedef const free_t* free_iptr_t; + + struct free_t { + free_mptr_t next; +}; + +typedef struct { + allocator_t allocator; + allocator_mptr_t upstream; + + malunal_size_t stride; + malunal_size_t count; + malunal_size_t capacity; + malunal_mptr_t buffer; + free_mptr_t freelist; +} impl_t; + +typedef impl_t* impl_mptr_t; +typedef const impl_t* impl_iptr_t; +_Static_assert( + sizeof(pool_allocator_t) == sizeof(impl_t), + "Pool allocator must be the size of its implemenation" +); + +static +error_t +pool_allocator_acquire_impl( + pool_allocator_mptr_t allocator, + malunal_size_t size, + malunal_mptr_t* out +) { + MALUNAL_UNUSED(size); + return pool_allocator_acquire(allocator, out); +} + +static +error_t +pool_allocator_dispose_impl( + pool_allocator_mptr_t allocator, + malunal_mptr_t address, + malunal_size_t size +) { + MALUNAL_UNUSED(size); + return pool_allocator_dispose(allocator, address); +} + +static const +allocator_vtable_t pool_allocator_vtable = { + .acquire = (allocator_acquire_pfn_t)&pool_allocator_acquire_impl, + .dispose = (allocator_dispose_pfn_t)&pool_allocator_dispose_impl +}; + +error_t +pool_allocator_stride( + pool_allocator_iptr_t allocator, + malunal_size_t* out +) { + if (allocator == null) + return (error_t) { + .domain = &ERROR_DOMAIN_ALLOCATOR_T, + .code = ALLOCATOR_ERROR_NULL_ALLOCATOR + }; + + impl_iptr_t self = (impl_iptr_t)allocator; + *out = self->stride; + return NO_ERROR; +} + +error_t +pool_allocator_count( + pool_allocator_iptr_t allocator, + malunal_size_t* out +) { + if (allocator == null) + return (error_t) { + .domain = &ERROR_DOMAIN_ALLOCATOR_T, + .code = ALLOCATOR_ERROR_NULL_ALLOCATOR + }; + + impl_iptr_t self = (impl_iptr_t)allocator; + *out = self->count; + return NO_ERROR; +} + +error_t +pool_allocator_capacity( + pool_allocator_iptr_t allocator, + malunal_size_t* out +) { + if (allocator == null) + return (error_t) { + .domain = &ERROR_DOMAIN_ALLOCATOR_T, + .code = ALLOCATOR_ERROR_NULL_ALLOCATOR + }; + + impl_iptr_t self = (impl_iptr_t)allocator; + *out = self->capacity; + return NO_ERROR; +} + +error_t +pool_allocator_init( + malunal_size_t stride, + malunal_size_t capacity, + allocator_mptr_t upstream, + pool_allocator_mptr_t allocator +) { + if (stride < 8) + return (error_t) { + .domain = &ERROR_DOMAIN_ALLOCATOR_T, + .code = ALLOCATOR_ERROR_FAILURE + }; + + if (allocator == null) + return (error_t) { + .domain = &ERROR_DOMAIN_ALLOCATOR_T, + .code = ALLOCATOR_ERROR_NULL_ALLOCATOR + }; + + if (upstream == null) + upstream = libc_allocator(); + + malunal_mptr_t address; + malunal_size_t bytes = stride * capacity; + error_t result = allocator_acquire(upstream, bytes, &address); + if (result.domain != null) + return result; + + malunal_size_t index; + malunal_uint8_t* asbytes = (malunal_uint8_t*)address; + free_mptr_t chunk = (free_mptr_t)address; + for (index = 1; index < capacity; index++) + chunk = chunk->next = (free_mptr_t)(asbytes + stride * index); + + impl_mptr_t self = (impl_mptr_t)allocator; + self->allocator = (allocator_t){ &pool_allocator_vtable }; + self->upstream = upstream; + self->stride = stride; + self->count = 0; + self->capacity = capacity; + self->buffer = address; + self->freelist = chunk; + return NO_ERROR; +} + +error_t +pool_allocator_acquire( + pool_allocator_mptr_t allocator, + malunal_mptr_t* out +) { + if (allocator == null) + return (error_t) { + .domain = &ERROR_DOMAIN_ALLOCATOR_T, + .code = ALLOCATOR_ERROR_NULL_ALLOCATOR + }; + + impl_mptr_t self = (impl_mptr_t)allocator; + if (self->count == self->capacity) + return (error_t) { + .domain = &ERROR_DOMAIN_ALLOCATOR_T, + .code = ALLOCATOR_ERROR_OUT_OF_MEMORY + }; + + free_mptr_t prev = self->freelist; + free_mptr_t curr = prev; + free_mptr_t next = prev->next; + while (next != null) { + prev = curr; + curr = next; + next = next->next; + } + + *out = curr; + self->count++; + if (curr == self->freelist) + self->freelist = null; + else + prev->next = null; + return NO_ERROR; +} + +error_t +pool_allocator_dispose( + pool_allocator_mptr_t allocator, + malunal_mptr_t address +) { + if (allocator == null) + return (error_t) { + .domain = &ERROR_DOMAIN_ALLOCATOR_T, + .code = ALLOCATOR_ERROR_NULL_ALLOCATOR + }; + + impl_mptr_t self = (impl_mptr_t)allocator; + malunal_uint8_t* addr = address; + malunal_uint8_t* beg = self->buffer; + malunal_uint8_t* end = beg + self->stride * self->capacity; + if (addr < beg || addr > end) + return (error_t) { + .domain = &ERROR_DOMAIN_ALLOCATOR_T, + .code = ALLOCATOR_ERROR_NOT_MY_ADDRESS + }; + + if (self->freelist == null) { + self->freelist = (free_mptr_t)addr; + self->freelist->next = null; + self->count--; + return NO_ERROR; + } + + free_mptr_t chunk = self->freelist; + while (chunk->next != null) + chunk = chunk->next; + chunk->next = (free_mptr_t)addr; + chunk->next->next = null; + self->count--; + return NO_ERROR; +} + +error_t +pool_allocator_free( + pool_allocator_mptr_t allocator +) { + if (allocator == null) + return (error_t) { + .domain = &ERROR_DOMAIN_ALLOCATOR_T, + .code = ALLOCATOR_ERROR_NULL_ALLOCATOR + }; + + impl_mptr_t self = (impl_mptr_t)allocator; + error_t result = allocator_dispose( + self->upstream, + self->buffer, + self->stride * self->capacity + ); + if (result.domain != null) + return result; + + self->stride = 0; + self->count = 0; + self->capacity = 0; + self->buffer = null; + self->freelist = null; + return NO_ERROR; +} diff --git a/sources/stack.c b/sources/stack.c new file mode 100644 index 0000000..79cba34 --- /dev/null +++ b/sources/stack.c @@ -0,0 +1,215 @@ +#include "malunal/allocators/stack.h" + +typedef struct { + allocator_t allocator; + allocator_mptr_t upstream; + + malunal_size_t stride; + malunal_size_t count; + malunal_size_t capacity; + malunal_mptr_t buffer; +} impl_t; + +typedef impl_t* impl_mptr_t; +typedef const impl_t* impl_iptr_t; +_Static_assert( + sizeof(stack_allocator_t) == sizeof(impl_t), + "Arena allocator must be the size of its implementation" +); + +static +error_t +stack_allocator_acquire_impl( + stack_allocator_mptr_t allocator, + malunal_size_t size, + malunal_mptr_t* out +) { + MALUNAL_UNUSED(size); + return stack_allocator_acquire(allocator, out); +} + +static +error_t +stack_allocator_dispose_impl( + stack_allocator_mptr_t allocator, + malunal_mptr_t address, + malunal_size_t size +) { + MALUNAL_UNUSED(size); + return stack_allocator_dispose(allocator, address); +} + +static const +allocator_vtable_t stack_allocator_vtable = { + .acquire = (allocator_acquire_pfn_t)&stack_allocator_acquire_impl, + .dispose = (allocator_dispose_pfn_t)&stack_allocator_dispose_impl +}; + +error_t +stack_allocator_stride( + stack_allocator_iptr_t allocator, + malunal_size_t* out +) { + if (allocator == null) + return (error_t) { + .domain = &ERROR_DOMAIN_ALLOCATOR_T, + .code = ALLOCATOR_ERROR_NULL_ALLOCATOR + }; + + impl_iptr_t self = (impl_iptr_t)allocator; + *out = self->stride; + return NO_ERROR; +} + +error_t +stack_allocator_count( + stack_allocator_iptr_t allocator, + malunal_size_t* out +) { + if (allocator == null) + return (error_t) { + .domain = &ERROR_DOMAIN_ALLOCATOR_T, + .code = ALLOCATOR_ERROR_NULL_ALLOCATOR + }; + + impl_iptr_t self = (impl_iptr_t)allocator; + *out = self->count; + return NO_ERROR; +} + +error_t +stack_allocator_capacity( + stack_allocator_iptr_t allocator, + malunal_size_t* out +) { + if (allocator == null) + return (error_t) { + .domain = &ERROR_DOMAIN_ALLOCATOR_T, + .code = ALLOCATOR_ERROR_NULL_ALLOCATOR + }; + + impl_iptr_t self = (impl_iptr_t)allocator; + *out = self->capacity; + return NO_ERROR; +} + +error_t +stack_allocator_init( + malunal_size_t stride, + malunal_size_t capacity, + allocator_mptr_t upstream, + stack_allocator_mptr_t allocator +) { + if (stride < 8) + return (error_t) { + .domain = &ERROR_DOMAIN_ALLOCATOR_T, + .code = ALLOCATOR_ERROR_FAILURE + }; + + if (allocator == null) + return (error_t) { + .domain = &ERROR_DOMAIN_ALLOCATOR_T, + .code = ALLOCATOR_ERROR_NULL_ALLOCATOR + }; + + if (upstream == null) + upstream = libc_allocator(); + + malunal_mptr_t address; + malunal_size_t bytes = stride * capacity; + error_t result = allocator_acquire(upstream, bytes, &address); + if (result.domain != null) + return result; + + impl_mptr_t self = (impl_mptr_t)allocator; + self->allocator = (allocator_t){ &stack_allocator_vtable }; + self->upstream = upstream; + self->stride = stride; + self->count = 0; + self->capacity = capacity; + self->buffer = address; + return NO_ERROR; +} + +error_t +stack_allocator_acquire( + stack_allocator_mptr_t allocator, + malunal_mptr_t* out +) { + if (allocator == null) + return (error_t) { + .domain = &ERROR_DOMAIN_ALLOCATOR_T, + .code = ALLOCATOR_ERROR_NULL_ALLOCATOR + }; + + impl_mptr_t self = (impl_mptr_t)allocator; + if (self->count == self->capacity) + return (error_t) { + .domain = &ERROR_DOMAIN_ALLOCATOR_T, + .code = ALLOCATOR_ERROR_OUT_OF_MEMORY + }; + + malunal_uint8_t* asbytes = self->buffer; + *out = asbytes + self->stride * self->count; + self->count++; + return NO_ERROR; +} + +error_t +stack_allocator_dispose( + stack_allocator_mptr_t allocator, + malunal_mptr_t address +) { + if (allocator == null) + return (error_t) { + .domain = &ERROR_DOMAIN_ALLOCATOR_T, + .code = ALLOCATOR_ERROR_NULL_ALLOCATOR + }; + + impl_mptr_t self = (impl_mptr_t)allocator; + malunal_uint8_t* addr = address; + malunal_uint8_t* beg = self->buffer; + malunal_uint8_t* end = beg + self->stride * self->capacity; + if (addr < beg || addr > end) + return (error_t) { + .domain = &ERROR_DOMAIN_ALLOCATOR_T, + .code = ALLOCATOR_ERROR_NOT_MY_ADDRESS + }; + + malunal_uint8_t* top = self->buffer; + top += self->stride * (self->count - 1); + if (addr != top) + return (error_t) { + .domain = &ERROR_DOMAIN_ALLOCATOR_T, + .code = ALLOCATOR_ERROR_FAILURE + }; + + self->count--; + return NO_ERROR; +} + +error_t +stack_allocator_free( + stack_allocator_mptr_t allocator +) { + if (allocator == null) + return (error_t) { + .domain = &ERROR_DOMAIN_ALLOCATOR_T, + .code = ALLOCATOR_ERROR_NULL_ALLOCATOR + }; + + impl_mptr_t self = (impl_mptr_t)allocator; + error_t result = allocator_dispose( + self->upstream, + self->buffer, + self->stride * self->capacity + ); + if (result.domain != null) + return result; + + self->stride = 0; + self->count = 0; + self->capacity = 0; + self->buffer = null; + return NO_ERROR; +} diff --git a/sources/win32.c b/sources/win32.c index ea05ec9..965b750 100644 --- a/sources/win32.c +++ b/sources/win32.c @@ -2,9 +2,8 @@ #if MALUNAL_PLATFORM_WIN32 #include -#include #include -#include "internal.h" +#include "malunal/allocator.h" static @@ -23,30 +22,11 @@ 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 +error_t +win32_allocator_acquire( + allocator_mptr_t allocator, + malunal_size_t size, + malunal_mptr_t* out ) { MALUNAL_UNUSED(allocator); @@ -55,80 +35,40 @@ win32_acquire( 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 + return (error_t) { + .domain = &ERROR_DOMAIN_ALLOCATOR_T, + .code = ALLOCATOR_ERROR_OUT_OF_MEMORY }; - allocator->allocated += size; - allocator->reserved += size; - allocator->acquires += 1; - return (allocation_result_t) { - .threw = 0, - .address = addr - }; + *out = addr; + return NO_ERROR; } static -allocation_error_t -win32_release( - impl_mptr_t allocator, - malunal_mptr_t address, - malunal_size_t size +error_t +win32_allocator_release( + allocator_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; + return NO_ERROR; } 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_vtable_t win32_alllocator_vtable = { + .acquire = (allocator_acquire_pfn_t)&win32_allocator_acquire, + .dispose = (allocator_dispose_pfn_t)&win32_allocator_release }; -allocator_t +static const +allocator_t win32_allocator_instance = { + .vtable = &win32_alllocator_vtable +}; + +allocator_mptr_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; + return &win32_allocator_instance; } #endif /* MALUNAL_PLATFORM_WIN32 */ diff --git a/tests/allocator.c b/tests/allocator.c deleted file mode 100644 index a874289..0000000 --- a/tests/allocator.c +++ /dev/null @@ -1,11 +0,0 @@ -extern int test_libc_allocator(); -extern int test_platform_allocator(); -extern int test_arena_allocator(); - -int main(void) { - return !( - test_libc_allocator() && - test_platform_allocator() && - test_arena_allocator() - ); -} diff --git a/tests/allocators.c b/tests/allocators.c new file mode 100644 index 0000000..134b53d --- /dev/null +++ b/tests/allocators.c @@ -0,0 +1,2 @@ +#include "malunal/microtest.h" +MICROTEST_MAIN() diff --git a/tests/arena_allocator.c b/tests/arena_allocator.c index 017cb2f..cb08b43 100644 --- a/tests/arena_allocator.c +++ b/tests/arena_allocator.c @@ -1,304 +1,78 @@ -#include #include "malunal/allocators/arena.h" +#include "malunal/microtest.h" -#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 \ - }; \ - } +MICROTEST(arena_allocator, can_init_and_free) { + error_t result = {}; + arena_allocator_t allocator = {}; + malunal_uint32_t temporary = 0; -#define assert_equals(condition) \ - if (!(condition)) { \ - fprintf( \ - stderr, \ - format, \ - __FILE__, \ - __LINE__, \ - MALUNAL_TOSTRING(condition) \ - ); \ - return 0; \ - } + result = arena_allocator_init(500, &allocator); + MICROTEST_EXPECT_NULL(result.domain); + result = arena_allocator_size(&allocator, &temporary); + MICROTEST_EXPECT_NULL(result.domain); + MICROTEST_EXPECT_EQ(temporary, platform_page_size()); -const malunal_cstr_t format = "[arena_allocator(%s:%d)]: %s failed\n"; + result = arena_allocator_used(&allocator, &temporary); + MICROTEST_EXPECT_NULL(result.domain); + MICROTEST_EXPECT_EQ(temporary, 16); -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; + result = arena_allocator_free(&allocator); + MICROTEST_EXPECT_NULL(result.domain); -static -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); + result = arena_allocator_size(&allocator, &temporary); + MICROTEST_EXPECT_NULL(result.domain); + MICROTEST_EXPECT_EQ(temporary, 0); - 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; + result = arena_allocator_used(&allocator, &temporary); + MICROTEST_EXPECT_NULL(result.domain); + MICROTEST_EXPECT_EQ(temporary, 0); } -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; +MICROTEST(arena_allocator, can_acquire_and_dispose) { + error_t result = {}; + arena_allocator_t allocator = {}; + malunal_mptr_t address = null; + malunal_uint32_t temporary = 0; - // Upstream checks. - allocator_mptr_t upstream; - assert_equals_result(allocator_upstream(arena, &upstream) == ALLOCATION_ERROR_SUCCESS); + arena_allocator_init(500, &allocator); + result = arena_allocator_acquire(&allocator, 64, &address); + MICROTEST_EXPECT_NULL(result.domain); + MICROTEST_ASSERT_NOT_NULL(address); - malunal_size_t count; - assert_equals_result(allocator_reserved_bytes(upstream, &count) == ALLOCATION_ERROR_SUCCESS); - assert_equals_result(count == page_bytes); + result = arena_allocator_used(&allocator, &temporary); + MICROTEST_EXPECT_NULL(result.domain); + MICROTEST_EXPECT_EQ(temporary, 80); - assert_equals_result(allocator_allocated_bytes(upstream, &count) == ALLOCATION_ERROR_SUCCESS); - assert_equals_result(count == page_bytes); + result = arena_allocator_dispose(&allocator, address, 64); + MICROTEST_EXPECT_NULL(result.domain); - 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. - assert_equals_result(allocator_reserved_bytes(arena, &count) == ALLOCATION_ERROR_SUCCESS); - assert_equals_result(count == page_bytes); - - assert_equals_result(allocator_allocated_bytes(arena, &count) == ALLOCATION_ERROR_SUCCESS); - assert_equals_result(count == init_with_pre); - - 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; + result = arena_allocator_used(&allocator, &temporary); + MICROTEST_EXPECT_NULL(result.domain); + MICROTEST_EXPECT_EQ(temporary, 80); + arena_allocator_free(&allocator); } -static int -test_arena_reacquire( - allocator_mptr_t arena, - malunal_mptr_t address -) { - malunal_int32_t index; - malunal_int32_t* vector; +MICROTEST(arena_allocator, can_reset_allocator) { + error_t result = {}; + arena_allocator_t allocator = {}; + malunal_mptr_t address = null; + malunal_uint32_t temporary = 0; - vector = address; - for (index = 0; index < 4; index++) - vector[index] = index + 1; + arena_allocator_init(500, &allocator); + result = arena_allocator_acquire(&allocator, 64, &address); + MICROTEST_EXPECT_NULL(result.domain); + MICROTEST_ASSERT_NOT_NULL(address); - allocation_result_t result = allocator_reacquire( - arena, - address, - init_bytes, - final_bytes - ); + result = arena_allocator_used(&allocator, &temporary); + MICROTEST_EXPECT_NULL(result.domain); + MICROTEST_EXPECT_EQ(temporary, 80); - if (result.threw) - return result.error; + result = arena_allocator_reset(&allocator); + MICROTEST_EXPECT_NULL(result.domain); - allocator_mptr_t upstream; - assert_equals(allocator_upstream(arena, &upstream) == 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 == all_bytes); - - assert_equals(allocator_acquires_count(arena, &count) == ALLOCATION_ERROR_SUCCESS); - assert_equals(count == 2); - - assert_equals(allocator_releases_count(arena, &count) == ALLOCATION_ERROR_SUCCESS); - assert_equals(count == 1); - return ALLOCATION_ERROR_SUCCESS; -} - -static int -test_arena_release( - allocator_mptr_t arena, - malunal_mptr_t address -) { - malunal_int32_t index; - malunal_int32_t* vector; - - vector = address; - for (index = 0; index < 4; index++) - if (vector[index] != index + 1) - return 1; - - allocation_error_t relexc = - allocator_release(arena, address, final_bytes); - - if (relexc != ALLOCATION_ERROR_SUCCESS) - return relexc; - - allocator_mptr_t upstream; - assert_equals(allocator_upstream(arena, &upstream) == 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 == all_bytes); - - assert_equals(allocator_acquires_count(arena, &count) == ALLOCATION_ERROR_SUCCESS); - assert_equals(count == 2); - - 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); - - allocator_mptr_t upstream; - assert_equals(allocator_upstream(arena, &upstream) == 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 == 0); - - assert_equals(allocator_acquires_count(arena, &count) == ALLOCATION_ERROR_SUCCESS); - assert_equals(count == 2); - - assert_equals(allocator_releases_count(arena, &count) == ALLOCATION_ERROR_SUCCESS); - assert_equals(count == 2); - return ALLOCATION_ERROR_SUCCESS; -} - -static int -test_arena_finalize(allocator_mptr_t arena) { - allocator_finalize(arena); - - allocator_mptr_t upstream; - assert_equals(allocator_upstream(arena, &upstream) == ALLOCATION_ERROR_SUCCESS); - - malunal_size_t count; - assert_equals(allocator_reserved_bytes(upstream, &count) == ALLOCATION_ERROR_SUCCESS); - assert_equals(count == 0); - - assert_equals(allocator_allocated_bytes(upstream, &count) == ALLOCATION_ERROR_SUCCESS); - assert_equals(count == 0); - - 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. - assert_equals(allocator_reserved_bytes(arena, &count) == ALLOCATION_ERROR_SUCCESS); - assert_equals(count == 0); - - assert_equals(allocator_allocated_bytes(arena, &count) == ALLOCATION_ERROR_SUCCESS); - assert_equals(count == 0); - - assert_equals(allocator_acquires_count(arena, &count) == ALLOCATION_ERROR_SUCCESS); - assert_equals(count == 2); - - 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(); - assert_equals(test_arena_initialize(&arena, &upstream) == ALLOCATION_ERROR_SUCCESS); - - allocation_result_t result = - test_arena_acquires(&arena); - - return !( - !result.threw && - test_arena_reacquire(&arena, result.address) && - test_arena_release(&arena, result.address) && - test_arena_reset(&arena) && - test_arena_finalize(&arena) - ); + result = arena_allocator_used(&allocator, &temporary); + MICROTEST_EXPECT_NULL(result.domain); + MICROTEST_EXPECT_EQ(temporary, 16); + arena_allocator_free(&allocator); } diff --git a/tests/libc_allocator.c b/tests/libc_allocator.c index dcc8cc3..50b685c 100644 --- a/tests/libc_allocator.c +++ b/tests/libc_allocator.c @@ -1,95 +1,13 @@ -#include -#include "malunal/allocators.h" +#include "malunal/allocator.h" +#include "malunal/microtest.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) +MICROTEST(libc_allocator, can_acquire_and_dispose) { + malunal_mptr_t address = null; + allocator_mptr_t allocator = libc_allocator(); + error_t result = allocator_acquire(allocator, 32, &address); + MICROTEST_EXPECT_NULL(result.domain); + MICROTEST_ASSERT_NOT_NULL(address); - -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; - - allocation_result_t result; - - allocator_t libc_alloc = libc_allocator(); - allocator_mptr_t allocator = &libc_alloc; - result = allocator_acquire(allocator, init_bytes); - - if (result.threw) - return result.error; - - malunal_size_t count; - assert_equals(allocator_reserved_bytes(allocator, &count) == ALLOCATION_ERROR_SUCCESS); - assert_equals(count == init_bytes); - - assert_equals(allocator_allocated_bytes(allocator, &count) == ALLOCATION_ERROR_SUCCESS); - assert_equals(count == init_bytes); - - assert_equals(allocator_acquires_count(allocator, &count) == ALLOCATION_ERROR_SUCCESS); - assert_equals(count == 1); - - assert_equals(allocator_releases_count(allocator, &count) == ALLOCATION_ERROR_SUCCESS); - assert_equals(count == 0); - - malunal_int32_t index; - malunal_int32_t* vector; - - vector = result.address; - for (index = 0; index < 4; index++) - vector[index] = index + 1; - - result = allocator_reacquire( - allocator, - result.address, - init_bytes, - final_bytes - ); - - if (result.threw) - return result.error; - - assert_equals(allocator_reserved_bytes(allocator, &count) == ALLOCATION_ERROR_SUCCESS); - assert_equals(count == final_bytes); - - assert_equals(allocator_allocated_bytes(allocator, &count) == ALLOCATION_ERROR_SUCCESS); - assert_equals(count == final_bytes); - - assert_equals(allocator_acquires_count(allocator, &count) == ALLOCATION_ERROR_SUCCESS); - assert_equals(count == 2); - - 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; - - allocation_error_t relexc = allocator_release( - allocator, - result.address, - final_bytes - ); - - assert_equals(allocator_reserved_bytes(allocator, &count) == ALLOCATION_ERROR_SUCCESS); - assert_equals(count == 0); - - assert_equals(allocator_allocated_bytes(allocator, &count) == ALLOCATION_ERROR_SUCCESS); - assert_equals(count == 0); - - assert_equals(allocator_acquires_count(allocator, &count) == ALLOCATION_ERROR_SUCCESS); - assert_equals(count == 2); - - assert_equals(allocator_releases_count(allocator, &count) == ALLOCATION_ERROR_SUCCESS); - assert_equals(count == 2); - - return relexc == ALLOCATION_ERROR_SUCCESS; + result = allocator_dispose(allocator, address, 32); + MICROTEST_EXPECT_NULL(result.domain); } diff --git a/tests/linear_allocator.c b/tests/linear_allocator.c new file mode 100644 index 0000000..57f5c86 --- /dev/null +++ b/tests/linear_allocator.c @@ -0,0 +1,47 @@ +#include "malunal/allocators/linear.h" +#include "malunal/microtest.h" + +MICROTEST(linear_allocator, can_init) { + error_t result = {}; + linear_allocator_t allocator = {}; + malunal_mptr_t address = null; + malunal_uint32_t temporary = 0; + + allocator_acquire(libc_allocator(), 128, &address); + result = linear_allocator_init(128, address, &allocator); + MICROTEST_EXPECT_NULL(result.domain); + + result = linear_allocator_size(&allocator, &temporary); + MICROTEST_EXPECT_NULL(result.domain); + MICROTEST_EXPECT_EQ(temporary, 128); + + result = linear_allocator_used(&allocator, &temporary); + MICROTEST_EXPECT_NULL(result.domain); + MICROTEST_EXPECT_EQ(temporary, 0); +} + +MICROTEST(linear_allocator, can_acquire_and_dispose) { + error_t result = {}; + linear_allocator_t allocator = {}; + malunal_mptr_t original = null; + malunal_mptr_t address = null; + malunal_uint32_t temporary = 0; + + allocator_acquire(libc_allocator(), 128, &original); + linear_allocator_init(128, original, &allocator); + result = linear_allocator_acquire(&allocator, 64, &address); + MICROTEST_EXPECT_NULL(result.domain); + MICROTEST_ASSERT_NOT_NULL(address); + + result = linear_allocator_used(&allocator, &temporary); + MICROTEST_EXPECT_NULL(result.domain); + MICROTEST_EXPECT_EQ(temporary, 64); + + result = linear_allocator_dispose(&allocator, address, 64); + MICROTEST_EXPECT_NULL(result.domain); + + result = linear_allocator_used(&allocator, &temporary); + MICROTEST_EXPECT_NULL(result.domain); + MICROTEST_EXPECT_EQ(temporary, 64); + allocator_dispose(libc_allocator(), original, 128); +} diff --git a/tests/platform_allocator.c b/tests/platform_allocator.c index 4b3daff..20d0614 100644 --- a/tests/platform_allocator.c +++ b/tests/platform_allocator.c @@ -1,95 +1,13 @@ -#include -#include "malunal/allocators.h" +#include "malunal/allocator.h" +#include "malunal/microtest.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) +MICROTEST(platform_allocator, can_acquire_and_dispose) { + malunal_mptr_t address = null; + allocator_mptr_t allocator = platform_allocator(); + error_t result = allocator_acquire(allocator, 32, &address); + MICROTEST_EXPECT_NULL(result.domain); + MICROTEST_ASSERT_NOT_NULL(address); - -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; - - allocation_result_t result; - - allocator_t plat_alloc = platform_allocator(); - allocator_mptr_t allocator = &plat_alloc; - result = allocator_acquire(allocator, init_bytes); - - if (result.threw) - return result.error; - - malunal_size_t count; - assert_equals(allocator_reserved_bytes(allocator, &count) == ALLOCATION_ERROR_SUCCESS); - assert_equals(count == init_bytes); - - assert_equals(allocator_allocated_bytes(allocator, &count) == ALLOCATION_ERROR_SUCCESS); - assert_equals(count == init_bytes); - - assert_equals(allocator_acquires_count(allocator, &count) == ALLOCATION_ERROR_SUCCESS); - assert_equals(count == 1); - - assert_equals(allocator_releases_count(allocator, &count) == ALLOCATION_ERROR_SUCCESS); - assert_equals(count == 0); - - malunal_int32_t index; - malunal_int32_t* vector; - - vector = result.address; - for (index = 0; index < 4; index++) - vector[index] = index + 1; - - result = allocator_reacquire( - allocator, - result.address, - init_bytes, - final_bytes - ); - - if (result.threw) - return result.error; - - assert_equals(allocator_reserved_bytes(allocator, &count) == ALLOCATION_ERROR_SUCCESS); - assert_equals(count == final_bytes); - - assert_equals(allocator_allocated_bytes(allocator, &count) == ALLOCATION_ERROR_SUCCESS); - assert_equals(count == final_bytes); - - assert_equals(allocator_acquires_count(allocator, &count) == ALLOCATION_ERROR_SUCCESS); - assert_equals(count == 2); - - 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; - - allocation_error_t relexc = allocator_release( - allocator, - result.address, - final_bytes - ); - - assert_equals(allocator_reserved_bytes(allocator, &count) == ALLOCATION_ERROR_SUCCESS); - assert_equals(count == 0); - - assert_equals(allocator_allocated_bytes(allocator, &count) == ALLOCATION_ERROR_SUCCESS); - assert_equals(count == 0); - - assert_equals(allocator_acquires_count(allocator, &count) == ALLOCATION_ERROR_SUCCESS); - assert_equals(count == 2); - - assert_equals(allocator_releases_count(allocator, &count) == ALLOCATION_ERROR_SUCCESS); - assert_equals(count == 2); - - return relexc == ALLOCATION_ERROR_SUCCESS; + result = allocator_dispose(allocator, address, 32); + MICROTEST_EXPECT_NULL(result.domain); } diff --git a/tests/pool_allocator.c b/tests/pool_allocator.c new file mode 100644 index 0000000..b1cc261 --- /dev/null +++ b/tests/pool_allocator.c @@ -0,0 +1,61 @@ +#include "malunal/allocators/pool.h" +#include "malunal/microtest.h" + +MICROTEST(pool_allocator, can_init_and_free) { + error_t result = {}; + pool_allocator_t allocator = {}; + malunal_size_t temporary = 0; + + result = pool_allocator_init(16, 8, null, &allocator); + MICROTEST_ASSERT_NULL(result.domain); + + result = pool_allocator_stride(&allocator, &temporary); + MICROTEST_EXPECT_NULL(result.domain); + MICROTEST_EXPECT_EQ(temporary, 16); + + result = pool_allocator_count(&allocator, &temporary); + MICROTEST_EXPECT_NULL(result.domain); + MICROTEST_EXPECT_EQ(temporary, 0); + + result = pool_allocator_capacity(&allocator, &temporary); + MICROTEST_EXPECT_NULL(result.domain); + MICROTEST_EXPECT_EQ(temporary, 8); + + result = pool_allocator_free(&allocator); + MICROTEST_ASSERT_NULL(result.domain); + + result = pool_allocator_stride(&allocator, &temporary); + MICROTEST_EXPECT_NULL(result.domain); + MICROTEST_EXPECT_EQ(temporary, 0); + + result = pool_allocator_count(&allocator, &temporary); + MICROTEST_EXPECT_NULL(result.domain); + MICROTEST_EXPECT_EQ(temporary, 0); + + result = pool_allocator_capacity(&allocator, &temporary); + MICROTEST_EXPECT_NULL(result.domain); + MICROTEST_EXPECT_EQ(temporary, 0); +} + +MICROTEST(pool_allocator, can_acquire_and_dispose) { + error_t result = {}; + pool_allocator_t allocator = {}; + malunal_mptr_t address = null; + malunal_size_t temporary = 0; + + pool_allocator_init(16, 8, null, &allocator); + result = pool_allocator_acquire(&allocator, &address); + MICROTEST_EXPECT_NULL(result.domain); + MICROTEST_ASSERT_NOT_NULL(address); + + result = pool_allocator_count(&allocator, &temporary); + MICROTEST_EXPECT_NULL(result.domain); + MICROTEST_EXPECT_EQ(temporary, 1); + + result = pool_allocator_dispose(&allocator, address); + MICROTEST_EXPECT_NULL(result.domain); + + result = pool_allocator_count(&allocator, &temporary); + MICROTEST_EXPECT_NULL(result.domain); + MICROTEST_EXPECT_EQ(temporary, 0); +} diff --git a/tests/stack_allocator.c b/tests/stack_allocator.c new file mode 100644 index 0000000..65574c9 --- /dev/null +++ b/tests/stack_allocator.c @@ -0,0 +1,61 @@ +#include "malunal/allocators/stack.h" +#include "malunal/microtest.h" + +MICROTEST(stack_allocator, can_init_and_free) { + error_t result = {}; + stack_allocator_t allocator = {}; + malunal_size_t temporary = 0; + + result = stack_allocator_init(16, 8, null, &allocator); + MICROTEST_ASSERT_NULL(result.domain); + + result = stack_allocator_stride(&allocator, &temporary); + MICROTEST_EXPECT_NULL(result.domain); + MICROTEST_EXPECT_EQ(temporary, 16); + + result = stack_allocator_count(&allocator, &temporary); + MICROTEST_EXPECT_NULL(result.domain); + MICROTEST_EXPECT_EQ(temporary, 0); + + result = stack_allocator_capacity(&allocator, &temporary); + MICROTEST_EXPECT_NULL(result.domain); + MICROTEST_EXPECT_EQ(temporary, 8); + + result = stack_allocator_free(&allocator); + MICROTEST_ASSERT_NULL(result.domain); + + result = stack_allocator_stride(&allocator, &temporary); + MICROTEST_EXPECT_NULL(result.domain); + MICROTEST_EXPECT_EQ(temporary, 0); + + result = stack_allocator_count(&allocator, &temporary); + MICROTEST_EXPECT_NULL(result.domain); + MICROTEST_EXPECT_EQ(temporary, 0); + + result = stack_allocator_capacity(&allocator, &temporary); + MICROTEST_EXPECT_NULL(result.domain); + MICROTEST_EXPECT_EQ(temporary, 0); +} + +MICROTEST(stack_allocator, can_acquire_and_dispose) { + error_t result = {}; + stack_allocator_t allocator = {}; + malunal_mptr_t address = null; + malunal_size_t temporary = 0; + + stack_allocator_init(16, 8, null, &allocator); + result = stack_allocator_acquire(&allocator, &address); + MICROTEST_EXPECT_NULL(result.domain); + MICROTEST_ASSERT_NOT_NULL(address); + + result = stack_allocator_count(&allocator, &temporary); + MICROTEST_EXPECT_NULL(result.domain); + MICROTEST_EXPECT_EQ(temporary, 1); + + result = stack_allocator_dispose(&allocator, address); + MICROTEST_EXPECT_NULL(result.domain); + + result = stack_allocator_count(&allocator, &temporary); + MICROTEST_EXPECT_NULL(result.domain); + MICROTEST_EXPECT_EQ(temporary, 0); +}