Milán Major

netscape-revival

1 branch
Code

nspr/src/md_Mac/mdmacmem.cp

#include "MemoryHeap.h"
#include "ObjectHeap.h"
#include "BestFitHeap.h"
#include "mdmacmem.h"
#include "prmacos.h"
#include <stdlib.h>
#include "MacErrorHandling.h"
#include <string.h> 

#define DEBUG_MAC_MEMORY 			0

#if DEBUG_MAC_MEMORY
	#define FORCE_HEAP_SCRAMBLING	{														\
										Handle		tmpHandle;								\
																							\
										tmpHandle = NewHandle(0);							\
										if ((tmpHandle != NULL) && (MemError() == noErr))	\
											DisposeHandle(tmpHandle);						\
									}
#else
	#define FORCE_HEAP_SCRAMBLING
#endif


//##############################################################################
//##############################################################################
#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;

void CallPreAllocators(void);

#if DEBUG_MAC_MEMORY
void VerifyAllocatedBlock(void *block, char *message);
void VerifyEntireMallocHeap(void);
#endif

//##############################################################################
//##############################################################################
#pragma mark -
#pragma mark STDC-STYLE MEMORY

ObjectHeap 	*sObjectHeap = NULL;

//
//	NOTE:  	The following declarations are used to hint to the 
//			best-fit heap memory manager which block sizes are
//			used most commonly.  Right now, these are
//
//				sizeof( lo_FontStack )
//				sizeof( PA_Tag ) 
//				sizeof( LO_Element )
//

static const FW_BlockSize blockSizes[] = {
					4 	+ ChunkyBlock::kBusyOverhead, 
					12	+ ChunkyBlock::kBusyOverhead, 
					26 	+ ChunkyBlock::kBusyOverhead,
					0};

// 
//	NOTE:	The following declarations indicate to the best-bit
//			heap manager what default blocks to allocate.
//

#define K			1024
#define SYSTEM_RESERVE		32 * (K)
#define MEMORY_INCREMENT	24 * (K)
#define MEMORY_LIMIT		32 * (K) / 2
#define	INITIAL_MEMORY		24 * (K)
#define CHUNKS_PER_BLOCK	16

void MacintoshInitializeMemory(void)
{

	MaxApplZone();
	for (uint32 i = 1; i <= 30; i++) {
		MoreMasters();
	}

	if ( !sObjectHeap )
	{
		sObjectHeap = new ObjectHeap(
								blockSizes, 
								INITIAL_MEMORY,
								MEMORY_INCREMENT,
								CHUNKS_PER_BLOCK );
		sObjectHeap->SetLimitFromMax( MEMORY_LIMIT );
		sObjectHeap->IObjectHeap();
	}

}

#if DEBUG_MAC_MEMORY

enum {
	kAllocatedBlockHeaderTag	= 'nsbh',
	kAllocatedBlockTrailerTag	= 'nsbt',
	kFreeBlockHeaderTag			= 'nsfh',
	kFreeBlockTrailerTag		= 'nsft',
	kMemUsedFillPattern			= 0xEFEFEFEF,
	kMemFreeFillPattern			= 0xDFDFDFDF
};

typedef struct DebugBlockHeader DebugBlockHeader;

static void *					gFirstMemoryBlock = NULL;
static UInt32					gBlockCount = 0;
static UInt32					gBlockAllocations = 0; 
static uint32					gVerifyMallocHeapOnMemOp = false;
static uint32					gFindMallocCaller = false;

struct DebugBlockHeader {
	void 						*prevBlock;
	void						*nextBlock;
	void						*callerPC;
	uint32						blockNum;
	size_t						blockSize;
	uint32						blockTag;
};

typedef struct DebugBlockTrailer DebugBlockTrailer;

struct DebugBlockTrailer {
	uint32						blockTag;
};

void VerifyAllocatedBlock(void *block, char *message)
{
	DebugBlockHeader		*header;
	DebugBlockTrailer		*trailer;

	block = (char *)block - sizeof(DebugBlockHeader);

	header = (DebugBlockHeader *)block;
	trailer = (DebugBlockTrailer *)(((char *)block) + header->blockSize - sizeof(DebugBlockTrailer));
	
	if (header->blockTag != kAllocatedBlockHeaderTag) {
		if (header->blockTag == kFreeBlockHeaderTag)
			DebugStr("\pblock corrupt - possible double dispose");
		else
			DebugStr("\pmalloc block corrupt - possible corrupt block");
	}
	
	if (trailer->blockTag != kAllocatedBlockTrailerTag) {
		if (trailer->blockTag == kFreeBlockTrailerTag)
			DebugStr("\pmalloc block corrupt - possible double dispose");
		else
			DebugStr("\pmalloc block corrupt - possible corrupt block");
	}

}		

void VerifyEntireMallocHeap(void)
{
	uint32			totalFoundBlocks = 0;
	void			*currentBlock = gFirstMemoryBlock;
	
	while (currentBlock != NULL) {

		DebugBlockHeader		*header = (DebugBlockHeader *)currentBlock;	
		VerifyAllocatedBlock((char *)header + sizeof(DebugBlockHeader), "heap verify");
		totalFoundBlocks++;
		currentBlock = header->nextBlock;

	}
	
	if (totalFoundBlocks != gBlockCount)
		DebugStr("\pheap verify: not all blocks accounted for");
	
}


#endif

void* malloc( size_t size )
{	
#if DEBUG_MAC_MEMORY
	UInt32		toFindStackPtr;
#endif
	char 		*newBlock = NULL;	
	UInt8		tryAgain = true;

#if DEBUG_MAC_MEMORY
	size += sizeof(DebugBlockHeader) + sizeof(DebugBlockTrailer);
#endif

	//	For the bootstrapping case, make sure that we call NewPtr
	//	instead of sObjectHeap->Allocate when sObjectHeap is NULL
	
	if (sObjectHeap == NULL)
		return (void *)NewPtr(size);

	CallPreAllocators();
	
#if DEBUG_MAC_MEMORY

	if (gVerifyMallocHeapOnMemOp)
		VerifyEntireMallocHeap();

	newBlock = (char *)NewPtr(size);

	if (newBlock != NULL) {
	
		DebugBlockHeader		*header;
		DebugBlockTrailer		*trailer;
		
		header = (DebugBlockHeader *)newBlock;
		trailer = (DebugBlockTrailer *)(((char *)newBlock) + size - sizeof(DebugBlockTrailer));

		if (gFindMallocCaller) {
			header->callerPC = *((void **)((char *)&toFindStackPtr + 104 - 60));  	// 	TOTAL HACK TO GET LR!!!
		}
		
		else {
			header->callerPC = NULL;	
		}
		
		header->blockTag = kAllocatedBlockHeaderTag;
		header->blockSize = size;
		trailer->blockTag = kAllocatedBlockTrailerTag;
		
		header->blockNum = gBlockAllocations;
		
		gBlockAllocations++;
		gBlockCount++;

		//	Insert the new memory block into our linked list of blocks.
		
		header->nextBlock = gFirstMemoryBlock;
		header->prevBlock = NULL;
		
		if (gFirstMemoryBlock != NULL)
			((DebugBlockHeader *)gFirstMemoryBlock)->prevBlock = header;

		gFirstMemoryBlock = header;
		
		newBlock += sizeof(DebugBlockHeader);

		memset(newBlock, kMemUsedFillPattern, size - sizeof(DebugBlockHeader) - sizeof(DebugBlockTrailer));

	}

#else

	while (tryAgain && (newBlock == NULL)) {
		newBlock = (char *)sObjectHeap->Allocate(size);
		if (newBlock == NULL)
			tryAgain = CallCacheFlushers(size);
	}

#endif

	FORCE_HEAP_SCRAMBLING

	return newBlock;

}

void free(void* block)
{
	if (block == NULL)
		return;

#if DEBUG_MAC_MEMORY

	if (gVerifyMallocHeapOnMemOp)
		VerifyEntireMallocHeap();

	if (block != NULL) {

		DebugBlockHeader		*header;
		DebugBlockTrailer		*trailer;

		VerifyAllocatedBlock(block, "free");

		block = (char *)block - sizeof(DebugBlockHeader);

		header = (DebugBlockHeader *)block;
		trailer = (DebugBlockTrailer *)(((char *)block) + header->blockSize - sizeof(DebugBlockTrailer));

		memset((char *)header + sizeof(DebugBlockHeader), kMemFreeFillPattern, 
			header->blockSize - sizeof(DebugBlockHeader) - sizeof(DebugBlockTrailer));

		header->blockTag = kFreeBlockHeaderTag;
		trailer->blockTag = kFreeBlockTrailerTag;

		//	Take this block out of the list of used blocks.
		
		if (header->nextBlock != NULL)
			((DebugBlockHeader *)header->nextBlock)->prevBlock = header->prevBlock;

		if (header->prevBlock != NULL)
			((DebugBlockHeader *)header->prevBlock)->nextBlock = header->nextBlock;
		else
			gFirstMemoryBlock = header->nextBlock;

		gBlockCount--;

		DisposePtr((Ptr)block);

	}
	
#else	
		
	sObjectHeap->Free(block);

#endif

	FORCE_HEAP_SCRAMBLING

}

void* realloc( void* block, size_t newSize )
{
	void 		*newBlock = NULL;	
	UInt8		tryAgain = true;
	
	CallPreAllocators();
	
#if DEBUG_MAC_MEMORY

	if (gVerifyMallocHeapOnMemOp)
		VerifyEntireMallocHeap();

	VerifyAllocatedBlock(block, "realloc");

	newBlock = malloc(newSize);
	
	if (newBlock != NULL) {	
		BlockMoveData(block, newBlock, newSize);
		free(block);
	}

#else

	while (tryAgain && (newBlock == NULL)) {
		newBlock = sObjectHeap->Reallocate(block, newSize);
		if (newBlock == NULL)
			tryAgain = CallCacheFlushers(newSize);
	}


#endif

	FORCE_HEAP_SCRAMBLING

	return newBlock;
}

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

	CallPreAllocators();

#if DEBUG_MAC_MEMORY

	if (gVerifyMallocHeapOnMemOp)
		VerifyEntireMallocHeap();

	newBlock = (char *)malloc(totalSize);

#else

	while (tryAgain && (newBlock == NULL)) {
		newBlock = (char *)sObjectHeap->Allocate(totalSize);
		if (newBlock == NULL)
			tryAgain = CallCacheFlushers(totalSize);
	}

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

	FORCE_HEAP_SCRAMBLING

	return newBlock;
}

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

void InstallPreAllocationHook(PreAllocationHookProc newHook)
{
	PreAllocationProcRec			*preAllocatorRec;

	preAllocatorRec = (PreAllocationProcRec *)(sObjectHeap->Allocate(sizeof(PreAllocationProcRec)));

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

}

void InstallMemoryCacheFlusher(MemoryCacheFlusherProc newFlusher)
{	
	MemoryCacheFlusherProcRec		*cacheFlusherRec;
	
	cacheFlusherRec =  (MemoryCacheFlusherProcRec *)(sObjectHeap->Allocate(sizeof(MemoryCacheFlusherProcRec)));

	if (cacheFlusherRec != NULL) {
	
		cacheFlusherRec->next = gFirstFlusher;
		cacheFlusherRec->flushProc = newFlusher;
		gFirstFlusher = cacheFlusherRec;		

	}
	
}

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

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

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

	while (currentCacheFlusher != NULL) {
		result = (*(currentCacheFlusher->flushProc))(blockSize);
		if (result)
			break;
		currentCacheFlusher = currentCacheFlusher->next;
	}
	
	return result;
	
}