#include <assert.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <sys/time.h>
#include <unistd.h>
#include <limits.h>

#include "postgres.h"
#include "utils/datum.h"
#include "utils/array.h"
#include "utils/memutils.h"

#ifdef PG_MODULE_MAGIC
PG_MODULE_MAGIC;
#endif

#define GET_AGG_CONTEXT(fname, fcinfo, aggcontext)  \
    if (! AggCheckCallContext(fcinfo, &aggcontext)) {   \
        elog(ERROR, "%s called in non-aggregate context", fname);  \
    }

/* prototypes */
PG_FUNCTION_INFO_V1(palloc_bench);
PG_FUNCTION_INFO_V1(palloc_bench_family);

// #define TRACKING_FLAG

Datum palloc_bench(PG_FUNCTION_ARGS);
Datum palloc_bench_family(PG_FUNCTION_ARGS);

Datum
palloc_bench(PG_FUNCTION_ARGS)
{

	/* info for anyelement */
	int i = 0;
	/* int32	ncontexts   = PG_GETARG_INT32(0); */
	int32	niterations = PG_GETARG_INT32(0);
	int32	allocsize   = PG_GETARG_INT32(1);

	struct timeval start_time, end_time;

	/* memory contexts */
	MemoryContext initctx = CurrentMemoryContext;
	MemoryContext ctx = initctx;


	ctx = AllocSetContextCreate(ctx,
								"test_context",
								ALLOCSET_DEFAULT_MINSIZE,
								ALLOCSET_DEFAULT_INITSIZE,
								ALLOCSET_DEFAULT_MAXSIZE);


	MemoryContextSwitchTo(ctx);

	gettimeofday(&start_time, NULL);
	
	for (i = 0; i < niterations; i++) {

		char * p = palloc(allocsize);
		
		if (p != NULL)
			pfree(p);

	}

	gettimeofday(&end_time, NULL);
	
	MemoryContextSwitchTo(initctx);

	elog(WARNING, "duration = %.2f ms", (end_time.tv_sec - start_time.tv_sec) * 1000 + (end_time.tv_usec - start_time.tv_usec) / 1000.0);

    PG_RETURN_VOID();

}

Datum
palloc_bench_family(PG_FUNCTION_ARGS)
{

	/* info for anyelement */
	int i = 0;
	/* int32	ncontexts   = PG_GETARG_INT32(0); */
	int32	niterations = PG_GETARG_INT32(0);
	int32	allocsize   = PG_GETARG_INT32(1);

	struct timeval start_time, end_time;

	/* memory contexts */
	MemoryContext initctx = CurrentMemoryContext;
	MemoryContext ctx = initctx;
	MemoryContext child_ctx;
	

	ctx = AllocSetContextCreate(ctx,
								"test_context",
								ALLOCSET_DEFAULT_MINSIZE,
								ALLOCSET_DEFAULT_INITSIZE,
								ALLOCSET_DEFAULT_MAXSIZE);
	
	/* account size with descent */
	MemoryContextSetCollectGroupSize(ctx);

	child_ctx = AllocSetContextCreate(ctx,
									  "child_context",
									  ALLOCSET_DEFAULT_MINSIZE,
									  ALLOCSET_DEFAULT_INITSIZE,
									  ALLOCSET_DEFAULT_MAXSIZE);
	


	MemoryContextSwitchTo(child_ctx);

	gettimeofday(&start_time, NULL);
	
	for (i = 0; i < niterations; i++) {

		char * p = palloc(allocsize);
		
		if (p != NULL)
			pfree(p);

	}

	gettimeofday(&end_time, NULL);
	
	MemoryContextSwitchTo(initctx);

	elog(WARNING, "duration = %.2f ms", (end_time.tv_sec - start_time.tv_sec) * 1000 + (end_time.tv_usec - start_time.tv_usec) / 1000.0);

    PG_RETURN_VOID();

}
