Milán Major

netscape-revival

1 branch
Code

nspr/src/md_Mac/ObjectHeap.cp

// ObjectHeap.cp 
// Copyright © 1985-1994 by Apple Computer, Inc.  All rights reserved.

#ifndef PLATFORMMEMORY_H
#include "PlatformMemory.h"
#endif

#ifndef __OBJECTHEAP__
#include "ObjectHeap.h"
#endif

#ifdef PROFILE
#pragma profile on
#endif

#ifdef DEBUG

char * keepConditionAlive; // dummy

void AssertPointer (const void * p, char *condition)
{
	keepConditionAlive = condition; // dummy to keep this around in the debugger
	long pp = (long) p;
	PR_ASSERT (
		pp == 0 
		|| (0x00200000 <= pp && pp <= 0x08000000)
	);
}

#endif

//========================================================================================
// CLASS ChunkyBlock
//========================================================================================

#define SET_SIZE_INDEX( blk, sizeIndex ) \
	(blk)->fBits |= ( (sizeIndex) << ChunkyBlock_kSizeIndexShift ) & ChunkyBlock_kSizeIndexMask;

#define SET_BLOCK_INDEX( blk, blockIndex ) \
	(blk)->fBits |= ( (blockIndex) << ChunkyBlock_kBlockIndexShift ) & ChunkyBlock_kBlockIndexMask;

#define SET_BLOCK_TYPE( blk, blockType ) \
	(blk)->fBits |= ( (blockType) << ChunkyBlock_kBlockTypeShift ) & ChunkyBlock_kBlockTypeMask;

#define SET_MAGIC_NUMBER( blk, magic ) \
	(blk)->fBits |= ( (magic) << ChunkyBlock_kMagicNumberShift ) & ChunkyBlock_kMagicNumberMask;

//----------------------------------------------------------------------------------------
// ChunkyBlock::ChunkyBlock
//----------------------------------------------------------------------------------------

ChunkyBlock::ChunkyBlock()
{
	SET_BLOCK_TYPE( this, kBlockTypeId );
	SET_MAGIC_NUMBER( this, kMagicNumber );
	fNext = NULL;
}

//----------------------------------------------------------------------------------------
// ChunkyBlock::ChunkyBlock
//----------------------------------------------------------------------------------------

ChunkyBlock::ChunkyBlock( unsigned short sizeIndex, unsigned short blockIndex )
{
	fBits = 0;
	SET_SIZE_INDEX( this, sizeIndex );
	SET_BLOCK_INDEX( this, blockIndex );
	SET_BLOCK_TYPE( this, kBlockTypeId );
	SET_MAGIC_NUMBER( this, kMagicNumber );
	fNext = NULL;
}

//========================================================================================
// Chunk
//========================================================================================

//----------------------------------------------------------------------------------------
// Chunk::Chunk
//----------------------------------------------------------------------------------------
#pragma segment HeapSeg

Chunk::Chunk( short blocksPerChunk, unsigned short sizeIndex, FW_BlockSize blockSize )
{
	fHeader.fBlockBusyBits = 0;
	void* blkPtr = (void*) ((FW_BytePtr)this + sizeof( SChunkHeader ));
	for ( int i = 0; i < blocksPerChunk; i++ )
	{
		ChunkyBlock* blk = new ( blkPtr ) ChunkyBlock( sizeIndex, i );
		blkPtr = (void*) ( (FW_BytePtr)blkPtr + blockSize );
	}
}

//========================================================================================
// ObjectHeap
//========================================================================================

const FW_BlockSize ObjectHeap::kDefaultBlockSizes[] = {sizeof(ChunkyBlock), 10, 14, 18, 0};

//----------------------------------------------------------------------------------------
// ObjectHeap::ObjectHeap
//----------------------------------------------------------------------------------------
#pragma segment HeapSeg

ObjectHeap::ObjectHeap( unsigned long initialSize,
				   unsigned long incrementSize,
				   short blocksPerChunk ) :
	BestFitHeap( initialSize, incrementSize ),
	fBlockSizes( kDefaultBlockSizes )
{
#ifdef DEBUG
	CompilerCheck();
#endif

	fBlocksPerChunk = blocksPerChunk;

	for ( fNumberOfBlockSizes = 0; fBlockSizes[ fNumberOfBlockSizes ]; fNumberOfBlockSizes++ )
		;
		
	fLargestBlockSize = fBlockSizes[ fNumberOfBlockSizes - 1 ];
}

//----------------------------------------------------------------------------------------
// ObjectHeap::ObjectHeap
//----------------------------------------------------------------------------------------
#pragma segment HeapSeg

ObjectHeap::ObjectHeap(const FW_BlockSize* blockSizes,
				   unsigned long initialSize,
				   unsigned long incrementSize,
				   short blocksPerChunk) :
	BestFitHeap( initialSize, incrementSize ),
	fBlockSizes( blockSizes )
{
#ifdef DEBUG
	CompilerCheck();
#endif

	fBlocksPerChunk = blocksPerChunk;

	for ( fNumberOfBlockSizes = 0; fBlockSizes[ fNumberOfBlockSizes ]; fNumberOfBlockSizes++ )
		;
		
	fLargestBlockSize = fBlockSizes[ fNumberOfBlockSizes - 1 ];
}

//----------------------------------------------------------------------------------------
// ObjectHeap::IObjectHeap
//----------------------------------------------------------------------------------------
#pragma segment HeapSeg

void ObjectHeap::IObjectHeap()
{
	this->IBestFitHeap();
}

//----------------------------------------------------------------------------------------
// ObjectHeap::~ObjectHeap
//----------------------------------------------------------------------------------------
#pragma segment HeapSeg

ObjectHeap::~ObjectHeap()
{
}

//----------------------------------------------------------------------------------------
// ObjectHeap::DoAllocate
//----------------------------------------------------------------------------------------
#pragma segment HeapSeg

#define ALLOCATE_SLOP

extern void TraceAllocate (size_t size);

void* ObjectHeap::DoAllocate( FW_BlockSize size, FW_BlockSize& allocatedSize )
{
#ifdef ALLOCATE_SLOP
	FW_BlockSize	realSize = size + ChunkyBlock::kBusyOverhead;
	
	if ( realSize > fLargestBlockSize )
		return BestFitHeap::DoAllocate( size, allocatedSize );

	register unsigned short		sizeIndex;
		
	allocatedSize = fBlockSizes[ sizeIndex = this->SizeIndex( size ) ] -
					ChunkyBlock::kBusyOverhead;
	return this->AllocateBlock( sizeIndex );
#else

	unsigned short sizeIndex = fNumberOfBlockSizes;
	// always allocate minimum a [0] byte block.
//	if (size + ChunkyBlock::kBusyOverhead < fBlockSizes[0])
//		sizeIndex = 0;
//	else

	for ( unsigned short i = 0; i < fNumberOfBlockSizes; i++ )
		// don't use bins for odd sizes to avoid wasted slop
		if ( size + ChunkyBlock::kBusyOverhead == fBlockSizes[i] )
		{
			sizeIndex = i;
			break;
		}
		
	if ( sizeIndex == fNumberOfBlockSizes )
		return BestFitHeap::DoAllocate( size, allocatedSize );
	else
	{
		allocatedSize = fBlockSizes[ sizeIndex ] - ChunkyBlock::kBusyOverhead;
		return this->AllocateBlock( sizeIndex );
	}
#endif
}

//----------------------------------------------------------------------------------------
// ObjectHeap::DoBlockSize
//----------------------------------------------------------------------------------------
#pragma segment HeapSeg

FW_BlockSize ObjectHeap::DoBlockSize(const void* ptr) const
{
	ChunkyBlock *block
		= (ChunkyBlock *) ((FW_BytePtr) ptr - ChunkyBlock::kBusyOverhead);
		
	if (block->GetBlockType() == BestFitBlock::kBlockTypeId)
		return BestFitHeap::DoBlockSize(ptr);
	else
		return fBlockSizes[block->GetSizeIndex()] - ChunkyBlock::kBusyOverhead;
}

//----------------------------------------------------------------------------------------
// ObjectHeap::DoFree
//----------------------------------------------------------------------------------------
#pragma segment HeapSeg

void ObjectHeap::DoFree(void* ptr)
{
	ChunkyBlock *block 
		= (ChunkyBlock *) ((FW_BytePtr) ptr - ChunkyBlock::kBusyOverhead);
		
	if (block->GetBlockType() == BestFitBlock::kBlockTypeId)
		BestFitHeap::DoFree(ptr);
	else
		this->FreeBlock(block);
}

#ifdef DEBUG
//----------------------------------------------------------------------------------------
// ObjectHeap::DoIsValidBlock
//----------------------------------------------------------------------------------------
#pragma segment HeapSeg

Boolean ObjectHeap::DoIsValidBlock(void* ptr) const
{
	Boolean isBlockValid = false;
	
	ChunkyBlock *block
		= (ChunkyBlock *) ((FW_BytePtr) ptr - ChunkyBlock::kBusyOverhead);

	if (block->GetBlockType() == BestFitBlock::kBlockTypeId)
		isBlockValid = BestFitHeap::DoIsValidBlock(ptr);
	else
		isBlockValid
			= block->GetSizeIndex() <= fNumberOfBlockSizes &&
			  block->GetBlockIndex() <= fBlocksPerChunk &&
			  block->GetMagicNumber() == (unsigned short) ChunkyBlock::kMagicNumber;

	return isBlockValid;
}
#endif

//----------------------------------------------------------------------------------------
// ObjectHeap::DoReset
//----------------------------------------------------------------------------------------
#pragma segment HeapSeg

void ObjectHeap::DoReset()
{
	ChunkyBlockStack clr;

	for (int i = 0; i < fNumberOfBlockSizes; i++)
		fFreeLists[i] = clr;

	BestFitHeap::DoReset();
}

//----------------------------------------------------------------------------------------
// ObjectHeap::AllocateBlock
//----------------------------------------------------------------------------------------
#pragma segment HeapSeg

void* ObjectHeap::AllocateBlock( unsigned short sizeIndex )
{
	ChunkyBlockStack*	freeList = &fFreeLists[ sizeIndex ];
	ChunkyBlock*		blk = NULL;
	
	// ¥ pull the first block off the free list
	POP_BLK( blk, freeList );
	
	// ¥Êif we have one, set it busy and return it
	if ( blk )
		goto done;
		
	// ¥ else do a lot of extra work to create a new chunk, get a block out of it
	//		and return that one
	this->CreateNewChunk( sizeIndex );
	POP_BLK( blk, freeList );

	// ¥ we're flat out empty now
	if ( !blk )
		return NULL;

done:
	// ¥Êget our block size
	FW_BlockSize		blockSize = fBlockSizes[ sizeIndex ];
	// ¥ and our block index
	unsigned short		blockIndex = BLOCK_INDEX( blk );
	
	// ¥ get the chunk ptr so we can get to it's fBlockBusyBits
	Chunk*				chk = CHUNK_PTR( blk, blockSize, blockIndex );
	
	// ¥Êmark the block busy
	SET_CHK_BLOCK_BUSY( chk, blockIndex );

	return (void *) ((FW_BytePtr) blk + ChunkyBlock::kBusyOverhead );
}	


/*	if ( ! ( fFreeLists[ sizeIndex ].Top() ) )
		this->CreateNewChunk( sizeIndex );

	ChunkyBlock* blk = fFreeLists[ sizeIndex ].Pop();
	if ( blk )
	{
		blk->SetBusy( fBlockSizes[ sizeIndex ], true );
		return (void *) ((FW_BytePtr) blk + ChunkyBlock::kBusyOverhead );
	}
	else
		return NULL;
*/

//----------------------------------------------------------------------------------------
// ObjectHeap::FreeBlock
//----------------------------------------------------------------------------------------
#pragma segment HeapSeg

void ObjectHeap::FreeBlock( ChunkyBlock* blk )
{
/*	blk->SetBusy(fBlockSizes[blk->GetSizeIndex()], false);
	fFreeLists[blk->GetSizeIndex()].Push(blk);

	// Check to see if all blocks in this block's Chunk are free, if so then free the
	// Chunk.

	Chunk *chk = blk->GetChunk(fBlockSizes[blk->GetSizeIndex()]);
	if (!chk->IsBusy())
	{
		// Remove blocks in this Chunk from the free list. This is the achililles hill
		// of the Heap. Its difficult to remove blocks from a singly linked list
		// rapidly.

		void *begAddr = chk;
		void *endAddr = (void *) ((FW_BytePtr) chk + 
									sizeof(SChunkHeader) +
									fBlocksPerChunk * fBlockSizes[chk->GetSizeIndex()]);
		fFreeLists[chk->GetSizeIndex()].RemoveRange(begAddr, endAddr);
		BestFitHeap::DoFree(chk);
	}
*/
	unsigned short		sizeIndex = SIZE_INDEX( blk );
	unsigned short		blockIndex = BLOCK_INDEX( blk );
	unsigned long		blockSize = fBlockSizes[ sizeIndex ];
	ChunkyBlockStack*	freeList = &fFreeLists[ sizeIndex ];

	// ¥ get the chunk ptr so we can get to it's fBlockBusyBits
	Chunk*				chk = CHUNK_PTR( blk, blockSize, blockIndex );

	CLEAR_CHK_BLOCK_BUSY( chk, blockIndex );
	
	// ¥ push the block onto the free list
	PUSH_BLK( blk, freeList );
	
	// ¥ check to see if all blocks in this block's Chunk are free, if so then free the
	//		chunk.

	if ( !chk->fHeader.fBlockBusyBits )
	{
		// ¥ remove blocks in this Chunk from the free list. This is the achililles hill of the Heap.
		//		Its difficult to remove blocks from a singly linked list rapidly.

		void*	begAddr = chk;
		void*	endAddr = (void *) ((FW_BytePtr)chk + 
								sizeof( SChunkHeader ) +
								fBlocksPerChunk * blockSize );
		freeList->RemoveRange( begAddr, endAddr );
		BestFitHeap::DoFree( chk );
	}
}

//----------------------------------------------------------------------------------------
// ObjectHeap::CreateNewChunk
//----------------------------------------------------------------------------------------
#pragma segment HeapSeg

void ObjectHeap::CreateNewChunk( unsigned short sizeIndex )
{
/*	FW_BlockSize allocatedSize;
	FW_BlockSize allocSize = sizeof(SChunkHeader) + fBlocksPerChunk * fBlockSizes[sizeIndex];
	void * memory = BestFitHeap::DoAllocate(allocSize, allocatedSize);
	Chunk * chk = new(memory)Chunk(fBlocksPerChunk, sizeIndex, fBlockSizes[sizeIndex]);
	if (chk != NULL)
	{
		for (int i = 0; i < fBlocksPerChunk; i++)
		{
			ChunkyBlock* blk = CHUNKY_BLOCK_PTR( chk, i, fBlockSizes[ sizeIndex ] );
			//ChunkyBlock * blk = chk->GetBlock(i, fBlockSizes[sizeIndex]);
			//blk->SetBusy(fBlockSizes[sizeIndex], false);
			fFreeLists[sizeIndex].Push(blk);
		}
	}

*/
	FW_BlockSize		blockSize = fBlockSizes[ sizeIndex ];
	FW_BlockSize		allocatedSize;
	FW_BlockSize		allocSize = sizeof( SChunkHeader ) + ( fBlocksPerChunk * blockSize );
	
	void* memory = BestFitHeap::DoAllocate( allocSize, allocatedSize );

	Chunk* chk = new ( memory ) Chunk( fBlocksPerChunk, sizeIndex, blockSize );

	if ( chk )
	{
		ChunkyBlockStack*	freeList = &fFreeLists[ sizeIndex ];
		ChunkyBlock*		blk;
		
		for ( int blockIndex = 0; blockIndex < fBlocksPerChunk; blockIndex++ )
		{
			blk = CHUNKY_BLOCK_PTR( chk, blockIndex, blockSize );
			PUSH_BLK( blk, freeList );
		}
	}
}

//----------------------------------------------------------------------------------------
// ObjectHeap::SizeIndex
//----------------------------------------------------------------------------------------
#pragma segment HeapSeg

unsigned short ObjectHeap::SizeIndex( FW_BlockSize size )
{
	register FW_BlockSize		realSize = size + ChunkyBlock::kBusyOverhead;
	register unsigned short		count = 0;
	
	while ( count < fNumberOfBlockSizes )
	{
#ifdef ALLOCATE_SLOP
		if ( realSize <= fBlockSizes[ count ] )
#else
		if ( realSize == fBlockSizes[ count ] )
#endif
			return count;
		count++;
	}

	// Uh oh! An internal error:

#ifdef XP_MAC
	DebugStr("\pObjectHeap::SizeIndex internal error");
#else
#ifdef ALLOCATE_SLOP
	BreakToSourceDebugger_();
#endif
#endif

	return 0xFFFFFFFF;	// the former way of handling the internal error
						// we leave it in to defeat an xlC warning
}

#ifdef DEBUG
//----------------------------------------------------------------------------------------
// ObjectHeap::CompilerCheck
//----------------------------------------------------------------------------------------
#pragma segment HeapSeg

void ObjectHeap::CompilerCheck()
{
	BestFitHeap::CompilerCheck();
	
	ChunkyBlock block;
	
	block.SetSizeIndex(0xF);
	block.SetBlockIndex(0xE);
	block.SetBlockType(ChunkyBlock::kBlockTypeId);
	block.SetMagicNumber(0xC);
	
	PLATFORM_ASSERT(block.GetSizeIndex() == 0xF);
	PLATFORM_ASSERT(block.GetBlockIndex() == 0xE);
	PLATFORM_ASSERT(block.GetBlockType() == ChunkyBlock::kBlockTypeId);
	PLATFORM_ASSERT(block.GetBlockType() != BestFitBlock::kBlockTypeId);
	PLATFORM_ASSERT(block.GetMagicNumber() == 0xC);
	
	block.SetSizeIndex(0x7);
	block.SetBlockIndex(0x6);
	block.SetBlockType(BestFitBlock::kBlockTypeId);
	block.SetMagicNumber(0x4);
	
	PLATFORM_ASSERT(block.GetSizeIndex() == 0x7);
	PLATFORM_ASSERT(block.GetBlockIndex() == 0x6);
	PLATFORM_ASSERT(block.GetBlockType() == BestFitBlock::kBlockTypeId);
	PLATFORM_ASSERT(block.GetBlockType() != ChunkyBlock::kBlockTypeId);
	PLATFORM_ASSERT(block.GetMagicNumber() == 0x4);
}
#endif

//========================================================================================
// ChunkyBlockStack
//========================================================================================

//----------------------------------------------------------------------------------------
// ChunkyBlockStack::ChunkyBlockStack
//----------------------------------------------------------------------------------------
#pragma segment HeapSeg

ChunkyBlockStack::ChunkyBlockStack()
{
	fHead.SetNext(NULL);
}

//----------------------------------------------------------------------------------------
// ChunkyBlockStack::ChunkyBlockStack
//----------------------------------------------------------------------------------------
#pragma segment HeapSeg

ChunkyBlockStack::ChunkyBlockStack(const ChunkyBlockStack& blk) :
	fHead(blk.fHead)
{
}

//----------------------------------------------------------------------------------------
// ChunkyBlockStack::operator=
//----------------------------------------------------------------------------------------
#pragma segment HeapSeg

ChunkyBlockStack& ChunkyBlockStack::operator=(const ChunkyBlockStack& blk)
{
	fHead = blk.fHead;
	return *this;
}

//----------------------------------------------------------------------------------------
// ChunkyBlockStack::RemoveRange
//----------------------------------------------------------------------------------------
#pragma segment HeapSeg

void ChunkyBlockStack::RemoveRange( void* begAddr, void* endAddr )
{
	ASSERT_POINTER( begAddr );
	ASSERT_POINTER( endAddr );
	ChunkyBlock*	prevBlk = &fHead;
	ChunkyBlock*	curBlk = fHead.GetNext();

	while ( curBlk )
	{
		void*	curAddr = curBlk;

		if ( curAddr >= begAddr && curAddr <= endAddr )
		{
			prevBlk->SetNext( curBlk->GetNext() );
			curBlk = curBlk->GetNext();
		}
		else
		{
			prevBlk = curBlk;
			curBlk = curBlk->GetNext();
		}
	}
}

//----------------------------------------------------------------------------------------
// ChunkyBlockStack::~ChunkyBlockStack
//----------------------------------------------------------------------------------------
#pragma segment HeapSeg

ChunkyBlockStack::~ChunkyBlockStack()
{
}

#ifdef PROFILE
#pragma profile off
#endif