Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

910 lines
20 KiB

// This is a part of the Active Template Library.
// Copyright (C) 1996-2001 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Active Template Library Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Active Template Library product.
#ifndef __ATLSIMPSTR_H__
#define __ATLSIMPSTR_H__
#pragma once
#include <atldef.h>
#include <atlbase.h>
#include <atlexcept.h>
#include <atlmem.h>
namespace ATL
{
struct CStringData;
__interface IAtlStringMgr
{
public:
// Allocate a new CStringData
CStringData* Allocate( int nAllocLength, int nCharSize ) throw();
// Free an existing CStringData
void Free( CStringData* pData ) throw();
// Change the size of an existing CStringData
CStringData* Reallocate( CStringData* pData, int nAllocLength, int nCharSize ) throw();
// Get the CStringData for a Nil string
CStringData* GetNilString() throw();
IAtlStringMgr* Clone() throw();
};
#ifdef _M_IX86
#ifndef _M_CEE
extern "C"
{
LONG _InterlockedIncrement( LONG* pn );
LONG _InterlockedDecrement( LONG* pn );
};
#pragma intrinsic( _InterlockedIncrement )
#pragma intrinsic( _InterlockedDecrement )
#else
#define _InterlockedIncrement InterlockedIncrement
#define _InterlockedDecrement InterlockedDecrement
#endif // !_M_CEE
#endif // _M_IX86_
struct CStringData
{
IAtlStringMgr* pStringMgr; // String manager for this CStringData
int nDataLength; // Length of currently used data in XCHARs (not including terminating null)
int nAllocLength; // Length of allocated data in XCHARs (not including terminating null)
long nRefs; // Reference count: negative == locked
// XCHAR data[nAllocLength+1] // A CStringData is always followed in memory by the actual array of character data
void* data() throw()
{
return (this+1);
}
void AddRef() throw()
{
ATLASSERT(nRefs > 0);
_InterlockedIncrement(&nRefs);
}
bool IsLocked() const throw()
{
return nRefs < 0;
}
bool IsShared() const throw()
{
return( nRefs > 1 );
}
void Lock() throw()
{
ATLASSERT( nRefs <= 1 );
nRefs--; // Locked buffers can't be shared, so no interlocked operation necessary
if( nRefs == 0 )
{
nRefs = -1;
}
}
void Release() throw()
{
ATLASSERT( nRefs != 0 );
if( _InterlockedDecrement( &nRefs ) <= 0 )
{
pStringMgr->Free( this );
}
}
void Unlock() throw()
{
ATLASSERT( IsLocked() );
nRefs++; // Locked buffers can't be shared, so no interlocked operation necessary
if( nRefs == 0 )
{
nRefs = 1;
}
}
};
class CNilStringData :
public CStringData
{
public:
CNilStringData() throw()
{
pStringMgr = NULL;
nRefs = 2; // Never gets freed by IAtlStringMgr
nDataLength = 0;
nAllocLength = 0;
achNil[0] = 0;
achNil[1] = 0;
}
void SetManager( IAtlStringMgr* pMgr ) throw()
{
ATLASSERT( pStringMgr == NULL );
pStringMgr = pMgr;
}
public:
wchar_t achNil[2];
};
class CAtlStringMgr :
public IAtlStringMgr
{
public:
CAtlStringMgr( IAtlMemMgr* pMemMgr = NULL ) throw() :
m_pMemMgr( pMemMgr )
{
m_nil.SetManager( this );
}
~CAtlStringMgr() throw()
{
}
void SetMemoryManager( IAtlMemMgr* pMemMgr ) throw()
{
ATLASSERT( m_pMemMgr == NULL );
m_pMemMgr = pMemMgr;
}
// IAtlStringMgr
public:
virtual CStringData* Allocate( int nChars, int nCharSize ) throw()
{
size_t nTotalSize;
CStringData* pData;
size_t nDataBytes;
nChars = AtlAlignUp( nChars + 1, 8 ); // Prevent excessive reallocation. The heap will usually round up anyway.
nDataBytes = nChars*nCharSize;
nTotalSize = sizeof( CStringData )+nDataBytes;
pData = static_cast< CStringData* >( m_pMemMgr->Allocate( nTotalSize ) );
if( pData == NULL )
{
return( NULL );
}
pData->pStringMgr = this;
pData->nRefs = 1;
pData->nAllocLength = nChars - 1;
pData->nDataLength = 0;
return( pData );
}
virtual void Free( CStringData* pData ) throw()
{
ATLASSERT( pData->pStringMgr == this );
m_pMemMgr->Free( pData );
}
virtual CStringData* Reallocate( CStringData* pData, int nChars, int nCharSize ) throw()
{
CStringData* pNewData;
ULONG nTotalSize;
ULONG nDataBytes;
ATLASSERT( pData->pStringMgr == this );
nChars = AtlAlignUp( nChars+1, 8 ); // Prevent excessive reallocation. The heap will usually round up anyway.
nDataBytes = nChars*nCharSize;
nTotalSize = sizeof( CStringData )+nDataBytes;
pNewData = static_cast< CStringData* >( m_pMemMgr->Reallocate( pData, nTotalSize ) );
if( pNewData == NULL )
{
return NULL;
}
pNewData->nAllocLength = nChars - 1;
return pNewData;
}
virtual CStringData* GetNilString() throw()
{
m_nil.AddRef();
return &m_nil;
}
virtual IAtlStringMgr* Clone() throw()
{
return this;
}
protected:
IAtlMemMgr* m_pMemMgr;
CNilStringData m_nil;
};
template< typename BaseType, const int t_nSize >
class CStaticString
{
public:
CStaticString( const BaseType* psz ) :
m_psz( psz )
{
}
operator const BaseType*() const
{
return m_psz;
}
static int GetLength()
{
return (t_nSize/sizeof( BaseType ))-1;
}
private:
const BaseType* m_psz;
private:
CStaticString( const CStaticString& str ) throw();
CStaticString& operator=( const CStaticString& str ) throw();
};
#define _ST( psz ) ATL::CStaticString< TCHAR, sizeof( _T( psz ) ) >( _T( psz ) )
#define _SA( psz ) ATL::CStaticString< char, sizeof( psz ) >( psz )
#define _SW( psz ) ATL::CStaticString< wchar_t, sizeof( L##psz ) >( L##psz )
#define _SO( psz ) _SW( psz )
template< typename BaseType = char >
class ChTraitsBase
{
public:
typedef char XCHAR;
typedef LPSTR PXSTR;
typedef LPCSTR PCXSTR;
typedef wchar_t YCHAR;
typedef LPWSTR PYSTR;
typedef LPCWSTR PCYSTR;
};
template<>
class ChTraitsBase< wchar_t >
{
public:
typedef wchar_t XCHAR;
typedef LPWSTR PXSTR;
typedef LPCWSTR PCXSTR;
typedef char YCHAR;
typedef LPSTR PYSTR;
typedef LPCSTR PCYSTR;
};
template< typename BaseType >
class CSimpleStringT
{
public:
typedef ChTraitsBase< BaseType >::XCHAR XCHAR;
typedef ChTraitsBase< BaseType >::PXSTR PXSTR;
typedef ChTraitsBase< BaseType >::PCXSTR PCXSTR;
typedef ChTraitsBase< BaseType >::YCHAR YCHAR;
typedef ChTraitsBase< BaseType >::PYSTR PYSTR;
typedef ChTraitsBase< BaseType >::PCYSTR PCYSTR;
public:
explicit CSimpleStringT( IAtlStringMgr* pStringMgr ) throw()
{
ATLASSERT( pStringMgr != NULL );
CStringData* pData = pStringMgr->GetNilString();
Attach( pData );
}
CSimpleStringT( const CSimpleStringT& strSrc )
{
CStringData* pSrcData = strSrc.GetData();
CStringData* pNewData = CloneData( pSrcData );
Attach( pNewData );
}
CSimpleStringT( PCXSTR pszSrc, IAtlStringMgr* pStringMgr )
{
ATLASSERT( pStringMgr != NULL );
int nLength = StringLength( pszSrc );
CStringData* pData = pStringMgr->Allocate( nLength, sizeof( XCHAR ) );
if( pData == NULL )
{
ThrowMemoryException();
}
Attach( pData );
SetLength( nLength );
CopyChars( m_pszData, pszSrc, nLength );
}
CSimpleStringT( const XCHAR* pchSrc, int nLength, IAtlStringMgr* pStringMgr )
{
ATLASSERT( pStringMgr != NULL );
CStringData* pData = pStringMgr->Allocate( nLength, sizeof( XCHAR ) );
if( pData == NULL )
{
ThrowMemoryException();
}
Attach( pData );
SetLength( nLength );
CopyChars( m_pszData, pchSrc, nLength );
}
~CSimpleStringT() throw()
{
CStringData* pData = GetData();
pData->Release();
}
CSimpleStringT& operator=( const CSimpleStringT& strSrc )
{
CStringData* pSrcData = strSrc.GetData();
CStringData* pOldData = GetData();
if( pSrcData != pOldData )
{
if( pOldData->IsLocked() )
{
SetString( strSrc.GetString(), strSrc.GetLength() );
}
else
{
CStringData* pNewData = CloneData( pSrcData );
pOldData->Release();
Attach( pNewData );
}
}
return( *this );
}
CSimpleStringT& operator=( PCXSTR pszSrc )
{
SetString( pszSrc );
return( *this );
}
CSimpleStringT& operator+=( const CSimpleStringT& strSrc )
{
Append( strSrc );
return( *this );
}
CSimpleStringT& operator+=( PCXSTR pszSrc )
{
Append( pszSrc );
return( *this );
}
template< int t_nSize >
CSimpleStringT& operator+=( const CStaticString< XCHAR, t_nSize >& strSrc )
{
Append( strSrc.m_psz, strSrc.GetLength() );
return( *this );
}
CSimpleStringT& operator+=( char ch )
{
XCHAR chTemp = XCHAR( ch );
Append( &chTemp, 1 );
return( *this );
}
CSimpleStringT& operator+=( unsigned char ch )
{
XCHAR chTemp = XCHAR( ch );
Append( &chTemp, 1 );
return( *this );
}
CSimpleStringT& operator+=( wchar_t ch )
{
XCHAR chTemp = XCHAR( ch );
Append( &chTemp, 1 );
return( *this );
}
XCHAR operator[]( int iChar ) const throw()
{
ATLASSERT( (iChar >= 0) && (iChar <= GetLength()) ); // Indexing the '\0' is OK
return( m_pszData[iChar] );
}
operator PCXSTR() const throw()
{
return( m_pszData );
}
void Append( PCXSTR pszSrc )
{
Append( pszSrc, StringLength( pszSrc ) );
}
void Append( PCXSTR pszSrc, int nLength )
{
// See comment in SetString() about why we do this
UINT_PTR nOffset = pszSrc-GetString();
UINT nOldLength = GetLength();
int nNewLength = nOldLength+nLength;
PXSTR pszBuffer = GetBuffer( nNewLength );
if( nOffset <= nOldLength )
{
pszSrc = pszBuffer+nOffset;
// No need to call CopyCharsOverlapped, since the destination is
// beyond the end of the original buffer
}
CopyChars( pszBuffer+nOldLength, pszSrc, nLength );
ReleaseBuffer( nNewLength );
}
void Append( const CSimpleStringT& strSrc )
{
Append( strSrc.GetString(), strSrc.GetLength() );
}
void Empty() throw()
{
CStringData* pOldData = GetData();
IAtlStringMgr* pStringMgr = pOldData->pStringMgr;
if( pOldData->nDataLength == 0 )
{
return;
}
if( pOldData->IsLocked() )
{
// Don't reallocate a locked buffer that's shrinking
SetLength( 0 );
}
else
{
pOldData->Release();
CStringData* pNewData = pStringMgr->GetNilString();
Attach( pNewData );
}
}
void FreeExtra() throw()
{
CStringData* pOldData = GetData();
int nLength = pOldData->nDataLength;
IAtlStringMgr* pStringMgr = pOldData->pStringMgr;
if( pOldData->nAllocLength == nLength )
{
return;
}
if( !pOldData->IsLocked() ) // Don't reallocate a locked buffer that's shrinking
{
CStringData* pNewData = pStringMgr->Allocate( nLength, sizeof( XCHAR ) );
if( pNewData == NULL )
{
SetLength( nLength );
return;
}
CopyChars( PXSTR( pNewData->data() ), PCXSTR( pOldData->data() ), nLength );
pOldData->Release();
Attach( pNewData );
SetLength( nLength );
}
}
int GetAllocLength() const throw()
{
return( GetData()->nAllocLength );
}
XCHAR GetAt( int iChar ) const throw()
{
ATLASSERT( (iChar >= 0) && (iChar <= GetLength()) ); // Indexing the '\0' is OK
return( m_pszData[iChar] );
}
PXSTR GetBuffer()
{
CStringData* pData = GetData();
if( pData->IsShared() )
{
Fork( pData->nDataLength );
}
return( m_pszData );
}
PXSTR GetBuffer( int nMinBufferLength )
{
return( PrepareWrite( nMinBufferLength ) );
}
PXSTR GetBufferSetLength( int nLength )
{
PXSTR pszBuffer = GetBuffer( nLength );
SetLength( nLength );
return( pszBuffer );
}
int GetLength() const throw()
{
return( GetData()->nDataLength );
}
IAtlStringMgr* GetManager() const throw()
{
return( GetData()->pStringMgr->Clone() );
}
PCXSTR GetString() const throw()
{
return( m_pszData );
}
bool IsEmpty() const throw()
{
return( GetLength() == 0 );
}
PXSTR LockBuffer()
{
CStringData* pData = GetData();
if( pData->IsShared() )
{
Fork( pData->nDataLength );
pData = GetData(); // Do it again, because the fork might have changed it
}
pData->Lock();
return( m_pszData );
}
void UnlockBuffer() throw()
{
CStringData* pData = GetData();
pData->Unlock();
}
void Preallocate( int nLength )
{
PrepareWrite( nLength );
}
void ReleaseBuffer( int nNewLength = -1 ) throw()
{
if( nNewLength == -1 )
{
nNewLength = StringLength( m_pszData );
}
SetLength( nNewLength );
}
void Truncate( int nNewLength )
{
ATLASSERT( nNewLength <= GetLength() );
GetBuffer( nNewLength );
ReleaseBuffer( nNewLength );
}
void SetAt( int iChar, XCHAR ch )
{
ATLASSERT( (iChar >= 0) && (iChar < GetLength()) );
int nLength = GetLength();
PXSTR pszBuffer = GetBuffer();
pszBuffer[iChar] = ch;
ReleaseBuffer( nLength );
}
void SetManager( IAtlStringMgr* pStringMgr )
{
ATLASSERT( IsEmpty() );
CStringData* pData = GetData();
pData->Release();
pData = pStringMgr->GetNilString();
Attach( pData );
}
void SetString( PCXSTR pszSrc )
{
SetString( pszSrc, StringLength( pszSrc ) );
}
void SetString( PCXSTR pszSrc, int nLength )
{
if( nLength == 0 )
{
Empty();
}
else
{
// It is possible that pszSrc points to a location inside of our
// buffer. GetBuffer() might change m_pszData if (1) the buffer
// is shared or (2) the buffer is too small to hold the new
// string. We detect this aliasing, and modify pszSrc to point
// into the newly allocated buffer instead.
UINT nOldLength = GetLength();
UINT_PTR nOffset = pszSrc-GetString();
// If 0 <= nOffset <= nOldLength, then pszSrc points into our
// buffer
PXSTR pszBuffer = GetBuffer( nLength );
if( nOffset <= nOldLength )
{
CopyCharsOverlapped( pszBuffer, pszBuffer+nOffset, nLength );
}
else
{
CopyChars( pszBuffer, pszSrc, nLength );
}
ReleaseBuffer( nLength );
}
}
public:
friend CSimpleStringT operator+(
const CSimpleStringT& str1,
const CSimpleStringT& str2 )
{
CSimpleStringT s( str1.GetManager() );
Concatenate( s, str1, str1.GetLength(), str2, str2.GetLength() );
return( s );
}
friend CSimpleStringT operator+(
const CSimpleStringT& str1,
PCXSTR psz2 )
{
CSimpleStringT s( str1.GetManager() );
Concatenate( s, str1, str1.GetLength(), psz2, StringLength( psz2 ) );
return( s );
}
friend CSimpleStringT operator+(
PCXSTR psz1,
const CSimpleStringT& str2 )
{
CSimpleStringT s( str2.GetManager() );
Concatenate( s, psz1, StringLength( psz1 ), str2, str2.GetLength() );
return( s );
}
static void CopyChars( XCHAR* pchDest, const XCHAR* pchSrc, int nChars ) throw()
{
memcpy( pchDest, pchSrc, nChars*sizeof( XCHAR ) );
}
static void CopyCharsOverlapped( XCHAR* pchDest, const XCHAR* pchSrc, int nChars ) throw()
{
memmove( pchDest, pchSrc, nChars*sizeof( XCHAR ) );
}
#ifdef _ATL_MIN_CRT
ATL_NOINLINE static int StringLength( PCXSTR psz ) throw()
{
int nLength = 0;
if( psz != NULL )
{
const XCHAR* pch = psz;
while( *pch != 0 )
{
nLength++;
pch++;
}
}
return( nLength );
}
#else
static int StringLength( const char* psz ) throw()
{
if( psz == NULL )
{
return( 0 );
}
return( int( strlen( psz ) ) );
}
template<>
static int StringLength( const wchar_t* psz ) throw()
{
if( psz == NULL )
{
return( 0 );
}
return( int( wcslen( psz ) ) );
}
#endif
protected:
static void Concatenate( CSimpleStringT& strResult, PCXSTR psz1, int nLength1, PCXSTR psz2, int nLength2 )
{
int nNewLength = nLength1+nLength2;
PXSTR pszBuffer = strResult.GetBuffer( nNewLength );
CopyChars( pszBuffer, psz1, nLength1 );
CopyChars( pszBuffer+nLength1, psz2, nLength2 );
strResult.ReleaseBuffer( nNewLength );
}
ATL_NOINLINE static void ThrowMemoryException()
{
AtlThrow( E_OUTOFMEMORY );
}
// Implementation
private:
void Attach( CStringData* pData ) throw()
{
m_pszData = static_cast< PXSTR >( pData->data() );
}
ATL_NOINLINE void Fork( int nLength )
{
CStringData* pOldData = GetData();
int nOldLength = pOldData->nDataLength;
CStringData* pNewData = pOldData->pStringMgr->Clone()->Allocate( nLength, sizeof( XCHAR ) );
if( pNewData == NULL )
{
ThrowMemoryException();
}
int nCharsToCopy = ((nOldLength < nLength) ? nOldLength : nLength)+1; // Copy '\0'
CopyChars( PXSTR( pNewData->data() ), PCXSTR( pOldData->data() ), nCharsToCopy );
pNewData->nDataLength = nOldLength;
pOldData->Release();
Attach( pNewData );
}
CStringData* GetData() const throw()
{
return( reinterpret_cast< CStringData* >( m_pszData )-1 );
}
PXSTR PrepareWrite( int nLength )
{
CStringData* pOldData = GetData();
int nShared = 1-pOldData->nRefs; // nShared < 0 means true, >= 0 means false
int nTooShort = pOldData->nAllocLength-nLength; // nTooShort < 0 means true, >= 0 means false
if( (nShared|nTooShort) < 0 ) // If either sign bit is set (i.e. either is less than zero), we need to copy data
{
PrepareWrite2( nLength );
}
return( m_pszData );
}
ATL_NOINLINE void PrepareWrite2( int nLength )
{
CStringData* pOldData = GetData();
if( pOldData->nDataLength > nLength )
{
nLength = pOldData->nDataLength;
}
if( pOldData->IsShared() )
{
Fork( nLength );
}
else if( pOldData->nAllocLength < nLength )
{
// Grow exponentially, until we hit 1K.
int nNewLength = pOldData->nAllocLength;
if( nNewLength > 1024 )
{
nNewLength += 1024;
}
else
{
nNewLength *= 2;
}
if( nNewLength < nLength )
{
nNewLength = nLength;
}
Reallocate( nNewLength );
}
}
ATL_NOINLINE void Reallocate( int nLength )
{
CStringData* pOldData = GetData();
ATLASSERT( pOldData->nAllocLength < nLength );
IAtlStringMgr* pStringMgr = pOldData->pStringMgr;
CStringData* pNewData = pStringMgr->Reallocate( pOldData, nLength, sizeof( XCHAR ) );
if( pNewData == NULL )
{
ThrowMemoryException();
}
Attach( pNewData );
}
void SetLength( int nLength ) throw()
{
ATLASSERT( nLength >= 0 );
ATLASSERT( nLength <= GetData()->nAllocLength );
GetData()->nDataLength = nLength;
m_pszData[nLength] = 0;
}
static CStringData* CloneData( CStringData* pData )
{
CStringData* pNewData = NULL;
IAtlStringMgr* pNewStringMgr = pData->pStringMgr->Clone();
if( !pData->IsLocked() && (pNewStringMgr == pData->pStringMgr) )
{
pNewData = pData;
pNewData->AddRef();
}
else
{
pNewData = pNewStringMgr->Allocate( pData->nDataLength, sizeof( XCHAR ) );
if( pNewData == NULL )
{
ThrowMemoryException();
}
pNewData->nDataLength = pData->nDataLength;
CopyChars( PXSTR( pNewData->data() ), PCXSTR( pData->data() ), pData->nDataLength+1 ); // Copy '\0'
}
return( pNewData );
}
private:
PXSTR m_pszData;
};
template< typename TCharType >
class CStrBufT
{
public:
typedef CSimpleStringT< TCharType > StringType;
typedef StringType::XCHAR XCHAR;
typedef StringType::PXSTR PXSTR;
typedef StringType::PCXSTR PCXSTR;
static const DWORD AUTO_LENGTH = 0x01; // Automatically determine the new length of the string at release. The string must be null-terminated.
static const DWORD SET_LENGTH = 0x02; // Set the length of the string object at GetBuffer time
public:
explicit CStrBufT( StringType& str ) throw( ... ) :
m_str( str ),
m_pszBuffer( NULL ),
#ifdef _DEBUG
m_nBufferLength( str.GetLength() ),
#endif
m_nLength( str.GetLength() )
{
m_pszBuffer = m_str.GetBuffer();
}
CStrBufT( StringType& str, int nMinLength, DWORD dwFlags = AUTO_LENGTH ) throw( ... ) :
m_str( str ),
m_pszBuffer( NULL ),
#ifdef _DEBUG
m_nBufferLength( nMinLength ),
#endif
m_nLength( (dwFlags&AUTO_LENGTH) ? -1 : nMinLength )
{
if( dwFlags&SET_LENGTH )
{
m_pszBuffer = m_str.GetBufferSetLength( nMinLength );
}
else
{
m_pszBuffer = m_str.GetBuffer( nMinLength );
}
}
~CStrBufT() throw()
{
m_str.ReleaseBuffer( m_nLength );
}
operator PXSTR() throw()
{
return( m_pszBuffer );
}
operator PCXSTR() const throw()
{
return( m_pszBuffer );
}
void SetLength( int nLength ) throw()
{
ATLASSERT( nLength <= m_nBufferLength );
m_nLength = nLength;
}
// Implementation
private:
StringType& m_str;
PXSTR m_pszBuffer;
int m_nLength;
#ifdef _DEBUG
int m_nBufferLength;
#endif
// Private copy constructor and copy assignment operator to prevent accidental use
private:
CStrBufT( const CStrBufT& ) throw();
CStrBufT& operator=( const CStrBufT& ) throw();
};
typedef CSimpleStringT< TCHAR > CSimpleString;
typedef CSimpleStringT< char > CSimpleStringA;
typedef CSimpleStringT< wchar_t > CSimpleStringW;
typedef CStrBufT< TCHAR > CStrBuf;
typedef CStrBufT< char > CStrBufA;
typedef CStrBufT< wchar_t > CStrBufW;
}; // namespace ATL
#endif // __ATLSIMPSTR_H__