servicediscoveryandcontrol/pnp/test/upnp/chunkmgr/src/cchunkmanager.cpp
changeset 0 f5a58ecadc66
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/servicediscoveryandcontrol/pnp/test/upnp/chunkmgr/src/cchunkmanager.cpp	Tue Feb 02 01:12:20 2010 +0200
@@ -0,0 +1,274 @@
+// Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+// @file
+// @internalComponent
+// 
+//
+
+#include <e32base.h>
+#include "cchunkmanager.h"
+#include "cchunkallocator.h"
+#include "cmemchunk.h"
+#include "rchunkpool.h"
+#include "rmemcell.h"
+
+EXPORT_C CChunkManager* CChunkManager::NewL ( TInt aHeapSize )
+	{
+	CChunkManager* self = new ( ELeave ) CChunkManager ( aHeapSize );
+	CleanupStack::PushL ( self );
+	self->ConstructL ();
+	CleanupStack::Pop ( self );
+	return self;
+	}
+
+CChunkManager::CChunkManager ( TInt aHeapSize )
+	:iHeapSize ( aHeapSize )
+	{
+	}
+	
+EXPORT_C CChunkManager::~CChunkManager ()
+	{
+	delete iChunkAllocator;
+	for ( TInt i = 0; i < iChunkPools.Count (); i++ )
+		{
+		iChunkPools[i].Close ();
+		}
+	iChunkPools.Close ();
+	iLock.Close ();
+	}
+	
+void CChunkManager::ConstructL ()
+	{
+	User::LeaveIfError ( iLock.CreateLocal () );
+	iChunkAllocator = CChunkAllocator::NewL ( iHeapSize );
+	}
+	
+CChunkAllocator& CChunkManager::Allocator ()
+	{
+	return *iChunkAllocator;
+	}
+	
+EXPORT_C void CChunkManager::AddPoolL ( TInt aBlockSize, TInt aNumBlocks, TInt aThresholdValue, TInt aMinGrowth )
+	{
+	__ASSERT_ALWAYS ( aBlockSize > 0, User::Invariant () ); // TODO
+    __ASSERT_ALWAYS ( aNumBlocks > 0, User::Invariant () );
+    __ASSERT_ALWAYS ( aThresholdValue >= 0, User::Invariant () );
+   	__ASSERT_ALWAYS ( aMinGrowth >= 0, User::Invariant () );
+   	    
+    // aling the block size
+   	const TUint8 KMemAlignment = 8;
+   	TInt blockPadding = aBlockSize % KMemAlignment;
+	if ( blockPadding != 0 )
+    	{
+	 	aBlockSize += KMemAlignment - blockPadding;
+        }
+    if ( aMinGrowth == 0 )
+      	{
+        aMinGrowth = 1;
+        }
+ 	
+ 	
+ 	//maintains iChunkPools in blocks size order
+    TInt found = KErrNotFound;
+    TInt index = 0;
+    RChunkPool tempPool ( *this, aBlockSize );
+    found  = iChunkPools.FindInOrder ( tempPool, index, TLinearOrder<RChunkPool>(RChunkPool::Compare) );
+    
+    // create a new pool chain
+    if ( found == KErrNotFound )
+   		{
+        RChunkPool pool ( aBlockSize, aThresholdValue, aMinGrowth, *this );
+        //maintains iChunkPools in blocks size order
+        User::LeaveIfError ( iChunkPools.Insert ( pool, index ) );
+        
+        RChunkPool* chunkPool = &iChunkPools[index];
+		chunkPool->OpenL ();
+		
+        // Lock the freelist, inorder to add new blocks to the free list
+        chunkPool->Wait ();
+        TInt err = AllocChunk ( *chunkPool, aNumBlocks );
+        chunkPool->Signal ();
+		
+        User::LeaveIfError ( err );
+    	}
+	else
+    	{
+        // update existing values
+        iChunkPools[found].SetGrowth ( aMinGrowth );
+        iChunkPools[found].SetThreshold ( aThresholdValue );
+        }
+	}
+	
+TInt CChunkManager::AllocChunk ( RChunkPool& aChunkPool, TInt aNumBlocks )
+	{
+    // verify if maximum size is exceeded
+    if ( ( iUsedSpace + aNumBlocks * aChunkPool.BlockSize () ) > iHeapSize )
+        {
+        return KErrNoMemory;
+        }
+	
+    CMemChunk* newChunk = CMemChunk::New ( *iChunkAllocator, aChunkPool, aNumBlocks, aChunkPool.BlockSize () );
+	if ( newChunk != NULL )
+        {
+        iLock.Wait ();
+        aChunkPool.Append ( newChunk );
+        iLock.Signal ();
+		
+		// RChunkPool lock is already acquired
+        aChunkPool.AppendToFreeList ( newChunk );
+		
+        iUsedSpace += sizeof ( CMemChunk ) + ( aNumBlocks * aChunkPool.BlockSize () ) ;
+        return KErrNone;
+        }
+	
+	return KErrNoMemory;
+	}
+
+RMemCell* CChunkManager::Alloc ( TInt aSize )
+	{
+	TInt poolsCount = iChunkPools.Count ();
+	__ASSERT_DEBUG ( poolsCount != 0, User::Invariant () ); //TODO
+	
+	RMemCellList list;
+	// Search and get the avaliable memCell list
+	SearchAndAlloc ( list, aSize );
+	
+	return list.First ();
+	}
+	
+void CChunkManager::SearchAndAlloc ( RMemCellList& aList, TInt aSize )
+	{
+	if ( aSize == 0 ) 
+		{
+		__ASSERT_ALWAYS ( 0, User::Invariant () ); //TODO
+		}
+	
+	RChunkPool* poolChosen = NULL;
+	RChunkPool* startPool = &iChunkPools[0];
+	RChunkPool* pool = &iChunkPools[iChunkPools.Count () - 1];
+	
+	TInt minUnusedBytes = 0, minBlocks = 0;
+	
+	do
+		{
+		TInt blockSize = pool->BlockSize ();
+		
+		TInt unusedBytes = aSize % blockSize;
+		TInt neededBlocks = aSize / blockSize;
+		
+		if ( unusedBytes != 0 )
+			{
+			unusedBytes = blockSize - unusedBytes;
+			neededBlocks += 1;
+			}
+		
+		if ( poolChosen == NULL ) //for the first time this will be true
+			{
+			poolChosen = pool;
+			minUnusedBytes = unusedBytes;
+			minBlocks = neededBlocks;
+			}
+		else
+			{
+			if ( unusedBytes == minUnusedBytes )
+				{
+				if ( neededBlocks < minBlocks )
+					{
+					poolChosen = pool;
+					minUnusedBytes = unusedBytes;
+					minBlocks = neededBlocks;
+					}
+				}
+			else if ( unusedBytes < minUnusedBytes )
+				{
+				poolChosen = pool;
+				minUnusedBytes = unusedBytes;
+				minBlocks = neededBlocks;
+				}
+			}
+		} while ( pool-- != startPool );
+	
+	if ( poolChosen == NULL )
+		{
+		__ASSERT_DEBUG ( 0, User::Invariant () );
+		}
+	
+	TUint allocatedBytes = 0;
+	TBool growthFailed = EFalse;
+	if ( aSize <= poolChosen->FreeSpace () )
+		{
+		allocatedBytes = poolChosen->Alloc ( aList, aSize );
+		}
+	else
+		{
+		allocatedBytes = poolChosen->GrowAndAlloc ( aList, aSize, minBlocks );
+				
+		if ( allocatedBytes == 0 )
+			{
+			// need to search for the next best match and however, this time
+			// requesting pool to grow may not be worth :), as only error condition is KErrNoMemory
+			growthFailed = ETrue;
+			}
+		}
+	
+	if ( allocatedBytes >= aSize )
+		{
+		return;
+		}
+	else if ( growthFailed )
+		{
+		// just get the pool that can satisfy the request, no more best,good,worst match
+		for ( TInt i = 0; i < iChunkPools.Count (); i++ )
+			{			
+			if ( aSize <= iChunkPools[i].FreeSpace () )
+				{
+				allocatedBytes = iChunkPools[i].Alloc ( aList, aSize );
+				break;
+				}
+			}
+		}
+	}
+	
+TInt CChunkManager::BytesAvailable ()
+	{
+	TInt freeSpace = iHeapSize - iUsedSpace;
+	
+	freeSpace -= sizeof ( CMemChunk );
+	
+	return freeSpace <= 0 ? KErrNoMemory : freeSpace;
+	}
+	
+// return a chain of MBufs to the pool
+void CChunkManager::Free ( RMemCell* aPtr )
+	{
+	RChunkPool* pool ( aPtr->Pool () );
+	pool->Wait ();
+	
+	while ( aPtr != NULL )
+		{
+		RMemCell* nextPtr = aPtr->Next ();
+		
+		// reset length to the size of the block and break the link
+		aPtr->SetLength ( aPtr->Size () );
+		aPtr->Link ( NULL );
+		
+		// adds single block back to its free list and adjusts its free space
+		pool->FreeToPool ( aPtr );
+		
+		aPtr = nextPtr;
+		}
+	pool->Signal ();
+	}
+
+