// 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 #include #include #include 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__