/////////////////////////////////////////////////////////////////////////////////////////
// Project: SP Library 1.2
//
// File: spDynamicString.h interface for the SP::CDynamicString class
// template.
//
// Developer(s): Sergei Pavlovsky
// sergei_vp@hotmail.com
//
// Description: SP::CDynamicString is a dynamic string. Preferably should be used
// for large strings.
//
// Platforms: Win32
//
/////////////////////////////////////////////////////////////////////////////////////////
#if !defined(__SP_SPDYNAMICSTRING_H__INCLUDED__)
#define __SP_SPDYNAMICSTRING_H__INCLUDED__
#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000
#include "spVirtualMemory.h"
/*
#if defined(_DEBUG) && defined(_MFC_VER)
#include <afx.h>
#endif // defined(_DEBUG) && defined(_MFC_VER)
*/
namespace SP
{
/////////////////////////////////////////////////////////////////////////////////////////
// CDynamicString class template
template < class t_trates, class t_memory >
class CDynamicString : public t_trates
{
// Types
public:
typedef t_memory TMemory;
typedef t_trates TTrates;
typedef typename t_trates::CTransStringTraits TSTrates;
typedef typename TTrates::CHAR_TYPE CHAR_TYPE;
typedef typename TSTrates::CHAR_TYPE SCHAR_TYPE;
typedef typename TTrates::SIZE_TYPE SIZE_TYPE;
typedef typename TTrates::INDEX_TYPE INDEX_TYPE;
typedef typename TTrates::LOCALEID_TYPE LOCALEID_TYPE;
// Constants
public:
enum
{
DEF_LOCALEID = TTrates::DEF_LOCALEID
};
// Construction
public:
CDynamicString()
{
}
CDynamicString(const CDynamicString& src)
{
if ( !_Assign(src) )
throw false;
}
CDynamicString(const CHAR_TYPE* lpcszSrc)
{
SIZE_TYPE cLength = TTrates::Length(lpcszSrc);
if ( !_Assign(lpcszSrc, cLength, cLength, cLength) )
throw false;
}
CDynamicString(const CHAR_TYPE* lpcSrc, SIZE_TYPE cLength)
{
if ( !_Assign(lpcSrc, cLength, cLength, cLength) )
throw false;
}
CDynamicString(const CHAR_TYPE* lpcSrc, SIZE_TYPE cLength, LOCALEID_TYPE unCP)
{
if ( !_Assign(lpcszSrc, cLength, cLength, cLength) )
throw false;
}
CDynamicString(const SCHAR_TYPE* lpcszSrc)
{
if ( !_Assign(lpcszSrc, TSTrates::Length(lpcszSrc), DEF_LOCALEID) )
throw false;
}
CDynamicString(const SCHAR_TYPE* lpcszSrc, SIZE_TYPE cLength)
{
if ( !_Assign(lpcszSrc, cLength, DEF_LOCALEID) )
throw false;
}
CDynamicString(const SCHAR_TYPE* lpcSrc, SIZE_TYPE cLength, LOCALEID_TYPE unCP)
{
if ( !_Assign(lpcSrc, cLength, unCP) )
throw false;
}
private:
CDynamicString(const CHAR_TYPE* lpcSrc1, SIZE_TYPE cLength1,
const CHAR_TYPE* lpcSrc2, SIZE_TYPE cLength2)
{
_ASSERTE( cLength1 >= 0 && cLength2 >= 0 );
_ASSERTE( !::IsBadReadPtr(lpcSrc1, sizeof(CHAR_TYPE) * cLength1) );
_ASSERTE( !::IsBadReadPtr(lpcSrc2, sizeof(CHAR_TYPE) * cLength2) );
SIZE_TYPE cCapacity = cLength1 + cLength2;
if ( !m_memBuffer.Allocate(LengthToSize(cCapacity),
LengthToSize(cCapacity)) )
throw false;
Assign(lpcSrc1, cLength1);
Append(lpcSrc2, cLength2);
}
// Operators
public:
CDynamicString& operator=(const CDynamicString& right)
{
if ( !Assign(right) )
throw false;
return *this;
}
CDynamicString& operator=(const CHAR_TYPE* lpcszSrc)
{
if ( !Assign(lpcszSrc) )
throw false;
return *this;
}
CDynamicString& operator=(const SCHAR_TYPE* lpcszSrc)
{
if ( !Assign(lpcszSrc) )
throw false;
return *this;
}
operator const CHAR_TYPE*() const
{
return GetBuffer();
}
operator CHAR_TYPE*()
{
return GetBuffer();
}
const CHAR_TYPE& operator[](INDEX_TYPE index) const
{
_ASSERTE( index >= 0 && (SIZE_TYPE)index <= GetCapacity() );
return GetBuffer()[index];
}
CHAR_TYPE& operator[](INDEX_TYPE index)
{
_ASSERTE( index >= 0 && (SIZE_TYPE)index <= GetCapacity() );
return GetBuffer()[index];
}
bool operator==(const CDynamicString& right) const
{
return TTrates::Compare(GetBuffer(), right.GetBuffer()) == 0;
}
bool operator==(const CHAR_TYPE* lpcszRight) const
{
return TTrates::Compare(GetBuffer(), lpcszRight) == 0;
}
bool operator!=(const CDynamicString& right) const
{
return TTrates::Compare(GetBuffer(), right.GetBuffer()) != 0;
}
bool operator!=(const CHAR_TYPE* lpcszRight) const
{
return TTrates::Compare(GetBuffer(), lpcszRight) != 0;
}
bool operator>(const CDynamicString& right) const
{
return TTrates::Compare(GetBuffer(), right.GetBuffer()) > 0;
}
bool operator>(const CHAR_TYPE* lpcszRight) const
{
return TTrates::Compare(GetBuffer(), lpcszRight) > 0;
}
bool operator<(const CDynamicString& right) const
{
return TTrates::Compare(GetBuffer(), right.GetBuffer()) < 0;
}
bool operator<(const CHAR_TYPE* lpcszRight) const
{
return TTrates::Compare(GetBuffer(), lpcszRight) < 0;
}
bool operator>=(const CDynamicString& right) const
{
return TTrates::Compare(GetBuffer(), right.GetBuffer()) >= 0;
}
bool operator>=(const CHAR_TYPE* lpcszRight) const
{
return TTrates::Compare(GetBuffer(), lpcszRight) >= 0;
}
bool operator<=(const CDynamicString& right) const
{
return TTrates::Compare(GetBuffer(), right.GetBuffer()) <= 0;
}
bool operator<=(const CHAR_TYPE* lpcszRight) const
{
return TTrates::Compare(GetBuffer(), lpcszRight) <= 0;
}
CDynamicString& operator+=(const CDynamicString& right)
{
_ASSERTE( this != &right );
if ( !Append(right.GetBuffer()) )
throw false;
return *this;
}
CDynamicString& operator+=(const CHAR_TYPE* lpcszRight)
{
_ASSERTE( lpcszRight != GetBuffer() );
if ( !Append(lpcszRight) )
throw false;
return *this;
}
CDynamicString operator+(const CDynamicString& right) const
{
return CDynamicString(GetBuffer(), GetLength(),
right.GetBuffer(), right.GetLength());
}
CDynamicString operator+(const CHAR_TYPE* lpcszRight) const
{
return CDynamicString(GetBuffer(), GetLength(),
lpcszRight, TTrates::Length(lpcszRight));
}
friend CDynamicString operator+(const CHAR_TYPE* lpcszSrc1,
const CDynamicString& src2)
{
return CDynamicString(lpcszSrc1, t_trates::Length(lpcszSrc1),
src2, src2.GetLength());
}
// Helpers
private:
static typename TMemory::SIZE_TYPE LengthToSize(SIZE_TYPE cLength)
{
_ASSERTE( cLength >= 0 );
return TMemory::SIZE_TYPE((cLength + 1) * sizeof(CHAR_TYPE));
}
static SIZE_TYPE SizeToLength(typename TMemory::SIZE_TYPE cbSize)
{
_ASSERTE( !cbSize || cbSize >= sizeof(CHAR_TYPE) );
return (cbSize) ? cbSize/sizeof(CHAR_TYPE) - 1 : 0;
}
// Operations: Memory allocation
public:
bool Allocate(SIZE_TYPE cLengthToAllocate, SIZE_TYPE cLengthToReserve = 0)
{
_ASSERTE( cLengthToAllocate >= 0 && cLengthToReserve >= 0 );
_ASSERTE( GetBuffer() == NULL );
bool bResult = m_memBuffer.Allocate(LengthToSize(cLengthToAllocate),
cLengthToReserve ? LengthToSize(cLengthToReserve) : 0);
_ASSERTE( bResult );
if ( !bResult ) return false;
if ( cLengthToAllocate )
GetBuffer()[0] = CHAR_TYPE(0);
return true;
}
bool Reallocate(SIZE_TYPE cLengthToAllocate, SIZE_TYPE cLengthToReserve = 0,
bool bKeepContents = true)
{
_ASSERTE( cLengthToAllocate >= 0 && cLengthToReserve >= 0 );
// _ASSERTE( (bKeepContents && GetBuffer()) || !bKeepContents );
bool bResult = m_memBuffer.Reallocate(LengthToSize(cLengthToAllocate),
cLengthToReserve ? LengthToSize(cLengthToReserve) : 0,
bKeepContents);
_ASSERTE( bResult );
if ( !bResult ) return false;
if ( !bKeepContents )
GetBuffer()[0] = CHAR_TYPE(0);
return true;
}
void Free()
{
m_memBuffer.Free();
}
// Helpers
private:
bool _Assign(const CHAR_TYPE* lpcSrc, SIZE_TYPE cSrcLength,
SIZE_TYPE cAllocLength, SIZE_TYPE cReserveLength)
{
_ASSERTE( cSrcLength >= 0 );
_ASSERTE( cAllocLength >= cSrcLength && cReserveLength >= cAllocLength );
_ASSERTE( !::IsBadReadPtr(lpcSrc, sizeof(CHAR_TYPE) * cSrcLength) );
_ASSERTE( !lpcSrc || GetBuffer() != lpcSrc );
if ( lpcSrc )
{
TMemory::SIZE_TYPE cbSrc = cSrcLength * sizeof(CHAR_TYPE);
if ( m_memBuffer.IsOverlappedWith(lpcSrc, cbSrc) )
{
TMemory::Move(GetBuffer(), lpcSrc, cbSrc);
}
else
{
TMemory::SIZE_TYPE cbAllocate = LengthToSize(cAllocLength);
TMemory::SIZE_TYPE cbReserve = LengthToSize(cReserveLength);
bool bResult = m_memBuffer.Reallocate(cbAllocate, cbReserve, false);
ATLASSERT( bResult );
if ( !bResult ) return false;
TMemory::Copy(GetBuffer(), lpcSrc, cbSrc);
}
GetBuffer()[cSrcLength] = CHAR_TYPE(0);
}
else
{
Free();
}
return true;
}
bool _Assign(const CDynamicString& src)
{
SIZE_TYPE cSrcLength = src.GetLength();
return _Assign(src.GetBuffer(), cSrcLength, cSrcLength, cSrcLength);
}
bool _Assign(const SCHAR_TYPE* lpcSrc, SIZE_TYPE cSrcLength, LOCALEID_TYPE unCP)
{
_ASSERTE( cSrcLength >= 0 );
_ASSERTE( !::IsBadReadPtr(lpcSrc, sizeof(SCHAR_TYPE) * cSrcLength) );
_ASSERTE( !m_memBuffer.IsOverlappedWith(lpcSrc, sizeof(SCHAR_TYPE) * cSrcLength) );
if ( lpcSrc )
{
SIZE_TYPE cSLength = TSTrates::TranscodedLength(lpcSrc, cSrcLength, unCP);
TMemory::SIZE_TYPE cSize = LengthToSize(cSLength);
// Reallocate buffer of required new size
bool bResult = m_memBuffer.Reallocate(cSize, cSize, false);
ATLASSERT( bResult );
if ( !bResult ) return false;
// Translation and copy data in our buffer
bResult = TSTrates::Transcode(GetBuffer(), cSrcLength, lpcSrc, cSrcLength,
unCP) == cSLength;
_ASSERTE( bResult );
GetBuffer()[cSLength] = CHAR_TYPE(0);
return bResult;
}
else
{
Free();
}
return true;
}
SIZE_TYPE _TrimRight(const CHAR_TYPE* lpcszCharsToTrim, SIZE_TYPE cLength)
{
_ASSERT( lpcszCharsToTrim );
_ASSERT( GetBuffer() );
CHAR_TYPE* lpcszBuffer = GetBuffer() + cLength - 1;
while ( *lpcszBuffer && lpcszBuffer >= GetBuffer() )
{
bool bTrim = false;
const CHAR_TYPE* lpcszToTrim = lpcszCharsToTrim;
while ( *lpcszToTrim && !bTrim )
{
bTrim = ( *lpcszBuffer == *lpcszToTrim );
lpcszToTrim++;
}
if ( bTrim )
{
*lpcszBuffer = CHAR_TYPE(0);
cLength--;
}
else
break;
lpcszBuffer--;
}
return cLength;
}
SIZE_TYPE _TrimLeft(const CHAR_TYPE* lpcszCharsToTrim, SIZE_TYPE cLength)
{
_ASSERT( lpcszCharsToTrim );
_ASSERT( GetBuffer() );
const CHAR_TYPE* lpcszBuffer = GetBuffer();
while ( *lpcszBuffer )
{
bool bTrim = false;
const CHAR_TYPE* lpcszToTrim = lpcszCharsToTrim;
while ( *lpcszToTrim && !bTrim )
{
bTrim = ( *lpcszBuffer == *lpcszToTrim );
lpcszToTrim++;
}
if ( bTrim )
lpcszBuffer++;
else
break;
}
if ( lpcszBuffer != GetBuffer() )
{
cLength = cLength - (lpcszBuffer - GetBuffer());
TMemory::Move(GetBuffer(), lpcszBuffer, (cLength + 1)* sizeof(CHAR_TYPE));
}
return cLength;
}
// Operations: Data assignment
public:
bool Assign(const CDynamicString& src)
{
return ( this != &src ) ? _Assign(src) : true;
}
bool Assign(const CHAR_TYPE* lpcszSrc)
{
return Assign(lpcszSrc, TTrates::Length(lpcszSrc));
}
bool Assign(const CHAR_TYPE* lpcSrc, SIZE_TYPE cSrcLength)
{
return ( lpcSrc != GetBuffer() )
? _Assign(lpcSrc, cSrcLength, cSrcLength, cSrcLength)
: true;
}
bool Assign(const CHAR_TYPE* lpcSrc, SIZE_TYPE cSrcLength, LOCALEID_TYPE unCP)
{
return Assign(lpcSrc, cSrcLength);
}
bool Assign(const SCHAR_TYPE* lpcszSrc)
{
return _Assign(lpcszSrc, TSTrates::Length(lpcszSrc), DEF_LOCALEID);
}
bool Assign(const SCHAR_TYPE* lpcszSrc, SIZE_TYPE cSrcLength)
{
return _Assign(lpcSrc, cSrcLength, DEF_LOCALEID);
}
bool Assign(const SCHAR_TYPE* lpcSrc, SIZE_TYPE cSrcLength, LOCALEID_TYPE unCP)
{
return _Assign(lpcSrc, cSrcLength, unCP);
}
// Operations: Data manipulation
public:
bool Append(const CHAR_TYPE* lpcszSrc)
{
return Append(lpcszSrc, TTrates::Length(lpcszSrc));
}
bool Append(const CHAR_TYPE* lpcSrc, SIZE_TYPE cSrcLength)
{
_ASSERTE( cSrcLength >= 0 );
_ASSERTE( !::IsBadReadPtr(lpcSrc, cSrcLength * sizeof(CHAR_TYPE)) );
if ( lpcSrc )
{
SIZE_TYPE cCurLength = GetLength();
SIZE_TYPE cNewLength = cCurLength + cSrcLength;
TMemory::SIZE_TYPE cbSrcSize = cSrcLength * sizeof(CHAR_TYPE);
TMemory::SIZE_TYPE cbNewSize = lpcSrc ? LengthToSize(cNewLength) : 0;
CDynamicString dsTemp;
bool bResult;
if ( m_memBuffer.IsOverlappedWith(lpcSrc, cbSrcSize) )
{
bResult = dsTemp.Assign(lpcSrc, cSrcLength);
_ASSERTE( bResult );
if ( !bResult ) return false;
lpcSrc = dsTemp;
}
bResult = m_memBuffer.Reallocate(cbNewSize, cbNewSize, true);
_ASSERTE( bResult );
if ( !bResult ) return false;
TMemory::Copy(GetBuffer() + cCurLength, lpcSrc, cbSrcSize);
GetBuffer()[cNewLength] = CHAR_TYPE(0);
}
return true;
}
bool Insert(INDEX_TYPE index, const CHAR_TYPE* lpcszSrc)
{
return Insert(index, lpcszSrc, TTrates::Length(lpcszSrc));
}
bool Insert(INDEX_TYPE index, const CHAR_TYPE* lpcSrc, SIZE_TYPE cSrcLength)
{
_ASSERTE( index >= 0 && cSrcLength >= 0 );
_ASSERTE( index <= GetLength() );
_ASSERTE( !::IsBadReadPtr(lpcSrc, cSrcLength * sizeof(CHAR_TYPE)) );
SIZE_TYPE cCurLength = GetLength();
SIZE_TYPE cNewLength = cCurLength + cSrcLength;
TMemory::SIZE_TYPE cbSrcSize = cSrcLength * sizeof(CHAR_TYPE);
TMemory::SIZE_TYPE cbNewSize = LengthToSize(cNewLength);
// If the text to be inserted is in our buffer we have to store it in
// temporal buffer.
bool bResult;
CDynamicString dsTemp;
if ( m_memBuffer.IsOverlappedWith(lpcSrc, cbSrcSize) )
{
bResult = dsTemp.Assign(lpcSrc, cSrcLength);
_ASSERTE( bResult );
if ( !bResult ) return false;
lpcSrc = dsTemp;
}
// Reallocate buffer
bResult = m_memBuffer.Reallocate(cbNewSize, cbNewSize, true);
_ASSERTE( bResult );
if ( !bResult ) return false;
// Move tralling part over insertion point
CHAR_TYPE* pIns = GetBuffer() + index;
SIZE_TYPE cTrlLength = cNewLength - cCurLength;
if ( cTrlLength )
{
TMemory::Move(pIns + cSrcLength, pIns, cTrlLength * sizeof(CHAR_TYPE));
}
// Copy the text to be inserted
TMemory::Copy(pIns, lpcSrc, cbSrcSize);
// Set null terminator
GetBuffer()[cNewLength] = CHAR_TYPE(0);
return true;
}
void Delete(INDEX_TYPE index, SIZE_TYPE cLength = 1)
{
_ASSERTE( index >= 0 && cLength >= 0 );
_ASSERTE( index + cLength <= GetLength() );
// Call destructors for elements
SIZE_TYPE cCurLength = GetLength();
SIZE_TYPE cNewLength = cCurLength - cLength;
SIZE_TYPE cTrlLength = cNewLength - index;
// Move tralling part over deletion
if ( cTrlLength )
{
CHAR_TYPE* pIns = GetBuffer() + index;
CHAR_TYPE* pTrl = pIns + cLength;
TMemory::Move(pIns, pTrl, cTrlLength * sizeof(CHAR_TYPE));
}
// Set null terminator
GetBuffer()[cNewLength] = CHAR_TYPE(0);
}
bool Replace(INDEX_TYPE iDst, SIZE_TYPE cDstLength, const CHAR_TYPE* lpcszSrc)
{
return Replace(iDst, cDstLength, lpcszSrc, TTrates::Length(lpcszSrc));
}
bool Replace(INDEX_TYPE iDst, SIZE_TYPE cDstLength, const CHAR_TYPE* lpcSrc,
SIZE_TYPE cSrcLength)
{
_ASSERTE( iDst >= 0 && cDstLength >= 0 && cSrcLength >= 0 );
_ASSERTE( !::IsBadReadPtr(lpcSrc, cSrcLength * sizeof(CHAR_TYPE)) );
SIZEDIF_TYPE cDifLength = cSrcLength - cDstLength;
SIZE_TYPE cRplLength = 0;
// If the text to be inserted is in our buffer we have to store it in
// temporal buffer.
SIZE_TYPE cbSrcSize = cSrcLength * sizeof(CHAR_TYPE);
bool bResult;
CDynamicString dsTemp;
if ( m_memBuffer.IsOverlappedWith(lpcSrc, cbSrcSize) )
{
bResult = dsTemp.Assign(lpcSrc, cSrcLength);
_ASSERTE( bResult );
if ( !bResult ) return false;
lpcSrc = dsTemp;
}
if ( cDifLength > 0 )
{
cRplLength = cSrcLength - cDifLength;
if ( !Insert(iDst + cRplLength, lpcSrc + cRplLength, cDifLength) )
return false;
}
else if ( cDifLength < 0 )
{
cRplLength = cDstLength + cDifLength;
Delete( iDst + cRplLength, -cDifLength);
}
// Copy overlapped elements
TMemory::Copy(GetBuffer() + iDst, lpcSrc, cRplLength * sizeof(CHAR_TYPE));
return true;
}
void TrimLeft(const CHAR_TYPE* lpcszCharsToTrim)
{
_TrimLeft(lpcszCharsToTrim, GetLength());
}
void TrimRight(const CHAR_TYPE* lpcszCharsToTrim)
{
_TrimRight(lpcszCharsToTrim, GetLength());
}
void Trim(const CHAR_TYPE* lpcszCharsToTrim)
{
_TrimLeft(lpcszCharsToTrim, _TrimRight(lpcszCharsToTrim, GetLength()));
}
//ToLower
//CharLowerBuff
//ToUpper
// Accessors & mutators
public:
const CHAR_TYPE* GetBuffer() const
{
return reinterpret_cast<const CHAR_TYPE*>(m_memBuffer.GetBuffer());
}
CHAR_TYPE* GetBuffer()
{
return reinterpret_cast<CHAR_TYPE*>(m_memBuffer.GetBuffer());
}
SIZE_TYPE GetLength() const
{
return TTrates::Length(GetBuffer());
}
bool IsEmpty() const
{
return ( !GetBuffer() || !(*GetBuffer()) );
}
SIZE_TYPE GetCapacity() const
{
return SizeToLength(m_memBuffer.GetAllocatedSize());
}
SIZE_TYPE GetReservedCapacity() const
{
return SizeToLength(m_memBuffer.GetReservedSize());
}
// Attributes
private:
TMemory m_memBuffer;
};
/*
#if defined(_DEBUG) && defined(_MFC_VER)
template < class t_trates, class t_memory, class CHAR_TYPE >
static CDumpContext& AFXAPI operator<<(CDumpContext& dc,
CDynamicString<t_trates, t_memory>& vs)
{
dc << static_cast<const CDynamicString<t_trates, t_memory>::CHAR_TYPE*>(vs);
return dc;
}
#endif // defined(_DEBUG) && defined(_MFC_VER)
*/
}; // namespace SP;
#endif // !defined(__SP_SPDYNAMICSTRING_H__INCLUDED__)