/****************************************************************
 *								*
 * Copyright (c) 2003-2019 Fidelity National Information	*
 * Services, Inc. and/or its subsidiaries. All rights reserved.	*
 *								*
 *	This source code contains the intellectual property	*
 *	of its copyright holder(s), and is made available	*
 *	under a license.  If you do not know the terms of	*
 *	the license, please stop and do not read further.	*
 *								*
 ****************************************************************/

#ifndef GTM_MALLOC_H__included
#define GTM_MALLOC_H__included

#define GTM_MEMORY_RESERVE_DEFAULT 64	/* 64K reserve "backpocket-cache" released on out-of-memory error */

typedef size_t gtm_msize_t;

/* Each allocated block has the following structure. The actual address returned to the user for 'malloc' and supplied by the
 * user for 'free' is actually the storage beginning at the 'userStorage.userStart' area. This holds true even for storage
 * that is truely malloc'd. Note that true allocated length is kept even in the pro header.
 */
typedef struct storElemStruct
{	/* While the following chars and short are not the best for performance, they enable us to keep the header size to
	 * 8 bytes in a pro build. This is important since our minimum allocation size is 16 bytes leaving 8 bytes for data.
	 * Also I have not researched what they are, there are a bunch of 8 byte allocates in GT.M that if we were to go to
	 * a 16 byte header would make the minimum block size 32 bytes thus doubling the storage requirements for these small
	 * blocks. SE 03/2002 [Note 16 byte header is the norm in 64 bit]
	 */
	signed char	queueIndex;			/* Index into TwoTable for this size of element */
	unsigned char	state;				/* State of this block */
	unsigned short	extHdrOffset;			/* For MAXTWO sized elements: offset to the
							 * header that describes the extent.
							 */
	GTM64_ONLY(char filler[4];) 			/* Explicit filler to align the length - may be repurposed */
	gtm_msize_t	realLen;			/* Real (total) length of allocation */
#	ifdef DEBUG
	struct	storElemStruct	*fPtr;			/* Next storage element on free/allocated queue */
	struct	storElemStruct	*bPtr;			/* Previous storage element on free/allocated queue */
	unsigned char	*allocatedBy;			/* Who allocated storage */
	gtm_msize_t	allocLen;			/* Requested length of allocation */
	gtm_msize_t	smTn;				/* Storage management transaction number allocated at */
	unsigned char	headMarker[GTM64_ONLY(8)NON_GTM64_ONLY(4)];	/* Header that should not be modified during usage */
	union
	{
		struct storElemStruct *deferFreeNext;	/* Pointer to next deferred free block */
		unsigned char	userStart;		/* First byte of user useable storage */
	} userStorage;
#	else
	union						/* In production mode, the links are used only when element is free */
	{
		struct storElemStruct *deferFreeNext;	/* Pointer to next deferred free block */
		struct					/* Free block information */
		{
			struct	storElemStruct	*fPtr;	/* Next storage element on free queue */
			struct	storElemStruct	*bPtr;	/* Previous storage element on free queue */
		} links;
		unsigned char	userStart;		/* First byte of user useable storage */
	} userStorage;
#	endif
} storElem;

size_t gtm_bestfitsize(size_t);
void verifyFreeStorage(void);
void verifyAllocatedStorage(void);
void raise_gtmmemory_error(void);
void printMallocInfo(void);
void printMallocDump(void);

/* When verifying the storage chains, check the allocated chain first in case overruns for allocated storage
 * have damaged the free chains. This way we find the culprit rather than the symptom. Of course, in the case
 * where free'd storage is continued to be used, this method breaks down but it is hoped the chosen way finds
 * the greater number of issues rather than their symptoms.
 */
#define VERIFY_STORAGE_CHAINS			\
{						\
	GBLREF uint4	gtmDebugLevel;		\
	if (GDL_SmAllocVerf & gtmDebugLevel)	\
		verifyAllocatedStorage();	\
	if (GDL_SmFreeVerf & gtmDebugLevel)	\
		verifyFreeStorage();		\
}

#endif /* GTM_MALLOC_H__included */
