/*static char *SCCSID = "@(#)heap.c	6.2 91/01/14";*/
#define SCCSID "@(#)heap.c	6.2 91/01/14"
/*
 *	IBM/Microsoft Confidential
 *
 *	Copyright (c) IBM Corporation 1987, 1989
 *	Copyright (c) Microsoft Corporation 1987, 1989
 *
 *	All Rights Reserved
 *
 */


/*
 *  HEAP.C
 *
 *    DESCRIPTION
 *
 *      Performs operations on the HEAP structures.
 *
 *
 *    FUNCTIONS DEFINED
 *
 *      InitHeap ()     -   Allocate and initialize a new segment.
 *
 *      GetHeapChunk () -   Get a chunk of memory of a certain size.
 *
 *      AddHeapSeg ()   -   Allocate and add a new HEAP segment to the
 *                          linked list of HEAPS.
 *
 *      RemoveHeapSeg() -   Discard the remaining bytes of a segment if
 *                          below a minimum amount.    
 *
 *
 *  REVISION HISTORY (in reverse chronological order)
 *  -----------------------------------------------------------------------
 *  10/30/89    Brian Andrew    Initial versions of InitHeap, GetHeapChunk,
 *                              AddHeapSeg and RemoveHeapSeg.
 *
 *
 *  TO DO
 *  -------------------------------------------------------------------------
 */
#define INCL_DOSERRORS
#define INCL_DOS
#define INCL_NOPMAPI

#include <os2.h>
#include <fsh.h>

#include "cdfs.h"
#include "sem.h"
#include "path.h"
#include "vol.h"
#include "file.h"
#include "buffer.h"
#include "mem.h"
#include "heap.h"
#include "message.h"

#ifdef STATS_H
#include "stats.h"
#endif

#include "fsdata.h"
#include "pr_heap.h"

#ifdef DEBUG
#include "pr_debug.h"
#endif


/***    InitHeap () -   Initial a HEAP segment.  
 *     
 *
 *  DESCRIPTION
 *
 *      This function allocates and initializes a heap segment.  It is
 *      passed the address to store the address of the new heap into.
 *      The return value is whether or not the allocation was successful.
 *      
 *
 *  ENTRY
 *
 *      pphHeap -   Pointer to address to store address of allocated HEAP.
 *
 *
 *  LOCAL
 *
 *      sSel    -   Selector to hold the allocated segment selector.
 *      usRet   -   Return value from function.
 *
 *
 *  EXIT
 *
 *      Return value from FSH_SEGALLOC.
 */

USHORT InitHeap (pphHeap)
PPHEAP  pphHeap;
{
	SEL     sSel;
	USHORT  usRet;

#ifdef DEBUG
	dprintf ("\tInitHeap:  Allocating a new heap\n");
	Int3 ();
#endif

	/* Try to allocate the segment */
	if (!(usRet = FSH_SEGALLOC (SWAP_ALLOC, HEAP_ALLOC, (PSEL) &sSel)))
	{
		*pphHeap = MAKEP (sSel, 0);
		(*pphHeap)->cbHeapSize = HEAP_ALLOC;
		(*pphHeap)->phNextHeap = NULL;

#ifdef STATS_H
		CurSt.alloc_faults++;
#endif

	} /* end if allocation successful */

#ifdef DEBUG
	/* else display message */
	else
	{
		dprintf ("\tInitHeap:  Unable to allocate Heap\n");
	}
#endif

	return usRet;

} /* end InitHeap */


/***    GetHeapChunk () -   Get a block of a certain size.
 *     
 *
 *  DESCRIPTION
 *
 *      This function copies the address of a certain sized block of
 *      memory into a passed pointer.  It walks through a linked list
 *      of HEAP structures to find the first with at least the required
 *      number of bytes.
 *      
 *      If none of the existing HEAPS has enough memory, InitHeap will be
 *      called to allocate a new one.
 *
 *      If after removing the required number of bytes, the remaining bytes
 *      are less than the minimum HEAP size, the segment is discarded.
 *
 *
 *  ENTRY
 *
 *      ppChar      -   Pointer to address to store address of memory chunk.
 *      cbChunkSize -   Number of bytes in this chunk.
 *
 *
 *  LOCAL
 *
 *      phNext      -   Pointer into the heap after the chunk removed.
 *      pphPrev     -   Pointer to address of previous heap.
 *      usRet       -   Return value from function.    
 *
 *
 *  EXIT
 *
 *      Return value from InitHeap if called.
 *      NO_ERROR otherwise.
 */

USHORT GetHeapChunk (ppChar, cbChunkSize)
PCHAR    FAR *ppChar;
USHORT              cbChunkSize;
{
	PHEAP       phNext;
	PPHEAP      pphPrev;
	USHORT      usRet;

	/* Assume operation is successful */
	usRet = NO_ERROR;

	/* Grab the heap semaphore */
	SEM_REQ (sHeap);

	/* Get the address of phHeapStart */
	pphPrev = &phHeapStart;

	/*
	 *  March through the list until *pphPrev is either the 
	 *  HEAP to take the chunk out of, or *pphPrev is NULL.  If NULL
	 *  then we need to try to allocate a new HEAP.
	 */
	while (*pphPrev && ((*pphPrev)->cbHeapSize < cbChunkSize))
	{
		pphPrev = &((*pphPrev)->phNextHeap);

	} /* end while walk through HEAPs */

	/*
	 *  We now check whether *pphPrev is NULL or not.  If not null, we
	 *  remove the chunk size.  If the remaining bytes are less than a
	 *  minimum, we discard that heap.
	 *
	 *  If the current heap is NULL, we allocate a new segment.  If the
	 *  allocation fails, we return the error code from the allocation.
	 */ 
	if (!*pphPrev)
	{
		usRet = InitHeap (pphPrev);

	} /* end if get new heap */

	/* Continue if no error */
	if (!usRet)
	{
		*ppChar = (CHAR FAR *) *pphPrev;

		/* If remaining bytes are too few, discard the heap */
		if (((*pphPrev)->cbHeapSize - cbChunkSize) < MIN_HEAP_SIZE)
		{
			*pphPrev = (*pphPrev)->phNextHeap;

		} /* end if too few bytes */

		/* Else modify the pointer to the start of this segment */
		else
		{
			phNext = (PHEAP) ((PCHAR) *pphPrev + cbChunkSize);
			phNext->cbHeapSize = (*pphPrev)->cbHeapSize - cbChunkSize;
			phNext->phNextHeap = (*pphPrev)->phNextHeap;
			*pphPrev = phNext;

		} /* end else shrink heap */

	} /* end if no errors */

	/* Release the heap semaphore */
	SEM_CLEAR (sHeap);

	return usRet;

} /* end GetHeapChunk */


