#include <stdlib.h>
#include <stdio.h>
#include "rts/gc.h"
#include "rts/profile.h"

#if _JHC_GC == _JHC_GC_BOEHM

void hs_perform_gc(void) {
        GC_gcollect();
}

void jhc_alloc_init(void) { GC_INIT(); }
void jhc_alloc_fini(void) { }

#elif _JHC_GC == _JHC_GC_NONE

// memory allocated in 1MB chunks.
#define JHC_MEM_CHUNK_SIZE (1 << 20)

static char initial_chunk[JHC_MEM_CHUNK_SIZE];

static void *jhc_current_chunk = initial_chunk;
static unsigned mem_chunks,mem_offset;

void jhc_alloc_init(void) {}

void
jhc_alloc_fini(void) {
        if(_JHC_PROFILE) {
                jhc_printf_stderr("Memory Allocated: %u bytes\n", (JHC_MEM_CHUNK_SIZE*(mem_chunks)) + mem_offset);
                print_alloc_size_stats();
        }
}

static void
jhc_malloc_grow(void) {
        void *c = malloc(JHC_MEM_CHUNK_SIZE);
        if(!c) {
                jhc_fputs_stderr("Out of memory!\n");
                abort();
        }
        mem_chunks++;
        jhc_current_chunk = c;
        mem_offset = 0;
}

#define M_ALIGN(a,n) ((n) - 1 + ((a) - ((n) - 1) % (a)))

static inline void * A_MALLOC
jhc_malloc_basic(size_t n) {
        n = M_ALIGN(sizeof(void *),n);
        if (n > (JHC_MEM_CHUNK_SIZE - mem_offset))
                jhc_malloc_grow();
        void *ret = jhc_current_chunk + mem_offset;
        mem_offset += n;
        return ret;
}

#if _JHC_DEBUG

void * A_MALLOC
jhc_malloc_debug(size_t n,int line,int atomic) {
        alloc_count(n,atomic);
        void *ret = jhc_malloc_basic(n + sizeof(uintptr_t));
        *((uintptr_t *)ret) = line;
        return ret + sizeof(uintptr_t);
}

#else

void * A_MALLOC
jhc_malloc(size_t n) {
        alloc_count(n,0);
        return jhc_malloc_basic(n);
}

#undef jhc_malloc_atomic
void * A_MALLOC
jhc_malloc_atomic(size_t n) {
        alloc_count(n,1);
        return jhc_malloc_basic(n);
}

#endif

#endif
