Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

633 lines
16 KiB

/*
* 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 <align.h>
// Safe allocators -----------------------------------------------------------
//
#include <ex\exmem.h>
// Stack buffers -------------------------------------------------------------
//
#include <ex\stackbuf.h>
// 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<class X>
inline int WINAPI CbStringSize(const X* const pszText)
{
int cch;
Assert (pszText);
cch = (sizeof(X) == sizeof(WCHAR))
? wcslen(reinterpret_cast<const WCHAR* const>(pszText))
: strlen(reinterpret_cast<const CHAR* const>(pszText));
return cch * sizeof(X);
}
template<class X>
inline int WINAPI CbStringSizeNull(const X* const pszText)
{
int cch;
Assert (pszText);
cch = (sizeof(X) == sizeof(WCHAR))
? wcslen(reinterpret_cast<const WCHAR* const>(pszText))
: strlen(reinterpret_cast<const CHAR* const>(pszText));
return (cch + 1) * sizeof(X);
}
template<class X>
inline int WINAPI CchStringLength(const X* const pszText)
{
int cch;
Assert (pszText);
cch = (sizeof(X) == sizeof(WCHAR))
? wcslen(reinterpret_cast<const WCHAR* const>(pszText))
: strlen(reinterpret_cast<const CHAR* const>(pszText));
return cch;
}
template<class X>
inline int WINAPI CchzStringLength(const X* const pszText)
{
int cch;
Assert (pszText);
cch = (sizeof(X) == sizeof(WCHAR))
? wcslen(reinterpret_cast<const WCHAR* const>(pszText))
: strlen(reinterpret_cast<const CHAR* const>(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 T>
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<T*>
(ExRealloc( m_pData, m_cbAllocated + cbSize ));
}
else
{
pData = static_cast<T*>
(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<UINT>(-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<LPBYTE>(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<T>(pszText), pszText );
}
UINT Append( const T * const pszText )
{
return AppendAt( CbSize(), CbStringSize<T>(pszText), pszText );
}
BOOL FAppend( const T * const pszText )
{
if (AppendAt( CbSize(), CbStringSize<T>(pszText), pszText ) ==
static_cast<UINT>(-1))
{
return FALSE;
}
return TRUE;
}
BOOL FTerminate()
{
T ch = 0;
if (AppendAt(CbSize(), sizeof(T), &ch) == static_cast<UINT>(-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 T>
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<CHAINBUF *>
(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<BYTE *>(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<T *>(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<PBYTE>(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<DWORD>( (pbBuffer) - (reinterpret_cast<PBYTE>(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 T>
class ChainedStringBuffer : public ChainedBuffer<T>
{
// 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<T>(pszText), pszText );
}
// Uncounted append with trailing NULL -----------------------------------
//
T * AppendWithNull( const T * const pszText )
{
return Append( CbStringSizeNull<T>(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<CHAR> for anything that must be aligned!
//
inline
ChainedStringBuffer<CHAR>::ChainedStringBuffer( ULONG cbChunkSize )
: ChainedBuffer<CHAR>(cbChunkSize, ALIGN_NONE )
{
}
// Specialized ChainedStringBuffer constructor for WCHAR ---------------------
//
// Pass ALIGN_WORD to the ChainedBuffer constructor because WCHAR strings
// require WORD alignment.
//
inline
ChainedStringBuffer<WCHAR>::ChainedStringBuffer( ULONG cbChunkSize )
: ChainedBuffer<WCHAR>(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 T>
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<T*>(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<T> & lnkbufOldOwner )
{
m_pvHead = lnkbufOldOwner.m_pvHead;
lnkbufOldOwner.m_pvHead = NULL;
}
};
#endif // _EX_BUFFER_H_