os/graphics/fbs/fontandbitmapserver/sfbs/PILE.CPP
author sl@SLION-WIN7.fritz.box
Fri, 15 Jun 2012 03:10:57 +0200
changeset 0 bde4ae8d615e
permissions -rw-r--r--
First public contribution.
     1 // Copyright (c) 1995-2009 Nokia Corporation and/or its subsidiary(-ies).
     2 // All rights reserved.
     3 // This component and the accompanying materials are made available
     4 // under the terms of "Eclipse Public License v1.0"
     5 // which accompanies this distribution, and is available
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
     7 //
     8 // Initial Contributors:
     9 // Nokia Corporation - initial contribution.
    10 //
    11 // Contributors:
    12 //
    13 // Description:
    14 //
    15 
    16 #include <hal.h>
    17 #include <fbs.h>
    18 #include <bitmap.h>
    19 #include "UTILS.H"
    20 
    21 
    22 GLREF_C void Panic(TFbsPanic aPanic);
    23 
    24 LOCAL_C TInt GranularRoundUp(TInt aCount)
    25 	{
    26 	return (aCount + 7) & ~7;
    27 	}
    28 
    29 CChunkPile::CChunkPile(const RChunk& aChunk)
    30 :	iChunk(aChunk)
    31 	{}
    32 
    33 EXPORT_C CChunkPile::~CChunkPile()
    34 	{
    35 	iChunk.Close();
    36 	iSmallCells.Close();
    37 	iFreeSmallCellLinks.Close();
    38 	iLargeCells.Close();
    39 	iFreeLargeCellLinks.Close();
    40 	iLock.Close();
    41 	}
    42 
    43 EXPORT_C CChunkPile* CChunkPile::NewL(const RChunk& aChunk)
    44 	{
    45 	CChunkPile* thisptr=new(ELeave) CChunkPile(aChunk);
    46 	CleanupStack::PushL(thisptr);
    47 	thisptr->ConstructL();
    48 	CleanupStack::Pop();
    49 	return(thisptr);
    50 	}
    51 
    52 void CChunkPile::ConstructL()
    53 	{
    54 	TInt ret = iChunk.Duplicate(RThread());
    55 	__ASSERT_ALWAYS(ret == KErrNone, Panic(EFbsPanicChunkError));
    56 	HAL::Get(HAL::EMemoryPageSize, iPageSize);
    57 	__ASSERT_ALWAYS(iPageSize > 4, Panic(EFbsPanicChunkError));
    58 	iPageMask = iPageSize - 1; // assume page size is a power of 2
    59 	__ASSERT_ALWAYS(KFbServLargeChunkMinPhysicalSize > 0, Panic(EFbsPanicChunkError));
    60 	User::LeaveIfError(iChunk.Commit(iPageSize, KFbServLargeChunkMinPhysicalSize));
    61 	iSmallCells.AppendL(iChunk.Base() + iPageSize); // prevent a data offset of 0 from chunk base
    62 	iSmallCells.AppendL(iChunk.Base() + iPageSize + KFbServLargeChunkMinPhysicalSize);
    63 	iFreeSmallCellLinks.AppendL(0);
    64 	iLargeSectionBottom = iChunk.MaxSize() >> KFbServLargeChunkSizeShifter;
    65 	iLargeCells.AppendL(iChunk.Base() + iLargeSectionBottom);
    66 	iLargeCells.AppendL(iChunk.Base() + iChunk.MaxSize());
    67 	iFreeLargeCellLinks.AppendL(0);
    68 	User::LeaveIfError(iLock.CreateLocal());
    69 	}
    70 
    71 EXPORT_C TUint8* CChunkPile::ChunkBase() const
    72 	{
    73 	return(iChunk.Base());
    74 	}
    75 
    76 EXPORT_C TInt CChunkPile::VirtualSize()
    77 	{
    78 	TInt maxmem = 0;
    79 	HAL::Get(HALData::EMemoryRAM, maxmem);
    80 	ASSERT(maxmem > 0);
    81 	return Min(Max(KFbServLargeChunkMinVirtualSize, maxmem << KFbServLargeChunkSizeShifter), KFbServLargeChunkMaxVirtualSize);
    82 	}
    83 
    84 EXPORT_C TUint8* CChunkPile::Alloc(TInt aSize)
    85 	{
    86 	__ASSERT_ALWAYS(aSize > 0 && aSize < KMaxTInt / 2, Panic(EFbsPanicChunkError));
    87 	TUint8* cell;
    88 	iLock.Wait();
    89 	if (aSize < KMaxLargeBitmapAlloc)
    90 		{
    91 		aSize = Align4(aSize); // round size up to nearest four bytes
    92 		TInt err = DoAlloc(cell, aSize, iSmallCells, iFreeSmallCellLinks, EFalse);
    93 		if (err != KErrNone && cell != NULL)
    94 			{
    95 			// OOM after the small section grew? if so decommit as much physical memory as possible
    96 			TInt topIndex = iSmallCells.Count() - 1;
    97 			if (cell == iSmallCells[topIndex - 1])
    98 				ShrinkSmallSection((iSmallCells[topIndex] - cell) & ~iPageMask);
    99 			cell = NULL;
   100 			}
   101 		}
   102 	else
   103 		{
   104 		aSize = (aSize + iPageMask) & ~iPageMask; // round size up to nearest page boundary
   105 		TInt err = DoAlloc(cell, aSize, iLargeCells, iFreeLargeCellLinks, ETrue);
   106 		if (err != KErrNone && cell != NULL)
   107 			{
   108 			iChunk.Decommit(cell - iChunk.Base(), aSize);
   109 			cell = NULL;
   110 			}
   111 		}
   112 	iLock.Signal();
   113 	return cell;
   114 	}
   115 
   116 EXPORT_C void CChunkPile::Free(TAny* aCell)
   117 	{
   118 	iLock.Wait();
   119 	if (static_cast<TUint8*>(aCell) < iChunk.Base() + iLargeSectionBottom)
   120 		DoFree(static_cast<TUint8*>(aCell), iSmallCells, iFreeSmallCellLinks, EFalse);
   121 	else
   122 		DoFree(static_cast<TUint8*>(aCell), iLargeCells, iFreeLargeCellLinks, ETrue);
   123 	iLock.Signal();
   124 	}
   125 
   126 TInt CChunkPile::DoAlloc(TUint8*& aCell, TInt aSize, RPointerArray<TUint8>& aCells, RArray<TInt>& aFreeCellLinks, TBool aLarge)
   127 /*
   128 This function tries to allocate a cell of aSize bytes and returns its address in aCell if successful.
   129 In case of failure aCell is either NULL, if there is not enough memory for a cell of that size,
   130 or it contains the address of a free cell of at least aSize bytes,
   131 if there is not enough memory for the internal structures used by CChunkPile.
   132 */
   133 	{
   134 	__ASSERT_DEBUG(aCells.Count() >= 2, Panic(EFbsPanicChunkError));
   135 	aCell = NULL;
   136 	TInt cellIndex = 0;
   137 	TInt freeCount = aFreeCellLinks.Count();
   138 	for (TInt freeIndex = 0; freeIndex < freeCount; ++freeIndex)
   139 		{
   140 		TInt freeCellLink = aFreeCellLinks[freeIndex];
   141 		cellIndex += freeCellLink;
   142 		TInt size = aCells[cellIndex + 1] - aCells[cellIndex];
   143 		if (size >= aSize)
   144 			{
   145 			if (aLarge)
   146 				{
   147 				TInt err = iChunk.Commit(aCells[cellIndex] - iChunk.Base(), aSize);
   148 				if (err != KErrNone)
   149 					return err;
   150 				}
   151 			aCell = aCells[cellIndex];
   152 			if (size == aSize) // exact fit?
   153 				{
   154 				aFreeCellLinks.Remove(freeIndex);
   155 				if (freeIndex < aFreeCellLinks.Count())
   156 					aFreeCellLinks[freeIndex] += freeCellLink;
   157 				}
   158 			else
   159 				{
   160 				// make sure there are always enough empty slots in aFreeCellLinks
   161 				TInt err = aFreeCellLinks.Reserve(GranularRoundUp((aCells.Count() + 1) >> 1));
   162 				if (err != KErrNone)
   163 					return err;
   164 				err = aCells.Insert(aCell + aSize, cellIndex + 1);
   165 				if (err != KErrNone)
   166 					return err;
   167 				++aFreeCellLinks[freeIndex];
   168 				}
   169 			return KErrNone;
   170 			}
   171 		}
   172 	if (aLarge)
   173 		return KErrNoMemory;
   174 	TInt err = GrowSmallSection(aSize, cellIndex);
   175 	if (err != KErrNone)
   176 		return err;
   177 	TInt topIndex = iSmallCells.Count() - 1;
   178 	aCell = iSmallCells[topIndex - 1];
   179 	if (iSmallCells[topIndex] - aCell == aSize) // exact fit?
   180 		{
   181 		iFreeSmallCellLinks.Remove(iFreeSmallCellLinks.Count() - 1);
   182 		}
   183 	else
   184 		{
   185 		// make sure there are always enough empty slots in aFreeCellLinks
   186 		err = iFreeSmallCellLinks.Reserve(GranularRoundUp((iSmallCells.Count() + 1) >> 1));
   187 		if (err != KErrNone)
   188 			return err;
   189 		err = iSmallCells.Insert(aCell + aSize, topIndex);
   190 		if (err != KErrNone)
   191 			return err;
   192 		++iFreeSmallCellLinks[iFreeSmallCellLinks.Count() - 1];
   193 		}
   194 	return KErrNone;
   195 	}
   196 
   197 void CChunkPile::DoFree(TUint8* aCell, RPointerArray<TUint8>& aCells, RArray<TInt>& aFreeCellLinks, TBool aLarge)
   198 	{
   199 	TInt cellIndex = aCells.FindInAddressOrder(aCell);
   200 	__ASSERT_DEBUG(cellIndex >= 0 && cellIndex < aCells.Count() - 1, Panic(EFbsPanicChunkError));
   201 	if (cellIndex < 0 || cellIndex == aCells.Count() - 1)
   202 		return;
   203 	TInt prevIndex = KMaxTInt;
   204 	TInt nextIndex = 0;
   205 	TInt freeCount = aFreeCellLinks.Count();
   206 	TInt freeIndex;
   207 	for (freeIndex = 0; freeIndex < freeCount; ++freeIndex)
   208 		{
   209 		nextIndex += aFreeCellLinks[freeIndex];
   210 		if (nextIndex >= cellIndex)
   211 			{
   212 			__ASSERT_DEBUG(nextIndex > cellIndex, Panic(EFbsPanicChunkError));
   213 			if (nextIndex == cellIndex)
   214 				return;
   215 			break;
   216 			}
   217 		prevIndex = nextIndex;
   218 		}
   219 	if (aLarge)
   220 		iChunk.Decommit(aCell - iChunk.Base(), aCells[cellIndex + 1] - aCell);
   221 	if (prevIndex == cellIndex - 1 && nextIndex == cellIndex + 1)
   222 		{
   223 		aFreeCellLinks.Remove(freeIndex);
   224 		aCells.Remove(cellIndex + 1);
   225 		aCells.Remove(cellIndex--);
   226 		}
   227 	else if (prevIndex == cellIndex - 1)
   228 		{
   229 		if (freeIndex < freeCount)
   230 			--aFreeCellLinks[freeIndex];
   231 		aCells.Remove(cellIndex--);
   232 		}
   233 	else if (nextIndex == cellIndex + 1)
   234 		{
   235 		--aFreeCellLinks[freeIndex];
   236 		aCells.Remove(cellIndex + 1);
   237 		}
   238 	else
   239 		{
   240 		if (freeIndex < freeCount)
   241 			aFreeCellLinks[freeIndex] = nextIndex - cellIndex;
   242 		aFreeCellLinks.Insert(freeIndex > 0 ? cellIndex - prevIndex : cellIndex, freeIndex); // can't fail
   243 		}
   244 	if (aLarge)
   245 		return;
   246 	TInt topIndex = aCells.Count() - 1;
   247 	if (cellIndex == topIndex - 1)
   248 		{
   249 		// last cell is free and has just grown, shrink with granularity iPageSize << KFbServLargeChunkGrowByShifter
   250 		TInt roundMask = (iPageSize << KFbServLargeChunkGrowByShifter) - 1;
   251 		ShrinkSmallSection((aCells[topIndex] - aCells[cellIndex]) & ~roundMask);
   252 		}
   253 	}
   254 
   255 TInt CChunkPile::GrowSmallSection(TInt aSize, TInt aLastFreeCell)
   256 /*
   257 This function tries to grow the section for small cells with granularity iPageSize << KFbServLargeChunkGrowByShifter.
   258 It appends or expands the last free cell so that it has at least aSize bytes if successful.
   259 aLastFreeCell must be the index of the last free cell in the small section or 0 if there is none.
   260 */
   261 	{
   262 	TInt roundMask = (iPageSize << KFbServLargeChunkGrowByShifter) - 1;
   263 	TInt topIndex = iSmallCells.Count() - 1;
   264 	TInt maxGrowBy = (iChunk.Base() + iLargeSectionBottom) - iSmallCells[topIndex];
   265 	__ASSERT_DEBUG(maxGrowBy >= 0, Panic(EFbsPanicChunkError));
   266 	if (iFreeSmallCellLinks.Count() > 0 && aLastFreeCell == topIndex - 1)
   267 		{
   268 		// last cell is free
   269 		TInt growBy = (iSmallCells[aLastFreeCell] + aSize) - iSmallCells[topIndex];
   270 		__ASSERT_DEBUG(growBy > 0, Panic(EFbsPanicChunkError));
   271 		if (growBy > maxGrowBy)
   272 			return KErrNoMemory;
   273 		growBy = Min((growBy + roundMask) & ~roundMask, maxGrowBy);
   274 		TInt err = iChunk.Commit(iSmallCells[topIndex] - iChunk.Base(), growBy);
   275 		if (err != KErrNone)
   276 			return err;
   277 		iSmallCells[topIndex] += growBy;
   278 		}
   279 	else
   280 		{
   281 		// last cell is not free
   282 		if (aSize > maxGrowBy)
   283 			return KErrNoMemory;
   284 		TInt growBy = Min((aSize + roundMask) & ~roundMask, maxGrowBy);
   285 		TInt err = iSmallCells.Reserve(GranularRoundUp(iSmallCells.Count() + 1));
   286 		if (err != KErrNone)
   287 			return err;
   288 		err = iFreeSmallCellLinks.Reserve(GranularRoundUp((iSmallCells.Count() + 1) >> 1));
   289 		if (err != KErrNone)
   290 			return err;
   291 		err = iChunk.Commit(iSmallCells[topIndex] - iChunk.Base(), growBy);
   292 		if (err != KErrNone)
   293 			return err;
   294 		iSmallCells.Append(iSmallCells[topIndex] + growBy); // can't fail
   295 		iFreeSmallCellLinks.Append(topIndex - aLastFreeCell); // can't fail
   296 		}
   297 	return KErrNone;
   298 	}
   299 
   300 void CChunkPile::ShrinkSmallSection(TInt aShrinkBy)
   301 	{
   302 	TInt topIndex = iSmallCells.Count() - 1;
   303 	TInt maxShrinkBy = (iSmallCells[topIndex] - iChunk.Base()) - (KFbServLargeChunkMinPhysicalSize + iPageSize);
   304 	__ASSERT_DEBUG(maxShrinkBy >= 0, Panic(EFbsPanicChunkError));
   305 	aShrinkBy = Min(aShrinkBy, maxShrinkBy);
   306 	if (aShrinkBy > 0)
   307 		{
   308 		iChunk.Decommit((iSmallCells[topIndex] -= aShrinkBy) - iChunk.Base(), aShrinkBy);
   309 		if (iSmallCells[topIndex] == iSmallCells[topIndex - 1])
   310 			{
   311 			iSmallCells.Remove(topIndex);
   312 			iFreeSmallCellLinks.Remove(iFreeSmallCellLinks.Count() - 1);
   313 			}
   314 		}
   315 	}