//+------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1994 - 2000. // // File: tblalloc.hxx // // Contents: Memory allocation wrappers for large tables // // Classes: PVarAllocator - protocol for variable data allocators // PFixedAllocator - protocol for fixed and variable alloctors // PFixedVarAllocator - protocol for fixed and variable alloctors // CVarBufferAllocator - simple allocation from a buffer // CFixedVarBufferAllocator - fixed/var allocation from a buffer // CWindowDataAllocator - data allocator for window var data // CFixedVarAllocator - data allocator for fixed and var pieces // // Functions: TblPageAlloc - Allocate page-alligned memory // TblPageRealloc - re-allocate page-alligned memory // TblPageDealloc - deallocate page-alligned memory // TblPageGrowSize - suggest a new size for page-alligned memory // // Notes: TODO - There is no means to limit growth of data segments. // How will memory allocation targets be maintained? // Does there need to be some means of signalling // between the memory allocators and their consumers? // // History: 14 Mar 1994 AlanW Created // //-------------------------------------------------------------------------- #pragma once #ifdef DISPLAY_INCLUDES #pragma message( "#include <" __FILE__ ">..." ) #endif #include typedef ULONG_PTR TBL_OFF; // // Signature structure (for debugging primarily) // struct TBL_PAGE_SIGNATURE { ULONG ulSig; ULONG cbSize; PVOID pbAddr; PVOID pCaller; // pad to paragraph for convenience }; // // Signature values; all start with the string 'tbl' // const ULONG TBL_SIG_COMPRESSED = 0x436c6274; // 'tblC' const ULONG TBL_SIG_ROWDATA = 0x526c6274; // 'tblR' const ULONG TBL_SIG_VARDATA = 0x566c6274; // 'tblV' const unsigned cbTblAllocAlignment = sizeof( double ); inline DWORD _RoundUpToChunk( DWORD x, DWORD chunksize ) { return (x + chunksize - 1) & ~(chunksize - 1); } // // Minimum size and incremental growth constants // const ULONG TBL_PAGE_ALLOC_MIN = 4096; const ULONG TBL_PAGE_MASK = (TBL_PAGE_ALLOC_MIN - 1); const ULONG TBL_PAGE_ALLOC_INCR = 32768; // // Maximum size of a growable data segment // const ULONG TBL_PAGE_MAX_SEGMENT_SIZE = 64 * 1024; //+------------------------------------------------------------------------- // // 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. NULL if // allocation failure. // // Notes: rcbSizeNeeded is set to the minimum required size // needed. TblPageGrowSize can be called to suggest // a new size for 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 = 0 ); //+------------------------------------------------------------------------- // // 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. NULL if // 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. // //-------------------------------------------------------------------------- PVOID TblPageRealloc( PVOID pbMem, ULONG& rcbPageAllocTracker, ULONG cbNewSize, ULONG cbOldSize ); //+------------------------------------------------------------------------- // // 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 // // Requires: memory to be deallocated must have previously been // allocated by TblPageAlloc. // // Returns: nothing // // Notes: // //-------------------------------------------------------------------------- void TblPageDealloc( PVOID pbMem, ULONG& rcbPageAllocTracker, ULONG cbSize = 0 ); //+------------------------------------------------------------------------- // // Function: TblPageGrowSize, inline // // Synopsis: Suggest a new size for memory growth // // Arguments: [cbCurrentSize] - current allocation size // [fSigned] - TRUE iff mem. block will include signature // // Returns: ULONG - size to be input to TblPageAlloc // // Notes: // //-------------------------------------------------------------------------- inline ULONG TblPageGrowSize( ULONG cbCurrentSize, ULONG const fSigned = FALSE ) { if (cbCurrentSize == 0) { return (ULONG)(fSigned ? TBL_PAGE_ALLOC_MIN - sizeof (TBL_PAGE_SIGNATURE): TBL_PAGE_ALLOC_MIN); } ULONG cbSize = cbCurrentSize; if (fSigned) { cbSize += sizeof (TBL_PAGE_SIGNATURE); } if (cbSize < TBL_PAGE_ALLOC_INCR) { cbSize *= 2; } else { cbSize += TBL_PAGE_ALLOC_INCR; } // Round to page size cbSize = _RoundUpToChunk( cbSize, TBL_PAGE_ALLOC_MIN ); if (fSigned) cbSize -= sizeof (TBL_PAGE_SIGNATURE); return cbSize; } //+------------------------------------------------------------------------- // // Class: PFixedAllocator // // Purpose: Base class for fixed data allocator // //-------------------------------------------------------------------------- class PFixedAllocator { public: // Allocate a piece of fixed memory. virtual PVOID AllocFixed() = 0; // Return the size of the allocation unit virtual size_t FixedWidth() const = 0; // Return the base address of the reserved area virtual BYTE* BufferAddr() const = 0; // Return the size of the reserved area virtual size_t GetReservedSize() const = 0; virtual void ReInit( BOOL fVarOffsets, size_t cbReserved, void * pvBuf, ULONG cbBuf ) { Win4Assert(!"Never called"); } }; //+------------------------------------------------------------------------- // // Class: PFixedVarAllocator // // Purpose: Base class for fixed/variable data allocator // //-------------------------------------------------------------------------- class PFixedVarAllocator: public PVarAllocator, public PFixedAllocator { }; //+------------------------------------------------------------------------- // // Class: CVarBufferAllocator // // Purpose: Simple data allocator for byte buffer allocation. // // Notes: This allocator just carves pieces out of a data // buffer and hands them out. // //-------------------------------------------------------------------------- class CVarBufferAllocator : public PVarAllocator { public: CVarBufferAllocator (PVOID pBuf, size_t cbBuf) : _pBuf( (BYTE *)pBuf), _pBaseAddr( (BYTE *)pBuf ), _cbBuf( cbBuf ) { } // Allocate a piece of memory. PVOID Allocate(ULONG cbNeeded); // Free a previously allocated piece of memory (can't in a buffer). void Free(PVOID pMem) { return; } // Return whether OffsetToPointer has non-null effect BOOL IsBasedMemory(void) { return _pBaseAddr != 0; } // Convert a pointer offset to a pointer. PVOID OffsetToPointer(TBL_OFF oBuf) { return _pBaseAddr + oBuf; } // Convert a pointer to a pointer offset. TBL_OFF PointerToOffset(PVOID pBuf) { return (BYTE *)pBuf - _pBaseAddr; } // Set base address for OffsetToPointer and PointerToOffset void SetBase(BYTE* pbBase) { _pBaseAddr = pbBase; } protected: BYTE* _pBuf; // Buffer base BYTE* _pBaseAddr; // Bias for addresses size_t _cbBuf; // Available space in _pBuf }; //+------------------------------------------------------------------------- // // Member: CVarBufferAllocator::Allocate, inline // // Synopsis: Allocates a piece of data out of a memory buffer // // 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: // //-------------------------------------------------------------------------- inline PVOID CVarBufferAllocator::Allocate( ULONG cbNeeded ) { BYTE* pRetBuf; if (cbNeeded <= _cbBuf) { pRetBuf = _pBuf; _pBuf += cbNeeded; _cbBuf -= cbNeeded; } else { QUIETTHROW( CException(E_OUTOFMEMORY) ); } return pRetBuf; } //+------------------------------------------------------------------------- // // Class: CFixedVarBufferAllocator // // Purpose: fixed/var data allocator for byte buffer allocation. // // Notes: This allocator just carves pieces out of a data // buffer and hands them out. It supports both fixed // and variable data allocation, growing variable data // downward from the end of the buffer. // //-------------------------------------------------------------------------- class CFixedVarBufferAllocator : public PFixedVarAllocator { public: CFixedVarBufferAllocator (PVOID pBuf, ULONG_PTR ulOffsetFixup, size_t cbBuf, size_t cbFixed, size_t cbReserved = 0) : _pBufferAddr( (BYTE *)pBuf ), _pBaseOffset( (BYTE *)pBuf ), _ulOffsetFixup( ulOffsetFixup ), _pFixedBuf( (BYTE *)pBuf + cbReserved), _pVarBuf( (BYTE *)pBuf + cbBuf), _cbReserved( cbReserved ), _cbFixed( cbFixed ) { // make the variable allocations 8-byte aligned while ( ( (ULONG_PTR) _pVarBuf ) & ( cbTblAllocAlignment - 1 ) ) _pVarBuf--; } // Allocate a piece of fixed memory. PVOID AllocFixed(); // Allocate a piece of variable memory. PVOID Allocate(ULONG cbNeeded); // Free a previously allocated piece of memory (can't in a buffer). void Free(PVOID pMem) {} // Return whether OffsetToPointer has non-null effect BOOL IsBasedMemory(void) { return _pBaseOffset != 0; } // Convert a pointer offset to a pointer. PVOID OffsetToPointer(TBL_OFF oBuf) { return oBuf - _ulOffsetFixup + _pBaseOffset; } // Convert a pointer to a pointer offset. TBL_OFF PointerToOffset(PVOID pBuf) { return (BYTE *) pBuf - _pBaseOffset + _ulOffsetFixup; } // Set base address for OffsetToPointer and PointerToOffset void SetBase(BYTE* pbBase) { _pBaseOffset = pbBase; } // Return size of fixed data records size_t FixedWidth() const { return _cbFixed; } // Return the size of the reserved area size_t GetReservedSize() const { return _cbReserved; } // Return the base address of the reserved area BYTE* BufferAddr() const { return _pBufferAddr; } protected: size_t _FreeSpace() { return (size_t)(_pVarBuf - _pFixedBuf); } BYTE* _pBufferAddr; // Buffer base BYTE* _pBaseOffset; // Buffer base for offset calculations ULONG_PTR _ulOffsetFixup; // Client's version of the buffer base BYTE* _pFixedBuf; // Buffer pointer for next fixed alloc. BYTE* _pVarBuf; // Buffer pointer for next variable alloc. size_t _cbFixed; // Size of fixed allocations size_t _cbReserved; // Size of reserved area }; //+------------------------------------------------------------------------- // // Member: CFixedVarBufferAllocator::AllocFixed, inline // // Synopsis: Allocates a piece of fixed data out of a memory buffer // // Effects: updates _pFixedBuf // // Arguments: [cbNeeded] - number of byes of memory needed // // Returns: PVOID - a pointer to the allocated memory // // Signals: Throws STATUS_BUFFER_TOO_SMALL if not enough memory // is available. // //-------------------------------------------------------------------------- inline PVOID CFixedVarBufferAllocator::AllocFixed() { BYTE* pRetBuf = _pFixedBuf; if (_cbFixed <= _FreeSpace()) _pFixedBuf += _cbFixed; else QUIETTHROW( CException( STATUS_BUFFER_TOO_SMALL ) ); return pRetBuf; } //+------------------------------------------------------------------------- // // Member: CFixedVarBufferAllocator::Allocate, inline // // Synopsis: Allocates a piece of data out of a memory buffer // // Effects: updates _pVarBuf // // Arguments: [cbNeeded] - number of byes of memory needed // // Returns: PVOID - a pointer to the allocated memory // // Signals: Throws STATUS_BUFFER_TOO_SMALL if not enough memory // is available. // //-------------------------------------------------------------------------- inline PVOID CFixedVarBufferAllocator::Allocate( ULONG cbNeeded ) { // align to 8 byte boundry -- a little wasteful? cbNeeded = _RoundUpToChunk( cbNeeded, cbTblAllocAlignment ); if (cbNeeded <= _FreeSpace()) _pVarBuf -= cbNeeded; else QUIETTHROW( CException( STATUS_BUFFER_TOO_SMALL ) ); return _pVarBuf; } //+------------------------------------------------------------------------- // // Class: CWindowDataAllocator // // Purpose: Data allocator for window variable data // // Interface: PVarAllocator // // Notes: This allocator manages a pool of pages as a simple // heap. // // Data allocated out of the variable data space will // be stored internally as offsets, so that the data may // be moved around without need for adjusting pointers. // //-------------------------------------------------------------------------- class CWindowDataAllocator: public PVarAllocator { friend class CFixedVarAllocator; // for access to CHeapHeader // // Heap header used to record sizes of allocated and // freed memory. // struct CHeapHeader { USHORT cbSize; // size of this memory block USHORT cbPrev; // size of preceeding mem. block // the size of this struct needs to be 8-byte aligned. ULONG _dummy; // Flags or'ed with cbSize or cbPrev enum EHeapFlags { HeapFree = 1, HeapEnd = 2, HEAP_FLAGS = 3 // HeapFree | HeapEnd }; USHORT Size(void) { return cbSize & ~HEAP_FLAGS; } BOOL IsFree(void) { return cbSize & HeapFree; } BOOL IsFirst(void) { return (cbPrev & HeapEnd) != 0; } BOOL IsLast(void) { return (cbSize & HeapEnd) != 0; } CHeapHeader* Next(void) { return IsLast() ? 0 : (CHeapHeader*) (((BYTE *) this) + Size()); } CHeapHeader* Prev(void) { return IsFirst() ? 0 : (CHeapHeader*) (((BYTE *) this) - (cbPrev & ~HEAP_FLAGS)); } }; // // Header used on each segment of arena to link them together // and provide for offset mapping. // struct CSegmentHeader { CSegmentHeader* pNextSegment; // linked list to other segments ULONG oBaseOffset; // assigned offset for first memory ULONG cbSize; ULONG cbFree; // Free space in segment }; public: CWindowDataAllocator (ULONG* pcbPageTracker = &_cbPageTracker) : _cbTotal( 0 ), _pBaseAddr( 0 ), _pcbPageUsed( pcbPageTracker ), _oNextOffset( sizeof (TBL_PAGE_SIGNATURE )) { } CWindowDataAllocator (PVOID pBuf, size_t cbBuf, CHeapHeader* pHeap = NULL, ULONG* pcbPageTracker = &_cbPageTracker ) : _cbTotal( 0 ), _pBaseAddr( 0 ), _pcbPageUsed( pcbPageTracker ), _oNextOffset( sizeof (TBL_PAGE_SIGNATURE )) { _SetArena(pBuf, cbBuf, pHeap, sizeof(TBL_PAGE_SIGNATURE)); } ~CWindowDataAllocator(); // Allocate a piece of memory. PVOID Allocate(ULONG cbNeeded); // Free a previously allocated piece of memory. void Free(PVOID pMem); // Return whether OffsetToPointer has non-null effect BOOL IsBasedMemory(void) { return TRUE; } // Convert a pointer offset to a pointer. PVOID OffsetToPointer(TBL_OFF oBuf); // Convert a pointer to a pointer offset. TBL_OFF PointerToOffset(PVOID pBuf); // Set base address for OffsetToPointer and PointerToOffset void SetBase(BYTE* pbBase); #if CIDBG void WalkHeap(void (pfnReport)(PVOID pb, USHORT cb, USHORT f)); #endif private: // Set the limits of the allocation area. void _SetArena( PVOID pBufBase, size_t cbBuf, CHeapHeader* pHeap = NULL, ULONG oBuf = 0); PVOID _SplitHeap(CSegmentHeader* pSegHdr, CHeapHeader* pThisHeap, size_t cbNeeded); CSegmentHeader* _pBaseAddr; // pointer to first segment in arena ULONG _oNextOffset; // next offset value to be assigned size_t _cbTotal; // total size of allocation area ULONG* _pcbPageUsed; // page usage tracking variable static ULONG _cbPageTracker; // default page tracker }; //+------------------------------------------------------------------------- // // Class: CFixedVarAllocator // // Purpose: The ultimate data allocator. Supports fixed and // variable memory allocations from the same segment. // Automatically grows both fixed and variable allocations // automatically. // When memory usage grows beyond 1 page, fixed and // variable allocations will be taken from separate // segments. // //-------------------------------------------------------------------------- class CFixedVarAllocator: public PFixedVarAllocator { public: CFixedVarAllocator (BOOL fGrowInitially, BOOL fVarOffsets, size_t cbDataWidth, size_t cbReserved = 0) : _fVarOffsets( fVarOffsets ), _pbBaseAddr( NULL ), _pbLastAddrPlusOne( NULL ), _pbBuf( NULL ), _cbBuf( 0 ), _cbReserved( cbReserved ), _cbDataWidth( cbDataWidth ), _VarAllocator( NULL ), _cbPageUsed( 0 ), _cbTotal( 0 ), _fDidReInit( FALSE ) { if ( fGrowInitially ) _GrowData(); } ~CFixedVarAllocator ( ); void ReInit( BOOL fVarOffsets, size_t cbReserved, void * pvBuf, ULONG cbBuf ) { Win4Assert( ! _fDidReInit ); Win4Assert( 0 == _pbBaseAddr ); Win4Assert( 0 == _VarAllocator ); _cbPageUsed = cbBuf; _cbReserved = cbReserved; _fVarOffsets = fVarOffsets; _pbBaseAddr = (BYTE *) pvBuf; _pbLastAddrPlusOne = _pbBaseAddr + _cbBuf; _fDidReInit = TRUE; } // Allocate a piece of fixed memory. PVOID AllocFixed() { Win4Assert( ! _fDidReInit ); BYTE* pRetBuf; if (_pbBaseAddr == NULL) _GrowData(); if ( _cbDataWidth <= FreeSpace() ) { pRetBuf = _pbBuf; _pbBuf += _cbDataWidth; return pRetBuf; } if (_cbDataWidth > FreeSpace() ) { if (_cbTotal != _cbBuf) _SplitData(TRUE); else _GrowData(); } if ( _cbDataWidth <= FreeSpace() ) { pRetBuf = _pbBuf; _pbBuf += _cbDataWidth; } else { Win4Assert( !"unexpected out of memory" ); } return pRetBuf; } // Return the size of the allocation unit size_t FixedWidth() const { return _cbDataWidth; } // Return the base address of the reserved area BYTE* BufferAddr() const { return _pbBaseAddr; } // Return the size of the reserved area size_t GetReservedSize() const { return _cbReserved; } // Convert an offset to a pointer BYTE* FixedPointer( TBL_OFF obData ) const { return _pbBaseAddr+obData; } // Convert an offset to a pointer TBL_OFF FixedOffset( BYTE* pbData ) const { return pbData - _pbBaseAddr; } // Return the base address of the allocation area BYTE* FirstRow() const { return _pbBaseAddr + _cbReserved; } // Set the size of the allocation unit VOID SetRowSize(size_t cbDataWidth) { _cbDataWidth = cbDataWidth; } // Set the size of the reserved area VOID SetReservedSize(size_t cbReserved) { _cbReserved = cbReserved; if (_pbBaseAddr == NULL) _pbBuf = _pbBaseAddr + cbReserved; } // Resize the fixed portion of the buffer void ResizeAndInitFixed( size_t cbDataWidth, ULONG cItems ); // Get a buffer for the specified number of fixed size allocations void * AllocMultipleFixed( ULONG cItems ); // Allocate a piece of variable memory. PVOID Allocate(ULONG cbNeeded); // Free a previously allocated piece of memory. void Free(PVOID pMem); // Return whether OffsetToPointer has non-null effect BOOL IsBasedMemory(void) { return _fVarOffsets; } // Set base address for OffsetToPointer and PointerToOffset void SetBase(BYTE* pbBase) { if (pbBase == 0) _fVarOffsets = FALSE; else Win4Assert( !"CFixedVarAllocator::SetBase not supported" ); } // Convert a pointer offset to a pointer. PVOID OffsetToPointer(TBL_OFF oBuf) { if (_fVarOffsets) { if (_VarAllocator) return _VarAllocator->OffsetToPointer(oBuf); else if ( _fDidReInit ) return _pbBaseAddr + oBuf; else return _pbBaseAddr + oBuf - sizeof (TBL_PAGE_SIGNATURE); } else { return (PVOID) oBuf; } } // Convert a pointer to a pointer offset. Bias it so the offset // adds easily with a page address. TBL_OFF PointerToOffset(PVOID pBuf) { if (_fVarOffsets) { if (_VarAllocator) return _VarAllocator->PointerToOffset(pBuf); else if ( _fDidReInit ) return (BYTE *) pBuf - _pbBaseAddr; else return ((BYTE *)pBuf - _pbBaseAddr) + sizeof (TBL_PAGE_SIGNATURE); } else { return (TBL_OFF) pBuf; } } // Return free space in buffer size_t FreeSpace() const { return (_cbBuf - (UINT)(_pbBuf - _pbBaseAddr)); } // Return total memory used ULONG MemUsed( void ) { return _cbPageUsed; } #if CIDBG CWindowDataAllocator* VarAllocator(void) { return _VarAllocator; } #endif #ifdef CIEXTMODE void CiExtDump(void *ciExtSelf); #endif protected: size_t _cbTotal; // Total allocated size of _pbBaseAddr ULONG _cbPageUsed; // Total memory used private: void _GrowData(size_t cbNeeded = 0); void _SplitData(BOOL fMoveFixedData); CWindowDataAllocator* _VarAllocator; // Var data allocator after split BOOL _fVarOffsets; // offsets != pointers for var allocations BOOL _fDidReInit; // hence the allocator is read-only and // has one buffer for fixed & variable data BYTE* _pbBaseAddr; // Bias for addresses BYTE* _pbLastAddrPlusOne; // First byte past allocated memory BYTE* _pbBuf; // Buffer base size_t _cbBuf; // Available space in _pbBaseAddr size_t _cbDataWidth; // Size of each allocated piece of memory size_t _cbReserved; // Size of reserved area }; class CFixedVarTableWindowAllocator: public PFixedVarAllocator { enum { cbFixedIncrement = 4096, cbVariableIncrement = 8192 }; public: CFixedVarTableWindowAllocator() : _cbFixedSize(0), _cbFixedUsed(0), _iCurVar(0), _cbVarUsed(0) {} ~CFixedVarTableWindowAllocator() { for ( unsigned i = 0; i < _aVariableBufs.Count(); i++ ) delete _aVariableBufs[i]; } PVOID AllocFixed() { Win4Assert( _cbFixedSize <= cbFixedIncrement ); if ( ( _cbFixedSize + _cbFixedUsed ) > _aFixed.Count() ) _aFixed.ReSize( _aFixed.Count() + cbFixedIncrement ); void *pv = _aFixed.Get() + _cbFixedUsed; _cbFixedUsed += _cbFixedSize; return pv; } size_t FixedWidth() const { return _cbFixedSize; } BYTE * BufferAddr() const { return _aFixed.Get(); } size_t GetReservedSize() const { return 0; } void ReInit( BOOL fVarOffsets, size_t cbReserved, void * pvBuf, ULONG cbBuf ) { Win4Assert(!"Never called"); } PVOID Allocate(ULONG cbNeeded) { cbNeeded = _RoundUpToChunk( cbNeeded, sizeof( double ) ); Win4Assert( cbNeeded <= cbVariableIncrement ); if ( 0 == _aVariableBufs.Count() ) { XArray xTmp( cbVariableIncrement ); _aVariableBufs[0] = xTmp.Get(); xTmp.Acquire(); } else if ( ( cbNeeded + _cbVarUsed ) > cbVariableIncrement ) { _iCurVar = _aVariableBufs.Count(); XArray xTmp( cbVariableIncrement ); _aVariableBufs[_aVariableBufs.Count()] = xTmp.Get(); xTmp.Acquire(); _cbVarUsed = 0; } void * pv = _aVariableBufs[_iCurVar] + _cbVarUsed; _cbVarUsed += cbNeeded; return pv; } void Free(PVOID pMem) { Win4Assert( !"not called" ); } BOOL IsBasedMemory(void) { return FALSE; } PVOID OffsetToPointer(TBL_OFF oBuf) { return (PVOID) oBuf; } TBL_OFF PointerToOffset(PVOID pBuf) { return (TBL_OFF) pBuf; } void SetBase(BYTE* pbBase) {} VOID SetRowSize(size_t cbDataWidth) { _cbFixedSize = cbDataWidth; } BYTE * FixedPointer( TBL_OFF obData ) { return _aFixed.Get()+obData; } TBL_OFF FixedOffset( BYTE* pbData ) { return pbData - _aFixed.Get(); } ULONG MemUsed() { return _cbFixedUsed + _aVariableBufs.Count() * cbVariableIncrement; } BYTE * FirstRow() const { return _aFixed.Get(); } private: size_t _cbFixedSize; size_t _cbFixedUsed; XArray _aFixed; unsigned _iCurVar; unsigned _cbVarUsed; CDynArrayInPlace _aVariableBufs; };