//+------------------------------------------------------------------------- // // 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 #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; }