911 lines
20 KiB
C++
911 lines
20 KiB
C++
// 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__
|