windows-nt/Source/XPSP1/NT/inetsrv/query/bigtable/tblalloc.cxx

1334 lines
41 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
//+-------------------------------------------------------------------------
//
// 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;
}