//========================================================================
// E library
//
// - Memory management and memory-tracker
//
// AUTHOR:  Gabor Nagy
// DATE:    2026-Aug-12 22:26:50
//
// Copyright (C) 1996-2026 by Gabor Nagy. All rights reserved.
//========================================================================
#ifndef _EMalloc_h
#define _EMalloc_h

#include <sys/types.h>
#include <string.h>
#include <stddef.h>  // For wchar_t

#ifdef __APPLE__
	#include <stdlib.h>
#else
	#include <malloc.h>
#endif

#include <ETypes.h>

#include <stdint.h>  // For uint32_t etc. for E_Random


#if defined (__cplusplus) && (!(defined (__CPLUSLIBS)))
extern "C" {
#endif

#if __GNUC__ >= 2 && !defined __OPTIMIZE__ && !defined __APPLE__ && !defined sgi && !defined WASM
// Add -lbfd to the app's link like when this is enabled (Linux)
// sudo apt install binutils-dev
// sudo apt install libiberty-dev
#define	EBACKTRACE  0
#else
#define	EBACKTRACE  0
#endif


// Do memory profiling
// 0 : OFF
// 1 : Take over malloc(), emalloc() etc., but no memory tracking. Only optionally total process memory check
// 2 : Take over malloc(), emalloc() etc., with memory tracking.
#define EMemDEBUG  0


#if EMemDEBUG >= 1

extern void*  EMalloc(const size_t PBSize);
extern void*  ECalloc(const size_t PNMemb, const size_t PBSize);
extern void*  ERealloc(void* PBlock, const size_t PBSize);
extern void   EFree(void* PBlock);

#else

#define EMalloc(pSize)           malloc(pSize)
#define ECalloc(pN, pSize)       calloc(pN, pSize)
#define ERealloc(pBlock, pSize)  realloc(pBlock, pSize)
#define EFree(pBlock)            free(pBlock)

#endif


#if EMemDEBUG >= 2
extern char*  EMem_Name;
extern EBool  EMem_Verbose;
extern char*  EMem_File;
extern int    EMem_Line;
extern char*  EMem_Function;

extern EIndex	EMem_BlocksPrintStart;
extern EIndex	EMem_Blocks_Count;

#define E_MemDebug\
	EMem_Function = (char*)__FUNCTION__;\
	EMem_File = (char*)__FILE__;\
	EMem_Line = __LINE__



extern void   EPrintUnfreedMemory(void);


#else // EMemDEBUG

#define E_MemDebug

#endif // EMemDEBUG

extern void*  EMallocClr(const size_t PBSize);

#define EMallocT(pType, pSize, pDst)     do { E_MemDebug;pDst = (pType)EMalloc(pSize); } while(0)
#define EMallocTClr(pType, pSize, pDst)  do { const size_t  lSize = pSize;E_MemDebug;pDst = (pType)EMalloc(lSize);memset(pDst, 0, lSize); } while(0)
#define ENew(pDst)                       do { E_MemDebug;const EIndex  lSize = sizeof(*(pDst));pDst = (typeof(pDst))EMalloc(lSize); } while(0)
#define ENewT(pType, pDst)               do { E_MemDebug;const EIndex  lSize = sizeof(pType);pDst = (pType*)EMalloc(lSize); } while(0)
#define ENewNT(pType, pCount, pDst)      do { E_MemDebug;pDst = (pType*)EMalloc(sizeof(pType) * (pCount)); } while(0)

#define ENewClr(pDst)                    do { E_MemDebug;const EIndex  lSize = sizeof(*(pDst));pDst = (typeof(pDst))EMalloc(lSize);if(pDst) memset(pDst, 0, lSize); } while(0)
#define ENewSClr(pSize, pDst)            do { E_MemDebug;const EIndex  lSize = pSize;pDst = (typeof(pDst))EMalloc(lSize);if(pDst) memset(pDst, 0, lSize); } while(0)
#define ENewTClr(pType, pDst)            do { E_MemDebug;const EIndex  lSize = sizeof(pType);pDst = (pType*)EMalloc(lSize);if(pDst) memset(pDst, 0, lSize); } while(0)

#define ENewN(pCount, pDst)              do { E_MemDebug;pDst = (typeof(pDst))EMalloc(sizeof(pDst[0]) * (pCount)); } while(0)
#define ENewSN(pSize, pCount, pDst)      do { E_MemDebug;pDst = (typeof(pDst))EMalloc((pSize) * (pCount)); } while(0)

#define ENewNClr(pCount, pDst)           do { E_MemDebug;const EIndex  lSize = sizeof(pDst[0]) * (pCount);pDst = (typeof(pDst[0])*)EMalloc(lSize);memset(pDst, 0, lSize); } while(0)
#define ENewSNClr(pSize, pCount, pDst)   do { E_MemDebug;const EIndex  lSize = (pSize) * (pCount);pDst = (typeof(pDst[0])*)EMalloc(lSize);memset(pDst, 0, lSize); } while(0)


//================================================
// Resize an array the type is given
//================================================
#define EResizeNT(pType, pCount,  pBuffer) do\
{\
	const EIndex  llCount = pCount;if(llCount > 0)\
	{\
		E_MemDebug;\
		pType*  lTBuffer = (pType*)ERealloc(pBuffer, sizeof(pType) * llCount);\
		if(lTBuffer) pBuffer = lTBuffer;\
	}\
	else\
	{\
		EFreeClr(pBuffer);\
	}\
} while(0)


//================================================
// Resize an array the type is inferred
//================================================
#define EResizeN(pCount, pBuffer) do\
{\
	const EIndex  llCount = pCount;if(llCount > 0)\
	{\
		E_MemDebug;\
		typeof(pBuffer[0])*  lTBuffer = (typeof(pBuffer[0])*)ERealloc(pBuffer, sizeof(typeof(pBuffer[0])) * llCount);\
		if(lTBuffer) pBuffer = lTBuffer;\
	}\
	else\
	{\
		EFreeClr(pBuffer);\
	}\
} while(0)


#define EList_Resize(pList, pCount) do\
{\
	EResizeNT(typeof((pList)[0]), pCount, pList);\
	pList##_Count = pCount;\
} while(0)

#define EList_Resize_s(pList, pCount) do\
{\
	EResizeNT(typeof(pList.Items[0]), pCount, (pList).Items);\
	(pList).Count = pCount;\
} while(0)

#define EList_ResizeA_s(pList, pCount) do\
{\
	EResizeNT(typeof(pList.Items[0]), pCount, (pList).Items);\
	(pList).Count = (pList).CountAllocated = pCount;\
} while(0)

#define EList_ResizeClr_s(pList, pCount) do\
{\
	const EIndex  lCount = pCount;\
	EResizeNT(typeof((pList).Items[0]), lCount, (pList).Items);\
	(pList).Count = lCount;\
	memset((pList).Items, 0, sizeof((pList).Items[0]) * lCount);\
} while(0)


#define EList_ResizeN(pList, pCount) do\
{\
	EResizeNT(typeof(pList[0]), pCount, pList);\
} while(0)


#define EList_SizeCheckA(pList, pNewNElements) do\
{\
	const EIndex  lN = pNewNElements;if(pList##_CountAllocated < lN)\
	{\
		EResizeNT(typeof(pList[0]), lN, pList);pList##_CountAllocated = lN;\
	}\
} while(0)


#define EList_Copy(pSrc, pDst) do\
{\
	const EIndex  lCount = pSrc##_Count;\
	EResizeNT(typeof(pDst[0]), lCount, pDst);\
	memcpy(pDst, pSrc, sizeof(pDst[0]) * lCount);\
	pDst##_Count = lCount;\
} while(0)


//================================================================
// Resize memory buffer and clear the newly added segment
//================================================================
#define EResizeNClr(pType, pOldCount, pNewCount,  pBuffer) do\
{\
	const EIndex  lOldCount = pOldCount;\
	const EIndex  lNewCount = pNewCount;if(lNewCount)\
	{\
		E_MemDebug;\
		pType*  lBuffer = (pType*)ERealloc(pBuffer, sizeof(pType) * lNewCount);if(lBuffer)\
		{\
			pBuffer = lBuffer;\
			if(lNewCount > lOldCount) memset(lBuffer + lOldCount, 0, sizeof(pType) * (lNewCount - lOldCount));\
		}\
	}\
	else EFreeClr(pBuffer);\
} while(0)


//================================================================
// Resize memory buffer
// - Expose temporary variable, so the outcome can be queried
//================================================================
#define EResizeNN(pType, pCount,  pBuffer, pTBuffer) do\
{\
	const EIndex  lCount = pCount;if(lCount > 0)\
	{\
		E_MemDebug;\
		pTBuffer = (pType*)ERealloc(pBuffer, sizeof(pType) * lCount);if(pTBuffer) pBuffer = pTBuffer;\
	}\
	else\
	{\
		EFreeClr(pBuffer);pTBuffer = NULL;\
	}\
} while(0)


//================================================
// Resize memory buffer and return new count
//================================================
#define EResizeNC(pType, pCount,  pBuffer, pCountOut) do\
{\
	const EIndex  llCount = pCount;if(llCount > 0)\
	{\
		E_MemDebug;\
		pType*  pTBuffer = (pType*)ERealloc(pBuffer, sizeof(pType) * llCount);\
		if(pTBuffer) { pBuffer = pTBuffer;pCountOut = llCount; }\
	}\
	else\
	{\
		EFreeClr(pBuffer);\
		pCountOut = llCount;\
	}\
} while(0)


//================================================
// Resize memory buffer and return new count
//================================================
#define EResizeSNC(pType, pSize, pCount,  pBuffer, pCountOut) do\
{\
	const EIndex  llCount = pCount;if(llCount > 0)\
	{\
		E_MemDebug;\
		pType*  pTBuffer = (pType*)ERealloc(pBuffer, (pSize) * llCount);\
		if(pTBuffer) { pBuffer = pTBuffer;pCountOut = llCount; }\
	}\
	else\
	{\
		EFreeClr(pBuffer);\
		pCountOut = llCount;\
	}\
} while(0)


//================================================
// Resize memory buffer and return new count
//================================================
#define EResizeNCA(pType, pCount,  pBuffer, pCountOut, pCountAllocatedOut) do\
{\
	const EIndex  llCount = pCount;if(llCount > 0)\
	{\
		E_MemDebug;\
		pType*  pTBuffer = (pType*)ERealloc(pBuffer, sizeof(pType) * llCount);\
		if(pTBuffer) { pBuffer = pTBuffer;pCountOut = pCountAllocatedOut = llCount; }\
	}\
	else\
	{\
		EFreeClr(pBuffer);\
		pCountOut = pCountAllocatedOut = llCount;\
	}\
} while(0)


#define EFreeClr(p)     do { EFree(p);p = NULL; } while(0)
#define EFreeChk(p)     do { if(p) EFree(p); } while(0)
#define EFreeChkClr(p)  do { if(p) { EFree(p);p = NULL; } } while(0)


#define ECopyN(pSrc, pDst, pCount) do\
{\
	const EIndex  lCount = pCount;\
	E_MemDebug;\
	if(lCount > 0)\
	{\
		const size_t  lSize = sizeof(pSrc[0]) * lCount;\
		pDst = (typeof(&pSrc[0]))EMalloc(lSize);if(pDst)\
		{\
			memcpy(pDst, pSrc, lSize);\
		}\
	}\
	else pDst = NULL;\
} while(0)

#define ECopyN_t(pSrc, pDst, pCount, pType) do\
{\
	const EIndex  lCount = pCount;\
	E_MemDebug;\
	if(lCount > 0)\
	{\
		pDst = (pType*)EMalloc(sizeof(pType) * (lCount));if(pDst)\
		{\
			memcpy(pDst, pSrc, sizeof(pType) * lCount);\
		}\
	}\
	else pDst = NULL;\
} while(0)

#define ECopyNChk(pSrc, pDst, pCount) do\
{\
	const EIndex  lCount = pCount;\
	E_MemDebug;\
	EFreeChk(pDst);\
	if(lCount > 0)\
	{\
		pDst = (typeof((pSrc)[0])*)EMalloc(sizeof((pSrc)[0]) * lCount);if(pDst)\
		{\
			memcpy(pDst, pSrc, sizeof((pSrc)[0]) * lCount);\
		}\
	}\
	else pDst = NULL;\
} while(0)

#define ECopyNChk_t(pSrc, pDst, pCount, pType) do\
{\
	const EIndex  lCount = pCount;\
	E_MemDebug;\
	EFreeChk(pDst);\
	if(lCount > 0)\
	{\
		pDst = (pType*)EMalloc(sizeof(pType) * (lCount));if(pDst)\
		{\
			memcpy(pDst, pSrc, sizeof(pType) * lCount);\
		}\
	}\
	else pDst = NULL;\
} while(0)

#define ECopyN_p(pSrc, pDst, pField) do\
{\
	const EIndex  lElementSize = sizeof((pSrc)->pField[0]);\
	if(pDst->pField) { EFree(pDst->pField);pDst->pField = NULL;pDst->pField##_Count = 0; }\
	const EIndex  lCount = pSrc->pField##_Count;if(lCount > 0)\
	{\
		pDst->pField = (typeof((pSrc)->pField[0])*)EMalloc(lElementSize * lCount);if(pDst->pField)\
		{\
			memcpy(pDst->pField, pSrc->pField, lElementSize * lCount);\
			pDst->pField##_Count = lCount;\
		}\
	}\
} while(0)


extern char*      EStrDup(const char* PString);
extern char*      EStrDupLine(const char* PString);
extern uint8_t*   EStrDupU(const uint8_t* PString);
extern char*      EStrNDup(const char* PString, const EIndex PLen);
extern wchar_t*   EStrDup8toW(const char* PString);
extern char*      EStrDupWto8(const wchar_t* PString);
extern wchar_t*   EStrDupW(const wchar_t* PString);
extern uint16_t*  EStrDupW16(const uint16_t* PString);
extern char*      EStrDupWtoUTF8(const wchar_t* PString);
extern char*      EStrNDupWtoUTF8(const wchar_t* PWString, const EIndex PLength);

extern size_t     EStrLenW(const wchar_t* PString);
extern size_t     EStrLenW16(const uint16_t* PString);
extern size_t     EStrLenW16Unaligned(const uint8_t* PString);
extern size_t     EStrLenW32Unaligned(const uint8_t* PString);

extern char*      EStrDupSuffix(const char* PString, const char* PSuffix);
extern const char* EStr_StringAfterPrefix(const char* PStr, const char* PPrefix);



//================================================
// Generate random bits
//================================================
static inline uint32_t _RandomBits(ERandomState* PState)
{
	const uint64_t  LSum = (uint64_t)2111111111ul * (uint64_t)PState[3] +
	                       (uint64_t)1492 * (uint64_t)(PState[2]) +
	                       (uint64_t)1776 * (uint64_t)(PState[1]) +
	                       (uint64_t)5115 * (uint64_t)(PState[0]) +
	                       (uint64_t)PState[4];
	PState[3] = PState[2];PState[2] = PState[1];PState[1] = PState[0];
	PState[4] = (uint32_t)(LSum >> 32);            // Carry
	PState[0] = (uint32_t)(LSum);                  // Low 32 bits of sum
	return(PState[0]);
} 


//================================================
// Random number between 0.0 and 1.0
//================================================
static inline double E_RandomD(ERandomState* PState)
{
	return((double)_RandomBits(PState) * (1.0 / (65536.0 * 65536.0)));
}


//================================================
// Random number between 0.0 and 0.9999999...
//================================================
static inline double E_RandomDN(ERandomState* PState)
{
	return((double)_RandomBits(PState) * (1.0 / (65536.0 * 65536.0 + 1.0)));
}


//========================================================
// Returns integer random number in desired interval
//========================================================
static inline int E_IRandom(const int min, const int max, ERandomState* PState)
{
// Output random integer in the interval min <= x <= max
// Relative error on frequencies < 2^-32
//
	if(max <= min)
	{
		if(max == min) return min; else return 0x80000000;
	}

// Multiply interval with random and truncate
//
	const int  r = (int)((max - min + 1) * E_RandomD(PState)) + min; 
	if(r > max) return(max);
	else return(r);
}



//extern double  E_RandomD(ERandomState* PState);
//extern double  E_RandomDN(ERandomState* PState);


#define EStackAlloc(pSize, pBuffer)    ((pSize) <= sizeof(pBuffer) ? (EPointer)(pBuffer) : EMalloc(pSize))
#define EStackFree(pPointer, pBuffer)  { if((pPointer) != ((EPointer)(pBuffer))) EFree(pPointer); }

extern ECodePoint  E_CodePoint;

extern void        E_MallocInit(void);
extern void        E_MallocFree(void);

extern void        E_RandomInit(const uint32_t seed, ERandomState* PState);

extern int         Easprintf(char** PResult, const char* PFormatStr, ...);

extern long long   E_GetMemoryUse(long long* PTotalRAMRet);


#if EBACKTRACE
	#include <execinfo.h>
	#define EM_Backtrace(pStack, pCount)  backtrace(pStack, pCount)
	#define _EM_backtrace(pCallStack)     backtrace(pCallStack, EMAX_CALLSTACK_DEPTH)

	#define E_CallStackPrint(pPrintCurrent, pMaxLevels)\
	{\
		const int  lExecutableNameLen = E_ExecutableName ? strlen(E_ExecutableName) : 0;\
\
		ECallerInfo  lCaller;\
		lCaller.FileName = __FILE__;\
		lCaller.Function = __FUNCTION__;\
		lCaller.Line = __LINE__;\
		lCaller.CallStackDepth = _EM_backtrace(lCaller.CallStack);\
		if(pPrintCurrent)\
		{\
			printf("  %s() %s:%d\n", lCaller.Function, lCaller.FileName, lCaller.Line - 1);fflush(stdout);\
		}\
\
		if(lCaller.CallStackDepth > 0)\
		{\
			int  lLevels;\
			if(pMaxLevels == 0) lLevels = lCaller.CallStackDepth;\
			else if(pMaxLevels <= lCaller.CallStackDepth) lLevels = pMaxLevels;\
			else lLevels = lCaller.CallStackDepth;\
			_E_CallStackPrint(lCaller.CallStack, lLevels, 1, lExecutableNameLen);\
		}\
	}
#else
	#define EM_Backtrace(pStack, pCount)	0
	#define _EM_backtrace(pCallStack)	0
//#define _EM_backtrace(pCallStack)	do { printf("  %s>%s %s() %s:%d\n",  ECodeGreen, ECodeNormal,  __FUNCTION__, __FILE__, __LINE__,);fflush(stdout); } while(0)

	#define E_CallStackPrint(pPrintCurrent, pMaxLevels)\
	{\
		printf("%s() %s:%d E_CallStackPrint not supported. Make sure you're running a debug build.\n", __FUNCTION__, __FILE__, __LINE__);fflush(stdout);\
	}
#endif




#if defined (__cplusplus) && (!(defined (__CPLUSLIBS)))
}	// Close scope of 'extern "C"' declaration
#endif

#endif	// _EMalloc_h
