--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/Writerside/topics/allocator.h.md Tue Feb 11 19:55:32 2025 +0100 @@ -0,0 +1,147 @@ +# Allocator + +The allocator interface provides a mechanism to implement own custom allocators +that can also be used in many other function in UCX. + +A default allocator implementation using the stdlib functions is +available via the global symbol `cxDefaultAllocator` +and UCX also provides a [memory pool](mempool.h.md) implementation. +You are free to add additional own custom implementations. +A general sketch that illustrates how to do this can be found [below](#custom-allocator). + +## Overview + +```C +#include <cx/allocator.h> + +void *cxMalloc(const CxAllocator *allocator, size_t n); + +void *cxCalloc(const CxAllocator *allocator, + size_t nmemb, size_t size); + +void *cxRealloc(const CxAllocator *allocator, void *mem, size_t n); + +void *cxReallocArray(const CxAllocator *allocator, void *mem, + size_t nmemb, size_t size); + +int cxReallocate(const CxAllocator *allocator, void **mem, size_t n); + +int cxReallocateArray(const CxAllocator *allocator, void **mem, + size_t nmemb, size_t size); + +void cxFree(const CxAllocator *allocator, void *mem); + +int cx_reallocate(void **mem, size_t size); + +int cx_reallocatearray(void **mem, size_t nmemb, size_t size); + +// predefined allocator that uses stdlib functions +CxAllocator *cxDefaultAllocator; +``` + +> All UCX functions that are not _explicitly_ designed for taking an allocator argument +> (recognizable by a `_a` suffix in the function's name) do support a `NULL` argument +> in which case the `cxDefaultAllocator` will be used. + +## Description + +The functions `cxMalloc()`, `cxCalloc()`, `cxRealloc()`, `cxReallocArray()`, and `cxFree()` +invoke the memory management functions specified in the `allocator` and should behave like +their respective stdlibc pendants. +Implementations of the allocator interface are strongly encouraged to guarantee this behavior, +most prominently that invocations of `cxFree()` with a `NULL`-pointer for `mem` are ignored +instead of causing segfault error. + +Additionally, UCX provides the functions `cxReallocate()` and `cxReallocateArray()`, as well as +their independent pendants `cx_reallocate()` and `cx_reallocatearray()`. +All those functions solve the problem that a possible reallocation might fail, +leading to a quite common programming mistake: + +```C +// common mistake - mem will be lost hen realloc() returns NULL +mem = realloc(mem, capacity + 32); +if (mem == NULL) // ... do error handling +``` + +The above code can be replaced with `cx_reallocate()` which keeps the pointer intact and returns an error code instead. + +```C +// when cx_reallocate() fails, mem will still point to the old memory +if (cx_reallocate(&mem, capacity + 32)) // ... do error handling +``` + +> Please pay special attention to always use `cxFree()` and the `cxRealloc()`-family of functions +> with the **same** allocator that was used to allocate the memory. +{style="warning"} + +## Custom Allocator + +If you want to define your own allocator, you need to initialize the `CxAllocator` structure +with a pointer to an allocator class (containing function pointers for the memory management +functions) and an optional pointer to custom data. An example is shown below: + +```c + +struct my_allocator_state { + // ... some internal state ... +}; + +static cx_allocator_class my_allocator_class = { + my_malloc_impl, + my_realloc_impl, // all these functions are somewhere defined + my_calloc_impl, + my_free_impl +}; + +CxAllocator create_my_allocator(void) { + CxAllocator alloc; + alloc.cl = &my_allocator_class; + struct my_allocator_state *state = malloc(sizeof(*state)); + // ... initialize state ... + alloc.data = state; + return alloc; +} + +void destroy_my_allocator(CxAllocator *al) { + struct my_allocator_state *state = al->state; + // ... destroy state -- + free(state); +} +``` + +When you are implementing + +## Destructor Functions + +The `allocator.h` header also declares two function pointers for destructor functions. + +```C +typedef void (*cx_destructor_func)(void *memory); +typedef void (*cx_destructor_func2)(void *data, void *memory); +``` + +The first one is called _simple_ destructor (e.g. in the context of [collections](collection.h.md)), +and the second one is called _advanced_ destructor. +The only difference is that you can pass additional custom `data` to an advanced destructor function. + +Destructor functions play a vital role in deep de-allocations. +Another scenarios, besides destroying elements in a collection, are the de-allocation of objects +stored in a [memory pool](mempool.h.md) or de-allocations of deeply nested [JSON](json.h.md) objects. + +> Destructor functions are not to be confused with `free()`-like functions. +> The fundamental differences are that +> * it is not safe to pass `NULL` to a destructor function +> * a destructor may only de-allocate the contents inside an object but not the object itself, depending on context +> +{style="note"} + +> For example, when you are using a [list](list.h.md) that stores elements directly, a destructor function +> assigned to that collection may only destroy the element's contents but must not deallocate the element's memory. +> On the other hand, when the list is storing just pointers to the elements, you _may_ want the destructor +> function to also de-allocate the element's memory when the element is removed from that list. + +<seealso> +<category ref="apidoc"> +<a href="https://ucx.sourceforge.io/api/allocator_8h.html">allocator.h</a> +</category> +</seealso>