1334 lines
41 KiB
C++
1334 lines
41 KiB
C++
|
//+-------------------------------------------------------------------------
|
|||
|
//
|
|||
|
// Microsoft Windows
|
|||
|
// Copyright (C) Microsoft Corporation, 1994 - 2000
|
|||
|
//
|
|||
|
// File: tblalloc.cxx
|
|||
|
//
|
|||
|
// Contents: Memory allocation wrappers for large tables
|
|||
|
//
|
|||
|
// Classes: CWindowDataAllocator - data allocator for window var data
|
|||
|
// CFixedVarAllocator - data allocator for fixed and var data
|
|||
|
//
|
|||
|
// Functions: TblPageAlloc - Allocate page-alligned memory
|
|||
|
// TblPageRealloc - reallocate page-alligned memory to new size
|
|||
|
// TblPageDealloc - deallocate page-alligned memory
|
|||
|
//
|
|||
|
// History: 14 Mar 1994 AlanW Created
|
|||
|
//
|
|||
|
//--------------------------------------------------------------------------
|
|||
|
|
|||
|
|
|||
|
#include "pch.cxx"
|
|||
|
#pragma hdrstop
|
|||
|
|
|||
|
#include <tblalloc.hxx>
|
|||
|
|
|||
|
#include "tabledbg.hxx"
|
|||
|
|
|||
|
|
|||
|
//+-------------------------------------------------------------------------
|
|||
|
//
|
|||
|
// Function: TblPageAlloc, public
|
|||
|
//
|
|||
|
// Synopsis: Allocate page-alligned memory
|
|||
|
//
|
|||
|
// Effects: The memory allocation counter is incremented.
|
|||
|
// rcbSizeNeeded is adjusted on return to indicate the
|
|||
|
// size of the allocated memory.
|
|||
|
//
|
|||
|
// Arguments: [rcbSizeNeeded] - required size of memory area
|
|||
|
// [rcbPageAllocTracker] - memory allocation counter
|
|||
|
// [ulSig] - signature of the memory (optional)
|
|||
|
//
|
|||
|
// Returns: PVOID - pointer to the allocated memory. Throws
|
|||
|
// E_OUTOFMEMORY on allocation failure.
|
|||
|
//
|
|||
|
// Notes: rcbSizeNeeded is set to the minimum required size
|
|||
|
// needed. TblPageGrowSize can be called to grow an
|
|||
|
// existing memory region.
|
|||
|
//
|
|||
|
// If ulSig is non-zero, the beginning of the allocated
|
|||
|
// block is initialized with a signature block which
|
|||
|
// identifies the caller, and which gives the size of the
|
|||
|
// memory block. In this case, the returned pointer is
|
|||
|
// advanced beyond the signature block.
|
|||
|
//
|
|||
|
//--------------------------------------------------------------------------
|
|||
|
|
|||
|
PVOID TblPageAlloc(
|
|||
|
ULONG& rcbSizeNeeded,
|
|||
|
ULONG& rcbPageAllocTracker,
|
|||
|
ULONG const ulSig
|
|||
|
) {
|
|||
|
ULONG cbSize = rcbSizeNeeded +
|
|||
|
(ulSig != 0) * sizeof (TBL_PAGE_SIGNATURE);
|
|||
|
BYTE* pbAlloc = 0;
|
|||
|
|
|||
|
Win4Assert(cbSize && (cbSize & TBL_PAGE_MASK) == 0);
|
|||
|
|
|||
|
if (cbSize == 0)
|
|||
|
cbSize++;
|
|||
|
if ((cbSize & TBL_PAGE_MASK) != 0) {
|
|||
|
cbSize = (cbSize + TBL_PAGE_MASK) & ~TBL_PAGE_MASK;
|
|||
|
}
|
|||
|
|
|||
|
if (cbSize < TBL_PAGE_MAX_SEGMENT_SIZE)
|
|||
|
{
|
|||
|
pbAlloc = (BYTE *)VirtualAlloc( 0,
|
|||
|
TBL_PAGE_MAX_SEGMENT_SIZE,
|
|||
|
MEM_RESERVE,
|
|||
|
PAGE_READWRITE);
|
|||
|
if ( 0 == pbAlloc )
|
|||
|
THROW(CException(E_OUTOFMEMORY));
|
|||
|
|
|||
|
pbAlloc = (BYTE *)VirtualAlloc( pbAlloc,
|
|||
|
cbSize,
|
|||
|
MEM_COMMIT,
|
|||
|
PAGE_READWRITE);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
pbAlloc = (BYTE *)VirtualAlloc( 0,
|
|||
|
cbSize,
|
|||
|
MEM_COMMIT,
|
|||
|
PAGE_READWRITE);
|
|||
|
}
|
|||
|
|
|||
|
if (pbAlloc == 0)
|
|||
|
THROW(CException(E_OUTOFMEMORY));
|
|||
|
|
|||
|
Win4Assert( (((ULONG_PTR)pbAlloc) & TBL_PAGE_MASK) == 0 );
|
|||
|
rcbPageAllocTracker += cbSize;
|
|||
|
|
|||
|
if (ulSig)
|
|||
|
{
|
|||
|
TBL_PAGE_SIGNATURE * pSigStruct = (TBL_PAGE_SIGNATURE *) pbAlloc;
|
|||
|
|
|||
|
pSigStruct->ulSig = ulSig;
|
|||
|
pSigStruct->cbSize = cbSize;
|
|||
|
pSigStruct->pbAddr = pbAlloc;
|
|||
|
#if CIDBG
|
|||
|
PVOID CallersCaller;
|
|||
|
RtlGetCallersAddress(&pSigStruct->pCaller, &CallersCaller);
|
|||
|
#else
|
|||
|
pSigStruct->pCaller = 0;
|
|||
|
#endif // CIDBG
|
|||
|
|
|||
|
pbAlloc = (BYTE *) ++pSigStruct;
|
|||
|
cbSize -= sizeof (TBL_PAGE_SIGNATURE);
|
|||
|
}
|
|||
|
rcbSizeNeeded = cbSize;
|
|||
|
|
|||
|
return pbAlloc;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//+-------------------------------------------------------------------------
|
|||
|
//
|
|||
|
// Function: TblPageRealloc, public
|
|||
|
//
|
|||
|
// Synopsis: Re-allocate page-alligned memory
|
|||
|
//
|
|||
|
// Effects: The memory allocation counter is incremented.
|
|||
|
// Memory is committed or decommitted to the required size.
|
|||
|
//
|
|||
|
// Arguments: [rcbSizeNeeded] - required size of memory area
|
|||
|
// [rcbPageAllocTracker] - memory allocation counter
|
|||
|
// [cbNewSize] - required size of memory area
|
|||
|
// [cbOldSize] - current size of memory area
|
|||
|
//
|
|||
|
// Returns: PVOID - pointer to the allocated memory. Throws
|
|||
|
// E_OUTOFMEMORY on allocation failure.
|
|||
|
//
|
|||
|
// Notes: cbOldSize need not be supplied if the memory area
|
|||
|
// begins with a signature block. In this case, the
|
|||
|
// pbMem passed in should point to beyond the signature
|
|||
|
// block, and the cbNewSize should not include the size
|
|||
|
// of the signature block.
|
|||
|
//
|
|||
|
// Not available in Kernel. Although VirtualAlloc
|
|||
|
// functionality is not available in the kernel, we
|
|||
|
// could do a poor man's realloc by optimistically
|
|||
|
// assuming that a page allocation would be contiguous
|
|||
|
// with the existing page, then setting some mark in
|
|||
|
// the page header that multiple frees need to be done
|
|||
|
// on the segment.
|
|||
|
//
|
|||
|
//--------------------------------------------------------------------------
|
|||
|
|
|||
|
PVOID TblPageRealloc(
|
|||
|
PVOID pbMem,
|
|||
|
ULONG& rcbPageAllocTracker,
|
|||
|
ULONG cbNewSize,
|
|||
|
ULONG cbOldSize
|
|||
|
) {
|
|||
|
BYTE* pbAlloc = (BYTE *)pbMem;
|
|||
|
TBL_PAGE_SIGNATURE * pSigStruct = 0;
|
|||
|
|
|||
|
ULONG cbPageOffset = (ULONG)(((ULONG_PTR)pbMem) & TBL_PAGE_MASK);
|
|||
|
|
|||
|
Win4Assert(cbPageOffset == 0 ||
|
|||
|
cbPageOffset == sizeof (TBL_PAGE_SIGNATURE));
|
|||
|
|
|||
|
if (cbPageOffset == sizeof (TBL_PAGE_SIGNATURE))
|
|||
|
{
|
|||
|
pSigStruct = (TBL_PAGE_SIGNATURE *)
|
|||
|
((BYTE*)pbMem - sizeof (TBL_PAGE_SIGNATURE));
|
|||
|
|
|||
|
Win4Assert (pSigStruct->ulSig != 0 &&
|
|||
|
pSigStruct->cbSize >= TBL_PAGE_ALLOC_MIN &&
|
|||
|
pSigStruct->pbAddr == (BYTE *)pSigStruct &&
|
|||
|
cbOldSize == 0);
|
|||
|
|
|||
|
cbOldSize = pSigStruct->cbSize;
|
|||
|
|
|||
|
Win4Assert(((cbNewSize + sizeof (TBL_PAGE_SIGNATURE)) & TBL_PAGE_MASK) ==
|
|||
|
0);
|
|||
|
cbNewSize += sizeof (TBL_PAGE_SIGNATURE);
|
|||
|
pbAlloc -= sizeof (TBL_PAGE_SIGNATURE);
|
|||
|
}
|
|||
|
|
|||
|
if (cbNewSize > cbOldSize)
|
|||
|
{
|
|||
|
if (0 == VirtualAlloc(pbAlloc + cbOldSize,
|
|||
|
cbNewSize - cbOldSize,
|
|||
|
MEM_COMMIT,
|
|||
|
PAGE_READWRITE))
|
|||
|
THROW(CException(E_OUTOFMEMORY));
|
|||
|
|
|||
|
rcbPageAllocTracker += (cbNewSize - cbOldSize);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
VirtualFree(pbAlloc + cbNewSize, cbOldSize-cbNewSize, MEM_DECOMMIT);
|
|||
|
rcbPageAllocTracker -= (cbOldSize - cbNewSize);
|
|||
|
}
|
|||
|
|
|||
|
if (pSigStruct) {
|
|||
|
pSigStruct->cbSize = cbNewSize;
|
|||
|
pbAlloc += sizeof (TBL_PAGE_SIGNATURE);
|
|||
|
}
|
|||
|
return (PVOID)pbMem;
|
|||
|
}
|
|||
|
|
|||
|
//+-------------------------------------------------------------------------
|
|||
|
//
|
|||
|
// Function: TblPageDealloc, public
|
|||
|
//
|
|||
|
// Synopsis: Deallocate page-alligned memory
|
|||
|
//
|
|||
|
// Effects: The memory allocation counter is decremented
|
|||
|
//
|
|||
|
// Arguments: [pbMem] - pointer to the memory to be deallocated
|
|||
|
// [rcbPageAllocTracker] - memory allocation counter
|
|||
|
// [cbSize] - optional size of memory segment
|
|||
|
//
|
|||
|
// Requires: memory to be deallocated must have previously been
|
|||
|
// allocated by TblPageAlloc.
|
|||
|
//
|
|||
|
// Returns: nothing
|
|||
|
//
|
|||
|
// Notes:
|
|||
|
//
|
|||
|
//--------------------------------------------------------------------------
|
|||
|
|
|||
|
void TblPageDealloc(
|
|||
|
PVOID pbMem,
|
|||
|
ULONG& rcbPageAllocTracker,
|
|||
|
ULONG cbSize
|
|||
|
) {
|
|||
|
ULONG cbPageOffset = (ULONG)(((ULONG_PTR)pbMem) & TBL_PAGE_MASK);
|
|||
|
|
|||
|
Win4Assert(cbPageOffset == 0 ||
|
|||
|
cbPageOffset == sizeof (TBL_PAGE_SIGNATURE));
|
|||
|
|
|||
|
if (cbPageOffset == sizeof (TBL_PAGE_SIGNATURE)) {
|
|||
|
TBL_PAGE_SIGNATURE * pSigStruct =
|
|||
|
(TBL_PAGE_SIGNATURE *) ((BYTE*)pbMem - sizeof (TBL_PAGE_SIGNATURE));
|
|||
|
|
|||
|
Win4Assert (pSigStruct->ulSig != 0 &&
|
|||
|
pSigStruct->cbSize >= TBL_PAGE_ALLOC_MIN &&
|
|||
|
pSigStruct->pbAddr == (BYTE *)pSigStruct);
|
|||
|
|
|||
|
cbSize = pSigStruct->cbSize;
|
|||
|
pbMem = (BYTE *)pSigStruct;
|
|||
|
} else {
|
|||
|
Win4Assert(cbSize != 0);
|
|||
|
}
|
|||
|
|
|||
|
BOOL fOK = VirtualFree(pbMem, 0, MEM_RELEASE);
|
|||
|
|
|||
|
Win4Assert( fOK && "virtual free failed!" );
|
|||
|
|
|||
|
rcbPageAllocTracker -= cbSize;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Default memory tracking variable
|
|||
|
//
|
|||
|
ULONG CWindowDataAllocator::_cbPageTracker = 0;
|
|||
|
|
|||
|
|
|||
|
//+-------------------------------------------------------------------------
|
|||
|
//
|
|||
|
// Member: CWindowDataAllocator::~CWindowDataAllocator, public
|
|||
|
//
|
|||
|
// Synopsis: Destroys a window data allocator
|
|||
|
//
|
|||
|
// Notes:
|
|||
|
//
|
|||
|
//--------------------------------------------------------------------------
|
|||
|
|
|||
|
CWindowDataAllocator::~CWindowDataAllocator ()
|
|||
|
{
|
|||
|
CSegmentHeader* pSegHdr = (CSegmentHeader*) _pBaseAddr;
|
|||
|
|
|||
|
while (pSegHdr) {
|
|||
|
_pBaseAddr = pSegHdr->pNextSegment;
|
|||
|
|
|||
|
TblPageDealloc(pSegHdr, *_pcbPageUsed);
|
|||
|
|
|||
|
pSegHdr = _pBaseAddr;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//+-------------------------------------------------------------------------
|
|||
|
//
|
|||
|
// Member: CWindowDataAllocator::_SetArena, private
|
|||
|
//
|
|||
|
// Synopsis: Creates the initial memory arena in a memory segment
|
|||
|
//
|
|||
|
// Arguments: [pBufBase] - start of memory area
|
|||
|
// [cbBuf] - total size of memory area
|
|||
|
// [pHeap] - optional pointer to previously set up heap
|
|||
|
// Assumed to be entirely within pBufBase.
|
|||
|
// [oBuf] - buffer offset assigned to segment
|
|||
|
//
|
|||
|
// Returns: nothing
|
|||
|
//
|
|||
|
// Notes:
|
|||
|
//
|
|||
|
//--------------------------------------------------------------------------
|
|||
|
|
|||
|
void CWindowDataAllocator::_SetArena (PVOID pBufBase,
|
|||
|
size_t cbBuf,
|
|||
|
CHeapHeader* pHeap,
|
|||
|
ULONG oBuf
|
|||
|
) {
|
|||
|
CSegmentHeader* pSegHdr = (CSegmentHeader*) pBufBase;
|
|||
|
|
|||
|
Win4Assert(cbBuf > sizeof (CSegmentHeader) + sizeof (CHeapHeader));
|
|||
|
|
|||
|
if (oBuf) {
|
|||
|
pSegHdr->oBaseOffset = oBuf;
|
|||
|
if (oBuf + cbBuf > _oNextOffset)
|
|||
|
_oNextOffset = oBuf + cbBuf;
|
|||
|
if (oBuf + TBL_PAGE_MAX_SEGMENT_SIZE > _oNextOffset)
|
|||
|
_oNextOffset = oBuf + TBL_PAGE_MAX_SEGMENT_SIZE;
|
|||
|
} else {
|
|||
|
//
|
|||
|
// Offset is not constrained to some previously used
|
|||
|
// value. For convenience, round up to make it easy
|
|||
|
// to add to page addresses.
|
|||
|
//
|
|||
|
_oNextOffset = (_oNextOffset + TBL_PAGE_MASK) &
|
|||
|
~(TBL_PAGE_MASK);
|
|||
|
_oNextOffset |= (ULONG)((ULONG_PTR)pBufBase & (TBL_PAGE_MASK));
|
|||
|
|
|||
|
pSegHdr->oBaseOffset = _oNextOffset;
|
|||
|
_oNextOffset += (cbBuf > TBL_PAGE_MAX_SEGMENT_SIZE) ?
|
|||
|
cbBuf : TBL_PAGE_MAX_SEGMENT_SIZE;
|
|||
|
}
|
|||
|
pSegHdr->cbSize = cbBuf;
|
|||
|
|
|||
|
CHeapHeader* pFirstHeap = (CHeapHeader*) (pSegHdr+1);
|
|||
|
cbBuf -= sizeof (CSegmentHeader);
|
|||
|
Win4Assert((cbBuf & (sizeof (CHeapHeader) - 1)) == 0);
|
|||
|
|
|||
|
if (pHeap) {
|
|||
|
Win4Assert((BYTE*)pHeap < ((BYTE*)pFirstHeap) + cbBuf &&
|
|||
|
(BYTE*)pHeap >= ((BYTE*)pFirstHeap) + 2*sizeof (CHeapHeader));
|
|||
|
Win4Assert(! pHeap->IsFree() && pHeap->IsFirst());
|
|||
|
|
|||
|
cbBuf = (size_t)((BYTE*)pHeap - (BYTE*)pFirstHeap);
|
|||
|
pHeap->cbPrev = (USHORT)cbBuf;
|
|||
|
pFirstHeap->cbSize = cbBuf | CHeapHeader::HeapFree;
|
|||
|
pSegHdr->cbFree = cbBuf;
|
|||
|
|
|||
|
while (! pHeap->IsLast()) {
|
|||
|
pHeap = pHeap->Next();
|
|||
|
if (pHeap->IsFree())
|
|||
|
pSegHdr->cbFree += pHeap->Size();
|
|||
|
}
|
|||
|
} else {
|
|||
|
pFirstHeap->cbSize = cbBuf | CHeapHeader::HeapFree|CHeapHeader::HeapEnd;
|
|||
|
pSegHdr->cbFree = cbBuf;
|
|||
|
}
|
|||
|
pFirstHeap->cbPrev = CHeapHeader::HeapEnd;
|
|||
|
|
|||
|
//
|
|||
|
// Now insert this segment into the list of segments.
|
|||
|
//
|
|||
|
pSegHdr->pNextSegment = _pBaseAddr;
|
|||
|
_pBaseAddr = pSegHdr;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//+-------------------------------------------------------------------------
|
|||
|
//
|
|||
|
// Member: CWindowDataAllocator::Allocate, public
|
|||
|
//
|
|||
|
// Synopsis: Allocates a piece of data out of a memory heap
|
|||
|
//
|
|||
|
// Effects: updates _pBuf and _cbBuf
|
|||
|
//
|
|||
|
// Arguments: [cbNeeded] - number of byes of memory needed
|
|||
|
//
|
|||
|
// Returns: PVOID - a pointer to the allocated memory
|
|||
|
//
|
|||
|
// Signals: Throws E_OUTOFMEMORY if not enough memory is available.
|
|||
|
//
|
|||
|
// Notes: Allocated blocks must be less than 64Kb in size.
|
|||
|
// Allocated blocks may move in virtual memory (while
|
|||
|
// there are no outstanding pointers within them), but
|
|||
|
// the offset value saved for an item must always be valid.
|
|||
|
//
|
|||
|
// It is assumed that no locking need be done for multi-threaded
|
|||
|
// protection because higher-level callers will do their
|
|||
|
// own synchronization.
|
|||
|
//
|
|||
|
// TODO:
|
|||
|
// - Enforce memory allocation limit
|
|||
|
//
|
|||
|
// History:
|
|||
|
//
|
|||
|
//--------------------------------------------------------------------------
|
|||
|
|
|||
|
PVOID CWindowDataAllocator::Allocate(
|
|||
|
ULONG cbNeeded
|
|||
|
) {
|
|||
|
CSegmentHeader* pSegHdr = (CSegmentHeader*) _pBaseAddr;
|
|||
|
BYTE* pRetBuf = 0;
|
|||
|
CHeapHeader* pThisHeap;
|
|||
|
|
|||
|
Win4Assert(cbNeeded < USHRT_MAX - 2*(sizeof (CHeapHeader)));
|
|||
|
|
|||
|
// Reserve room for the header.
|
|||
|
|
|||
|
cbNeeded += sizeof (CHeapHeader);
|
|||
|
|
|||
|
// Only allocate on 8-byte boundaries
|
|||
|
|
|||
|
cbNeeded = _RoundUpToChunk( cbNeeded, cbTblAllocAlignment );
|
|||
|
|
|||
|
if (pSegHdr == 0)
|
|||
|
{
|
|||
|
ULONG cbSize = TblPageGrowSize(cbNeeded + sizeof (CSegmentHeader),
|
|||
|
TRUE);
|
|||
|
BYTE* pbNewData = (BYTE *)TblPageAlloc(cbSize,
|
|||
|
*_pcbPageUsed, TBL_SIG_VARDATA);
|
|||
|
|
|||
|
tbDebugOut((DEB_TRACE,
|
|||
|
"First variable allocation segment,"
|
|||
|
" new segment = %08x, size = %06x\n",
|
|||
|
pbNewData, cbSize));
|
|||
|
Win4Assert(pbNewData != 0);
|
|||
|
|
|||
|
_SetArena(pbNewData, cbSize);
|
|||
|
pSegHdr = _pBaseAddr;
|
|||
|
}
|
|||
|
|
|||
|
do
|
|||
|
{
|
|||
|
//
|
|||
|
// If there's not enough free space in this segment, just
|
|||
|
// go on to the next one.
|
|||
|
//
|
|||
|
if (cbNeeded > pSegHdr->cbFree)
|
|||
|
continue;
|
|||
|
|
|||
|
for (pThisHeap = (CHeapHeader*)(pSegHdr+1);
|
|||
|
pThisHeap;
|
|||
|
pThisHeap = pThisHeap->Next())
|
|||
|
{
|
|||
|
|
|||
|
Win4Assert(pThisHeap->cbPrev != 0 &&
|
|||
|
pThisHeap->Size() < pSegHdr->cbSize &&
|
|||
|
(BYTE*)pThisHeap < ((BYTE*)pSegHdr) + pSegHdr->cbSize);
|
|||
|
|
|||
|
if (pThisHeap->IsFree() && pThisHeap->Size() >= cbNeeded)
|
|||
|
{
|
|||
|
return _SplitHeap(pSegHdr, pThisHeap, cbNeeded);
|
|||
|
}
|
|||
|
}
|
|||
|
} while (pSegHdr = pSegHdr->pNextSegment);
|
|||
|
|
|||
|
tbDebugOut((DEB_TRACE, "Need to grow available data for var allocation\n"));
|
|||
|
|
|||
|
//
|
|||
|
// Not enough space in the set of currently allocated segments.
|
|||
|
// See if one can be grown to accomodate the new allocation.
|
|||
|
//
|
|||
|
|
|||
|
for (pSegHdr = (CSegmentHeader*) _pBaseAddr;
|
|||
|
pSegHdr;
|
|||
|
pSegHdr = pSegHdr->pNextSegment)
|
|||
|
{
|
|||
|
|
|||
|
if (cbNeeded >
|
|||
|
(TBL_PAGE_MAX_SEGMENT_SIZE -
|
|||
|
(pSegHdr->cbSize + sizeof (TBL_PAGE_SIGNATURE))))
|
|||
|
continue;
|
|||
|
|
|||
|
ULONG cbNew = (cbNeeded + (TBL_PAGE_MASK)) & ~TBL_PAGE_MASK;
|
|||
|
Win4Assert(cbNew + pSegHdr->cbSize < 0xFFFF);
|
|||
|
|
|||
|
tbDebugOut((DEB_TRACE,
|
|||
|
"Grow var allocation segment %x, new size %06x\n",
|
|||
|
pSegHdr, pSegHdr->cbSize + cbNew));
|
|||
|
TblPageRealloc(pSegHdr, *_pcbPageUsed, cbNew + pSegHdr->cbSize, 0);
|
|||
|
|
|||
|
//
|
|||
|
// Now add the newly allocated data to the heap.
|
|||
|
//
|
|||
|
for (pThisHeap = (CHeapHeader*)(pSegHdr+1);
|
|||
|
!pThisHeap->IsLast();
|
|||
|
pThisHeap = pThisHeap->Next()) {
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
if (pThisHeap->IsFree())
|
|||
|
{
|
|||
|
//
|
|||
|
// Just add the size of the newly allocated data
|
|||
|
//
|
|||
|
|
|||
|
pThisHeap->cbSize = (pThisHeap->Size() + (USHORT)cbNew) |
|
|||
|
CHeapHeader::HeapFree |
|
|||
|
CHeapHeader::HeapEnd;
|
|||
|
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
//
|
|||
|
// Create a new arena header which is free and last
|
|||
|
//
|
|||
|
|
|||
|
pThisHeap->cbSize &= ~CHeapHeader::HeapEnd;
|
|||
|
pThisHeap->Next()->cbSize = (USHORT)cbNew |
|
|||
|
CHeapHeader::HeapFree |
|
|||
|
CHeapHeader::HeapEnd;
|
|||
|
pThisHeap->Next()->cbPrev = pThisHeap->Size();
|
|||
|
pThisHeap = pThisHeap->Next();
|
|||
|
}
|
|||
|
|
|||
|
pSegHdr->cbSize += cbNew;
|
|||
|
pSegHdr->cbFree += cbNew;
|
|||
|
return _SplitHeap(pSegHdr, pThisHeap, cbNeeded);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Not enough space in the available segments; allocate a new
|
|||
|
// segment and allocate from it.
|
|||
|
//
|
|||
|
|
|||
|
{
|
|||
|
ULONG cbSize = TblPageGrowSize(cbNeeded + sizeof (CSegmentHeader),
|
|||
|
TRUE);
|
|||
|
BYTE* pbNewData = (BYTE *)TblPageAlloc(cbSize,
|
|||
|
*_pcbPageUsed, TBL_SIG_VARDATA);
|
|||
|
|
|||
|
tbDebugOut((DEB_TRACE, "New variable allocation segment linked to %x,"
|
|||
|
" new segment = %08x, size = %06x\n",
|
|||
|
_pBaseAddr, pbNewData, cbSize));
|
|||
|
Win4Assert(pbNewData != 0);
|
|||
|
|
|||
|
_SetArena(pbNewData, cbSize);
|
|||
|
pSegHdr = _pBaseAddr;
|
|||
|
pThisHeap = (CHeapHeader *)(pSegHdr+1);
|
|||
|
Win4Assert(pThisHeap->Size() >= cbNeeded && pThisHeap->IsFree());
|
|||
|
|
|||
|
return _SplitHeap(pSegHdr, pThisHeap, cbNeeded);
|
|||
|
}
|
|||
|
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//+-------------------------------------------------------------------------
|
|||
|
//
|
|||
|
// Member: CWindowDataAllocator::_SplitHeap, private
|
|||
|
//
|
|||
|
// Synopsis: Allocates a piece of data out of a memory heap
|
|||
|
//
|
|||
|
// Arguments: [pSegHdr] - pointer to the memory segment header
|
|||
|
// [pThisHeap] - pointer to the arena header to be split
|
|||
|
// [cbNeeded] - count of bytes to be allocated
|
|||
|
//
|
|||
|
// Returns: PVOID - a pointer to the allocated memory
|
|||
|
//
|
|||
|
// Notes: The Heap pointer passed must have enough space
|
|||
|
// to accomodate the memory request.
|
|||
|
//
|
|||
|
// History:
|
|||
|
//
|
|||
|
//--------------------------------------------------------------------------
|
|||
|
|
|||
|
PVOID CWindowDataAllocator::_SplitHeap(
|
|||
|
CSegmentHeader* pSegHdr,
|
|||
|
CHeapHeader* pThisHeap,
|
|||
|
size_t cbNeeded
|
|||
|
) {
|
|||
|
CHeapHeader* pPrevHeap;
|
|||
|
|
|||
|
Win4Assert(pThisHeap->IsFree() && pThisHeap->Size() >= cbNeeded);
|
|||
|
|
|||
|
//
|
|||
|
// Found a suitable chunk of free memory. Carve off
|
|||
|
// a piece of memory to be returned; adjust the total
|
|||
|
// free size in the segment.
|
|||
|
//
|
|||
|
if (pThisHeap->Size() < cbNeeded + 2*sizeof (CHeapHeader)) {
|
|||
|
//
|
|||
|
// Not enough room to split the arena entry; just
|
|||
|
// return it all.
|
|||
|
//
|
|||
|
pSegHdr->cbFree -= pThisHeap->Size();
|
|||
|
pThisHeap->cbSize &= ~CHeapHeader::HeapFree;
|
|||
|
return pThisHeap+1;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Need to split the arena entry. We'll return the upper
|
|||
|
// portion as the allocated memory so the free memory
|
|||
|
// will be closer to the beginning of the arena.
|
|||
|
//
|
|||
|
pPrevHeap = pThisHeap;
|
|||
|
pThisHeap = (CHeapHeader*)(((BYTE*)pPrevHeap) +
|
|||
|
pPrevHeap->Size() - cbNeeded);
|
|||
|
|
|||
|
pThisHeap->cbPrev = pPrevHeap->Size() - cbNeeded;
|
|||
|
pThisHeap->cbSize = (USHORT)cbNeeded;
|
|||
|
if (pPrevHeap->IsLast()) {
|
|||
|
pThisHeap->cbSize |= CHeapHeader::HeapEnd;
|
|||
|
} else {
|
|||
|
pPrevHeap->Next()->cbPrev = (USHORT)cbNeeded;
|
|||
|
}
|
|||
|
pPrevHeap->cbSize = (pPrevHeap->Size() - cbNeeded) |
|
|||
|
CHeapHeader::HeapFree;
|
|||
|
|
|||
|
pSegHdr->cbFree -= pThisHeap->Size();
|
|||
|
return pThisHeap+1;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//+-------------------------------------------------------------------------
|
|||
|
//
|
|||
|
// Member: CWindowDataAllocator::Free, public
|
|||
|
//
|
|||
|
// Synopsis: Frees a previously allocated piece of data
|
|||
|
//
|
|||
|
// Arguments: [pMem] - pointer to the memory to be freed
|
|||
|
//
|
|||
|
// Returns: Nothing
|
|||
|
//
|
|||
|
// Notes: Coalesces freed block with previous and/or next if they
|
|||
|
// are freed.
|
|||
|
//
|
|||
|
// As with the alloc method, it is assumed that a higher-
|
|||
|
// level caller will have taken care of multi-threaded
|
|||
|
// synchronization.
|
|||
|
//
|
|||
|
// TODO: - check that freed block doesn't grow larger than
|
|||
|
// USHRT_MAX (if segment length is larger)
|
|||
|
// - Release freed memory if > 1 page at end of block
|
|||
|
// - Release freed mem if > 1 page at beginning or
|
|||
|
// middle of block and there's room for a page header
|
|||
|
// at beginning of next used block. (this may not
|
|||
|
// be worth doing due to additional complexity in
|
|||
|
// realloc case).
|
|||
|
//
|
|||
|
// History:
|
|||
|
//
|
|||
|
//--------------------------------------------------------------------------
|
|||
|
|
|||
|
void CWindowDataAllocator::Free(
|
|||
|
PVOID pMem
|
|||
|
) {
|
|||
|
Win4Assert( (((ULONG_PTR)pMem) & (sizeof (CHeapHeader) - 1)) == 0 );
|
|||
|
|
|||
|
CHeapHeader* pHeader = ((CHeapHeader *) pMem) - 1;
|
|||
|
Win4Assert( pHeader->Size() >= (sizeof CHeapHeader)*2 &&
|
|||
|
!pHeader->IsFree());
|
|||
|
|
|||
|
CHeapHeader* pPrev = pHeader;
|
|||
|
while (!pPrev->IsFirst()) {
|
|||
|
pPrev = pPrev->Prev();
|
|||
|
}
|
|||
|
CSegmentHeader* pSegHdr = ((CSegmentHeader*)pPrev) - 1;
|
|||
|
pSegHdr->cbFree += pHeader->Size();
|
|||
|
|
|||
|
pHeader->cbSize |= CHeapHeader::HeapFree;
|
|||
|
//
|
|||
|
// Attempt to coalesce with next and previous blocks.
|
|||
|
//
|
|||
|
|
|||
|
if (!(pHeader->IsLast() )) {
|
|||
|
CHeapHeader* pNext = pHeader->Next();
|
|||
|
if (pNext->IsFree()) {
|
|||
|
pHeader->cbSize = pHeader->Size();
|
|||
|
pHeader->cbSize += pNext->Size();
|
|||
|
pHeader->cbSize |= CHeapHeader::HeapFree;
|
|||
|
if (pNext->IsLast()) {
|
|||
|
pHeader->cbSize |= CHeapHeader::HeapEnd;
|
|||
|
} else {
|
|||
|
pNext = pHeader->Next();
|
|||
|
pNext->cbPrev = pHeader->Size();
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
if (!(pHeader->IsFirst() )) {
|
|||
|
CHeapHeader* pPrev = pHeader->Prev();
|
|||
|
if (pPrev->IsFree()) {
|
|||
|
pPrev->cbSize = pPrev->Size();
|
|||
|
pPrev->cbSize += pHeader->Size();
|
|||
|
pPrev->cbSize |= CHeapHeader::HeapFree;
|
|||
|
if (pHeader->IsLast()) {
|
|||
|
pPrev->cbSize |= CHeapHeader::HeapEnd;
|
|||
|
} else {
|
|||
|
pHeader = pPrev->Next();
|
|||
|
pHeader->cbPrev = pPrev->Size();
|
|||
|
}
|
|||
|
pHeader = pPrev;
|
|||
|
}
|
|||
|
}
|
|||
|
if (pHeader->IsFirst() && pHeader->IsLast()) {
|
|||
|
tbDebugOut((DEB_WARN, "Empty pool segment to be freed %x\n", pHeader));
|
|||
|
} else if (pHeader->Size() >= 2*TBL_PAGE_ALLOC_MIN) {
|
|||
|
tbDebugOut((DEB_WARN, "Pool page(s) can be deallocated,"
|
|||
|
" loc = %x, size = %x\n", pHeader, pHeader->Size()));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//+-------------------------------------------------------------------------
|
|||
|
//
|
|||
|
// Member: CWindowDataAllocator::OffsetToPointer, public
|
|||
|
//
|
|||
|
// Synopsis: Convert a pointer offset to a pointer.
|
|||
|
//
|
|||
|
// Arguments: [oBuf] - buffer offset to be mapped
|
|||
|
//
|
|||
|
// Returns: PVOID - a pointer to the data buffer
|
|||
|
//
|
|||
|
// Notes:
|
|||
|
//
|
|||
|
//--------------------------------------------------------------------------
|
|||
|
|
|||
|
PVOID CWindowDataAllocator::OffsetToPointer(
|
|||
|
TBL_OFF oBuf
|
|||
|
) {
|
|||
|
CSegmentHeader* pSegHdr = _pBaseAddr;
|
|||
|
|
|||
|
for (pSegHdr = _pBaseAddr; pSegHdr; pSegHdr = pSegHdr->pNextSegment) {
|
|||
|
if (oBuf < pSegHdr->oBaseOffset)
|
|||
|
continue;
|
|||
|
if ((ULONG_PTR)(oBuf - pSegHdr->oBaseOffset) < pSegHdr->cbSize) {
|
|||
|
return (oBuf - pSegHdr->oBaseOffset) + (BYTE *)pSegHdr;
|
|||
|
}
|
|||
|
}
|
|||
|
Win4Assert(! "CWindowDataAllocator::OffsetToPointer failed");
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//+-------------------------------------------------------------------------
|
|||
|
//
|
|||
|
// Member: CWindowDataAllocator::PointerToOffset, public
|
|||
|
//
|
|||
|
// Synopsis: Convert a pointer to a pointer offset.
|
|||
|
//
|
|||
|
// Arguments: [pBuf] - buffer pointer to be mapped
|
|||
|
//
|
|||
|
// Returns: ULONG - a unique value for the buffer in the memory
|
|||
|
// arena
|
|||
|
//
|
|||
|
// Notes:
|
|||
|
//
|
|||
|
//--------------------------------------------------------------------------
|
|||
|
|
|||
|
TBL_OFF CWindowDataAllocator::PointerToOffset(
|
|||
|
PVOID pBuf
|
|||
|
) {
|
|||
|
CSegmentHeader* pSegHdr = _pBaseAddr;
|
|||
|
|
|||
|
for (pSegHdr = _pBaseAddr; pSegHdr; pSegHdr = pSegHdr->pNextSegment) {
|
|||
|
if (pBuf < pSegHdr)
|
|||
|
continue;
|
|||
|
if ((TBL_OFF)((BYTE *)pBuf - (BYTE *)pSegHdr) < pSegHdr->cbSize) {
|
|||
|
return ((BYTE *)pBuf - (BYTE *)pSegHdr) + pSegHdr->oBaseOffset;
|
|||
|
}
|
|||
|
}
|
|||
|
Win4Assert(! "CWindowDataAllocator::PointerToOffset failed");
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
void CWindowDataAllocator::SetBase( BYTE* pbBase )
|
|||
|
{
|
|||
|
Win4Assert(! "CWindowDataAllocator::SetBase not supported");
|
|||
|
}
|
|||
|
|
|||
|
#if CIDBG
|
|||
|
//+-------------------------------------------------------------------------
|
|||
|
//
|
|||
|
// Member: CWindowDataAllocator::WalkHeap, public
|
|||
|
//
|
|||
|
// Synopsis: Walks the memory arena, calling out to a caller-supplied
|
|||
|
// function for each memory area.
|
|||
|
//
|
|||
|
// Arguments: [pfnReport] - pointer to function for memory report
|
|||
|
//
|
|||
|
// Returns: Nothing
|
|||
|
//
|
|||
|
// Notes: For debugging support only.
|
|||
|
// The pfnReport is called with three parameters, giving the
|
|||
|
// memory address of the block, the size of the block, and the
|
|||
|
// free flag associated with the block. The block address and
|
|||
|
// size do not include the arena header.
|
|||
|
//
|
|||
|
// History:
|
|||
|
//
|
|||
|
//--------------------------------------------------------------------------
|
|||
|
|
|||
|
|
|||
|
void
|
|||
|
CWindowDataAllocator::WalkHeap(
|
|||
|
void (pfnReport)(PVOID pb, USHORT cb, USHORT fFree)
|
|||
|
) {
|
|||
|
CSegmentHeader* pSegHdr = (CSegmentHeader*) _pBaseAddr;
|
|||
|
|
|||
|
while (pSegHdr) {
|
|||
|
CHeapHeader *pPrevHeap = 0;
|
|||
|
CHeapHeader *pThisHeap;
|
|||
|
ULONG cbFree = 0;
|
|||
|
|
|||
|
pThisHeap = (CHeapHeader*)(pSegHdr + 1);
|
|||
|
|
|||
|
Win4Assert(pThisHeap->IsFirst());
|
|||
|
|
|||
|
while ( TRUE ) {
|
|||
|
pfnReport(pThisHeap+1, pThisHeap->Size() - sizeof (CHeapHeader),
|
|||
|
(USHORT)pThisHeap->IsFree());
|
|||
|
if (pPrevHeap) {
|
|||
|
Win4Assert(pThisHeap->Prev() == pPrevHeap &&
|
|||
|
! pThisHeap->IsFirst());
|
|||
|
}
|
|||
|
if (pThisHeap->IsFree())
|
|||
|
cbFree += pThisHeap->Size();
|
|||
|
if (pThisHeap->IsLast())
|
|||
|
break;
|
|||
|
pPrevHeap = pThisHeap;
|
|||
|
pThisHeap = pThisHeap->Next();
|
|||
|
}
|
|||
|
Win4Assert(cbFree == pSegHdr->cbFree);
|
|||
|
pSegHdr = pSegHdr->pNextSegment;
|
|||
|
}
|
|||
|
}
|
|||
|
#endif // CIDBG
|
|||
|
|
|||
|
|
|||
|
//+-------------------------------------------------------------------------
|
|||
|
//
|
|||
|
// Member: CFixedVarAllocator::~CFixedVarAllocator, public
|
|||
|
//
|
|||
|
// Synopsis: Destroys a window data allocator
|
|||
|
//
|
|||
|
// Notes:
|
|||
|
//
|
|||
|
//--------------------------------------------------------------------------
|
|||
|
|
|||
|
CFixedVarAllocator::~CFixedVarAllocator ()
|
|||
|
{
|
|||
|
delete _VarAllocator;
|
|||
|
|
|||
|
if (_pbBaseAddr)
|
|||
|
{
|
|||
|
if ( _fDidReInit )
|
|||
|
delete (BYTE *) _pbBaseAddr;
|
|||
|
else
|
|||
|
{
|
|||
|
TblPageDealloc( _pbBaseAddr,
|
|||
|
_cbPageUsed,
|
|||
|
0 );
|
|||
|
Win4Assert( _cbPageUsed == 0 );
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//+-------------------------------------------------------------------------
|
|||
|
//
|
|||
|
// Member: CFixedVarAllocator::Allocate, public
|
|||
|
//
|
|||
|
// Synopsis: Allocates a piece of variable data out of a memory buffer
|
|||
|
// Take it from the same buffer as the fixed data until there
|
|||
|
// is no more room available in the that buffer, then split
|
|||
|
// the fixed and variable portions and convert the variable
|
|||
|
// portion to a CVarDataAllocator which will handle subsequent
|
|||
|
// allocations.
|
|||
|
//
|
|||
|
// Effects: updates _cbBuf
|
|||
|
//
|
|||
|
// Arguments: [cbNeeded] - number of byes of memory needed
|
|||
|
//
|
|||
|
// Returns: PVOID - a pointer to the allocated memory
|
|||
|
//
|
|||
|
// Signals: Throws E_OUTOFMEMORY if not enough memory is available.
|
|||
|
//
|
|||
|
// Notes: Allocated blocks must be less than 64Kb in size.
|
|||
|
// Allocated blocks may move in virtual memory (while
|
|||
|
// there are no outstanding pointers within them), but
|
|||
|
// the offset value saved for an item must always be valid.
|
|||
|
//
|
|||
|
// It is assumed that a higher-level caller will take
|
|||
|
// care of any multi-thread synchronization issues.
|
|||
|
//
|
|||
|
// History: 22 Apr 1994 Alanw Created
|
|||
|
//
|
|||
|
//--------------------------------------------------------------------------
|
|||
|
|
|||
|
PVOID CFixedVarAllocator::Allocate(
|
|||
|
ULONG cbNeeded
|
|||
|
) {
|
|||
|
if ( 0 == _VarAllocator && !_fVarOffsets )
|
|||
|
{
|
|||
|
// Don't use offsets -- just pointers. Need an allocator from
|
|||
|
// which to allocate.
|
|||
|
|
|||
|
_VarAllocator = new CWindowDataAllocator();
|
|||
|
}
|
|||
|
|
|||
|
if (_VarAllocator)
|
|||
|
{
|
|||
|
Win4Assert( 0 != _VarAllocator );
|
|||
|
return _VarAllocator->Allocate(cbNeeded);
|
|||
|
}
|
|||
|
|
|||
|
Win4Assert(cbNeeded <
|
|||
|
USHRT_MAX - 2*(sizeof (CWindowDataAllocator::CHeapHeader)));
|
|||
|
|
|||
|
//
|
|||
|
// Only allocate on eight-byte boundaries
|
|||
|
//
|
|||
|
|
|||
|
cbNeeded = (cbNeeded + (sizeof (CWindowDataAllocator::CHeapHeader)-1)) &
|
|||
|
~(sizeof (CWindowDataAllocator::CHeapHeader) - 1);
|
|||
|
cbNeeded += sizeof (CWindowDataAllocator::CHeapHeader);
|
|||
|
|
|||
|
if (_pbBaseAddr == 0)
|
|||
|
_GrowData(cbNeeded);
|
|||
|
|
|||
|
size_t cbFree = FreeSpace();
|
|||
|
|
|||
|
if (cbNeeded >= cbFree)
|
|||
|
{
|
|||
|
_SplitData(FALSE);
|
|||
|
Win4Assert(_VarAllocator != 0);
|
|||
|
return _VarAllocator->Allocate(cbNeeded -
|
|||
|
sizeof (CWindowDataAllocator::CHeapHeader));
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Allocate the memory and prefix with a heap header so the
|
|||
|
// memory can be handed over to the Heap manager eventually.
|
|||
|
//
|
|||
|
CWindowDataAllocator::CHeapHeader* pThisHeap;
|
|||
|
CWindowDataAllocator::CHeapHeader* pPrevHeap;
|
|||
|
|
|||
|
pThisHeap = (CWindowDataAllocator::CHeapHeader*)
|
|||
|
(_pbBaseAddr + _cbBuf - cbNeeded);
|
|||
|
pThisHeap->cbSize = (USHORT)cbNeeded;
|
|||
|
pThisHeap->cbPrev = CWindowDataAllocator::CHeapHeader::HeapEnd;
|
|||
|
|
|||
|
if (_cbBuf != _cbTotal)
|
|||
|
{
|
|||
|
pPrevHeap = (CWindowDataAllocator::CHeapHeader *)(_pbBaseAddr+_cbBuf);
|
|||
|
pPrevHeap->cbPrev = ((USHORT)cbNeeded) |
|
|||
|
(pPrevHeap->cbPrev & (CWindowDataAllocator::CHeapHeader::HeapFree));
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
pThisHeap->cbSize |= CWindowDataAllocator::CHeapHeader::HeapEnd;
|
|||
|
}
|
|||
|
_cbBuf -= cbNeeded;
|
|||
|
Win4Assert(_cbBuf >= (unsigned)(_pbBuf - _pbBaseAddr));
|
|||
|
|
|||
|
return (BYTE*)(pThisHeap + 1);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//+-------------------------------------------------------------------------
|
|||
|
//
|
|||
|
// Member: CFixedVarAllocator::Free, public
|
|||
|
//
|
|||
|
// Synopsis: Frees a previously allocated piece of data. Passed along
|
|||
|
// to the CVarDataAllocator if fixed and var data have been
|
|||
|
// split.
|
|||
|
//
|
|||
|
// Arguments: [pMem] - pointer to the memory to be freed
|
|||
|
//
|
|||
|
// Returns: Nothing
|
|||
|
//
|
|||
|
// Notes: Coalesces freed block with previous and/or next if they
|
|||
|
// are freed.
|
|||
|
//
|
|||
|
// It is assumed that a higher-level caller will take
|
|||
|
// care of any multi-thread synchronization issues.
|
|||
|
//
|
|||
|
// History:
|
|||
|
//
|
|||
|
//--------------------------------------------------------------------------
|
|||
|
|
|||
|
void CFixedVarAllocator::Free(
|
|||
|
PVOID pMem
|
|||
|
) {
|
|||
|
if (_VarAllocator) {
|
|||
|
_VarAllocator->Free(pMem);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
Win4Assert( (((ULONG_PTR)pMem) & (sizeof (CWindowDataAllocator::CHeapHeader) - 1)) == 0 );
|
|||
|
|
|||
|
CWindowDataAllocator::CHeapHeader* pHeader = ((CWindowDataAllocator::CHeapHeader *) pMem) - 1;
|
|||
|
Win4Assert( pHeader->Size() >= (sizeof CWindowDataAllocator::CHeapHeader)*2 &&
|
|||
|
!pHeader->IsFree());
|
|||
|
|
|||
|
pHeader->cbSize |= CWindowDataAllocator::CHeapHeader::HeapFree;
|
|||
|
|
|||
|
//
|
|||
|
// Attempt to coalesce with next and previous blocks.
|
|||
|
//
|
|||
|
if (!(pHeader->IsLast() )) {
|
|||
|
CWindowDataAllocator::CHeapHeader* pNext = pHeader->Next();
|
|||
|
if (pNext->IsFree()) {
|
|||
|
pHeader->cbSize = pHeader->Size();
|
|||
|
pHeader->cbSize += pNext->Size();
|
|||
|
pHeader->cbSize |= CWindowDataAllocator::CHeapHeader::HeapFree;
|
|||
|
if (pNext->IsLast()) {
|
|||
|
pHeader->cbSize |= CWindowDataAllocator::CHeapHeader::HeapEnd;
|
|||
|
} else {
|
|||
|
pNext = pHeader->Next();
|
|||
|
pNext->cbPrev = pHeader->Size();
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
if (!(pHeader->IsFirst() )) {
|
|||
|
CWindowDataAllocator::CHeapHeader* pPrev = pHeader->Prev();
|
|||
|
if (pPrev->IsFree()) {
|
|||
|
pPrev->cbSize = pPrev->Size();
|
|||
|
pPrev->cbSize += pHeader->Size();
|
|||
|
pPrev->cbSize |= CWindowDataAllocator::CHeapHeader::HeapFree;
|
|||
|
if (pHeader->IsLast()) {
|
|||
|
pPrev->cbSize |= CWindowDataAllocator::CHeapHeader::HeapEnd;
|
|||
|
} else {
|
|||
|
pHeader = pPrev->Next();
|
|||
|
pHeader->cbPrev = pPrev->Size();
|
|||
|
}
|
|||
|
pHeader = pPrev;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (pHeader->IsFirst()) {
|
|||
|
//
|
|||
|
// Free this memory for use by the fixed allocator
|
|||
|
//
|
|||
|
if (! pHeader->IsLast()) {
|
|||
|
pHeader->Next()->cbPrev =
|
|||
|
CWindowDataAllocator::CHeapHeader::HeapEnd;
|
|||
|
}
|
|||
|
_cbBuf += pHeader->Size();
|
|||
|
if (pHeader->IsLast()) {
|
|||
|
Win4Assert(_cbBuf == _cbTotal);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//+---------------------------------------------------------------------------
|
|||
|
//
|
|||
|
// Function: ResizeAndInitFixed
|
|||
|
//
|
|||
|
// Synopsis: Resizes the "Fixed Width" part of the buffer to hold atleast
|
|||
|
// as many as "cItems" entries.
|
|||
|
//
|
|||
|
// Arguments: [cbDataWidth] -- Width of the new fixed data.
|
|||
|
// [cItems] -- Starting number of items that will be added.
|
|||
|
//
|
|||
|
// History: 11-29-94 srikants Created
|
|||
|
//
|
|||
|
// Notes:
|
|||
|
//
|
|||
|
//----------------------------------------------------------------------------
|
|||
|
|
|||
|
void CFixedVarAllocator::ResizeAndInitFixed( size_t cbDataWidth, ULONG cItems )
|
|||
|
{
|
|||
|
//
|
|||
|
// This method should be called only if the fixed portion is being
|
|||
|
// currently used.
|
|||
|
//
|
|||
|
Win4Assert( _cbTotal == _cbBuf );
|
|||
|
|
|||
|
ULONG cbNewNeeded = (ULONG)(cbDataWidth * cItems + _cbReserved);
|
|||
|
//
|
|||
|
// Round up the size to be an integral number of "pages".
|
|||
|
//
|
|||
|
ULONG cbNewActual = TblPageGrowSize(cbNewNeeded, TRUE);
|
|||
|
_cbDataWidth = cbDataWidth;
|
|||
|
|
|||
|
if ( cbNewActual == _cbBuf )
|
|||
|
{
|
|||
|
//
|
|||
|
// The old and new totals are the same. There is no need to do any
|
|||
|
// allocation. Just adjust the pointers and return.
|
|||
|
//
|
|||
|
_pbBuf = _pbBaseAddr+_cbReserved;
|
|||
|
return;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
//
|
|||
|
// Reallocate the buffer to fit the new size.
|
|||
|
//
|
|||
|
BYTE* pbNewData;
|
|||
|
|
|||
|
if (_pbBaseAddr && cbNewActual <= TBL_PAGE_MAX_SEGMENT_SIZE)
|
|||
|
{
|
|||
|
pbNewData = (BYTE *)TblPageRealloc(_pbBaseAddr, _cbPageUsed, cbNewActual, 0);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
pbNewData = (BYTE *)TblPageAlloc( cbNewActual, _cbPageUsed, TBL_SIG_ROWDATA);
|
|||
|
}
|
|||
|
|
|||
|
Win4Assert( cbNewActual > _cbReserved );
|
|||
|
if (_pbBaseAddr)
|
|||
|
{
|
|||
|
if ( _pbBaseAddr != pbNewData )
|
|||
|
{
|
|||
|
RtlCopyMemory( pbNewData, _pbBaseAddr, _cbReserved );
|
|||
|
TblPageDealloc(_pbBaseAddr, _cbPageUsed);
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
if (_cbReserved)
|
|||
|
{
|
|||
|
RtlZeroMemory(pbNewData, _cbReserved);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
_pbBuf = pbNewData + _cbReserved;
|
|||
|
_pbBaseAddr = pbNewData;
|
|||
|
_cbTotal = _cbBuf = cbNewActual;
|
|||
|
_pbLastAddrPlusOne = _pbBaseAddr + _cbBuf;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//+---------------------------------------------------------------------------
|
|||
|
//
|
|||
|
// Function: AllocMultipleFixed
|
|||
|
//
|
|||
|
// Synopsis: Allocates a buffer for the specified number of items
|
|||
|
// and returns its pointer.
|
|||
|
//
|
|||
|
// Arguments: [cItems] -- Number of "fixed width" items.
|
|||
|
//
|
|||
|
// Returns: A pointer to a buffer to hold the specified number of
|
|||
|
// "fixed width" items.
|
|||
|
//
|
|||
|
// History: 11-29-94 srikants Created
|
|||
|
//
|
|||
|
// Notes:
|
|||
|
//
|
|||
|
//----------------------------------------------------------------------------
|
|||
|
|
|||
|
void * CFixedVarAllocator::AllocMultipleFixed( ULONG cItems )
|
|||
|
{
|
|||
|
ULONG cbNeeded = cItems * _cbDataWidth;
|
|||
|
if ( cbNeeded > FreeSpace() )
|
|||
|
{
|
|||
|
if ( _cbTotal != _cbBuf )
|
|||
|
_SplitData(TRUE);
|
|||
|
}
|
|||
|
|
|||
|
BYTE * pRetBuf = 0;
|
|||
|
|
|||
|
//
|
|||
|
// Need to grow the buffer to accommodate more fixed size allocations.
|
|||
|
//
|
|||
|
if ( cbNeeded > FreeSpace() )
|
|||
|
{
|
|||
|
Win4Assert( _cbTotal == _cbBuf );
|
|||
|
_GrowData( cbNeeded );
|
|||
|
}
|
|||
|
|
|||
|
if ( cbNeeded <= FreeSpace() )
|
|||
|
{
|
|||
|
pRetBuf = _pbBuf;
|
|||
|
_pbBuf += cbNeeded;
|
|||
|
}
|
|||
|
|
|||
|
return pRetBuf;
|
|||
|
}
|
|||
|
|
|||
|
//+-------------------------------------------------------------------------
|
|||
|
//
|
|||
|
// Member: CFixedVarAllocator::_GrowData, private
|
|||
|
//
|
|||
|
// Synopsis: Grow the data area allocated to row data.
|
|||
|
//
|
|||
|
// Arguments: [cbNeeded] - number of bytes needed. If zero,
|
|||
|
// assumed to be called for fixed allocation.
|
|||
|
//
|
|||
|
// Returns: Nothing
|
|||
|
//
|
|||
|
// Effects: _pbBaseAddr will be reallocated to a new size. Existing
|
|||
|
// reserved and row data will be copied to the new location.
|
|||
|
//
|
|||
|
// Notes: _GrowData cannot be called if row data and variable data
|
|||
|
// share the same buffer. Use _SplitData instead.
|
|||
|
//
|
|||
|
//--------------------------------------------------------------------------
|
|||
|
|
|||
|
void CFixedVarAllocator::_GrowData( size_t cbNeeded )
|
|||
|
{
|
|||
|
if (cbNeeded == 0)
|
|||
|
cbNeeded = _cbDataWidth;
|
|||
|
|
|||
|
Win4Assert(!_pbBaseAddr || (_pbBuf - _pbBaseAddr) + cbNeeded >= _cbTotal);
|
|||
|
Win4Assert(_cbBuf == _cbTotal); // can be no var data when growing
|
|||
|
|
|||
|
ULONG cbSize = TblPageGrowSize((ULONG)(_cbTotal + cbNeeded), TRUE);
|
|||
|
BYTE* pbNewData;
|
|||
|
|
|||
|
if (_pbBaseAddr && cbSize <= TBL_PAGE_MAX_SEGMENT_SIZE)
|
|||
|
{
|
|||
|
pbNewData = (BYTE *)TblPageRealloc(_pbBaseAddr, _cbPageUsed, cbSize, 0);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
pbNewData = (BYTE *)TblPageAlloc(cbSize, _cbPageUsed, TBL_SIG_ROWDATA);
|
|||
|
}
|
|||
|
|
|||
|
Win4Assert(pbNewData != 0);
|
|||
|
tbDebugOut((DEB_TRACE,
|
|||
|
"Growing fixed data for allocation at %x, new size = %x, new data = %x\n",
|
|||
|
_pbBaseAddr, cbSize, pbNewData));
|
|||
|
|
|||
|
if (_pbBaseAddr) {
|
|||
|
Win4Assert(cbSize > _cbBuf);
|
|||
|
if (_pbBaseAddr != pbNewData) {
|
|||
|
memcpy(pbNewData, _pbBaseAddr, _cbBuf);
|
|||
|
_pbBuf = (_pbBuf - _pbBaseAddr) + pbNewData;
|
|||
|
|
|||
|
TblPageDealloc(_pbBaseAddr, _cbPageUsed);
|
|||
|
}
|
|||
|
} else {
|
|||
|
|
|||
|
#if CIDBG == 1
|
|||
|
BYTE *pByte = (BYTE *) pbNewData;
|
|||
|
|
|||
|
for ( unsigned i=0; i<_cbReserved; i++ )
|
|||
|
Win4Assert( pByte[i] == 0 );
|
|||
|
#endif
|
|||
|
|
|||
|
_pbBuf = pbNewData + _cbReserved;
|
|||
|
}
|
|||
|
_pbBaseAddr = pbNewData;
|
|||
|
_cbTotal = _cbBuf = cbSize;
|
|||
|
_pbLastAddrPlusOne = _pbBaseAddr + _cbBuf;
|
|||
|
Win4Assert(_pbBaseAddr && (_pbBuf - _pbBaseAddr) + cbNeeded < _cbBuf);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//+-------------------------------------------------------------------------
|
|||
|
//
|
|||
|
// Member: CFixedVarAllocator::_SplitData, private
|
|||
|
//
|
|||
|
// Synopsis: Split the joint allocation of row and variable data
|
|||
|
//
|
|||
|
// Arguments: [fMoveRowData] - if TRUE, the row data is moved; else
|
|||
|
// the variable data is moved.
|
|||
|
//
|
|||
|
// Returns: Nothing
|
|||
|
//
|
|||
|
// Effects: _pbBaseAddr will be realloced to a new size. Existing
|
|||
|
// row data will be copied to the new location. If a
|
|||
|
// data buffer was previously shared between row data and
|
|||
|
// variable data, it will be no longer.
|
|||
|
//
|
|||
|
// Notes:
|
|||
|
//
|
|||
|
//--------------------------------------------------------------------------
|
|||
|
|
|||
|
void
|
|||
|
CFixedVarAllocator::_SplitData( BOOL fMoveRowData )
|
|||
|
{
|
|||
|
Win4Assert(_pbBaseAddr != 0 && _VarAllocator == 0);
|
|||
|
|
|||
|
ULONG cbSize = _cbTotal;
|
|||
|
ULONG cbVarSize = (ULONG)(_cbTotal - _cbBuf);
|
|||
|
BYTE* pbNewData = (BYTE *)TblPageAlloc(cbSize, _cbPageUsed,
|
|||
|
fMoveRowData ? TBL_SIG_ROWDATA : TBL_SIG_VARDATA);
|
|||
|
|
|||
|
//
|
|||
|
// BROKENCODE - shouldn't pbNewData be in a smart pointer. o/w, there
|
|||
|
// could be a memory leak.
|
|||
|
//
|
|||
|
|
|||
|
Win4Assert(pbNewData != 0);
|
|||
|
Win4Assert(cbVarSize != 0 || fMoveRowData);
|
|||
|
|
|||
|
tbDebugOut((DEB_TRACE,
|
|||
|
"Splitting fixed data for window from variable data\n"));
|
|||
|
tbDebugOut((DEB_ITRACE,
|
|||
|
"Current fixed size = %x, var size = %x, new %s data = %x\n",
|
|||
|
_cbBuf, _cbTotal - _cbBuf,
|
|||
|
fMoveRowData? "fixed":"var", pbNewData));
|
|||
|
|
|||
|
if (fMoveRowData)
|
|||
|
{
|
|||
|
RtlCopyMemory(pbNewData, _pbBaseAddr, (_pbBuf - _pbBaseAddr));
|
|||
|
|
|||
|
CWindowDataAllocator* VarAllocator =
|
|||
|
new CWindowDataAllocator (_pbBaseAddr, _cbTotal,
|
|||
|
(CWindowDataAllocator::CHeapHeader*)(_pbBaseAddr + _cbBuf),
|
|||
|
&_cbPageUsed);
|
|||
|
|
|||
|
_pbBuf = (_pbBuf - _pbBaseAddr) + pbNewData;
|
|||
|
_pbBaseAddr = pbNewData;
|
|||
|
_pbLastAddrPlusOne = _pbBaseAddr + _cbBuf;
|
|||
|
_VarAllocator = VarAllocator;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
CWindowDataAllocator::CHeapHeader* pHeap = 0;
|
|||
|
|
|||
|
if (cbVarSize)
|
|||
|
{
|
|||
|
RtlCopyMemory(pbNewData + _cbBuf, _pbBaseAddr + _cbBuf, _cbTotal - _cbBuf);
|
|||
|
pHeap = (CWindowDataAllocator::CHeapHeader*)(pbNewData + _cbBuf);
|
|||
|
}
|
|||
|
CWindowDataAllocator* VarAllocator =
|
|||
|
new CWindowDataAllocator (pbNewData, _cbTotal,
|
|||
|
pHeap, &_cbPageUsed);
|
|||
|
_VarAllocator = VarAllocator;
|
|||
|
}
|
|||
|
_cbBuf = _cbTotal;
|
|||
|
}
|
|||
|
|
|||
|
|