/****************************************************************
 *								*
 * Copyright (c) 2001-2015 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.	*
 *								*
 ****************************************************************/

#include "mdef.h"

#include "gtm_string.h"

#include "buddy_list.h"
#include "gdsroot.h"
#include "gtm_facility.h"
#include "fileinfo.h"
#include "gdsbt.h"
#include "gdsfhead.h"
#include "relqop.h"

#define MAX_MEM_SIZE_IN_BITS     63     /* the unreachable maximum size of memory (in bits) */

void    initialize_list(buddy_list *list, int4 elemSize, int4 initAlloc)
{
        int4    i, maxPtrs;
        int4    temp;

        if (!initAlloc)
                initAlloc = 1;

	assert(0 < elemSize);
	assert(0 < initAlloc);

	elemSize = ROUND_UP2(elemSize, 8) ;	/* make it 8 byte aligned */
	temp = initAlloc;
        while (temp & (temp - 1))     		/* floor it to a perfect power of 2 */
                temp = temp & (temp - 1);
	if (initAlloc != temp)
		initAlloc = temp << 1;
	for (i = 0; initAlloc >> i; i++);

	list->initAllocBits = i - 1;
        list->elemSize = elemSize;
        list->initAlloc = initAlloc;
        list->cumulMaxElems = initAlloc;
        list->nElems = 0;
	/* Note: size_t typecast done below to enable 64-bit arithmetic so sizes > 4GB can be allocated */
        list->ptrArray = (char **)malloc((size_t)SIZEOF(char *) * (MAX_MEM_SIZE_IN_BITS + 2));
						/* +2 = +1 for holding the NULL pointer and +1 for ptrArray[0] */
        memset(list->ptrArray, 0, SIZEOF(char *) * (MAX_MEM_SIZE_IN_BITS + 2));
        list->ptrArrayCurr = list->ptrArray;
	/* Note: size_t typecast done below to enable 64-bit arithmetic so sizes > 4GB can be allocated */
        list->nextFreePtr = list->ptrArray[0] = (char *)malloc((size_t)initAlloc * elemSize);
	list->free_que = NULL; /* initialize the list to have no free element queue */
	DEBUG_ONLY(list->used_free_last_n_elements = FALSE;)
	DEBUG_ONLY(list->used_free_element = FALSE;)
	DEBUG_ONLY(list->nElems_greater_than_one = FALSE;)
}

/* Any changes to this routine need corresponding changes to the VERIFY_LIST_IS_REINITIALIZED macro (defined in buddy_list.h) */
void	reinitialize_list(buddy_list *list)
{
	assert(list);
	list->nElems = 0;
	list->cumulMaxElems = list->initAlloc;
	list->ptrArrayCurr = list->ptrArray;
	list->nextFreePtr = list->ptrArray[0];
	list->free_que = NULL; /* reset the list to have no free element queue */
	DEBUG_ONLY(list->used_free_last_n_elements = FALSE;)
	DEBUG_ONLY(list->used_free_element = FALSE;)
	DEBUG_ONLY(list->nElems_greater_than_one = FALSE;)
}

boolean_t	free_last_n_elements(buddy_list *list, int4 num)
{
	int4	rowElemsMax, rowElemsLeft, numLeft, nElems;
	char	**ptrArrayCurr;

	assert(list);
	assert(!list->used_free_element);
	DEBUG_ONLY(list->used_free_last_n_elements = TRUE;)
	nElems = list->nElems;
	if (nElems >= num)
	{
		ptrArrayCurr = list->ptrArrayCurr;
		numLeft = num;
		rowElemsLeft = (int4)(list->nextFreePtr - ptrArrayCurr[0]) / list->elemSize;
		rowElemsMax = nElems - rowElemsLeft;
		while (numLeft >= rowElemsLeft  &&  ptrArrayCurr != list->ptrArray)
		{
			assert(0 == rowElemsMax % list->initAlloc);
			numLeft -= rowElemsLeft;
			list->cumulMaxElems -= rowElemsMax;
			if (rowElemsMax != list->initAlloc)
				rowElemsMax = rowElemsMax >> 1;
			rowElemsLeft = rowElemsMax;
			ptrArrayCurr--;
			assert(rowElemsMax >= list->initAlloc);
		}
		list->nElems = nElems - num;
		list->ptrArrayCurr = ptrArrayCurr;
		list->nextFreePtr = ptrArrayCurr[0] + list->elemSize * (rowElemsLeft - numLeft);
		assert(list->ptrArrayCurr >= list->ptrArray);
		return TRUE;
	} else
		return FALSE;
}

char    *get_new_element(buddy_list *list, int4 nElements)
{
        char    *retPtr;
        char    **ptrArrayCurr;
	int4	cumulMaxElems, nElems, elemSize;

	if (0 >= nElements)
	{
		assert(FALSE);
		return NULL;
	}
	DEBUG_ONLY(if (1 < nElements) list->nElems_greater_than_one = TRUE;)
	nElems = list->nElems;
	cumulMaxElems = list->cumulMaxElems;
	elemSize = list->elemSize;
        if (nElems + nElements <= cumulMaxElems)
        {
                retPtr = list->nextFreePtr;
                list->nextFreePtr += nElements * elemSize;
                list->nElems = nElems + nElements;
        } else
        {
		do
		{
			ptrArrayCurr = ++list->ptrArrayCurr;
			/* Note: size_t typecast done below to enable 64-bit arithmetic so sizes > 4GB can be allocated */
			if (!(retPtr = *ptrArrayCurr))
				retPtr = *ptrArrayCurr = (char *)malloc((size_t)cumulMaxElems * elemSize);
			nElems = cumulMaxElems;
			cumulMaxElems *= 2;
		} while (nElems + nElements > cumulMaxElems);
                list->nElems = nElems + nElements;
                list->nextFreePtr = retPtr + elemSize * nElements;
		list->cumulMaxElems = cumulMaxElems;
        }
        return retPtr;
}

char	*get_new_free_element(buddy_list *list)
{
	char	*elem;

	assert(!list->used_free_last_n_elements);
	assert(!list->nElems_greater_than_one);
	DEBUG_ONLY(list->used_free_element = TRUE;)
	/* Assert that each element has enough space to store a pointer. This will be used to maintain the singly linked list
	 * of freed up elements in the buddy list. The head of this list will be list->free_que.
	 */
	assert(SIZEOF(char *) <= list->elemSize);
	elem = list->free_que;
	if (NULL != elem)
	{
		list->free_que = *(char **)elem;
		assert(elem != list->free_que);
		return elem;
	}
	return get_new_element(list, 1);
}

void	free_element(buddy_list *list, char *elem)
{
	assert(!list->used_free_last_n_elements);
	assert(!list->nElems_greater_than_one);
	DEBUG_ONLY(list->used_free_element = TRUE;)
	assert(elem);
	assert(elem != list->free_que);
	/* Add it to the singly linked list of freed up elements in the buddy_list */
	*(char **)elem = list->free_que;
	list->free_que = elem;
}

char    *find_element(buddy_list *list, int4 index)
{
        int4    i, initAllocBits;

	assert(!list->nElems_greater_than_one);
        if (index >= list->nElems)
                return NULL;
	initAllocBits = list->initAllocBits;
	for (i = initAllocBits; index >> i; i++)	/* not sure if "ceil_log2_32bit" would be faster here */
		;
	i = i - initAllocBits;
	return list->ptrArray[i] + list->elemSize * (index - list->initAlloc * (i ? 1 << (i-1) : 0));
}

void    cleanup_list(buddy_list *list)
{
        char    **curr;

	assert(list);
	if (!list ||  !(curr = list->ptrArray))
		return;
        while(*curr)
	{
                free(*curr);
                curr++;
	}
        free(list->ptrArray);
}

/* Copy the first "numElems" elements of a buddy_list into a contiguous buffer pointed to by "dst".
 * This function assumes caller has allocated "dst" as appropriate.
 * Returns : The # of bytes copied onto "dst".
 */
size_t	copy_list_to_buf(buddy_list *list, int4 numElems, char *dst)
{
	int4	cumulMaxElems, curElems, copyElems, elemSize;
	size_t	copysize, totalsize;
        char    **ptrArrayCurr;

	assert(numElems);
	assert(list->nElems >= numElems);
	assert(!list->used_free_element);
	assert(!list->nElems_greater_than_one);
	cumulMaxElems = list->initAlloc;
	curElems = cumulMaxElems;
	elemSize = list->elemSize;
	ptrArrayCurr = list->ptrArray;
	totalsize = 0;
	do
	{
		if (numElems < curElems)
		{
			copyElems = numElems;
			numElems = 0;
		} else
		{
			copyElems = curElems;
			numElems = numElems - copyElems;
		}
		copysize = (size_t)copyElems * elemSize;
		memcpy(dst, *ptrArrayCurr, copysize);
		totalsize += copysize;
		if (!numElems)
			break;
		dst += copysize;
		curElems = cumulMaxElems;
		cumulMaxElems *= 2;
		ptrArrayCurr++;
	} while (TRUE);
	return totalsize;
}
