/* * B U F F E R . H * * Data buffer processing * * Copyright 1986-1997 Microsoft Corporation, All Rights Reserved */ #ifndef _EX_BUFFER_H_ #define _EX_BUFFER_H_ // Alignment macros ---------------------------------------------------------- // #include // Safe allocators ----------------------------------------------------------- // #include // Stack buffers ------------------------------------------------------------- // #include // StringSize usage ---------------------------------------------------------- // // CbStringSize is the size of the string without the NULL termination // CbStringSizeNull is the size of the string with the NULL termination // CchStringLength is the length of the string without the NULL termination // CchzStringLength is the length of the string with the NULL termination // template inline int WINAPI CbStringSize(const X* const pszText) { int cch; Assert (pszText); cch = (sizeof(X) == sizeof(WCHAR)) ? wcslen(reinterpret_cast(pszText)) : strlen(reinterpret_cast(pszText)); return cch * sizeof(X); } template inline int WINAPI CbStringSizeNull(const X* const pszText) { int cch; Assert (pszText); cch = (sizeof(X) == sizeof(WCHAR)) ? wcslen(reinterpret_cast(pszText)) : strlen(reinterpret_cast(pszText)); return (cch + 1) * sizeof(X); } template inline int WINAPI CchStringLength(const X* const pszText) { int cch; Assert (pszText); cch = (sizeof(X) == sizeof(WCHAR)) ? wcslen(reinterpret_cast(pszText)) : strlen(reinterpret_cast(pszText)); return cch; } template inline int WINAPI CchzStringLength(const X* const pszText) { int cch; Assert (pszText); cch = (sizeof(X) == sizeof(WCHAR)) ? wcslen(reinterpret_cast(pszText)) : strlen(reinterpret_cast(pszText)); return cch + 1; } // StringBuffer vs ChainedStringBuffer usage --------------------------------- // // When should you use which one? // StringBuffer characteristics: // o Data stored in one contiguous memory block. // o Memory may be realloc'd. // o Only offsets (ib) to strings are returned. // ChainedStringBuffer characteristics: // o Memory not contiguous. Multiple chained buffers. // o Memory is never realloc'd. // o String pointers directly into chained buffers are returned. // Both have logarithmic allocation behavior (order log(n) alloc operations // will be done, where n is the max size of the data). This behavior is // governed by the m_cbChunkSize starting size and increments. // // StringBuffer template class ----------------------------------------------- // // A simple variable-size, demand paged buffer abstraction. // template class StringBuffer { T * m_pData; UINT m_cbAllocated; UINT m_cbUsed; UINT m_cbChunkSize; // Count of bytes to alloc (dynamic). enum { CHUNKSIZE_START = 64 }; // Default starting chunk size (in bytes). // Memory allocation mechanism ------------------------------------------- // UINT Alloc( UINT ibLoc, UINT cbAppend ) { // Grow the data buffer if necessary // if ( ibLoc + cbAppend > m_cbAllocated ) { T* pData; // Alloc the buffer. // UINT cbSize = max( m_cbChunkSize, cbAppend ); if (m_pData) { pData = static_cast (ExRealloc( m_pData, m_cbAllocated + cbSize )); } else { pData = static_cast (ExAlloc( m_cbAllocated + cbSize )); } // When we are in the context of the server, our allocators // can fail without throwing. Bubble the error out. // if (NULL == pData) return static_cast(-1); m_cbAllocated += cbSize; m_pData = pData; // Increase the chunk size, to get "logarithmic allocation behavior" // m_cbChunkSize *= 2; } return cbAppend; } // non-implemented operators // StringBuffer(const StringBuffer& ); StringBuffer& operator=(const StringBuffer& ); public: StringBuffer( ULONG cbChunkSize = CHUNKSIZE_START ) : m_pData(NULL), m_cbAllocated(0), m_cbUsed(0), m_cbChunkSize(cbChunkSize) { } ~StringBuffer() { ExFree( m_pData ); } // There is no reason to make it constant on relinquish // we do not own the memory any more // T * relinquish() { T * tRet = m_pData; m_pData = NULL; m_cbUsed = 0; m_cbAllocated = 0; return tRet; } const T * PContents() const { return m_pData; } UINT CbSize() const { return m_cbUsed; } UINT CchSize() const { return m_cbUsed/sizeof(T); } VOID Reset() { m_cbUsed = 0; } // Counted type appends -------------------------------------------------- // UINT AppendAt( UINT ibLoc, UINT cbAppend, const T * pAppend) { UINT cb; // Ensure there is enough memory to hold what is needed // cb = Alloc( ibLoc, cbAppend ); // When we are in the context of the server, our allocators // can fail without throwing. Bubble the error out. // if (cb != cbAppend) return cb; // Append the data to the buffer // CopyMemory( reinterpret_cast(m_pData) + ibLoc, pAppend, cbAppend ); m_cbUsed = ibLoc + cbAppend; return cbAppend; } UINT Append( UINT cbAppend, const T * pAppend ) { return AppendAt( CbSize(), cbAppend, pAppend ); } // Uncounted appends ----------------------------------------------------- // UINT AppendAt( UINT ibLoc, const T * const pszText ) { return AppendAt( ibLoc, CbStringSize(pszText), pszText ); } UINT Append( const T * const pszText ) { return AppendAt( CbSize(), CbStringSize(pszText), pszText ); } BOOL FAppend( const T * const pszText ) { if (AppendAt( CbSize(), CbStringSize(pszText), pszText ) == static_cast(-1)) { return FALSE; } return TRUE; } BOOL FTerminate() { T ch = 0; if (AppendAt(CbSize(), sizeof(T), &ch) == static_cast(-1)) return FALSE; return TRUE; } }; // ChainedBuffer template class ----------------------------------------- // // A variable-size, demand paged, non-realloc-ing buffer pool abstraction. // When would you use this guy? When you need to allocate heap memory for // many small data items and would rather do it in sizeable chunks rather // than allocate each small data item individually. You need the data to // to stay because you are going to point to it (no reallocs are allowed // under your feet). // // NOTE: Caller is required to allocate items to be properly aligned if // it the item being allocated is an element that requires a specific // alignment (ie. struct's). // template class ChainedBuffer { // CHAINBUF -- Hungarian hb // struct CHAINBUF { CHAINBUF * phbNext; UINT cbAllocated; UINT cbUsed; BYTE * pData; }; CHAINBUF * m_phbData; // The data. CHAINBUF * m_phbCurrent; // The current buffer for appends. UINT m_cbChunkSizeInit; // Initial value of m_cbChunkSize UINT m_cbChunkSize; // Count of bytes to alloc (dynamic). // Alignments // UINT m_uAlign; // Destruction function // void FreeChainBuf( CHAINBUF * phbBuf ) { while (phbBuf) { CHAINBUF * phbNext = phbBuf->phbNext; ExFree(phbBuf); phbBuf = phbNext; } } protected: enum { CHUNKSIZE_START = 64 }; // Default starting chunk size (in bytes). public: ChainedBuffer( ULONG cbChunkSize = CHUNKSIZE_START, UINT uAlign = ALIGN_NATURAL) : m_phbData(NULL), m_phbCurrent(NULL), m_cbChunkSizeInit(cbChunkSize), m_cbChunkSize(cbChunkSize), m_uAlign(uAlign) { } ~ChainedBuffer() { FreeChainBuf( m_phbData ); } // Alloc a fixed size buffer --------------------------------------- // T * Alloc( UINT cbAlloc ) { BYTE * pbAdd; // So that we don't do anything stupid.... Make sure we allocate // stuff aligned for the template-parameterized type 'T'. // cbAlloc = AlignN(cbAlloc, m_uAlign); // Add another data buffer if necessary. // // It's necessary if we don't have a buffer, or // if the current buffer doesn't have enough free space. // if ( ( !m_phbCurrent ) || ( m_phbCurrent->cbUsed + cbAlloc > m_phbCurrent->cbAllocated ) ) { // Alloc the new buffer. // UINT cbSize = max(m_cbChunkSize, cbAlloc); CHAINBUF * phbNew = static_cast (ExAlloc( cbSize + sizeof(CHAINBUF) )); // When we are in the context of the server, our allocators // can fail without throwing. Bubble the error out. // if (NULL == phbNew) return NULL; // Fill in the header fields. // phbNew->phbNext = NULL; phbNew->cbAllocated = cbSize; phbNew->cbUsed = 0; phbNew->pData = reinterpret_cast(phbNew) + sizeof(CHAINBUF); // Add the new buffer into the chain. // if ( !m_phbData ) { Assert(!m_phbCurrent); m_phbData = phbNew; } else { Assert(m_phbCurrent); phbNew->phbNext = m_phbCurrent->phbNext; m_phbCurrent->phbNext = phbNew; } // Use the new buffer (it is now the current buffer). // m_phbCurrent = phbNew; // Increase the chunk size, to get "logarithmic allocation behavior". // m_cbChunkSize *= 2; } Assert(m_phbCurrent); Assert(m_phbCurrent->pData); // Find the correct starting spot in the current buffer. // pbAdd = m_phbCurrent->pData + m_phbCurrent->cbUsed; // Update our count of bytes actually used. // m_phbCurrent->cbUsed += cbAlloc; // Return the alloced data's starting point to the caller. // return reinterpret_cast(pbAdd); } // Clear all buffers ----------------------------------------------------- // void Clear() { // // Clear out data from, but do not free, the buffers // in the chain. This allows a ChainedStringBuffer to be // reused without necessarily having to reallocate its // consituent buffers. // for ( CHAINBUF * phb = m_phbData; phb; phb = phb->phbNext ) phb->cbUsed = 0; // Free any nodes after the first, they do not get reused // as you might expect. // if ( m_phbCurrent ) { FreeChainBuf( m_phbCurrent->phbNext ); m_phbCurrent->phbNext = NULL; } // // Reset the current buffer to the first one // m_phbCurrent = m_phbData; // // Reset the chunk size to the initial chunk size // m_cbChunkSize = m_cbChunkSizeInit; } // Get the total size of the buffer --------------------------------------- // DWORD CbBufferSize() const { DWORD cbTotal = 0; for ( CHAINBUF * phb = m_phbData; phb; phb = phb->phbNext ) cbTotal += phb->cbUsed; return cbTotal; } // Dump the whole buffer contents into a contiguous buffer------------------ // DWORD Dump(T *tBuffer, DWORD cbSize) const { BYTE *pbBuffer = NULL; Assert(tBuffer); Assert(cbSize >= CbBufferSize()); pbBuffer = reinterpret_cast(tBuffer); // walk thru the list and dump all the contents // for ( CHAINBUF * phb = m_phbData; phb; phb = phb->phbNext ) { memcpy(pbBuffer, phb->pData, phb->cbUsed); pbBuffer += phb->cbUsed; } // return the actual size // return static_cast( (pbBuffer) - (reinterpret_cast(tBuffer)) ); } }; // ChainedStringBuffer template class ----------------------------------------- // // A variable-size, demand paged, non-realloc-ing string buffer pool abstraction. // Why would you use this guy instead of StringBuffer (above)? // If you want the strings to STAY, and you don't care about them being // in a contiguous block of memory. // NOTE: We still keep the data in order, it's just not all in one block. // // This template is only to be used for CHAR and WCHAR strings. // Use the ChainedBuffer template for other types. // template class ChainedStringBuffer : public ChainedBuffer { // non-implemented operators // ChainedStringBuffer(const ChainedStringBuffer& ); ChainedStringBuffer& operator=(const ChainedStringBuffer& ); public: // Declare constructor inline (for efficiency) but do not provide // a definition here. Definitions for the two template paramater // types that we support (CHAR and WCHAR) are provided explicitly // below. // inline ChainedStringBuffer( ULONG cbChunkSize = CHUNKSIZE_START ); // Counted append -------------------------------------------------- // T * Append( UINT cbAppend, const T * pAppend ) { T* pAdd; // Reserve the space // pAdd = Alloc( cbAppend ); // When we are in the context of the server, our allocators // can fail without throwing. Bubble the error out. // if (NULL == pAdd) return NULL; // Append the data to the current buffer. // CopyMemory( pAdd, pAppend, cbAppend ); // Return the data's starting point to the caller. // return pAdd; } // Uncounted append ------------------------------------------------------ // NOTE: The append does NOT count the trailing NULL of the string! // T * Append( const T * const pszText ) { return Append( CbStringSize(pszText), pszText ); } // Uncounted append with trailing NULL ----------------------------------- // T * AppendWithNull( const T * const pszText ) { return Append( CbStringSizeNull(pszText), pszText ); } }; // Specialized ChainedStringBuffer constructor for CHAR ---------------------- // // Pass ALIGN_NONE to the ChainedBuffer constructor because CHAR strings // do not require alignment. // // !!! DO NOT use ChainedStringBuffer for anything that must be aligned! // inline ChainedStringBuffer::ChainedStringBuffer( ULONG cbChunkSize ) : ChainedBuffer(cbChunkSize, ALIGN_NONE ) { } // Specialized ChainedStringBuffer constructor for WCHAR --------------------- // // Pass ALIGN_WORD to the ChainedBuffer constructor because WCHAR strings // require WORD alignment. // inline ChainedStringBuffer::ChainedStringBuffer( ULONG cbChunkSize ) : ChainedBuffer(cbChunkSize, ALIGN_WORD ) { } // LinkedBuffer template class ----------------------------------------------- // // A variable-size, demand paged, non-realloc-ing buffer pool abstraction. // When would you use this guy? When you need to allocate heap memory for // many small data items and would rather do it in sizeable chunks rather // than allocate each small data item individually and the resulting pointer // you need to pass into the store needs to be "linked". // // IMPORTANT: // // Linked allocation mechanism is stolen from \store\src\_util\mdbmig.cxx // and needs to always match that mechanism. // PVOID ExAllocLinked(LPVOID pvLinked, UINT cb); VOID ExFreeLinked(LPVOID* ppv); template class LinkedBuffer { PVOID m_pvHead; PVOID PvAllocLinked(UINT cb) { PVOID pv = ExAllocLinked(m_pvHead, cb); if (NULL == m_pvHead) m_pvHead = pv; return pv; } public: LinkedBuffer() : m_pvHead(NULL) { } ~LinkedBuffer() { if (m_pvHead) ExFreeLinked(&m_pvHead); } // Alloc a fixed size buffer --------------------------------------- // T * Alloc( UINT cbAlloc ) { return reinterpret_cast(PvAllocLinked (cbAlloc)); } PVOID PvTop() { Assert (m_pvHead); return m_pvHead; } PVOID relinquish() { PVOID pv = m_pvHead; m_pvHead = NULL; return pv; } void clear() { if (m_pvHead) { ExFreeLinked(&m_pvHead); m_pvHead = NULL; } } void takeover ( LinkedBuffer & lnkbufOldOwner ) { m_pvHead = lnkbufOldOwner.m_pvHead; lnkbufOldOwner.m_pvHead = NULL; } }; #endif // _EX_BUFFER_H_