/* -------------------------------------------------------------------------
 *
 * memutils.h
 *	  This file contains declarations for memory allocation utility
 *	  functions.  These are functions that are not quite widely used
 *	  enough to justify going in utils/palloc.h, but are still part
 *	  of the API of the memory management subsystem.
 *
 *
 * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * src/include/utils/memutils.h
 *
 * -------------------------------------------------------------------------
 */
#ifndef MEMUTILS_H
#define MEMUTILS_H

#include "nodes/memnodes.h"
#include "pgtime.h"
#include "storage/proc.h"

enum MemoryContextType {
    STANDARD_CONTEXT,  // postgres orignal context
    STACK_CONTEXT,     // a simple context, do not support free single pointer
    SHARED_CONTEXT,    // shared context used by different threads
    MEMALIGN_CONTEXT,  // the context only used to allocate the aligned memory
    MEMALIGN_SHRCTX,   // the shared context only used to allocate the aligned memory
    RACK_CONTEXT,      // borrow memory context
};

extern bool EnableBorrowWorkMemory();
extern bool RackMemoryBusy(int64 used);
extern Size GetAvailRackMemory(int dop);

/*
 * MaxAllocSize
 *		Quasi-arbitrary limit on size of allocations.
 *
 * Note:
 *		There is no guarantee that allocations smaller than MaxAllocSize
 *		will succeed.  Allocation requests larger than MaxAllocSize will
 *		be summarily denied.
 *
 * XXX This is deliberately chosen to correspond to the limiting size
 * of varlena objects under TOAST.	See VARSIZE_4B() and related macros
 * in postgres.h.  Many datatypes assume that any allocatable size can
 * be represented in a varlena header.
 *
 * XXX Also, various places in aset.c assume they can compute twice an
 * allocation's size without overflow, so beware of raising this.
 */
#define MaxAllocSize ((Size)0x3fffffff) /* 1 gigabyte - 1 */

#define MaxBuildAllocSize ((Size)0x3ffffff) /* 64MB - 1 */

#define AllocSizeIsValid(size) ((Size)(size) <= MaxAllocSize)

#define MaxAllocHugeSize ((Size)-1 >> 1) /* SIZE_MAX / 2 */

#define AllocHugeSizeIsValid(size) ((Size)(size) <= MaxAllocHugeSize)

/*
 * All chunks allocated by any memory context manager are required to be
 * preceded by a StandardChunkHeader at a spacing of STANDARDCHUNKHEADERSIZE.
 * A currently-allocated chunk must contain a backpointer to its owning
 * context as well as the allocated size of the chunk.	The backpointer is
 * used by pfree() and repalloc() to find the context to call.	The allocated
 * size is not absolutely essential, but it's expected to be needed by any
 * reasonable implementation.
 */
typedef struct StandardChunkHeader {
    MemoryContext context; /* owning context */
    Size size;             /* size of data space allocated in chunk */
#ifdef MEMORY_CONTEXT_CHECKING
    /* when debugging memory usage, also store actual requested size */
    Size requested_size;
#endif
#ifdef MEMORY_CONTEXT_TRACK
    const char* file;
    int line;
#endif
} StandardChunkHeader;

#define STANDARDCHUNKHEADERSIZE MAXALIGN(sizeof(StandardChunkHeader))

// Process wise memory context
//
extern MemoryContext AdioSharedContext;
extern MemoryContext ProcSubXidCacheContext;
extern MemoryContext PmTopMemoryContext;
extern MemoryContext StreamInfoContext;
extern MemoryContext DMSDrcContext;

extern THR_LOCAL PGDLLIMPORT MemoryContext ErrorContext;

/*
 * Memory-context-type-independent functions in mcxt.c
 */
extern void MemoryContextInit(void);
#define MemoryContextReset(context) \
    (((MemoryContext)context)->mcxt_methods->mcxt_reset(context))
#define MemoryContextDelete(context) \
    (((MemoryContext)context)->mcxt_methods->mcxt_delete(context))
#define MemoryContextDeleteChildren(context, list) \
    (((MemoryContext)context)->mcxt_methods->mcxt_delete_children(context, list))
#define MemoryContextDestroyAtThreadExit(context) \
    (((MemoryContext)context)->mcxt_methods->mcxt_destroy(context))
#define MemoryContextResetAndDeleteChildren(context) \
    (((MemoryContext)context)->mcxt_methods->mcxt_reset_and_delete_children(context))
#define MemoryContextSetParent(context, new_parent) \
    (((MemoryContext)context)->mcxt_methods->mcxt_set_parent(context, new_parent))

extern void MemoryContextResetChildren(MemoryContext context);
extern MemoryContext MemoryContextGetParent(MemoryContext context);
extern bool MemoryContextIsEmpty(MemoryContext context);
extern void MemoryContextStats(MemoryContext context);
extern void MemoryContextSeal(MemoryContext context);
extern void MemoryContextUnSeal(MemoryContext context);
extern void MemoryContextUnSealChildren(MemoryContext context);
extern void MemoryContextAllowInCriticalSection(MemoryContext context, bool allow);
extern bool MemoryContextContains(MemoryContext context, void* pointer);
extern MemoryContext MemoryContextOriginal(const char* node);
extern Size GetMemoryTotalSpace(MemoryContext context, bool recurse);
extern Size GetMemoryChildrenTotalSpace(MemoryContext context, bool recurse);
extern Size GetMemoryChunkSpace(void* pointer);
extern MemoryContext GetMemoryChunkContext(void* pointer);

#ifdef MEMORY_CONTEXT_CHECKING
#define MemoryContextCheck(context, own_by_session) \
    (((MemoryContext)context)->mcxt_methods->mcxt_check(context, own_by_session))
#define MemoryContextCheck2(mctx)
#endif

extern void std_MemoryContextReset(MemoryContext context);
extern void std_MemoryContextDelete(MemoryContext context);
extern void std_MemoryContextDeleteChildren(MemoryContext context, List* context_list = NULL);
extern void std_MemoryContextDestroyAtThreadExit(MemoryContext context);
extern void std_MemoryContextResetAndDeleteChildren(MemoryContext context);
extern void std_MemoryContextSetParent(MemoryContext context, MemoryContext new_parent);
extern void std_MemoryContextCheck(MemoryContext context, bool own_by_session);

extern void opt_MemoryContextReset(MemoryContext context);
extern void opt_MemoryContextDelete(MemoryContext context);
extern void opt_MemoryContextDeleteChildren(MemoryContext context, List* context_list = NULL);
extern void opt_MemoryContextDestroyAtThreadExit(MemoryContext context);
#define opt_MemoryContextResetAndDeleteChildren(context) \
    opt_MemoryContextReset(context)
extern void opt_MemoryContextSetParent(MemoryContext context, MemoryContext new_parent);
#ifdef MEMORY_CONTEXT_CHECKING
extern void opt_MemoryContextCheck(MemoryContext context, bool own_by_session);
#endif

/*
 * This routine handles the context-type-independent part of memory
 * context creation.  It's intended to be called from context-type-
 * specific creation routines, and noplace else.
 */
extern MemoryContext MemoryContextCreate(
    NodeTag tag, Size size, MemoryContext parent, const char* name, const char* file, int line);

/* Interface for PosgGIS and has the same structure with PG's MemoryContextCreate*/
extern MemoryContext MemoryContextCreate(
    NodeTag tag, Size size, MemoryContextMethods* methods, MemoryContext parent, const char* name);

extern MemoryContext opt_MemoryContextCreate(MemoryContext node, NodeTag tag,
    MemoryContextMethods *methods, MemoryContext parent, const char* name);
/*
 * Memory-context-type-specific functions
 */
#define DEFAULT_MEMORY_CONTEXT_MAX_SIZE 0                  /* 0 MB as default value for AllocSetContextCreat function */
#define SHARED_MEMORY_CONTEXT_MAX_SIZE (100 * 1024 * 1024) /* 100 MB */
/* aset.c */
extern MemoryContext AllocSetContextCreate(MemoryContext parent, const char* name, Size minContextSize,
    Size initBlockSize, Size maxBlockSize, MemoryContextType type = STANDARD_CONTEXT,
    Size maxSize = DEFAULT_MEMORY_CONTEXT_MAX_SIZE, bool isSession = false);
extern MemoryContext opt_AllocSetContextCreate(MemoryContext parent,
    const char* name, Size minContextSize, Size initBlockSize, Size maxBlockSize);

/*
 * Recommended default alloc parameters, suitable for "ordinary" contexts
 * that might hold quite a lot of data.
 */
#define ALLOCSET_DEFAULT_MINSIZE 0
#define ALLOCSET_DEFAULT_INITSIZE (8 * 1024)
#define ALLOCSET_DEFAULT_MAXSIZE (8 * 1024 * 1024)
#define ALLOCSET_DEFAULT_SIZES ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE

/*
 * Recommended alloc parameters for "small" contexts that are not expected
 * to contain much data (for example, a context to contain a query plan).
 */
#define ALLOCSET_SMALL_MINSIZE 0
#define ALLOCSET_SMALL_INITSIZE (1 * 1024)
#define ALLOCSET_SMALL_MAXSIZE (8 * 1024)
#define ALLOCSET_SMALL_SIZES ALLOCSET_SMALL_MINSIZE, ALLOCSET_SMALL_INITSIZE, ALLOCSET_SMALL_MAXSIZE

/* default grow ratio for sort and materialize when it spreads */
#define DEFAULT_GROW_RATIO 2.0

/* default auto spread min ratio, and memory spread under this ratio is abondaned */
#define MEM_AUTO_SPREAD_MIN_RATIO 0.1

// AutoContextSwitch
//      Auto object for memoryContext switch
//
class AutoContextSwitch {
public:
    AutoContextSwitch(MemoryContext memContext)
    {
        if (memContext == NULL) {
            m_oldMemContext = CurrentMemoryContext;
        } else {
            m_oldMemContext = MemoryContextSwitchTo(memContext);
        }
    };

    ~AutoContextSwitch()
    {
        MemoryContextSwitchTo(m_oldMemContext);
    }

private:
    MemoryContext m_oldMemContext;
};

/* In case we haven't init u_sess->when we try to create memory context and alloc memory */
#define IS_USESS_AVAILABLE (likely(u_sess != NULL))
#define GS_MP_INITED (t_thrd.utils_cxt.gs_mp_inited)
#define MEMORY_TRACKING_MODE (IS_USESS_AVAILABLE ? u_sess->attr.attr_memory.memory_tracking_mode : 0)
#define MEMORY_TRACKING_QUERY_PEAK (IS_USESS_AVAILABLE ? (u_sess->attr.attr_memory.memory_tracking_mode == MEMORY_TRACKING_PEAKMEMORY ) : 0)
#define ENABLE_MEMORY_CONTEXT_CONTROL \
    (IS_USESS_AVAILABLE ? u_sess->attr.attr_memory.enable_memory_context_control : false)
#define MEMORY_FAULT_PERCENT (IS_USESS_AVAILABLE ? u_sess->attr.attr_resource.memory_fault_percent : 0)
#define STATEMENT_MAX_MEM (IS_USESS_AVAILABLE ? u_sess->attr.attr_sql.statement_max_mem : 0)

#define SLAB_DEFAULT_BLOCK_SIZE     (8 * 1024)
#define SLAB_LARGE_BLOCK_SIZE       (8 * 1024 * 1024)

#endif /* MEMUTILS_H */