#define RZ_API
#define RZ_ALLOC_USE_MMAP 1
#define USE_MALLOC        0

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <string.h>

#if USE_MALLOC
RZ_API void rz_initmem(char *p, size_t s) {
}

RZ_API void *rz_malloc(size_t s) {
	return malloc(s);
}

RZ_API void rz_free(void *p) {
	free(s);
}
#else

typedef struct {
	int is_available;
	int size;
} MCB, *MCB_P;

static char *mem_start_p;
static int max_mem;
static int allocated_mem; /* this is the memory in use. */
static int mcb_count;
static char *heap_end;

enum { NEW_MCB = 0,
	NO_MCB,
	REUSE_MCB };
enum { FREE,
	IN_USE };

void InitMem(char *ptr, int size_in_bytes) {
	/* store the ptr and size_in_bytes in global variable */
	max_mem = size_in_bytes;
	mem_start_p = ptr;
	mcb_count = 0;
	allocated_mem = 0;
	heap_end = mem_start_p + size_in_bytes;
	memset(mem_start_p, 0x00, max_mem);
	/* This function is complete :-) */
}

RZ_API void *rz_malloc(int elem_size) {
	/* check whether any chunk (allocated before) is free first */
	MCB_P p_mcb;
	int flag = NO_MCB;
	int sz;

	p_mcb = (MCB_P)mem_start_p;
	sz = sizeof(MCB);

	if ((elem_size + sz) > (max_mem - (allocated_mem + mcb_count * sz)))
		return NULL;
	while (heap_end > ((char *)p_mcb + elem_size + sz)) {
		if (p_mcb->is_available == 0) {
			if (p_mcb->size == 0) {
				flag = NEW_MCB;
				break;
			}
			if (p_mcb->size >= (elem_size + sz)) {
				flag = REUSE_MCB;
				break;
			}
		}
		p_mcb = (MCB_P)((char *)p_mcb + p_mcb->size);
	}

	if (flag != NO_MCB) {
		p_mcb->is_available = 1;
		if (flag == NEW_MCB) {
			p_mcb->size = elem_size + sizeof(MCB);
		} else if (flag == REUSE_MCB) {
			elem_size = p_mcb->size - sizeof(MCB);
		}
		mcb_count++;
		allocated_mem += elem_size;
		return ((char *)p_mcb + sz);
	}
	return NULL;
}

void rz_free(void *p) {
	/* Mark in MCB that this chunk is free */
	MCB_P ptr = (MCB_P)p;
	ptr--;

	if (ptr->is_available != FREE) {
		mcb_count--;
		ptr->is_available = FREE;
		allocated_mem -= (ptr->size - sizeof(MCB));
	}
}
#endif

#if __MAIN__
int main() {
#define MB            1024 * 1024
#define RZ_MALLOC_MAX 2 * MB

#if RZ_ALLOC_USE_STACK
	char B[RZ_MALLOC_MAX];
#endif
#if RZ_ALLOC_USE_MMAP
	int fd = open(".mem", O_CREAT | O_RDWR, 0600);
	ftruncate(fd, RZ_MALLOC_MAX);
	char *B = mmap(NULL, RZ_MALLOC_MAX, PROT_WRITE | PROT_READ,
		MAP_FILE | MAP_PRIVATE, fd, 0);
	unlink(".mem");
	close(fd);
#endif

	InitMem(B, RZ_MALLOC_MAX);

	char *a = rz_malloc(10);
	if (!a) {
		printf("can't malloc\n");
		return 1;
	}
	strcpy(a, "hello");
	char *b = rz_malloc(10);
	strcpy(b, "world");
	printf("%p: %s\n%p: %s\n", a, a, b, b);
	rz_free(b);
	rz_free(a);
	return 0;
}
#endif
