/* -*- 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 <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "fastmem.h"
#include "prmacos.h"
#include <OSUtils.h>
#include <Processes.h>

//##############################################################################
//##############################################################################
#pragma mark malloc DEBUG DECLARATIONS

#if STATS_MAC_MEMORY
UInt32 gSmallAllocationTotalCountTable[1025];
UInt32 gSmallAllocationActiveCountTable[1025];
UInt32 gSmallAllocationMaxCountTable[1025];
#endif

#if DEBUG_MAC_MEMORY

enum {
	kActiveSiteHashTableSize		= 128,
	kActiveSizeListSize				= 1024
};

enum {
	kFreeBlockHeaderTag		= 'FREE',
	kFreeBlockTrailerTag	= 'free',
	kUsedBlockHeaderTag		= 'USED',
	kUsedBlockTrailerTag	= 'used',
	kRefdBlockHeaderTag		= 'REFD',
	kRefdBlockTrailerTag	= 'refd',
	kFreeMemoryFillPattern 	= 0xEF,
	kUsedMemoryFillPattern	= 0xDB
};

MemoryBlockHeader			*gFirstAllocatedBlock = NULL;
MemoryBlockHeader			*gLastAllocatedBlock = NULL;

enum {
	kAllocationSiteMalloc			= 0,
	kAllocationSiteNewHandle		= 1,
	kAllocationSiteNewPtr			= 2
};

typedef struct AllocationSite AllocationSite;

struct AllocationSite {
	UInt32							type;
	AllocationSite					*next;
	void							*whoAllocated[kRecordingDepthOfStackLevels];
	UInt32							mallocs;
	UInt32							frees;
	UInt32							totalAllocations;
};

AllocationSite		*gActiveSizeHashTable[kActiveSiteHashTableSize];
AllocationSite		gActiveSizeList[kActiveSizeListSize];
AllocationSite		*gCurrentActiveSize = gActiveSizeList;
UInt32				gRemainingSitesInList = kActiveSizeListSize;

AllocationSite *AddAllocationSite(UInt32 allocationType, void **allocatorStack);
void AddBlockToActiveList(void *newBlock, AllocationSite *alocationSite);
void RemoveBlockFromActiveList(void *deadBlock);

UInt32				gOutstandingPointers = 0;
UInt32				gOutstandingHandles = 0;

#endif

//##############################################################################
//##############################################################################
#pragma mark -
#pragma mark malloc DEBUG CONTROL VARIABLES

#if DEBUG_MAC_MEMORY

UInt32						gVerifyHeapOnEveryMalloc			= 0;
UInt32						gVerifyHeapOnEveryFree				= 0;
UInt32						gFillUsedBlocksWithPattern			= 0;
UInt32						gFillFreeBlocksWithPattern			= 1;
UInt32						gTrackLeaks							= 0;
UInt32						gDontActuallyFreeMemory				= 0;
UInt32						gOnMallocFailureReturnDEADBEEF		= 0;
SInt32						gFailToAllocateAfterNMallocs		= -1;

SInt32						gReportActiveBlocks					= 1;
SInt32						gTagReferencedBlocks				= 0;			// for best effect, turn on gFillFreeBlocksWithPattern also
SInt32						gReportLeaks						= 1;

#endif

//##############################################################################
//##############################################################################

#if STATS_MAC_MEMORY

UInt32		gTotalFixedSizeCompactBlocksAllocated = 0;
UInt32		gTotalFixedSizeCompactBlocksUsed = 0;
UInt32		gTotalFixedSizeCompactAllocated = 0;
UInt32		gTotalFixedSizeCompactUsed = 0;

UInt32		gTotalFixedSizeFastBlocksAllocated = 0;
UInt32		gTotalFixedSizeFastBlocksUsed = 0;
UInt32		gTotalFixedSizeFastAllocated = 0;
UInt32		gTotalFixedSizeFastUsed = 0;

UInt32		gTotalSmallHeapBlocksAllocated = 0;
UInt32		gTotalSmallHeapBlocksUsed = 0;
UInt32		gTotalSmallHeapAllocated = 0;
UInt32		gTotalSmallHeapUsed = 0;


//##############################################################################
//##############################################################################
#pragma mark -
#pragma mark DEBUG malloc AND free

#endif

#if DEBUG_MAC_MEMORY || STATS_MAC_MEMORY

void		*gOurApplicationHeapBase;
void		*gOurApplicationHeapMax;

#if GENERATINGPOWERPC
asm void *GetCurrentStackPointer() 
{
	mr		r3, sp
	lwz		r3, 0(r3)
	blr
}
#endif

UInt32		gNextBlockNum = 0;

void *malloc(size_t blockSize)
{
	MemoryBlockHeader		*newBlockHeader;
#if DEBUG_MAC_MEMORY
	MemoryBlockTrailer		*newBlockTrailer;	
#endif
	size_t					newCompositeSize = blockSize + sizeof(MemoryBlockHeader) + MEMORY_BLOCK_TAILER_SIZE;
	size_t					blockSizeCopy = blockSize;
	UInt32					stackIterator;
	void					*newBlock;
	void					*currentSP;

#if DEBUG_MAC_MEMORY

#if GENERATINGPOWERPC
	currentSP = GetCurrentStackPointer();
#endif

	if (gVerifyHeapOnEveryMalloc)
		VerifyMallocHeapIntegrity();
		
	if (gFailToAllocateAfterNMallocs != -1) {
		if (gFailToAllocateAfterNMallocs-- == 0)
			return NULL;
	}
#endif

#if STATS_MAC_MEMORY

	if (blockSizeCopy >= 1024)
		blockSizeCopy = 1024;

	gSmallAllocationTotalCountTable[blockSizeCopy]++;
	gSmallAllocationActiveCountTable[blockSizeCopy]++;
	if (gSmallAllocationActiveCountTable[blockSizeCopy] > gSmallAllocationMaxCountTable[blockSizeCopy])
		gSmallAllocationMaxCountTable[blockSizeCopy] = gSmallAllocationActiveCountTable[blockSizeCopy];

#endif

	if (blockSize <= kMaxTableAllocatedBlockSize) {
		AllocMemoryBlockDescriptor	*whichAllocator;
		whichAllocator = gFastMemSmallSizeAllocators + ((blockSize + 3) >> 2);
		newBlock = (char *)(whichAllocator->allocRoutine)(blockSize, whichAllocator->refcon) - sizeof(MemoryBlockHeader);
	}
	
	else {
		newBlock =  (char *)StandardAlloc(blockSize, 0) - sizeof(MemoryBlockHeader);
	}

#if DEBUG_MAC_MEMORY
	if (((Ptr)newBlock + sizeof(MemoryBlockHeader)) == NULL) {
		DebugStr("\pMALLOC FALUIRE");
		if (gOnMallocFailureReturnDEADBEEF)
			return (void *)0xDEADBEEF;
		else
			return NULL;
	}

	//	Fill in the blocks header.  This includes adding the 
	//	block to the used list.
	
	newBlockHeader = (MemoryBlockHeader *)newBlock;

	memset(newBlockHeader->whoAllocated, 0, sizeof(void *) * kRecordingDepthOfStackLevels);

#if GENERATINGPOWERPC
	for (stackIterator = 0; stackIterator < kRecordingDepthOfStackLevels; stackIterator++) {
		if ((currentSP < gOurApplicationHeapBase) || (currentSP > gOurApplicationHeapMax))
			break;
		newBlockHeader->whoAllocated[stackIterator] = *((void **)((char *)currentSP + 8));
		currentSP = *((void **)currentSP);
	}
#endif

	newBlockHeader->blockSize = blockSize;
	newBlockHeader->headerTag = kUsedBlockHeaderTag;
	newBlockHeader->blockNum = ++gNextBlockNum;
	
	newBlockHeader->next = gFirstAllocatedBlock;
	newBlockHeader->prev = NULL;	

	if (gFirstAllocatedBlock != NULL)
		gFirstAllocatedBlock->prev = newBlockHeader;
	else
		gLastAllocatedBlock = newBlockHeader;
	gFirstAllocatedBlock = newBlockHeader;
	
	//	Fill in the trailer
	
	newBlockTrailer = (MemoryBlockTrailer *)((char *)newBlock + newCompositeSize - sizeof(MemoryBlockTrailer));
	newBlockTrailer->trailerTag = kUsedBlockTrailerTag;
	
	//	Fill with the used memory pattern
	
	if (gFillUsedBlocksWithPattern)
		memset((char *)newBlock + sizeof(MemoryBlockHeader), kUsedMemoryFillPattern, blockSize);

	//	If leak detection is on, then add this block to the 
	//	tracking list.
	
	if (gTrackLeaks)
		AddAllocationSite(kAllocationSiteMalloc, newBlockHeader->whoAllocated);
	
#endif

	return (void *)((char *)newBlock + sizeof(MemoryBlockHeader));
	
}

void free(void *deadBlock)
{
	MemoryBlockHeader			*deadBlockHeader;
#if DEBUG_MAC_MEMORY
	MemoryBlockTrailer			*deadBlockTrailer;
#endif
	size_t						deadBlockSize;	
	char						*deadBlockBase;
	FreeMemoryBlockDescriptor	*descriptor;
	
#if DEBUG_MAC_MEMORY
	if (gVerifyHeapOnEveryFree)
		VerifyMallocHeapIntegrity();
#endif
		
	if (deadBlock == NULL)
		return;
	
	deadBlockBase = ((char *)deadBlock - sizeof(MemoryBlockHeader));

	deadBlockHeader = (MemoryBlockHeader *)deadBlockBase;
	deadBlockSize = deadBlockHeader->blockSize;

#if DEBUG_MAC_MEMORY
	deadBlockTrailer = (MemoryBlockTrailer *)(deadBlockBase + deadBlockSize + sizeof(MemoryBlockHeader));
#endif
	
#if STATS_MAC_MEMORY
	if (deadBlockSize >= 1024)
		deadBlockSize = 1024;
	gSmallAllocationActiveCountTable[deadBlockSize]--;
#endif
	
#if DEBUG_MAC_MEMORY
	if (deadBlockHeader->headerTag == kFreeBlockHeaderTag)
		DebugStr("\pfastmem: double dispose of malloc block");
	else
		if ((deadBlockHeader->headerTag != kUsedBlockHeaderTag) || (deadBlockTrailer->trailerTag != kUsedBlockTrailerTag))
			DebugStr("\pfastmem: attempt to dispose illegal block");
	
	//	Remove the block from the list of used blocks.
	
	if (deadBlockHeader->next != NULL)
		deadBlockHeader->next->prev = deadBlockHeader->prev;
	else
		gLastAllocatedBlock = deadBlockHeader->prev;
		
	if (deadBlockHeader->prev != NULL)
		deadBlockHeader->prev->next = deadBlockHeader->next;
	else
		gFirstAllocatedBlock = deadBlockHeader->next;
	
	//	Change the header and tailer tags to be the free ones.
	
	deadBlockHeader->headerTag = kFreeBlockHeaderTag;
	deadBlockTrailer->trailerTag = kFreeBlockTrailerTag;

	//	If we are tracking leaks, then decrement this blocks
	//	usage in the tracking table.
	
	if (gTrackLeaks) {

		UInt32				hashBin = ((UInt32)(deadBlockHeader->whoAllocated[0]) >> 2) % kActiveSiteHashTableSize;
		AllocationSite		*currentSite = gActiveSizeHashTable[hashBin];
		
		while (currentSite) {
			UInt32	currentStackLevel;
			for (currentStackLevel = 0; currentStackLevel < kRecordingDepthOfStackLevels; currentStackLevel++) {
				if (currentSite->whoAllocated[currentStackLevel] != deadBlockHeader->whoAllocated[currentStackLevel])
					break;
			}
			if (currentStackLevel == kRecordingDepthOfStackLevels)
				break;
			currentSite = currentSite->next;
		}
		
		if (currentSite != NULL)
			currentSite->frees++;

	}

	//	Fill with the free memory pattern
	
	if (gFillFreeBlocksWithPattern)
		memset(deadBlock, kFreeMemoryFillPattern, deadBlockSize);

	if (!gDontActuallyFreeMemory) {
		descriptor = deadBlockHeader->blockFreeRoutine;
		(descriptor->freeRoutine)(deadBlock, descriptor->refcon);
	}
	
#endif
	
}

#else

//##############################################################################
//##############################################################################
#pragma mark -
#pragma mark OPTIMIZED POWERPC malloc AND free

#if (defined(powerc) || defined(_powerc))

void *malloc(size_t blockSize)
{
	return AsmMalloc(blockSize);
}


asm void *AsmMalloc(size_t blockSize)
{
	cmplwi	r3, kMaxTableAllocatedBlockSize
	bgt		largeAlloc
	
	lwz		r6,gFastMemSmallSizeAllocators(RTOC)
	addi	r4,r3,3
	rlwinm	r4,r4,1,0,28
	add 	r6,r6,r4
	
	lwz		r5,0(r6)
	lwz		r5,0(r5)
	mtctr	r5
	lwz		r4,4(r6)

	bcctr	31, 0

largeAlloc:

	b		StandardAlloc
}


void free(void *block)
{
	AsmFree(block);
}


asm void AsmFree(void *)
{
	cmplwi	r3, 0
	beqlr
	
	subi	r5,r3,4
	lwz		r5,0(r5)
	lwz		r6,0(r5)
	lwz		r6,0(r6)
	mtctr	r6
	lwz		r4,4(r5)
	bcctr	31, 0
}

#else

//##############################################################################
//##############################################################################
#pragma mark -
#pragma mark STANDARD malloc AND free

void *malloc(size_t blockSize)
{

	if (blockSize <= kMaxTableAllocatedBlockSize) {
		AllocMemoryBlockDescriptor	*whichAllocator;
		whichAllocator = gFastMemSmallSizeAllocators + ((blockSize + 3) >> 2);
		return (whichAllocator->allocRoutine)(blockSize, whichAllocator->refcon);
	}
	
	else {
		return StandardAlloc(blockSize, 0);
	}
	
}

void free(void *block)
{
	MemoryBlockHeader			*blockHeader;
	FreeMemoryBlockDescriptor	*descriptor;
	if (block != NULL) {
		blockHeader = (MemoryBlockHeader *)((char *)block - sizeof(MemoryBlockHeader));
		descriptor = blockHeader->blockFreeRoutine;
		(descriptor->freeRoutine)(block, descriptor->refcon);
	}
}


#endif

#endif

//##############################################################################
//##############################################################################
#pragma mark -
#pragma mark realloc AND calloc

void* reallocSmaller(void* block, size_t newSize)
{
//	This actually doesn't reclaim memory!
#if 0
	MemoryBlockHeader			*blockHeader;
#if DEBUG_MAC_MEMORY
	MemoryBlockTrailer			*blockTrailer;
#endif
	if (block != NULL) {
		blockHeader = (MemoryBlockHeader *)((char *)block - sizeof(MemoryBlockHeader));
		if (blockHeader->blockFreeRoutine->freeRoutine == &StandardFree) { 
			size_t		newCompositeSize = newSize + sizeof(MemoryBlockHeader) + MEMORY_BLOCK_TAILER_SIZE;
			newCompositeSize = (newCompositeSize + 3) & 0xFFFFFFFC;
			SetPtrSize((Ptr)blockHeader, newCompositeSize);
#if DEBUG_MAC_MEMORY
			blockHeader->blockSize = newSize;
			blockTrailer = (MemoryBlockTrailer *)((char *)block + newSize + sizeof(MemoryBlockHeader));
			blockTrailer->trailerTag = kUsedBlockTrailerTag;
#endif
		}
	}
#endif
}

void* realloc(void* block, size_t newSize)
{
	void 		*newBlock = NULL;	
	UInt8		tryAgain = true;
	
	newBlock = malloc(newSize);
	
	if (newBlock != NULL) {	
		BlockMoveData(block, newBlock, newSize);
		free(block);
	}

	return newBlock;
}

void *calloc(size_t nele, size_t elesize)
{
	char 		*newBlock = NULL;
	UInt8		tryAgain = true;
	size_t		totalSize = nele * elesize;

	newBlock = (char *)malloc(totalSize);

	if (newBlock != NULL)
		memset(newBlock, 0, totalSize);

	return newBlock;
}


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

#if STATS_MAC_MEMORY

FILE 	*statsFile = NULL;

void DumpCurrentMemoryStatistics(char *operation)
{
	UInt32	count;
	
	if (statsFile == NULL)
		statsFile = fopen("stats", "w");
	
	fprintf(statsFile, "%s\n", operation);
	fprintf(statsFile, "Total Allocations\n");

	for (count = 0; count < 1025; count++)
		fprintf(statsFile, "%ld\n", gSmallAllocationTotalCountTable[count]);

	fprintf(statsFile, "Current Allocation Allocations\n");

	for (count = 0; count < 1025; count++)
		fprintf(statsFile, "%ld\n", gSmallAllocationActiveCountTable[count]);

}

#endif

#if DEBUG_MAC_MEMORY || STATS_MAC_MEMORY

void PrintHexNumber(UInt32 hexNumber)
{
	char		hexString[11];
	char		hexMap[] = "0123456789ABCDEF";
	long		digits = 8;
		
	
	hexString[0] = '0';
	hexString[1] = 'x';
	
	while (digits) {
		hexString[digits + 1] = *((hexNumber & 0x0F) + hexMap);
		hexNumber = hexNumber >> 4;
		digits--;
	}
	hexString[10] = 0;
	
	printf(hexString);
	
}

#endif


#if DEBUG_MAC_MEMORY

AllocationSite *
AddAllocationSite(UInt32 allocationType, void **allocatorStack)
{
	UInt32				hashBin = ((UInt32)allocatorStack[0] >> 2) % kActiveSiteHashTableSize;
	AllocationSite		*currentSite = gActiveSizeHashTable[hashBin];
	
	while (currentSite) {
		UInt32	currentStackLevel;
		for (currentStackLevel = 0; currentStackLevel < kRecordingDepthOfStackLevels; currentStackLevel++) {
			if (currentSite->whoAllocated[currentStackLevel] != allocatorStack[currentStackLevel])
				break;
		}
		if (currentStackLevel == kRecordingDepthOfStackLevels)
			break;
		currentSite = currentSite->next;
	}
	
	//	If this is a new site, then create a new entry from the table
	
	if ((currentSite == NULL) && (gRemainingSitesInList != 0)) {
		currentSite = gCurrentActiveSize++;
		gRemainingSitesInList--;
		currentSite->next = gActiveSizeHashTable[hashBin];
		gActiveSizeHashTable[hashBin] = currentSite;
		BlockMoveData(allocatorStack, currentSite->whoAllocated, sizeof(void *) * kRecordingDepthOfStackLevels);
		currentSite->totalAllocations = 0;
		currentSite->mallocs = 0;
		currentSite->frees = 0;
		currentSite->type = allocationType;
	}

	if (currentSite != NULL) {
		currentSite->mallocs++;
		currentSite->totalAllocations++;
	}
	
	return currentSite;
	
}

extern void VerifyMallocHeapIntegrity()
{
	MemoryBlockHeader	*currentBlock = gFirstAllocatedBlock;

	//	Walk the used block list, checking all of the headers.

	while (currentBlock != NULL) {
	
		MemoryBlockTrailer	*trailer;
		
		trailer = (MemoryBlockTrailer *)((char *)currentBlock + sizeof(MemoryBlockHeader) + currentBlock->blockSize);
	
		if ((currentBlock->headerTag != kUsedBlockHeaderTag) || (trailer->trailerTag != kUsedBlockTrailerTag))
			DebugStr("\pfastmem: malloc heap is corrupt!");
		
		currentBlock = currentBlock->next;
	
	}
	
}

enum {
	kMaxInstructionScanDistance	= 4096
};

void PrintRoutineNameFromPC(void *currentPC)
{
	UInt32		instructionsToLook = kMaxInstructionScanDistance;
	UInt32		*currentPCAsLong = (UInt32 *)currentPC;
	UInt32		offset = 0;
	char		labelBuffer[256];
	
	if (currentPC == NULL)
		return;
	
	while (instructionsToLook--) {
	
		if (*currentPCAsLong == 0x4E800020) {
			char	stringLength = *((char *)currentPCAsLong + 21);
			memset(labelBuffer, 0, 256);
			BlockMoveData(((char *)currentPCAsLong + 22), labelBuffer, stringLength);
			printf(labelBuffer);
			break;
		}
	
		currentPCAsLong++;
		
	}

	instructionsToLook = kMaxInstructionScanDistance;
	currentPCAsLong = (UInt32 *)currentPC;

	while (instructionsToLook--) {
		if (*currentPCAsLong == 0x7C0802A6) {
			printf("+");
			PrintHexNumber(offset - 4);
			break;
		}
		currentPCAsLong--;
		offset += 4;
	}

}

#endif

#if STATS_MAC_MEMORY
void PrintEfficiency(UInt32 current, UInt32 total)
{	
	UInt32 	efficiency = current * 1000 / total;
	UInt32	efficiencyLow = efficiency / 10;
	
	efficiency = efficiency/10;
	
	printf("%ld.%ld", efficiency, efficiencyLow);
	
}
#endif

#if DEBUG_MAC_MEMORY
static void TagReferencedBlocks(void)
{
	char *start = (char *) ApplicationZone();
	char *end = (char *) GetApplLimit();
	
	char *comb = start;
	
	while (comb < end) {
	
		MemoryBlockHeader *curCandidate = (MemoryBlockHeader *) comb;
		
		if (curCandidate->headerTag == kFreeBlockHeaderTag || 
			curCandidate->headerTag == kUsedBlockHeaderTag ||
			curCandidate->headerTag == kRefdBlockHeaderTag) {
			
			// we are pointing at a block header			
			// we don't want to consider the next or prev structure members
			// therefore, skip ahead 8 bytes = 2 * sizeof(MemoryBlockHeader *)
						
			comb += 8;
			
		} else if ((char *) curCandidate->next > start &&
				(char *) curCandidate->next < end) {
				
			MemoryBlockHeader *referencedBlock;
			referencedBlock = (MemoryBlockHeader *) ((char *) curCandidate->next - sizeof(MemoryBlockHeader));
				
			if (referencedBlock->headerTag == kUsedBlockHeaderTag) {
					
				// Bingo!!
				// Marked the referenced block as being referenced.
				// skip ahead 4 bytes	(or should we be conservative and just skip 2 bytes?)
				
				referencedBlock->headerTag = kRefdBlockHeaderTag;	// TODO: insert kRefdBlockTrailerTag as well
				comb += 4;
				
			} else {
			
				// False alarm
				// it looked like a pointer, but it didn't point to the beginning of a valid used block.
				// move forward two bytes and look some more
				
				comb += 2;
			}
				
		} else {
		
			// we aren't looking at anything interesting
			// move forward two bytes and look some more.
			
			comb += 2;
		}
		
	}
	
}
#endif

void DumpAllocHeap(void)
{
#if DEBUG_MAC_MEMORY
	MemoryBlockHeader			*currentBlock = gFirstAllocatedBlock;
#endif

#if STATS_MAC_MEMORY

	UInt32						currentItem;
	
	printf("********************************************************************************\n");	
	printf("********************************************************************************\n");	
	printf("USAGE REPORT (MALLOC HEAP)\n");
	printf("********************************************************************************\n");	
	printf("********************************************************************************\n");
	printf("TYPE      BLOCKS (A)  BLOCKS (U)  BLOCKS (F)  MEMORY (A)  MEMORY (U)  EFFICIENCY\n");
	printf("----      ----------  ----------  ----------  ----------  ----------  ----------\n");
	printf("COMPACT   ");
	PrintHexNumber(gTotalFixedSizeCompactBlocksAllocated);
	printf("  ");	
	PrintHexNumber(gTotalFixedSizeCompactBlocksUsed);
	printf("  ");	
	PrintHexNumber(gTotalFixedSizeCompactBlocksAllocated - gTotalFixedSizeCompactBlocksUsed);
	printf("  ");	
	PrintHexNumber(gTotalFixedSizeCompactAllocated);
	printf("  ");	
	PrintHexNumber(gTotalFixedSizeCompactUsed);
	printf("  ");
	PrintEfficiency(gTotalFixedSizeCompactBlocksUsed, gTotalFixedSizeCompactBlocksAllocated);
	printf("/");
	PrintEfficiency(gTotalFixedSizeCompactUsed, gTotalFixedSizeCompactAllocated);
	printf("  \n");	
	printf("FIXED     ");
	PrintHexNumber(gTotalFixedSizeFastBlocksAllocated);
	printf("  ");	
	PrintHexNumber(gTotalFixedSizeFastBlocksUsed);
	printf("  ");	
	PrintHexNumber(gTotalFixedSizeFastBlocksAllocated - gTotalFixedSizeFastBlocksUsed);
	printf("  ");	
	PrintHexNumber(gTotalFixedSizeFastAllocated);
	printf("  ");	
	PrintHexNumber(gTotalFixedSizeFastUsed);
	printf("  ");
	PrintEfficiency(gTotalFixedSizeFastBlocksUsed, gTotalFixedSizeFastBlocksAllocated);
	printf("/");
	PrintEfficiency(gTotalFixedSizeFastUsed, gTotalFixedSizeFastAllocated);
	printf("  \n");	
	printf("HEAP      ");
	PrintHexNumber(0);
	printf("  ");	
	PrintHexNumber(gTotalSmallHeapBlocksUsed);
	printf("  ");	
	PrintHexNumber(0);
	printf("  ");	
	PrintHexNumber(gTotalSmallHeapAllocated);
	printf("  ");	
	PrintHexNumber(gTotalSmallHeapUsed);
	printf("  ");
	PrintEfficiency(gTotalSmallHeapUsed, gTotalSmallHeapAllocated);
	printf("  \nTotal efficiency: ");
	PrintEfficiency(gTotalSmallHeapUsed + gTotalFixedSizeFastUsed + gTotalFixedSizeCompactUsed, 
		gTotalSmallHeapAllocated + gTotalFixedSizeFastAllocated + gTotalFixedSizeCompactAllocated);
	printf("  \n");	
	printf("********************************************************************************\n");	
	printf("********************************************************************************\n");	
	printf("BLOCK DISTRIBUTION (MALLOC HEAP: ACTIVE)\n");
	printf("********************************************************************************\n");	
	printf("********************************************************************************\n");
	printf("SIZE      +0x00       +0x01       +0x02      +0x03        TOTAL\n");
	printf("----      ----------  ----------  ----------  ----------  ----------\n");
	
	for (currentItem = 0; currentItem < 64; currentItem++) {
		PrintHexNumber(currentItem * 4);
		printf("  ");	
		PrintHexNumber(gSmallAllocationActiveCountTable[currentItem * 4]);
		printf("  ");	
		PrintHexNumber(gSmallAllocationActiveCountTable[currentItem * 4 + 1]);
		printf("  ");	
		PrintHexNumber(gSmallAllocationActiveCountTable[currentItem * 4 + 2]);
		printf("  ");	
		PrintHexNumber(gSmallAllocationActiveCountTable[currentItem * 4 + 3]);
		printf("  ");	
		PrintHexNumber(gSmallAllocationActiveCountTable[currentItem * 4] + 
			gSmallAllocationActiveCountTable[currentItem * 4 + 1] +
			gSmallAllocationActiveCountTable[currentItem * 4 + 2] +
			gSmallAllocationActiveCountTable[currentItem * 4 + 3]);
		printf("\n");	
	}
	printf("Blocks Over 1K:");
	PrintHexNumber(gSmallAllocationActiveCountTable[1024]);
	printf("\n");	
	printf("********************************************************************************\n");	
	printf("********************************************************************************\n");	
	printf("BLOCK DISTRIBUTION (MALLOC HEAP: MAX)\n");
	printf("********************************************************************************\n");	
	printf("********************************************************************************\n");
	printf("SIZE      +0x00       +0x01       +0x02      +0x03        TOTAL\n");
	printf("----      ----------  ----------  ----------  ----------  ----------\n");
	
	for (currentItem = 0; currentItem < 64; currentItem++) {
		PrintHexNumber(currentItem * 4);
		printf("  ");	
		PrintHexNumber(gSmallAllocationMaxCountTable[currentItem * 4]);
		printf("  ");	
		PrintHexNumber(gSmallAllocationMaxCountTable[currentItem * 4 + 1]);
		printf("  ");	
		PrintHexNumber(gSmallAllocationMaxCountTable[currentItem * 4 + 2]);
		printf("  ");	
		PrintHexNumber(gSmallAllocationMaxCountTable[currentItem * 4 + 3]);
		printf("  ");	
		PrintHexNumber(gSmallAllocationMaxCountTable[currentItem * 4] + 
			gSmallAllocationMaxCountTable[currentItem * 4 + 1] +
			gSmallAllocationMaxCountTable[currentItem * 4 + 2] +
			gSmallAllocationMaxCountTable[currentItem * 4 + 3]);
		printf("\n");	
	}
	printf("Blocks Over 1K:");
	PrintHexNumber(gSmallAllocationMaxCountTable[1024]);
	printf("\n");	
	printf("********************************************************************************\n");	
	printf("********************************************************************************\n");	
	printf("BLOCK DISTRIBUTION (MALLOC HEAP: TOTAL)\n");
	printf("********************************************************************************\n");	
	printf("********************************************************************************\n");
	printf("SIZE        +0x00       +0x01       +0x02      +0x03        TOTAL\n");
	printf("----        ----------  ----------  ----------  ----------  ----------\n");
	
	for (currentItem = 0; currentItem < 64; currentItem++) {
		PrintHexNumber(currentItem * 4);
		printf("  ");	
		PrintHexNumber(gSmallAllocationTotalCountTable[currentItem * 4]);
		printf("  ");	
		PrintHexNumber(gSmallAllocationTotalCountTable[currentItem * 4 + 1]);
		printf("  ");	
		PrintHexNumber(gSmallAllocationTotalCountTable[currentItem * 4 + 2]);
		printf("  ");	
		PrintHexNumber(gSmallAllocationTotalCountTable[currentItem * 4 + 3]);
		printf("  ");	
		PrintHexNumber(gSmallAllocationTotalCountTable[currentItem * 4] + 
			gSmallAllocationTotalCountTable[currentItem * 4 + 1] +
			gSmallAllocationTotalCountTable[currentItem * 4 + 2] +
			gSmallAllocationTotalCountTable[currentItem * 4 + 3]);
		printf("\n");	
	}
	printf("Blocks Over 1K:");
	PrintHexNumber(gSmallAllocationTotalCountTable[1024]);
	printf("\n");	

#endif
	
#if DEBUG_MAC_MEMORY

	currentBlock = gFirstAllocatedBlock;

	//	Report leaks
	
	if (gReportLeaks) {
	
		AllocationSite	*scanSite = gActiveSizeList;
		
		printf("********************************************************************************\n");	
		printf("********************************************************************************\n");	
		printf("LEAKS\n");
		printf("********************************************************************************\n");	
		printf("********************************************************************************\n");
		printf("TYPE    ALLOCS      FREES       LEAKS       STACK\n");
		printf("------  ------      -----       -----       -----\n");
		
		while (scanSite != gCurrentActiveSize) {
			
			if (scanSite->mallocs > (scanSite->frees + 1)) {
		
				UInt32	currentStackLevel;
			
				switch (scanSite->type) {
					case kAllocationSiteMalloc:
						printf("MALLOC  ");
						break;	
					case kAllocationSiteNewHandle:
						printf("HANDLE  ");
						break;	
					case kAllocationSiteNewPtr:
						printf("POINTR  ");
						break;	
					default:
						break;	
				}
			
				PrintHexNumber(scanSite->mallocs);
				printf("\t");	
				PrintHexNumber(scanSite->frees);
				printf("\t");	
				PrintHexNumber(scanSite->totalAllocations - scanSite->frees);
				printf("\t");	

				for (currentStackLevel = 0; currentStackLevel < kRecordingDepthOfStackLevels; currentStackLevel++) {
					PrintRoutineNameFromPC(scanSite->whoAllocated[currentStackLevel]);
					if (currentStackLevel == kRecordingDepthOfStackLevels - 1)
						printf("\n");
					else
						printf(",");
				}
				
			}
			
			scanSite++;
			
		}	
		
		printf("Outstanding Pointers:");
		PrintHexNumber(gOutstandingPointers);
		printf("\n");	

		printf("Outstanding Handles:");
		PrintHexNumber(gOutstandingHandles);
		printf("\n");	

	}
	
	// Tag blocks which are referenced from somewhere in the heap.
	
	if (gTagReferencedBlocks) {
		TagReferencedBlocks();
	}

	//	Dump out active blocks
	
	if (gReportActiveBlocks) {
	
		printf("********************************************************************************\n");	
		printf("********************************************************************************\n");	
		printf("ACTIVE BLOCKS\n");
		printf("********************************************************************************\n");	
		printf("********************************************************************************\n");
		printf("ADDRESS     NUMBER      SIZE        STACK\n");
		printf("-------     ------      ----        -----\n");
		
		while (currentBlock) {
		
			UInt32 	currentStackLevel;

			if (!gTagReferencedBlocks || currentBlock->headerTag == kUsedBlockHeaderTag) {
				PrintHexNumber((UInt32)currentBlock);
				printf("\t");	
				PrintHexNumber((UInt32)(currentBlock->blockNum));
				printf("\t");	
				PrintHexNumber((UInt32)(currentBlock->blockSize));
				printf("\t");

				for (currentStackLevel = 0; currentStackLevel < kRecordingDepthOfStackLevels; currentStackLevel++) {
					PrintRoutineNameFromPC(currentBlock->whoAllocated[currentStackLevel]);
					if (currentStackLevel == kRecordingDepthOfStackLevels - 1)
						printf("\n");
					else
						printf(",");
				}
			}

			currentBlock = currentBlock->next;

		}	

	}

#endif

}

FreeMemoryBlockDescriptor	StandardFreeDescriptor = { &StandardFree, 0 };

//##############################################################################
//##############################################################################
#pragma mark -
#pragma mark STANDARD ALLOCATOR (SLOW!)

void *StandardAlloc(size_t blockSize, void *refcon)
{
	MemoryBlockHeader	*newBlockHeader = (MemoryBlockHeader*)AllocateRawMemory(((blockSize + 3) & 0xFFFFFFFC) + sizeof(MemoryBlockHeader) + MEMORY_BLOCK_TAILER_SIZE);
	if (newBlockHeader == NULL)
		return NULL;
	newBlockHeader->blockFreeRoutine = &StandardFreeDescriptor;
	return (void *)((char *)newBlockHeader + sizeof(MemoryBlockHeader));
}

void StandardFree(void *block, void *refcon)
{
	MemoryBlockHeader *blockHeader = (MemoryBlockHeader *)((char *)block - sizeof(MemoryBlockHeader));
	FreeRawMemory((Ptr)blockHeader);
}

//##############################################################################
//##############################################################################
#pragma mark -
#pragma mark FIXED SIZED ALLOCATOR (COMPACT)

UInt32 CountLeadingZeros(UInt32 value);

#if defined(powerc) || defined(_powerc)
asm UInt32 CountLeadingZeros(UInt32 value) 
{
	cntlzw	r3, r3
	blr
}
#else
UInt32 CountLeadingZeros(UInt32 value) 
{
	UInt32	whichZero = 0;
	while ((value & 0x80000000) == 0) {
		whichZero++;
		value = value << 1;
	}
	return whichZero;
}
#endif

void *FixedSizeCompactAlloc(size_t blockSize, void *refcon)
{
	FixedSizeCompactAllocationRoot		*sizeRoot = (FixedSizeCompactAllocationRoot *)refcon;
	FixedSizeCompactAllocationChunk		*currentChunk = sizeRoot->firstChunk;
	FreeMemoryBlockDescriptor			*whichFreeDescriptor;
	UInt32								whichBlockIsFree,
										blockOffset;
	MemoryBlockHeader					*blockHeader;
	
	//	Try to find an existing chunk with a free block.
	
	while ((currentChunk != NULL) && (currentChunk->chunkUsage == 0))
		currentChunk = currentChunk->next;

	//	If there is not chunk with a free block, then create
	//	a new one and put it at the beginning of the chunk list.
	
	if (currentChunk == NULL) {

		currentChunk = (FixedSizeCompactAllocationChunk *)AllocateRawMemory(sizeof(FixedSizeCompactAllocationChunk) + 
			(32 * (sizeRoot->blockSize + sizeof(MemoryBlockHeader) + MEMORY_BLOCK_TAILER_SIZE)));

		if (currentChunk == NULL) {
			return NULL;
		}

#if STATS_MAC_MEMORY
		gTotalFixedSizeCompactAllocated += sizeof(FixedSizeCompactAllocationChunk) + 
			(32 * (sizeRoot->blockSize + sizeof(MemoryBlockHeader) + MEMORY_BLOCK_TAILER_SIZE));
		gTotalFixedSizeCompactBlocksAllocated += 32;
#endif

		currentChunk->root = sizeRoot;
		currentChunk->chunkUsage = 0xFFFFFFFF;
	
		currentChunk->next = sizeRoot->firstChunk;
		sizeRoot->firstChunk = currentChunk;
		
	}
	
	//	Find the block in the chunk that is free.
	
	whichBlockIsFree = CountLeadingZeros(currentChunk->chunkUsage);
	
	//	Claim the block
	
	currentChunk->chunkUsage &= ~(0x80000000 >> whichBlockIsFree);
	
	//	Install the free descriptor
	
	whichFreeDescriptor = sizeRoot->freeDescriptorTable + whichBlockIsFree;
	
	blockOffset = (UInt32)(whichFreeDescriptor->refcon) & 0x0000FFFF;
	
	blockHeader = (MemoryBlockHeader *)((char *)currentChunk + blockOffset);
	blockHeader->blockFreeRoutine = whichFreeDescriptor;
	
#if STATS_MAC_MEMORY
	gTotalFixedSizeCompactUsed += sizeRoot->blockSize;
	gTotalFixedSizeCompactBlocksUsed++;
#endif

	return (void *)((char *)blockHeader + sizeof(MemoryBlockHeader));
	
}

void FixedSizeCompactFree(void *block, void *refcon)
{
	MemoryBlockHeader					*blockHeader;
	UInt32								blockNumber;
	UInt32								blockOffset;
	FixedSizeCompactAllocationChunk		*thisChunk;
	FixedSizeCompactAllocationChunk		*currentChunk,
										**previousChunk = NULL;
	FixedSizeCompactAllocationRoot		*root;
	
	blockNumber = (UInt32)refcon >> 16;
	blockOffset = (UInt32)refcon & 0xFFFF;
	
	//	Find the block header and the 
	
	blockHeader = (MemoryBlockHeader *)((char *)block - sizeof(MemoryBlockHeader));
	thisChunk = (FixedSizeCompactAllocationChunk *)((char *)blockHeader - blockOffset);

	//	Mark the block as unused.
	 
	thisChunk->chunkUsage |= (0x80000000 >> blockNumber);

	//	If the block is completely unused, then reclaim it

	root = thisChunk->root;
	currentChunk = root->firstChunk;

	if ((thisChunk->chunkUsage == 0xFFFFFFFF) && (currentChunk != thisChunk)) {
		
		while (currentChunk != thisChunk) {
			previousChunk = &currentChunk->next;
			currentChunk = currentChunk->next;
		}
		
		*previousChunk = currentChunk->next;
		
		FreeRawMemory((Ptr)thisChunk);
			
	}

#if STATS_MAC_MEMORY
	gTotalFixedSizeCompactUsed -= root->blockSize;
	gTotalFixedSizeCompactBlocksUsed--;
#endif
		
}

//##############################################################################
//##############################################################################
#pragma mark -
#pragma mark FIXED SIZED ALLOCATOR (FAST)

void *FixedSizeFastAlloc(size_t blockSize, void *refcon)
{
	FixedSizeFastAllocationRoot		*sizeRoot = (FixedSizeFastAllocationRoot *)refcon;
	FixedSizeFastAllocationChunk	*currentChunk;
	FixedSizeFastMemoryBlockHeader	*ourBlock;
	
	//	If the free list is empty, then we have to allocate another chunk.

	if (sizeRoot->firstFree == NULL) {
	
		FixedSizeFastMemoryBlockHeader	*currentBlock;
		UInt32							currentBlockNum;

#if STATS_MAC_MEMORY
		gTotalFixedSizeFastAllocated += sizeof(FixedSizeFastAllocationChunk) + 
			(sizeRoot->blocksPerChunk * (sizeRoot->blockSize + sizeof(FixedSizeFastMemoryBlockHeader) + MEMORY_BLOCK_TAILER_SIZE));
		gTotalFixedSizeFastBlocksAllocated += sizeRoot->blocksPerChunk;
#endif

		currentChunk = (FixedSizeFastAllocationChunk *)AllocateRawMemory(sizeof(FixedSizeFastAllocationChunk) + 
			(sizeRoot->blocksPerChunk * (sizeRoot->blockSize + sizeof(FixedSizeFastMemoryBlockHeader) + MEMORY_BLOCK_TAILER_SIZE)));
		if (currentChunk == NULL) {
			return NULL;
		}
	
		currentChunk->next = sizeRoot->firstChunk;
		sizeRoot->firstChunk = currentChunk;
	
		//	Thread all of the chunks blocks onto the free list.
	
		currentBlock = (FixedSizeFastMemoryBlockHeader *)((char *)currentChunk + sizeof(FixedSizeFastAllocationChunk));
	
		for (currentBlockNum = 0; currentBlockNum < sizeRoot->blocksPerChunk; currentBlockNum++) {
		
			currentBlock->nextFree = sizeRoot->firstFree;
			currentBlock->realHeader.blockFreeRoutine = sizeRoot->freeDescriptor;
			
			sizeRoot->firstFree = currentBlock;
			
			currentBlock = (FixedSizeFastMemoryBlockHeader *)((char *)currentBlock + 
				sizeof(FixedSizeFastMemoryBlockHeader) + MEMORY_BLOCK_TAILER_SIZE + sizeRoot->blockSize);
			
		}
	
	}

	//	Take the first block off of the free list.  It is the one
	//	we will use.
	
	ourBlock = sizeRoot->firstFree;
	sizeRoot->firstFree = ourBlock->nextFree;
	
#if STATS_MAC_MEMORY
	gTotalFixedSizeFastBlocksUsed++;
	gTotalFixedSizeFastUsed += sizeRoot->blockSize;
#endif
	return (void *)((char *)ourBlock + sizeof(FixedSizeFastMemoryBlockHeader));

}

void FixedSizeFastFree(void *block, void *refcon)
{
	FixedSizeFastMemoryBlockHeader	*deadBlock;
	FixedSizeFastAllocationRoot		*sizeRoot = (FixedSizeFastAllocationRoot *)refcon;
	
	deadBlock = (FixedSizeFastMemoryBlockHeader *)((char *)block - sizeof(FixedSizeFastMemoryBlockHeader));
	
	//	Put the block on the free list.
	
	deadBlock->nextFree = sizeRoot->firstFree;
	sizeRoot->firstFree = deadBlock;

#if STATS_MAC_MEMORY
	gTotalFixedSizeFastBlocksUsed--;
	gTotalFixedSizeFastUsed -= sizeRoot->blockSize;
#endif
	
}

//##############################################################################
//##############################################################################
#pragma mark -
#pragma mark SMALL HEAP ALLOCATOR

#define SmallHeapBlockToMemoryPtr(x) ((void *)((Ptr)x + sizeof(SmallHeapBlock)))
#define MemoryPtrToSmallHeapBlock(x) ((SmallHeapBlock *)((Ptr)x - sizeof(SmallHeapBlock)))

#define NextSmallHeapBlock(x) ((SmallHeapBlock *)((Ptr)x + (x->blockSize & ~kBlockInUseFlag) + sizeof(SmallHeapBlock) + MEMORY_BLOCK_TAILER_SIZE))
#define PrevSmallHeapBlock(x) (x->prevBlock)

#define SmallHeapBlockInUse(x) ((x->blockSize & kBlockInUseFlag) != 0)
#define SmallHeapBlockNotInUse(x) ((x->blockSize & kBlockInUseFlag) == 0)

#define TotalSmallHeapBlockUsage(x) ((x->blockSize & ~kBlockInUseFlag) + sizeof(SmallHeapBlock) + MEMORY_BLOCK_TAILER_SIZE)

#define AddSmallHeapBlockToFreeList(r, x)														\
{																								\
	UInt32				blockSize = x->blockSize & ~kBlockInUseFlag;							\
	UInt32				nextBlockBin;															\
	SmallHeapBlock		*tempBlock;																\
	x->blockSize = blockSize		;															\
	nextBlockBin = (blockSize - kDefaultSmallHeadMinSize) >> 2;									\
	x->info.freeInfo.prevFree = NULL;															\
	if (blockSize > kMaximumBinBlockSize) {														\
		tempBlock = r->overflow;																\
		x->info.freeInfo.nextFree = tempBlock;													\
		if (tempBlock != NULL)																	\
			tempBlock->info.freeInfo.prevFree = x;												\
		r->overflow = x;																		\
	}																							\
	else {																						\
		tempBlock = r->bins[nextBlockBin];														\
		x->info.freeInfo.nextFree = tempBlock;													\
		if (tempBlock != NULL)																	\
			tempBlock->info.freeInfo.prevFree = x;												\
		r->bins[nextBlockBin] = x;																\
	}																							\
}

#define RemoveSmallHeapBlockFromFreeList(r, x)													\
{																								\
	SmallHeapBlock		*nextFree,																\
						*prevFree;																\
	UInt32				blockSize = x->blockSize;												\
	UInt32				nextBlockBin;															\
	x->blockSize |= kBlockInUseFlag;															\
	nextBlockBin = (blockSize - kDefaultSmallHeadMinSize) >> 2;									\
	nextFree = x->info.freeInfo.nextFree;														\
	prevFree = x->info.freeInfo.prevFree;														\
	if (nextFree != NULL)																		\
		nextFree->info.freeInfo.prevFree = prevFree;											\
	if (prevFree != NULL)																		\
		prevFree->info.freeInfo.nextFree = nextFree;											\
	else																						\
		if (blockSize > kMaximumBinBlockSize)													\
			r->overflow = nextFree;																\
		else																					\
			r->bins[nextBlockBin] = nextFree;													\
}


#if DEBUG_MAC_MEMORY
void SmallHeapCheck(SmallHeapBlock *fromBlock)
{
	SmallHeapBlock 		*previousBlock = NULL;
	
	//	Go to the first block in the list
	
	while (fromBlock->prevBlock != NULL)
		fromBlock = fromBlock->prevBlock;
	
	//	Walk the list
	
	while (fromBlock) {
	
		if (fromBlock->blockSize == 0xFFFFFFFF)
			break;
	
		if (previousBlock != NULL) {
			if (previousBlock != PrevSmallHeapBlock(fromBlock)) {
				DebugStr("\pSmall Heap Corrupt");
				break;
			}
		}
		previousBlock = fromBlock;
		fromBlock = NextSmallHeapBlock(fromBlock);
	
	}	

}
#endif

void *SmallHeapAlloc(size_t blockSize, void *refcon)
{
	SmallHeapRoot			*heapRoot = (SmallHeapRoot *)refcon;
	UInt32					startingBinNum,
							remainingBins;
	SmallHeapBlock			**currentBin;
	SmallHeapBlock			*currentBinValue;
	SmallHeapBlock			*newFreeOverflowBlock,
							*blockToCarve,
							*newRawBlockHeader,
							*newRawBlockTrailer;

	//	Round up allocation to nearest 4 bytes.
	
	blockSize = (blockSize + 3) & 0xFFFFFFFC;

	//	Try to find the best fit in one of the bins.
	
	startingBinNum = (blockSize - kDefaultSmallHeadMinSize) >> 2;
	
	currentBin = heapRoot->bins + startingBinNum;
	currentBinValue = *currentBin;
		
	//	If the current bin has something in it,
	//	then use it for our allocation.
	
	if (currentBinValue != NULL) {
		
		RemoveSmallHeapBlockFromFreeList(heapRoot, currentBinValue);

		currentBinValue->info.inUseInfo.freeProc.blockFreeRoutine = heapRoot->blockFreeRoutine;

#if STATS_MAC_MEMORY
		gTotalSmallHeapBlocksUsed++;
		gTotalSmallHeapUsed += TotalSmallHeapBlockUsage(currentBinValue);
#endif

		return SmallHeapBlockToMemoryPtr(currentBinValue);

	}

	//	Otherwise, try to carve up an existing larger block.

	remainingBins = kDefaultSmallHeapBins - startingBinNum - 1;
	blockToCarve = NULL;
	
	while (remainingBins--) {
	
		currentBin++;
	
		currentBinValue = *currentBin;
		
		if (currentBinValue != NULL) {
			blockToCarve = currentBinValue;
			break;
		}
		
	}

	//	Try carving up up a block from the overflow bin.
	
	if (blockToCarve == NULL)
		blockToCarve = heapRoot->overflow;
	
doCarve:
	
	while (blockToCarve != NULL) {
	
		SInt32	blockToCarveSize = blockToCarve->blockSize;
	
		//	If the owerflow block is big enough to house the
		//	allocation, then use it.
	
		if (blockToCarveSize >= blockSize) {
		
			SInt32				leftovers;
		
			//	Remove the block from the free list... we will
			//	be using it for the allocation...
			
			RemoveSmallHeapBlockFromFreeList(heapRoot, blockToCarve);

			//	If taking our current allocation out of
			//	the overflow block would still leave enough
			//	room for another allocation out of the 
			//	block, then split the block up.
			
			leftovers = blockToCarveSize - sizeof(SmallHeapBlock) - MEMORY_BLOCK_TAILER_SIZE - blockSize;
			
			if (leftovers >= kDefaultSmallHeadMinSize) {
			
				SmallHeapBlock		*leftoverBlock;
				SmallHeapBlock		*nextBlock;
			
				nextBlock = NextSmallHeapBlock(blockToCarve);
				
				//	Create a new block out of the leftovers
				
				leftoverBlock = (SmallHeapBlock *)((char *)blockToCarve + 
					sizeof(SmallHeapBlock) + blockSize + MEMORY_BLOCK_TAILER_SIZE); 
			
				//	Add the block to linked list of blocks in this raw
				//	allocation chunk.
			
				nextBlock->prevBlock = leftoverBlock;
				blockToCarve->blockSize = blockSize | kBlockInUseFlag;		
					
				leftoverBlock->prevBlock = blockToCarve;
				leftoverBlock->blockSize = leftovers;
				
				//	And add the block to a free list, which will either be
				//	one of the sized bins or the overflow list, depending
				//	on its size.
			
				AddSmallHeapBlockToFreeList(heapRoot, leftoverBlock);
						
			}
		
			blockToCarve->info.inUseInfo.freeProc.blockFreeRoutine = heapRoot->blockFreeRoutine;

#if STATS_MAC_MEMORY
			gTotalSmallHeapBlocksUsed++;
			gTotalSmallHeapUsed += TotalSmallHeapBlockUsage(blockToCarve);
#endif
			return SmallHeapBlockToMemoryPtr(blockToCarve);
						
		} 
	
		blockToCarve = blockToCarve->info.freeInfo.nextFree;
	
	}
	
	//	No existing block was suitable.  We need to allocate
	//	some raw memory to try again.
	
#if STATS_MAC_MEMORY
	gTotalSmallHeapAllocated += kSmallHeapSize;
#endif

	newRawBlockHeader = (SmallHeapBlock *)AllocateRawMemory(kSmallHeapSize);
	if (newRawBlockHeader == NULL) {
		//	If all else fails, try allocating out of the NewPtr heap.
		return StandardAlloc(blockSize, 0);
	}

	//	The first few bytes of the block are a dummy header
	//	which is a block of size zero that is always alloacted.  
	//	This allows our coalesce code to work without modification
	//	on the edge case of coalescing the first real block.

	newRawBlockHeader->prevBlock = NULL;
	newRawBlockHeader->blockSize = kBlockInUseFlag;
	newRawBlockHeader->info.inUseInfo.freeProc.blockFreeRoutine = NULL;
	
	newFreeOverflowBlock = NextSmallHeapBlock(newRawBlockHeader);
	
	newFreeOverflowBlock->prevBlock = newRawBlockHeader;
	newFreeOverflowBlock->blockSize = kSmallHeapSize - 3 * sizeof(SmallHeapBlock) - 3 * MEMORY_BLOCK_TAILER_SIZE;

	//	The same is true for the last few bytes in the block 
	//	as well.
	
	newRawBlockTrailer = (SmallHeapBlock *)(((Ptr)newRawBlockHeader) + kSmallHeapSize - sizeof(SmallHeapBlock) - MEMORY_BLOCK_TAILER_SIZE);
	newRawBlockTrailer->prevBlock = newFreeOverflowBlock;
	newRawBlockTrailer->blockSize = kBlockInUseFlag | 0xFFFFFFFF;
	newRawBlockTrailer->info.inUseInfo.freeProc.blockFreeRoutine = NULL;
	
	AddSmallHeapBlockToFreeList(heapRoot, newFreeOverflowBlock);
	
	blockToCarve = newFreeOverflowBlock;
	
	//	Try again.
	
	goto doCarve;

}

void SmallHeapFree(void *address, void *refcon)
{
	SmallHeapRoot			*heapRoot = (SmallHeapRoot *)refcon;
	SmallHeapBlock			*deadBlock,
							*prevBlock,
							*nextBlock;
				
	deadBlock = MemoryPtrToSmallHeapBlock(address);

#if STATS_MAC_MEMORY
	gTotalSmallHeapBlocksUsed--;
	gTotalSmallHeapUsed -= TotalSmallHeapBlockUsage(deadBlock);
#endif

	//	If the block after us is free, then coalesce with it.
	
	nextBlock = NextSmallHeapBlock(deadBlock);
	prevBlock = PrevSmallHeapBlock(deadBlock);
	
	if (SmallHeapBlockNotInUse(nextBlock)) {	
		RemoveSmallHeapBlockFromFreeList(heapRoot, nextBlock);
		deadBlock->blockSize += TotalSmallHeapBlockUsage(nextBlock);
		nextBlock = NextSmallHeapBlock(nextBlock);
	}
	
	//	If the block before us is free, then coalesce with it.
	
	if (SmallHeapBlockNotInUse(prevBlock)) {
		RemoveSmallHeapBlockFromFreeList(heapRoot, prevBlock);
		prevBlock->blockSize = ((Ptr)nextBlock - (Ptr)prevBlock) - sizeof(SmallHeapBlock) - MEMORY_BLOCK_TAILER_SIZE;
		AddSmallHeapBlockToFreeList(heapRoot, prevBlock);
		deadBlock = prevBlock;
	}
	
	else {	
		AddSmallHeapBlockToFreeList(heapRoot, deadBlock);
	}
	
	nextBlock->prevBlock = deadBlock;

}

//##############################################################################
//##############################################################################
#pragma mark -
#pragma mark TRACKING MEMORY MANAGER MEMORY

#if DEBUG_MAC_MEMORY
#if GENERATINGPOWERPC

enum {
	kFigmentBlockHashTableSize			= 128,
	kFigmentBlockTotalTrackedBlocks		= 8192
};

typedef struct FigmentBlockInformation FigmentBlockInformation;

struct FigmentBlockInformation {
	FigmentBlockInformation			*next;
	void							*allocation;
	AllocationSite					*allocator;
};

enum {
	uppNewHandleProcInfo = kRegisterBased
		 | RESULT_SIZE(SIZE_CODE(sizeof(Handle)))
		 | REGISTER_RESULT_LOCATION(kRegisterA0)
		 | REGISTER_ROUTINE_PARAMETER(1, kRegisterD1, SIZE_CODE(sizeof(UInt16)))
		 | REGISTER_ROUTINE_PARAMETER(2, kRegisterD0, SIZE_CODE(sizeof(Size))),
	uppNewPtrProcInfo = kRegisterBased
		 | RESULT_SIZE(SIZE_CODE(sizeof(Handle)))
		 | REGISTER_RESULT_LOCATION(kRegisterA0)
		 | REGISTER_ROUTINE_PARAMETER(1, kRegisterD1, SIZE_CODE(sizeof(UInt16)))
		 | REGISTER_ROUTINE_PARAMETER(2, kRegisterD0, SIZE_CODE(sizeof(Size))),
	uppDisposeHandleProcInfo = kRegisterBased
		 | REGISTER_ROUTINE_PARAMETER(1, kRegisterD1, SIZE_CODE(sizeof(UInt16)))
		 | REGISTER_ROUTINE_PARAMETER(2, kRegisterA0, SIZE_CODE(sizeof(Handle))),
	uppDisposePtrProcInfo = kRegisterBased
		 | REGISTER_ROUTINE_PARAMETER(1, kRegisterD1, SIZE_CODE(sizeof(UInt16)))
		 | REGISTER_ROUTINE_PARAMETER(2, kRegisterA0, SIZE_CODE(sizeof(Ptr)))
};

extern pascal Handle NewHandlePatch(UInt16 trapWord, Size byteCount);
extern pascal Ptr NewPtrPatch(UInt16 trapWord, Size byteCount);
extern pascal void DisposeHandlePatch(UInt16 trapWord, Handle deadHandle);
extern pascal void DisposePtrPatch(UInt16 trapWord, Ptr deadPtr);

RoutineDescriptor	gNewHandlePatchRD = BUILD_ROUTINE_DESCRIPTOR(uppNewHandleProcInfo, &NewHandlePatch);
RoutineDescriptor	gNewPtrPatchRD = BUILD_ROUTINE_DESCRIPTOR(uppNewPtrProcInfo, &NewPtrPatch);
RoutineDescriptor	gDisposeHandlePatchRD = BUILD_ROUTINE_DESCRIPTOR(uppDisposeHandleProcInfo, &DisposeHandlePatch);
RoutineDescriptor	gDisposePtrPatchRD = BUILD_ROUTINE_DESCRIPTOR(uppDisposePtrProcInfo, &DisposePtrPatch);

UniversalProcPtr	gNewHandlePatchCallThru = NULL;
UniversalProcPtr 	gNewPtrPatchCallThru = NULL;
UniversalProcPtr	gDisposeHandlePatchCallThru = NULL;
UniversalProcPtr 	gDisposePtrPatchCallThru = NULL;

Ptr							figmentAllocationList = NULL;
FigmentBlockInformation		*figmentAllocationListHash[kFigmentBlockHashTableSize];
FigmentBlockInformation		*figmentAllocationFreeList = NULL;

void AddBlockToActiveList(void *newBlock, AllocationSite *alocationSite) 
{

	//	If we don't already have a block to store the list
	//	of our active figment blocks, then create one.

	if (figmentAllocationList == NULL) {

		UInt32	currentBlock;

		figmentAllocationList = (Ptr)CallOSTrapUniversalProc(gNewPtrPatchCallThru, uppNewPtrProcInfo, 0x031E, kFigmentBlockTotalTrackedBlocks * sizeof(FigmentBlockInformation));

		//	Thread all of the new blocks onto the free list.

		for (currentBlock = 0; currentBlock < kFigmentBlockTotalTrackedBlocks; currentBlock++) {

			FigmentBlockInformation	*currentBlockPtr;

			currentBlockPtr = (FigmentBlockInformation * )figmentAllocationList + currentBlock;
			currentBlockPtr->next = figmentAllocationFreeList;
			figmentAllocationFreeList = currentBlockPtr;

		}

	}
	
	//	If we have a free block on the free list, use it
	//	to store the information about this allocation.
	
	if (figmentAllocationFreeList != NULL) {
	
		UInt32						hashKey = ((UInt32)newBlock >> 2) % kFigmentBlockHashTableSize;
		FigmentBlockInformation		*blockToUse;
	
		//	Remove the block from the free list.
	
		blockToUse = figmentAllocationFreeList;
		figmentAllocationFreeList = blockToUse->next;

		//	Add it to the hash table of active blocks in the appropriate place.
		
		blockToUse->next = figmentAllocationListHash[hashKey];
		figmentAllocationListHash[hashKey] = blockToUse;
		
		//	And fill in its relevant information.
		
		blockToUse->allocation = newBlock;
		blockToUse->allocator = alocationSite;
	
	}
	
}

void RemoveBlockFromActiveList(void *deadBlock) 
{
	UInt32						hashKey = ((UInt32)deadBlock >> 2) % kFigmentBlockHashTableSize;
	FigmentBlockInformation		*blockToUse;
	FigmentBlockInformation		**previousBlockToUse = NULL;
	
	blockToUse = figmentAllocationListHash[hashKey];
	previousBlockToUse = figmentAllocationListHash + hashKey;
	
	while (blockToUse) {
	
		if (blockToUse->allocation == deadBlock) {
			if (blockToUse->allocator != NULL)
				blockToUse->allocator->frees++;
			*previousBlockToUse = blockToUse->next;
			blockToUse->next = figmentAllocationFreeList;
			figmentAllocationFreeList = blockToUse;
			break;
		}
	
		previousBlockToUse = &(blockToUse->next);
		blockToUse = blockToUse->next;
	
	}
	
}

void GetCurrentNativeStackTrace(void **stackCrawl)
{
	void	*currentSP;
	void	*interfaceLibSP;
	void	*callerFirstStackFrame;
	void	*callerSP;
	UInt32	stackFrameLevel;
	UInt8	isPowerPC = true;
	
	memset(stackCrawl, 0, sizeof(void *) * kRecordingDepthOfStackLevels);

#if !GENERATINGPOWERPC
	return;
#endif
 
	//	If the current SP is not in our heap, then bail (OT is evil).
	
#if GENERATINGPOWERPC
	currentSP = GetCurrentStackPointer();
#endif

	if ((currentSP > gOurApplicationHeapMax) || (currentSP < gOurApplicationHeapBase))
		return;
		
	interfaceLibSP = *((void **)currentSP);
	
	callerFirstStackFrame = interfaceLibSP;
	
	//	Walk up the stack until we get the first frame
	//	which is actually in our executable's heap... note that
	//	this only works if VM is OFF!
	
	while (1) {

		void	*nextFrame;

		if ((nextFrame > gOurApplicationHeapMax) || (nextFrame < gOurApplicationHeapBase))
			return;
		
		//	Walk the stack differently whether we are at a
		//	PowerPC or 68K frame...

		if (isPowerPC) {

			void	*framePC;

			//	If we are PowerPC, check to see if the PC
			//	corresponding to the current frame is in the
			//	the app's code space.  If so, then we are
			//	done and we break out.
			
			framePC = *((void **)callerFirstStackFrame + 2);
			if ((framePC < gOurApplicationHeapMax) && (framePC > gOurApplicationHeapBase))
				break;
				
			//	Otherwise, check the pointer to the next
			//	stack frame.  If its low bit is set, then
			//	it is a mixed-mode switch frame, and 
			//	we need to switch to 68K frames.

			nextFrame = *((void **)callerFirstStackFrame);
			if (((UInt32)nextFrame & 0x00000001) != 0) {
				callerFirstStackFrame = (void *)((UInt32)nextFrame & 0xFFFFFFFE);
				isPowerPC = false;
			} else
				callerFirstStackFrame = nextFrame;

		}
	
		else {
			
			//	68K case:  If the location immediately above the stack
			//	frame pointer is -1, then we are at a switch frame,
			//	move back to PowerPC.

			if (*((UInt32 *)callerFirstStackFrame - 1) == 0xFFFFFFFF)
				isPowerPC = true;
			callerFirstStackFrame = *((void **)callerFirstStackFrame);

		}
	
	}
	
	callerSP = callerFirstStackFrame;
	
	for (stackFrameLevel = 0; stackFrameLevel < kRecordingDepthOfStackLevels; stackFrameLevel++) {
	
		if ((callerSP > gOurApplicationHeapMax) || (callerSP < gOurApplicationHeapBase))
			return;
			
		stackCrawl[stackFrameLevel] = *(((void **)callerSP) + 2);
		
		callerSP = *((void **)callerSP);
	
	}

}

extern pascal Handle NewHandlePatch(UInt16 trapWord, Size byteCount)
{
	void			*stackCrawl[kRecordingDepthOfStackLevels];
	Handle			resultHandle;
	AllocationSite	*allocationSite;
	
	if (gTrackLeaks)
		GetCurrentNativeStackTrace(stackCrawl);

	resultHandle = (Handle)CallOSTrapUniversalProc(gNewHandlePatchCallThru, uppNewHandleProcInfo, trapWord, byteCount);

	if (gTrackLeaks && resultHandle) {
		allocationSite = AddAllocationSite(kAllocationSiteNewHandle, stackCrawl);
		AddBlockToActiveList((void *)resultHandle, allocationSite);
		gOutstandingHandles++;
	}
	
	return resultHandle;
	
}

extern pascal Ptr NewPtrPatch(UInt16 trapWord, Size byteCount)
{	
	void			*stackCrawl[kRecordingDepthOfStackLevels];
	Ptr				resultPtr;
	AllocationSite	*allocationSite;

	if (gTrackLeaks)
		GetCurrentNativeStackTrace(stackCrawl);
	
	resultPtr = (Ptr)CallOSTrapUniversalProc(gNewPtrPatchCallThru, uppNewPtrProcInfo, trapWord, byteCount);

	if (gTrackLeaks && resultPtr) {
		allocationSite = AddAllocationSite(kAllocationSiteNewPtr, stackCrawl);
		AddBlockToActiveList((void *)resultPtr, allocationSite);
		gOutstandingPointers++;
	}
	
	return resultPtr;
	
}

pascal void DisposeHandlePatch(UInt16 trapWord, Handle deadHandle)
{
	if (gTrackLeaks) {
		RemoveBlockFromActiveList(deadHandle);
		gOutstandingHandles--;
	}
	CallOSTrapUniversalProc(gDisposeHandlePatchCallThru, uppDisposeHandleProcInfo, trapWord, deadHandle);
}

pascal void DisposePtrPatch(UInt16 trapWord, Ptr deadPtr)
{
	if (gTrackLeaks) {
		RemoveBlockFromActiveList(deadPtr);
		gOutstandingPointers--;
	}
	CallOSTrapUniversalProc(gDisposePtrPatchCallThru, uppDisposePtrProcInfo, trapWord, deadPtr);
}


void InstallMemoryManagerPatches(void)
{
	ProcessSerialNumber	thisProcess = { 0, kCurrentProcess };
	ProcessInfoRec		processInfo;

	processInfo.processInfoLength = sizeof(processInfo);
	processInfo.processName = NULL;
	processInfo.processAppSpec = NULL;

	GetProcessInformation(&thisProcess, &processInfo);

	gOurApplicationHeapBase = processInfo.processLocation;
	gOurApplicationHeapMax = (Ptr)gOurApplicationHeapBase + processInfo.processSize;

	gNewHandlePatchCallThru = GetOSTrapAddress(0x0122);
	SetOSTrapAddress(&gNewHandlePatchRD, 0x0122);
	
	gNewPtrPatchCallThru = GetOSTrapAddress(0x011E);
	SetOSTrapAddress(&gNewPtrPatchRD, 0x011E);

	gDisposeHandlePatchCallThru = GetOSTrapAddress(0x023);
	SetOSTrapAddress(&gDisposeHandlePatchRD, 0x023);

	gDisposePtrPatchCallThru = GetOSTrapAddress(0x001F);
	SetOSTrapAddress(&gDisposePtrPatchRD, 0x001F);

}

#else

void InstallMemoryManagerPatches(void)
{
	
}

#endif
#endif



