/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

#include <Memory.h>
#include <Processes.h>

#include "stdlib.h"

#include "TypesAndSwitches.h"
#include "MacMemAllocator.h"

#ifndef NSPR20
#include "prglobal.h"
#include "prmacos.h"
#include "prthread.h"
#include "prgc.h"
#include "swkern.h"
#else
#include "prlog.h"
#endif


#if STATS_MAC_MEMORY
#include "prglobal.h"
#include "prfile.h"

#include <unistd.h>
#include <string.h>
#endif
#if DEBUG_MAC_MEMORY
#include "MemoryTracker.h"


extern AllocationSet *	gFixedSizeAllocatorSet;
extern AllocationSet *	gSmallHeapAllocatorSet;
extern AllocationSet *	gLargeBlockAllocatorSet;
#endif

void	*		gOurApplicationHeapBase;
void	*		gOurApplicationHeapMax;

Boolean			gMemoryInitialized = false;

//##############################################################################
//##############################################################################
#pragma mark DECLARATIONS AND ENUMERATIONS

typedef struct MemoryCacheFlusherProcRec MemoryCacheFlusherProcRec;

struct MemoryCacheFlusherProcRec {
	MemoryCacheFlusherProc			flushProc;
	MemoryCacheFlusherProcRec		*next;
};

typedef struct PreAllocationProcRec PreAllocationProcRec;

struct PreAllocationProcRec {
	PreAllocationHookProc			preAllocProc;
	PreAllocationProcRec			*next;
};

MemoryCacheFlusherProcRec	*gFirstFlusher = NULL;
PreAllocationProcRec		*gFirstPreAllocator = NULL;
MallocHeapLowWarnProc		gMallocLowProc = NULL;

void CallPreAllocators(void);
void InitializeSubAllocators ( void );
Boolean ReclaimMemory(size_t amountNeeded);
long pascal MallocGrowZoneProc(Size cbNeeded);

#define	kMinLargeBlockHeapSize			(65 * 1024)		/* Min size of the heaps in temp mem try to allocate */
#define	kMaxLargeBlockHeapSize			(1024 * 1024)	/* Max size of the heaps in temp mem try to allocate */
#define	kLargeBlockInitialPercentage	(65)			/* This is the % of the free space in the heap used for the
															large block allocation heap */
															
#define	kMallocLowTempMemoryBoundary	(256 * 1024)	/* This is a boundary point at which we assume we're
															running low on temp memory */

#define kHeapEmergencyReserve			(64 * 1024)		/* Keep 64k free in the app heap for emergencies */


#if GENERATINGCFM
RoutineDescriptor 	gMallocGrowZoneProcRD = BUILD_ROUTINE_DESCRIPTOR(uppGrowZoneProcInfo, &MallocGrowZoneProc);
#else
#define gMallocGrowZoneProcRD MallocGrowZoneProc
#endif

//##############################################################################
//##############################################################################
#pragma mark FIXED-SIZE ALLOCATION DECLARATIONS

/* The format here is:

	DeclareFixedSizeAllocator(blockSize lower bound, # blocks in a heap chunk, # blocks in a tempmem chunk);

	DeclareSmallHeapAllocator(first chunk heap size, temp mem chunk heap size );
	
*/

// the real declarators
DeclareFixedSizeAllocator(4, 2600, 1000);
DeclareFixedSizeAllocator(8, 2000, 1000);
DeclareFixedSizeAllocator(12, 5000, 1000);
DeclareFixedSizeAllocator(16, 4000, 1000);
DeclareFixedSizeAllocator(20, 7000, 3000);
DeclareFixedSizeAllocator(24, 9500, 3000);
DeclareFixedSizeAllocator(28, 3200, 1000);
DeclareFixedSizeAllocator(32, 3400, 1000);
DeclareFixedSizeAllocator(36, 2500, 500);
DeclareFixedSizeAllocator(40, 9300, 5000);
DeclareSmallHeapAllocator(512 * 1024, 256 * 1024);
DeclareLargeBlockAllocator(kLargeBlockInitialPercentage, kMaxLargeBlockHeapSize, kMinLargeBlockHeapSize);

AllocMemoryBlockDescriptor gFastMemSmallSizeAllocators[] = {
	DeclareLargeBlockHeapDescriptor(),			//	0
	DeclareFixedBlockHeapDescriptor(4),			//	4
	DeclareFixedBlockHeapDescriptor(8),			//	8
	DeclareFixedBlockHeapDescriptor(12),		//	12
	DeclareFixedBlockHeapDescriptor(16),		//	16
	DeclareFixedBlockHeapDescriptor(20),		//	20
	DeclareFixedBlockHeapDescriptor(24),		//	24
	DeclareFixedBlockHeapDescriptor(28),		//	28
	DeclareFixedBlockHeapDescriptor(32),		//	32
	DeclareFixedBlockHeapDescriptor(36),		//	36
	DeclareFixedBlockHeapDescriptor(40),		//	40
	DeclareSmallSmallHeapDescriptor(),			//	44
	DeclareSmallSmallHeapDescriptor(),			//	48
	DeclareSmallSmallHeapDescriptor(),			//	52
	DeclareSmallSmallHeapDescriptor(),			//	56
	DeclareSmallSmallHeapDescriptor(),			//	60
	DeclareSmallSmallHeapDescriptor(),			//	64
	DeclareSmallSmallHeapDescriptor(),			//	68
	DeclareSmallSmallHeapDescriptor(),			//	72
	DeclareSmallSmallHeapDescriptor(),			//	76
	DeclareSmallSmallHeapDescriptor(),			//	80
	DeclareSmallSmallHeapDescriptor(),			//	84
	DeclareSmallSmallHeapDescriptor(),			//	88
	DeclareSmallSmallHeapDescriptor(),			//	92
	DeclareSmallSmallHeapDescriptor(),			//	96
	DeclareSmallSmallHeapDescriptor(),			//	100
	DeclareSmallSmallHeapDescriptor(),			//	104
	DeclareSmallSmallHeapDescriptor(),			//	108
	DeclareSmallSmallHeapDescriptor(),			//	112
	DeclareSmallSmallHeapDescriptor(),			//	116
	DeclareSmallSmallHeapDescriptor(),			//	120
	DeclareSmallSmallHeapDescriptor(),			//	124
	DeclareSmallSmallHeapDescriptor(),			//	128
	DeclareSmallSmallHeapDescriptor(),			//	132
	DeclareSmallSmallHeapDescriptor(),			//	136
	DeclareSmallSmallHeapDescriptor(),			//	140
	DeclareSmallSmallHeapDescriptor(),			//	144
	DeclareSmallSmallHeapDescriptor(),			//	148
	DeclareSmallSmallHeapDescriptor(),			//	152
	DeclareSmallSmallHeapDescriptor(),			//	156
	DeclareSmallSmallHeapDescriptor(),			//	160
	DeclareSmallSmallHeapDescriptor(),			//	164
	DeclareSmallSmallHeapDescriptor(),			//	168
	DeclareSmallSmallHeapDescriptor(),			//	172
	DeclareSmallSmallHeapDescriptor(),			//	176
	DeclareSmallSmallHeapDescriptor(),			//	180
	DeclareSmallSmallHeapDescriptor(),			//	184
	DeclareSmallSmallHeapDescriptor(),			//	188
	DeclareSmallSmallHeapDescriptor(),			//	192
	DeclareSmallSmallHeapDescriptor(),			//	196
	DeclareSmallSmallHeapDescriptor(),			//	200
	DeclareSmallSmallHeapDescriptor(),			//	204
	DeclareSmallSmallHeapDescriptor(),			//	208
	DeclareSmallSmallHeapDescriptor(),			//	212
	DeclareSmallSmallHeapDescriptor(),			//	216
	DeclareSmallSmallHeapDescriptor(),			//	220
	DeclareSmallSmallHeapDescriptor(),			//	224
	DeclareSmallSmallHeapDescriptor(),			//	228
	DeclareSmallSmallHeapDescriptor(),			//	232
	DeclareSmallSmallHeapDescriptor(),			//	236
	DeclareSmallSmallHeapDescriptor(),			//	240
	DeclareSmallSmallHeapDescriptor(),			//	244
	DeclareSmallSmallHeapDescriptor(),			//	248
	DeclareSmallSmallHeapDescriptor(),			//	252
	DeclareSmallSmallHeapDescriptor(),			//	256
};


//##############################################################################
//##############################################################################
#pragma mark-
#pragma mark INSTRUMENTATION

#if INSTRUMENT_MAC_MEMORY


InstHistogramClassRef		gSmallHeapHistogram = 0;
InstHistogramClassRef		gLargeHeapHistogram = 0;


static OSErr CreateInstrumentationClasses()
{
	OSErr	err = noErr;

	err = InstCreateHistogramClass(kInstRootClassRef, "MacMemAllocator:SmallHeap:Histogram",
			 40, 256, 4, kInstEnableClassMask, &gSmallHeapHistogram);
	if (err != noErr) return err;
	
	err = InstCreateHistogramClass(kInstRootClassRef, "MacMemAllocator:LargeHeap:Histogram",
			 256, 10240, 64, kInstEnableClassMask, &gLargeHeapHistogram);
	if (err != noErr) return err;
	
	return err;
}


#endif

//##############################################################################
//##############################################################################
#pragma mark-
#pragma mark INITIALIZATION

void MacintoshInitializeMemory(void)
{
	UInt32				i;
	ProcessSerialNumber	thisProcess = { 0, kCurrentProcess };
	ProcessInfoRec		processInfo;

// Increase the stack space.
// This is because PA_MDL_ParseTag can go into deep recursion when dealing with 
// malformed HTML comments (the only occurrence where we bombed).
#ifndef powerc
	SetApplLimit(GetApplLimit() - 16384);	
#endif
	MaxApplZone();
	for (i = 1; i <= 30; i++)
		MoreMasters();

	// init our new compact allocators
	
	processInfo.processInfoLength = sizeof(processInfo);
	processInfo.processName = NULL;
	processInfo.processAppSpec = NULL;

	GetProcessInformation(&thisProcess, &processInfo);
	gOurApplicationHeapBase = processInfo.processLocation;
	gOurApplicationHeapMax = (Ptr)gOurApplicationHeapBase + processInfo.processSize;

#if INSTRUMENT_MAC_MEMORY
	if (CreateInstrumentationClasses() != noErr)
	{
		DebugStr("\pFailed to initialize instrumentation classes");
		ExitToShell();
	}
#endif

#if DEBUG_MAC_MEMORY || STATS_MAC_MEMORY
	InstallMemoryManagerPatches();

#if DEBUG_MAC_MEMORY
	// Create some allocation sets to track our allocators
	gFixedSizeAllocatorSet = NewAllocationSet ( 0, "Fixed Size Compact Allocator" );
	gSmallHeapAllocatorSet = NewAllocationSet ( 0, "Small Heap Allocator" );
	gLargeBlockAllocatorSet = NewAllocationSet ( 0, "Large Block Allocator" );
	 
	// disable them so we don't get random garbage
	DisableAllocationSet ( gFixedSizeAllocatorSet );
	DisableAllocationSet ( gSmallHeapAllocatorSet );
	DisableAllocationSet ( gLargeBlockAllocatorSet );
#endif

#endif
	
	// intialize the sub allocators
	InitializeSubAllocators();
	
	gMemoryInitialized = true;
}

void InitializeSubAllocators ( void )
{
	SubHeapAllocationChunk *	chunk;

#ifdef MALLOC_IS_NEWPTR
	/* Don't allocate memory pools if just using NewPtr */
	return;
#endif

	/* fixed size allocators */
	chunk = FixedSizeAllocChunk ( 0, &gFixedSize4Root );
	PR_ASSERT(chunk);
	if ( chunk == NULL ) goto fail;
	
	chunk = FixedSizeAllocChunk ( 0, &gFixedSize8Root );
	PR_ASSERT(chunk);
	if ( chunk == NULL ) goto fail;

	chunk = FixedSizeAllocChunk ( 0, &gFixedSize12Root );
	PR_ASSERT(chunk);
	if ( chunk == NULL ) goto fail;

	chunk = FixedSizeAllocChunk ( 0, &gFixedSize16Root );
	PR_ASSERT(chunk);
	if ( chunk == NULL ) goto fail;

	chunk = FixedSizeAllocChunk ( 0, &gFixedSize20Root );
	PR_ASSERT(chunk);
	if ( chunk == NULL ) goto fail;

	chunk = FixedSizeAllocChunk ( 0, &gFixedSize24Root );
	PR_ASSERT(chunk);
	if ( chunk == NULL ) goto fail;

	chunk = FixedSizeAllocChunk ( 0, &gFixedSize28Root );
	PR_ASSERT(chunk);
	if ( chunk == NULL ) goto fail;

	chunk = FixedSizeAllocChunk ( 0, &gFixedSize32Root );
	PR_ASSERT(chunk);
	if ( chunk == NULL ) goto fail;

	chunk = FixedSizeAllocChunk ( 0, &gFixedSize36Root );
	PR_ASSERT(chunk);
	if ( chunk == NULL ) goto fail;

	chunk = FixedSizeAllocChunk ( 0, &gFixedSize40Root );
	PR_ASSERT(chunk);
	if ( chunk == NULL ) goto fail;
	
	chunk = SmallHeapAllocChunk ( 0, &gSmallHeapRoot );
	PR_ASSERT(chunk);
	if ( chunk == NULL ) goto fail;
	
	chunk = LargeBlockAllocChunk ( 0, &gLargeBlockRoot );
	PR_ASSERT(chunk);
	if ( chunk == NULL ) goto fail;

#if DEBUG_MAC_MEMORY && TRACK_EACH_ALLOCATOR
	gFixedSize4Root.header.set = NewAllocationSet ( 0, "Fixed Block 4" );
	DisableAllocationSet ( gFixedSize4Root.header.set );
	
	gFixedSize8Root.header.set = NewAllocationSet ( 0, "Fixed Block 8" );
	DisableAllocationSet ( gFixedSize8Root.header.set );
	
	gFixedSize12Root.header.set = NewAllocationSet ( 0, "Fixed Block 12" );
	DisableAllocationSet ( gFixedSize12Root.header.set );
	
	gFixedSize16Root.header.set = NewAllocationSet ( 0, "Fixed Block 16" );
	DisableAllocationSet ( gFixedSize16Root.header.set );
	
	gFixedSize20Root.header.set = NewAllocationSet ( 0, "Fixed Block 20" );
	DisableAllocationSet ( gFixedSize20Root.header.set );
	
	gFixedSize24Root.header.set = NewAllocationSet ( 0, "Fixed Block 24" );
	DisableAllocationSet ( gFixedSize24Root.header.set );
	
	gFixedSize28Root.header.set = NewAllocationSet ( 0, "Fixed Block 28" );
	DisableAllocationSet ( gFixedSize28Root.header.set );
	
	gFixedSize32Root.header.set = NewAllocationSet ( 0, "Fixed Block 32" );
	DisableAllocationSet ( gFixedSize32Root.header.set );
	
	gFixedSize36Root.header.set = NewAllocationSet ( 0, "Fixed Block 36" );
	DisableAllocationSet ( gFixedSize36Root.header.set );
	
	gFixedSize40Root.header.set = NewAllocationSet ( 0, "Fixed Block 40" );
	DisableAllocationSet ( gFixedSize40Root.header.set );
	
	gSmallHeapRoot.header.set = NewAllocationSet ( 0, "Small Block" );
	DisableAllocationSet ( gSmallHeapRoot.header.set );
	
	gLargeBlockRoot.header.set = NewAllocationSet ( 0, "Large Block" );
	DisableAllocationSet ( gLargeBlockRoot.header.set );
#endif

	return;
	
fail:
	/* We couldn't initialize one of the sub allocators, so we're screwed */
	/* I don't think we need an alert here as we should never hit this case unless */
	/* a user really mucks up our heap partition */
	ExitToShell();
}

#if STATS_MAC_MEMORY

/* I hate copy and paste */
static void WriteString ( PRFileHandle file, const char * string )
{
	long	len;
	long	bytesWritten;
	
	len = strlen( string );
	if ( len >= 1024 ) Debugger();
	bytesWritten = _OS_WRITE ( file, string, len );
	PR_ASSERT(bytesWritten == len);
}

static void WriteFixedAllocatorStats(PRFileHandle outFile, const FixedSizeAllocationRoot *root)
{
	char		outString[ 1024 ];
	
	WriteString ( outFile, "--------------------------------------------------------------------------------\n" );
	
	sprintf(outString, "Stats for fixed size allocator for blocks of %d-%d bytes\n", root->blockSize - 3, root->blockSize);
	WriteString ( outFile, outString );
	
	WriteString ( outFile, "--------------------------------------------------------------------------------\n" );
	
	WriteString ( outFile, "                     Current         Max\n" );
	WriteString ( outFile, "                  ----------     -------\n" );
	
	sprintf( outString,    "Num chunks:       %10d  %10d\n", root->chunksAllocated, root->maxChunksAllocated );
	WriteString ( outFile, outString );
	
	sprintf( outString,    "Chunk total:      %10d  %10d\n", root->totalChunkSize, root->maxTotalChunkSize );
	WriteString ( outFile, outString );
	
	sprintf( outString,    "Num blocks:       %10d  %10d\n", root->blocksAllocated, root->maxBlocksAllocated );
	WriteString ( outFile, outString );

	sprintf( outString,    "Block space:      %10d  %10d\n", root->blockSpaceUsed, root->maxBlockSpaceUsed );
	WriteString ( outFile, outString );
	
	sprintf( outString,    "Blocks used:      %10d  %10d\n", root->blocksUsed, root->maxBlocksUsed );
	WriteString ( outFile, outString );
		
	WriteString ( outFile, "                                 -------\n" );
	
	sprintf( outString,    "%s of allocated blocks used:   %10.2f\n", "%", 100.0 * root->maxBlocksUsed / root->maxBlocksAllocated );
	WriteString ( outFile, outString );

	sprintf( outString,    "%s of chunk space used:        %10.2f\n", "%", 100.0 * root->maxBlockSpaceUsed / root->maxTotalChunkSize );
	WriteString ( outFile, outString );
	
	WriteString ( outFile, "\n\n");
}

void DumpAllocatorMemoryStats(PRFileHandle outFile);

void DumpAllocatorMemoryStats(PRFileHandle outFile)
{
	/* fixed size allocators */
	WriteFixedAllocatorStats( outFile, &gFixedSize4Root );
	WriteFixedAllocatorStats( outFile, &gFixedSize8Root );
	WriteFixedAllocatorStats( outFile, &gFixedSize12Root );
	WriteFixedAllocatorStats( outFile, &gFixedSize16Root );
	WriteFixedAllocatorStats( outFile, &gFixedSize20Root );
	WriteFixedAllocatorStats( outFile, &gFixedSize24Root );
	WriteFixedAllocatorStats( outFile, &gFixedSize32Root );
	WriteFixedAllocatorStats( outFile, &gFixedSize36Root );
	WriteFixedAllocatorStats( outFile, &gFixedSize40Root );

/*	
	chunk = SmallHeapAllocChunk ( 0, &gSmallHeapRoot );
	PR_ASSERT(chunk);
	if ( chunk == NULL ) goto fail;
	
	chunk = LargeBlockAllocChunk ( 0, &gLargeBlockRoot );
	PR_ASSERT(chunk);
	if ( chunk == NULL ) goto fail;
*/

}

#endif /* STATS_MAC_MEMORY */


//##############################################################################
//##############################################################################
#pragma mark -
#pragma mark INSTALLING MEMORY MANAGER HOOKS

void InstallPreAllocationHook(PreAllocationHookProc newHook)
{
	PreAllocationProcRec			*preAllocatorRec;

	preAllocatorRec = (PreAllocationProcRec *)(NewPtr(sizeof(PreAllocationProcRec)));

	if (preAllocatorRec != NULL) {
	
		preAllocatorRec->next = gFirstPreAllocator;
		preAllocatorRec->preAllocProc = newHook;
		gFirstPreAllocator = preAllocatorRec;		
	
	}

}

void InstallMemoryCacheFlusher(MemoryCacheFlusherProc newFlusher)
{	
	MemoryCacheFlusherProcRec		*previousFlusherRec = NULL;
	MemoryCacheFlusherProcRec		*cacheFlusherRec = gFirstFlusher;
	
	while (cacheFlusherRec != NULL) {
		previousFlusherRec = cacheFlusherRec;
		cacheFlusherRec = cacheFlusherRec->next;
	}

	cacheFlusherRec =  (MemoryCacheFlusherProcRec *)NewPtrClear(sizeof(MemoryCacheFlusherProcRec));
	
	if (cacheFlusherRec == NULL)
		return;

	cacheFlusherRec->flushProc = newFlusher;

	if (previousFlusherRec != NULL) {
		previousFlusherRec->next = cacheFlusherRec;
	}
	
	else {
		gFirstFlusher = cacheFlusherRec;
	}
	
}

void CallPreAllocators(void)
{
	PreAllocationProcRec		*currentPreAllocator = gFirstPreAllocator;

	while (currentPreAllocator != NULL) {
		(*(currentPreAllocator->preAllocProc))();
		currentPreAllocator = currentPreAllocator->next;
	}
	
}

static MemoryCacheFlusherProc sGarbageCollectorCacheFlusher = NULL;

UInt8 CallCacheFlushers(size_t blockSize)
{
	MemoryCacheFlusherProcRec		*currentCacheFlusher = gFirstFlusher;
	UInt8							result = false;

	// we might want to remember which flusher was called last and start
	// at the one after, to avoid always flushing the first one (image cache)
	// first.
	
	while (currentCacheFlusher != NULL)
	{
		result |= (*(currentCacheFlusher->flushProc))(blockSize);
		currentCacheFlusher = currentCacheFlusher->next;
	}
	
	//	We used to try calling the GC if malloc failed, but that's
	//  a waste of time since the GC never frees segments (bug?)
	//  and thus won't increase heap space.
		
	return result;
	
}

void InstallGarbageCollectorCacheFlusher(const MemoryCacheFlusherProc inFlusher)
{
	sGarbageCollectorCacheFlusher = inFlusher;
}

void InstallMallocHeapLowProc( MallocHeapLowWarnProc proc )
{
	gMallocLowProc = proc;
}

void CallFE_LowMemory(void)
{
	if ( gMallocLowProc != NULL )
		{
		gMallocLowProc();
		}
}

//##############################################################################
//##############################################################################
#pragma mark -
#pragma mark SUB HEAP ALLOCATION

SubHeapAllocationChunk * AllocateSubHeap ( SubHeapAllocationRoot * root, Size heapSize, Boolean useTemp )
{
	SubHeapAllocationChunk *	heapBlock;
	Handle						tempHandle;
	OSErr						err;
	
	heapBlock = NULL;
	tempHandle = NULL;
	
#if DEBUG_MAC_MEMORY
	DisableMemoryTracker();
#endif

	if ( useTemp )
		{
		tempHandle = TempNewHandle ( heapSize, &err );
		
		if ( tempHandle == NULL ) 
		{
			// failed to make temp handle. Let's try a handle in our heap
			tempHandle = NewHandle( heapSize );
			
			// ensure that enough free mem is available for emergencies
			if ( tempHandle != NULL && MaxBlock() < kHeapEmergencyReserve)
			{
				// we need to keep some contiguous space free for emergencies. Give up.
				DisposeHandle(tempHandle);
				tempHandle = NULL;
			}
		}
		
		if ( tempHandle != NULL && err == noErr )
		{
			HLockHi ( tempHandle );				// lock the handle hi now to reduce system heap fragmentation
			heapBlock = *(SubHeapAllocationChunk **) tempHandle;
			}
		}
	else
		{
		heapBlock = (SubHeapAllocationChunk *) NewPtr ( heapSize );
		}
	
	if ( heapBlock != NULL )
		{
		heapBlock->root = root;
		heapBlock->refCon = tempHandle;
		heapBlock->next = NULL;
		heapBlock->usedBlocks = 0;
		heapBlock->freeDescriptor.freeRoutine = NULL;
		heapBlock->freeDescriptor.refcon = NULL;
		
		// whack this on the root's chunk list
		if ( root->lastChunk == NULL )
			{
			root->firstChunk = heapBlock;
			}
		else
			{
			root->lastChunk->next = heapBlock;
			}
		
		root->lastChunk = heapBlock;
		}
	
#if DEBUG_MAC_MEMORY
	EnableMemoryTracker();
#endif
	
	return heapBlock;
}


void FreeSubHeap ( SubHeapAllocationRoot * root, SubHeapAllocationChunk * chunk )
{
	Handle						tempHandle;
	SubHeapAllocationChunk *	list;
	SubHeapAllocationChunk *	prev;
	SubHeapAllocationChunk *	next;
	
	if ( chunk != NULL )
		{
		// run through the root's chunk list and remove our block
		prev = NULL;
		list = root->firstChunk;
		
		while ( list != NULL )
			{
			next = list->next;
			
			if ( list == chunk )
				{
				break;
				}
			
			prev = list;
			list = next;
			}
		
		if ( list != NULL )
			{
			if ( prev != NULL )
				{
				prev->next = next;
				}
			
			if ( root->firstChunk == list )
				{
				root->firstChunk = next;
				}
			
			if ( root->lastChunk == list )
				{
				root->lastChunk = prev;
				}
			}
			
		tempHandle = (Handle) chunk->refCon;
		if ( tempHandle != NULL )
			{
			DisposeHandle ( tempHandle );
			}
		else
			{
			DisposePtr ( (Ptr) chunk );
			}
		}
}


//##############################################################################
//##############################################################################
#pragma mark -
#pragma mark MEMORY UTILS

Boolean gInMemory_ReserveInMacHeap = false;

Boolean InMemory_ReserveInMacHeap()
{
	return gInMemory_ReserveInMacHeap;
}

Boolean ReclaimMemory(size_t amountNeeded)
{
	Boolean			result;
			
	result = CallCacheFlushers(amountNeeded);
		
	return result;

}

Boolean Memory_ReserveInMacHeap(size_t spaceNeeded)
{
	Boolean		result = true;
	
	gInMemory_ReserveInMacHeap = true;
	
	if (MaxBlock() < spaceNeeded)
		result = ReclaimMemory(spaceNeeded);
	
	gInMemory_ReserveInMacHeap = false;

	return result;
	
}

Boolean Memory_ReserveInMallocHeap(size_t spaceNeeded)
{
	Boolean		result = true;
	Size		freeMem;
	
	gInMemory_ReserveInMacHeap = true;
	
	freeMem = MaxBlock();
	
	if (freeMem < spaceNeeded)
		result = ReclaimMemory(spaceNeeded);

	gInMemory_ReserveInMacHeap = false;

	return result;
	
}

size_t Memory_FreeMemoryRemaining()
{
	size_t		mainHeap;
	size_t		mallocHeap;
	
	mainHeap = FreeMem();
	
	mallocHeap = FreeMem();
	
	return (mainHeap < mallocHeap) ? mainHeap : mallocHeap; 

}



