Files
libdeflate/programs/test_custom_malloc.c
Eric Biggers 353a2e2fc2 Add support for per-(de)compressor malloc/free functions
Allow specifying a custom memory allocator per-(de)compressor, to
support use cases where there might be different users of libdeflate in
the same process who want to use different memory allocators.

This is a replacement for
https://github.com/ebiggers/libdeflate/pull/312.  I changed the design
slightly to be more extensible.  The allocator is specified by a new
'struct libdeflate_options' which is extensible, so the same struct will
also work for special compression options/hints, which is something that
has been requested in libdeflate before.  (I'm a bit hesitant to add
such options, but this at least makes a place to put them if it's ever
done.)  I also made the pointer to the free function be stored in the
(de)compressor struct, so the API for freeing is unchanged.
2023-05-20 10:02:28 -07:00

156 lines
3.5 KiB
C

/*
* test_custom_malloc.c
*
* Test the support for custom memory allocators.
* Also test injecting allocation failures.
*/
#include "test_util.h"
static int malloc_count = 0;
static int free_count = 0;
static void *do_malloc(size_t size)
{
malloc_count++;
return malloc(size);
}
static void *do_fail_malloc(size_t size)
{
malloc_count++;
return NULL;
}
static void do_free(void *ptr)
{
free_count++;
free(ptr);
}
static void reset_state(void)
{
libdeflate_set_memory_allocator(malloc, free);
malloc_count = 0;
free_count = 0;
}
/* Test that the custom allocator is actually used when requested. */
static void do_custom_memalloc_test(bool global)
{
static const struct libdeflate_options options = {
.sizeof_options = sizeof(options),
.malloc_func = do_malloc,
.free_func = do_free,
};
int level;
struct libdeflate_compressor *c;
struct libdeflate_decompressor *d;
if (global)
libdeflate_set_memory_allocator(do_malloc, do_free);
for (level = 0; level <= 12; level++) {
malloc_count = free_count = 0;
if (global)
c = libdeflate_alloc_compressor(level);
else
c = libdeflate_alloc_compressor_ex(level, &options);
ASSERT(c != NULL);
ASSERT(malloc_count == 1);
ASSERT(free_count == 0);
libdeflate_free_compressor(c);
ASSERT(malloc_count == 1);
ASSERT(free_count == 1);
}
malloc_count = free_count = 0;
if (global)
d = libdeflate_alloc_decompressor();
else
d = libdeflate_alloc_decompressor_ex(&options);
ASSERT(d != NULL);
ASSERT(malloc_count == 1);
ASSERT(free_count == 0);
libdeflate_free_decompressor(d);
ASSERT(malloc_count == 1);
ASSERT(free_count == 1);
reset_state();
}
#define offsetofend(type, field) \
(offsetof(type, field) + sizeof(((type *)NULL)->field))
/* Test some edge cases involving libdeflate_options. */
static void do_options_test(void)
{
struct libdeflate_options options = { 0 };
struct libdeflate_compressor *c;
struct libdeflate_decompressor *d;
/* Size in libdeflate v1.19 */
size_t min_size = offsetofend(struct libdeflate_options, free_func);
/* sizeof_options must be at least the minimum size. */
for (; options.sizeof_options < min_size;
options.sizeof_options++) {
c = libdeflate_alloc_compressor_ex(6, &options);
ASSERT(c == NULL);
d = libdeflate_alloc_decompressor_ex(&options);
ASSERT(d == NULL);
}
/* NULL malloc_func and free_func means "use the global allocator". */
options.sizeof_options = min_size;
malloc_count = free_count = 0;
libdeflate_set_memory_allocator(do_malloc, do_free);
c = libdeflate_alloc_compressor_ex(6, &options);
libdeflate_free_compressor(c);
ASSERT(malloc_count == 1);
ASSERT(free_count == 1);
d = libdeflate_alloc_decompressor_ex(&options);
libdeflate_free_decompressor(d);
ASSERT(malloc_count == 2);
ASSERT(free_count == 2);
reset_state();
}
/* Test injecting memory allocation failures. */
static void do_fault_injection_test(void)
{
int level;
struct libdeflate_compressor *c;
struct libdeflate_decompressor *d;
libdeflate_set_memory_allocator(do_fail_malloc, do_free);
for (level = 0; level <= 12; level++) {
malloc_count = free_count = 0;
c = libdeflate_alloc_compressor(level);
ASSERT(c == NULL);
ASSERT(malloc_count == 1);
ASSERT(free_count == 0);
}
malloc_count = free_count = 0;
d = libdeflate_alloc_decompressor();
ASSERT(d == NULL);
ASSERT(malloc_count == 1);
ASSERT(free_count == 0);
reset_state();
}
int
tmain(int argc, tchar *argv[])
{
begin_program(argv);
do_custom_memalloc_test(true);
do_custom_memalloc_test(false);
do_options_test();
do_fault_injection_test();
return 0;
}