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".
8 // Initial Contributors:
9 // Nokia Corporation - initial contribution.
22 GLREF_C void Panic(TFbsPanic aPanic);
24 LOCAL_C TInt GranularRoundUp(TInt aCount)
26 return (aCount + 7) & ~7;
29 CChunkPile::CChunkPile(const RChunk& aChunk)
33 EXPORT_C CChunkPile::~CChunkPile()
37 iFreeSmallCellLinks.Close();
39 iFreeLargeCellLinks.Close();
43 EXPORT_C CChunkPile* CChunkPile::NewL(const RChunk& aChunk)
45 CChunkPile* thisptr=new(ELeave) CChunkPile(aChunk);
46 CleanupStack::PushL(thisptr);
47 thisptr->ConstructL();
52 void CChunkPile::ConstructL()
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());
71 EXPORT_C TUint8* CChunkPile::ChunkBase() const
73 return(iChunk.Base());
76 EXPORT_C TInt CChunkPile::VirtualSize()
79 HAL::Get(HALData::EMemoryRAM, maxmem);
81 return Min(Max(KFbServLargeChunkMinVirtualSize, maxmem << KFbServLargeChunkSizeShifter), KFbServLargeChunkMaxVirtualSize);
84 EXPORT_C TUint8* CChunkPile::Alloc(TInt aSize)
86 __ASSERT_ALWAYS(aSize > 0 && aSize < KMaxTInt / 2, Panic(EFbsPanicChunkError));
89 if (aSize < KMaxLargeBitmapAlloc)
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)
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);
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)
108 iChunk.Decommit(cell - iChunk.Base(), aSize);
116 EXPORT_C void CChunkPile::Free(TAny* aCell)
119 if (static_cast<TUint8*>(aCell) < iChunk.Base() + iLargeSectionBottom)
120 DoFree(static_cast<TUint8*>(aCell), iSmallCells, iFreeSmallCellLinks, EFalse);
122 DoFree(static_cast<TUint8*>(aCell), iLargeCells, iFreeLargeCellLinks, ETrue);
126 TInt CChunkPile::DoAlloc(TUint8*& aCell, TInt aSize, RPointerArray<TUint8>& aCells, RArray<TInt>& aFreeCellLinks, TBool aLarge)
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.
134 __ASSERT_DEBUG(aCells.Count() >= 2, Panic(EFbsPanicChunkError));
137 TInt freeCount = aFreeCellLinks.Count();
138 for (TInt freeIndex = 0; freeIndex < freeCount; ++freeIndex)
140 TInt freeCellLink = aFreeCellLinks[freeIndex];
141 cellIndex += freeCellLink;
142 TInt size = aCells[cellIndex + 1] - aCells[cellIndex];
147 TInt err = iChunk.Commit(aCells[cellIndex] - iChunk.Base(), aSize);
151 aCell = aCells[cellIndex];
152 if (size == aSize) // exact fit?
154 aFreeCellLinks.Remove(freeIndex);
155 if (freeIndex < aFreeCellLinks.Count())
156 aFreeCellLinks[freeIndex] += freeCellLink;
160 // make sure there are always enough empty slots in aFreeCellLinks
161 TInt err = aFreeCellLinks.Reserve(GranularRoundUp((aCells.Count() + 1) >> 1));
164 err = aCells.Insert(aCell + aSize, cellIndex + 1);
167 ++aFreeCellLinks[freeIndex];
174 TInt err = GrowSmallSection(aSize, cellIndex);
177 TInt topIndex = iSmallCells.Count() - 1;
178 aCell = iSmallCells[topIndex - 1];
179 if (iSmallCells[topIndex] - aCell == aSize) // exact fit?
181 iFreeSmallCellLinks.Remove(iFreeSmallCellLinks.Count() - 1);
185 // make sure there are always enough empty slots in aFreeCellLinks
186 err = iFreeSmallCellLinks.Reserve(GranularRoundUp((iSmallCells.Count() + 1) >> 1));
189 err = iSmallCells.Insert(aCell + aSize, topIndex);
192 ++iFreeSmallCellLinks[iFreeSmallCellLinks.Count() - 1];
197 void CChunkPile::DoFree(TUint8* aCell, RPointerArray<TUint8>& aCells, RArray<TInt>& aFreeCellLinks, TBool aLarge)
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)
203 TInt prevIndex = KMaxTInt;
205 TInt freeCount = aFreeCellLinks.Count();
207 for (freeIndex = 0; freeIndex < freeCount; ++freeIndex)
209 nextIndex += aFreeCellLinks[freeIndex];
210 if (nextIndex >= cellIndex)
212 __ASSERT_DEBUG(nextIndex > cellIndex, Panic(EFbsPanicChunkError));
213 if (nextIndex == cellIndex)
217 prevIndex = nextIndex;
220 iChunk.Decommit(aCell - iChunk.Base(), aCells[cellIndex + 1] - aCell);
221 if (prevIndex == cellIndex - 1 && nextIndex == cellIndex + 1)
223 aFreeCellLinks.Remove(freeIndex);
224 aCells.Remove(cellIndex + 1);
225 aCells.Remove(cellIndex--);
227 else if (prevIndex == cellIndex - 1)
229 if (freeIndex < freeCount)
230 --aFreeCellLinks[freeIndex];
231 aCells.Remove(cellIndex--);
233 else if (nextIndex == cellIndex + 1)
235 --aFreeCellLinks[freeIndex];
236 aCells.Remove(cellIndex + 1);
240 if (freeIndex < freeCount)
241 aFreeCellLinks[freeIndex] = nextIndex - cellIndex;
242 aFreeCellLinks.Insert(freeIndex > 0 ? cellIndex - prevIndex : cellIndex, freeIndex); // can't fail
246 TInt topIndex = aCells.Count() - 1;
247 if (cellIndex == topIndex - 1)
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);
255 TInt CChunkPile::GrowSmallSection(TInt aSize, TInt aLastFreeCell)
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.
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)
269 TInt growBy = (iSmallCells[aLastFreeCell] + aSize) - iSmallCells[topIndex];
270 __ASSERT_DEBUG(growBy > 0, Panic(EFbsPanicChunkError));
271 if (growBy > maxGrowBy)
273 growBy = Min((growBy + roundMask) & ~roundMask, maxGrowBy);
274 TInt err = iChunk.Commit(iSmallCells[topIndex] - iChunk.Base(), growBy);
277 iSmallCells[topIndex] += growBy;
281 // last cell is not free
282 if (aSize > maxGrowBy)
284 TInt growBy = Min((aSize + roundMask) & ~roundMask, maxGrowBy);
285 TInt err = iSmallCells.Reserve(GranularRoundUp(iSmallCells.Count() + 1));
288 err = iFreeSmallCellLinks.Reserve(GranularRoundUp((iSmallCells.Count() + 1) >> 1));
291 err = iChunk.Commit(iSmallCells[topIndex] - iChunk.Base(), growBy);
294 iSmallCells.Append(iSmallCells[topIndex] + growBy); // can't fail
295 iFreeSmallCellLinks.Append(topIndex - aLastFreeCell); // can't fail
300 void CChunkPile::ShrinkSmallSection(TInt aShrinkBy)
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);
308 iChunk.Decommit((iSmallCells[topIndex] -= aShrinkBy) - iChunk.Base(), aShrinkBy);
309 if (iSmallCells[topIndex] == iSmallCells[topIndex - 1])
311 iSmallCells.Remove(topIndex);
312 iFreeSmallCellLinks.Remove(iFreeSmallCellLinks.Count() - 1);