
// We only use a custom allocator on linux (for now)
// The allocator we chose is 'dlmalloc' (doug lea's malloc).
// It's a good allocator for single-threaded programs.
// It needs to be configured by setting a bunch of defines.
//
#ifdef __linux__

// We need this to define dlmalloc, not malloc.
#define USE_DL_PREFIX 1

// We don't need mspaces.
#define ONLY_MSPACES 0
#define MSPACES 0

// We don't need mallinfo
#define NO_MALLINFO 1

// We don't need dlmalloc_inspect_all
#define MALLOC_INSPECT_ALL 0

// Disable locking.  The entire engine is single-threaded.
#define USE_LOCKS 0

// This allocator can use sbrk to obtain RAM.  We're going to
// emulate sbrk, in order to put it in a different memory region
// than the system malloc, which also uses sbrk.
#define HAVE_MORECORE 1
#define MORECORE emulated_sbrk
#define MORECORE_CANNOT_TRIM 1

// We won't let the memory allocator use mmap.  This is because
// opening the replay log may be implemented in stdio using mmap.
// So using mmap might trigger different results under replay.
#define HAVE_MMAP 0

// Fix a warning in dlmalloc.
#define MAX_RELEASE_CHECK_RATE INT_MAX

// Don't generate random seeds, or time-based seeds.
#define USE_DEV_RANDOM 0
#define LACKS_TIME_H 1

// Don't export any dlmalloc functions.
#define DLMALLOC_EXPORT static inline

#include <sys/mman.h>
#include <cstdint>
#include <cassert>
#include <climits>

static char     *emulated_sbrk_base;
static intptr_t  emulated_sbrk_used;
static intptr_t  emulated_sbrk_limit;

// We assume that the increments are already aligned to the system
// page size, because dlmalloc does that for us.
//
// Our emulated sbrk cannot trim the memory size.  It can only grow.
//
static void *emulated_sbrk(intptr_t increment) {
    if (emulated_sbrk_base == 0) {
        emulated_sbrk_limit = intptr_t(64) * 1024 * 1024 * 1024;
        void *map = mmap(nullptr, emulated_sbrk_limit, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
        assert(map != MAP_FAILED);
        assert(map != nullptr);
        emulated_sbrk_base = (char *)map;
        emulated_sbrk_used = 0;
    }
    int64_t old_used = emulated_sbrk_used;
    int64_t new_used = emulated_sbrk_used + increment;
    if (new_used > emulated_sbrk_limit) {
        return (void *)(-1);
    }
    assert(new_used >= old_used);
    emulated_sbrk_used = new_used;
    if (new_used > old_used) {
        int status = mprotect(emulated_sbrk_base + old_used, new_used - old_used, PROT_READ | PROT_WRITE);
        assert(status == 0);
    }
    return emulated_sbrk_base + old_used;
}

#include "../../ext/dlmalloc.c"

namespace eng {
    static uint32_t hash;
    void *malloc(size_t size) {
        void *result = dlmalloc(size);
        hash += uint32_t(uintptr_t(result)) + 0x83748374;
        hash += hash << 10;
        hash ^= hash >> 6;
        hash += uint32_t(size) + 0x85893489;
        hash += hash << 10;
        hash ^= hash >> 6;
        return result;
    }
    void *realloc(void *p, size_t size) {
        void *result = dlrealloc(p, size);
        hash += uint32_t(uintptr_t(p)) + 0x74741912;
        hash += hash << 10;
        hash ^= hash >> 6;
        hash += uint32_t(uintptr_t(result)) + 0x68843823;
        hash += hash << 10;
        hash ^= hash >> 6;
        hash += uint32_t(size) + 0x81444120;
        hash += hash << 10;
        hash ^= hash >> 6;
        return result;
    }
    void  free(void *p) {
        hash += uint32_t(uintptr_t(p)) + 0x87823448;
        dlfree(p);
    }
    int memhash() {
        return (hash & 0x3FFFFFFF) | 0x40000000;
    }
} // namespace eng

#endif // ifdef __linux__


