How to Use Custom Memory Allocators in libavif
This article explains how the libavif API handles custom memory
allocation. It covers the core avifMemoryAllocator
structure, how to implement custom allocation functions, and the steps
required to register these custom allocators globally within your
application. By overriding the default memory management, developers can
easily integrate libavif into constrained environments, game engines, or
systems requiring strict memory tracking.
The avifMemoryAllocator Structure
By default, libavif relies on the standard C library functions
(malloc, realloc, and free) for
dynamic memory management. However, for systems that require custom heap
management, libavif provides the avifMemoryAllocator
structure. This structure encapsulates custom allocation behavior by
holding function pointers to your custom allocator implementations.
The avifMemoryAllocator struct generally contains the
following function pointer members:
alloc: A function pointer with the signaturevoid * (*alloc)(size_t size)that allocates a block of memory of the specified size.realloc: A function pointer with the signaturevoid * (*realloc)(void * ptr, size_t size)that resizes an existing memory block.free: A function pointer with the signaturevoid (*free)(void * ptr)that deallocates a previously allocated memory block.
Registering a Custom Allocator
To override the default memory functions, you must configure and
register an instance of avifMemoryAllocator before
performing any operations with libavif. This is done using the global
configuration function:
void avifSetMemoryAllocator(const avifMemoryAllocator * allocator);Passing a pointer to your custom avifMemoryAllocator
populates the library’s internal function pointers. If you pass
NULL to this function, libavif will revert to using the
standard system allocators.
Implementation Example
Below is a basic implementation showing how to define custom memory routines and register them with libavif:
#include "avif/avif.h"
#include <stdlib.h>
#include <stdio.h>
// Custom allocation wrapper
void * MyCustomAlloc(size_t size) {
// Implement custom logic (e.g., tracking allocation size or using a custom arena)
return malloc(size);
}
// Custom reallocation wrapper
void * MyCustomRealloc(void * ptr, size_t size) {
return realloc(ptr, size);
}
// Custom free wrapper
void MyCustomFree(void * ptr) {
free(ptr);
}
int main() {
// Define the custom allocator structure
avifMemoryAllocator myAllocator;
myAllocator.alloc = MyCustomAlloc;
myAllocator.realloc = MyCustomRealloc;
myAllocator.free = MyCustomFree;
// Register the custom allocator globally
avifSetMemoryAllocator(&myAllocator);
// Any libavif operations from this point forward will use the custom allocator
avifDecoder * decoder = avifDecoderCreate();
// Clean up
avifDecoderDestroy(decoder);
return 0;
}Thread Safety and Usage Considerations
When using custom memory allocators with libavif, keep the following considerations in mind:
- Global Scope: The memory allocator set via
avifSetMemoryAllocatoris global. It affects all encoder, decoder, and image operations across the entire application process. - Thread Safety: If your application decodes or
encodes AVIF images concurrently across multiple threads, your custom
alloc,realloc, andfreefunctions must be completely thread-safe. - Initialization Timing: You must call
avifSetMemoryAllocatorbefore calling any other libavif API functions. Changing the memory allocator while libavif structures are actively allocated will lead to undefined behavior and memory corruption, as memory allocated with one allocator might be freed by another.