/**
 * @file ccd_arena_shim.h
 * @brief Points libccd's deallocation at libakgl's arena. Injected, not included.
 *
 * libccd allocates through one macro -- `__CCD_ALLOC_MEMORY` in
 * `deps/libccd/src/alloc.h`, which every one of `CCD_ALLOC`, `CCD_ALLOC_ARR`
 * and `CCD_REALLOC_ARR` expands to -- and that one is redirected on the command
 * line, in `CMakeLists.txt`, where it is visible next to the sources it applies
 * to.
 *
 * Deallocation has no such seam. libccd calls `free()` directly at four sites
 * (`ccd.c:170`, `polytope.h:200`, `:222`, `:247`), so a macro is the only way to
 * reach them without patching a submodule we do not own. This file is that
 * macro, injected into libccd's translation units with `-include` and into
 * nothing else.
 *
 * **`<stdlib.h>` is included first and that ordering is the whole point.**
 * `-include` places this file ahead of the translation unit's own text, so
 * defining `free` before pulling `<stdlib.h>` in would rewrite the C library's
 * own declaration of it -- `void akgl_ccd_arena_free(void *)` where the header
 * meant to declare `free`. Including it here first leaves that declaration
 * intact, and `alloc.h`'s own `#include <stdlib.h>` later is a no-op through its
 * include guard.
 *
 * Passing an arena pointer to the real `free(3)` is undefined behaviour, so a
 * site this macro fails to reach is a heap corruption rather than a leak. What
 * proves the interception is total is that `akgl_ccd_arena_used()` returns to 0
 * after a narrowphase call, which `tests/collision.c` asserts.
 */

#ifndef _AKGL_CCD_ARENA_SHIM_H_
#define _AKGL_CCD_ARENA_SHIM_H_

#include <stdlib.h>

/*
 * libccd's own allocation header, reached through the PRIVATE include path
 * CMakeLists.txt puts deps/libccd/src on. Pulling it in *here* is what lets the
 * redefinition below stick: it defines __CCD_ALLOC_MEMORY unconditionally, so a
 * definition made before it would simply be overwritten, and the translation
 * unit's own `#include "alloc.h"` later is a no-op through its include guard.
 *
 * This was originally a -D on the compile line. CMake's COMPILE_DEFINITIONS
 * cannot carry a function-like macro -- the parentheses and comma do not
 * survive -- and it dropped the definition without a diagnostic, so libccd went
 * on calling the C library's realloc while the `free` below quietly discarded
 * the result. That leaks rather than crashes, and no test of the *outcome* --
 * do these two boxes collide -- would have noticed. What catches it is
 * asserting the wiring: that a query allocates from the arena at all.
 */
#include <alloc.h>

#include <akgl/collision_arena.h>

#undef __CCD_ALLOC_MEMORY
#define __CCD_ALLOC_MEMORY(type, ptr_old, size)			\
    ((type *)akgl_ccd_arena_realloc((void *)(ptr_old), (size)))

#undef free
#define free(p) akgl_ccd_arena_free(p)

#endif
