8. Implementing malloc and free

The MPS function mps_free() is unlike the Standard C Library function free() in that it takes a size argument. That’s because it’s nearly always the case that either the size of a block is known statically based on its type (for example, a structure), or else the size of the block is easily computed from information that needs to be stored anyway (for example, a vector), and so memory can be saved by not storing the size separately. It’s also better for virtual memory performance, as a block does not have to be touched in order to free it.

But sometimes you need to interact with foreign code which requires malloc() and free() (or a pair of functions with the same interface). In this situation you can implement this interface using a global pool variable, and putting the size of each block into its header, like this:

#include "mps.h"

static mps_pool_t malloc_pool;

typedef union {
    size_t size;
    char alignment[MPS_PF_ALIGN]; /* see note below */
} header_u;

void *malloc(size_t size) {
    mps_res_t res;
    mps_addr_t p;
    header_u *header;
    size += sizeof *header;
    res = mps_alloc(&p, malloc_pool, size);
    if (res != MPS_RES_OK)
        return NULL;
    header = p;
    header->size = size;
    return header + 1;
}

void free(void *p) {
    if (p) {
        header_u *header = ((header_u *)p) - 1;
        mps_free(malloc_pool, header, header->size);
    }
}

The alignment member of the header_u union ensures that allocations are aligned to the platform’s natural alignment (see Alignment).

The pool needs to belong to a manually managed pool class, for example MVFF (Manual Variable First Fit) (or its debugging counterpart):

#include "mpscmvff.h"

void malloc_pool_init(mps_arena_t arena) {
    mps_res_t res;
    res = mps_pool_create_k(&malloc_pool, arena, mps_class_mvff(), mps_args_none);
    if (res != RES_OK)
        abort();
}