From 795f8fd245582f4cc66b3320418b13b8e15729b0 Mon Sep 17 00:00:00 2001 From: George Hazan Date: Sun, 25 Aug 2013 11:49:17 +0000 Subject: MString moved to the core git-svn-id: http://svn.miranda-ng.org/main/trunk@5823 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- bin10/lib/mir_core.lib | Bin 50800 -> 53074 bytes bin10/lib/mir_core64.lib | Bin 46222 -> 48536 bytes bin11/lib/mir_core.lib | Bin 50800 -> 53074 bytes bin11/lib/mir_core64.lib | Bin 46222 -> 48536 bytes include/m_string.h | 2288 ++++++++++++++++++++++++++++++ src/mir_core/mir_core.def | 7 + src/mir_core/mir_core_10.vcxproj | 1 + src/mir_core/mir_core_10.vcxproj.filters | 3 + src/mir_core/mir_core_11.vcxproj | 1 + src/mir_core/mir_core_11.vcxproj.filters | 3 + src/mir_core/mstring.cpp | 144 ++ 11 files changed, 2447 insertions(+) create mode 100644 include/m_string.h create mode 100644 src/mir_core/mstring.cpp diff --git a/bin10/lib/mir_core.lib b/bin10/lib/mir_core.lib index 3d9af45bc5..73afe233ad 100644 Binary files a/bin10/lib/mir_core.lib and b/bin10/lib/mir_core.lib differ diff --git a/bin10/lib/mir_core64.lib b/bin10/lib/mir_core64.lib index ffcb26d47e..39d2e1145f 100644 Binary files a/bin10/lib/mir_core64.lib and b/bin10/lib/mir_core64.lib differ diff --git a/bin11/lib/mir_core.lib b/bin11/lib/mir_core.lib index 0b38903142..80278043f7 100644 Binary files a/bin11/lib/mir_core.lib and b/bin11/lib/mir_core.lib differ diff --git a/bin11/lib/mir_core64.lib b/bin11/lib/mir_core64.lib index ffcb26d47e..32bc35c106 100644 Binary files a/bin11/lib/mir_core64.lib and b/bin11/lib/mir_core64.lib differ diff --git a/include/m_string.h b/include/m_string.h new file mode 100644 index 0000000000..28f13673a9 --- /dev/null +++ b/include/m_string.h @@ -0,0 +1,2288 @@ +#pragma once + +#include +#include +#include + +#include + +#ifdef __MINGW32__ +#include + +__inline size_t strnlen(const char *string, size_t maxlen) +{ + const char *end = (const char *)memchr ((const void *)string, '\0', maxlen); + return end ? (size_t) (end - string) : maxlen; +} +__inline size_t wcsnlen(const wchar_t *string, size_t maxlen) +{ + const wchar_t *end = wmemchr (string, L'\0', maxlen); + return end ? (size_t) (end - string) : maxlen; +} + +/* FIXME: This may be wrong assumption about _AtlGetConversionACP */ +#define _AtlGetConversionACP() CP_THREAD_ACP +/* FIXME: This is unsafe */ +#define memcpy_s(dest,size,src,count) memcpy(dest,src,count) +/* FIXME: This is quite silly implementation of _mbsstr */ +#define _mbsstr(str,search) strstr((const char *)str,(const char *)search) +#define __max(x,y) (((x)<(y))?(y):(x)) +#endif /* __MINGW32__ */ + +///////////////////////////////////////////////////////////////////////////////////////// + +struct CMStringData; + +MIR_CORE_DLL(CMStringData*) mirstr_allocate(int nChars, int nCharSize); +MIR_CORE_DLL(void) mirstr_free(CMStringData *pData); +MIR_CORE_DLL(CMStringData*) mirstr_realloc(CMStringData* pData, int nChars, int nCharSize); +MIR_CORE_DLL(CMStringData*) mirstr_getNil(); + +MIR_CORE_DLL(void) mirstr_lock(CMStringData* pThis); +MIR_CORE_DLL(void) mirstr_release(CMStringData* pThis); +MIR_CORE_DLL(void) mirstr_unlock(CMStringData* pThis); + +///////////////////////////////////////////////////////////////////////////////////////// + +struct CMStringData +{ + 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 + + __forceinline void* data() { return (this + 1); } + __forceinline void AddRef() { InterlockedIncrement(&nRefs); } + __forceinline bool IsLocked() const { return nRefs < 0; } + __forceinline bool IsShared() const { return (nRefs > 1); } + + __forceinline void Lock() { mirstr_lock(this); } + __forceinline void Release() { mirstr_release(this); } + __forceinline void Unlock() { mirstr_unlock(this); } +}; + +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 CMSimpleStringT +{ +public: + typedef typename ChTraitsBase< BaseType >::XCHAR XCHAR; + typedef typename ChTraitsBase< BaseType >::PXSTR PXSTR; + typedef typename ChTraitsBase< BaseType >::PCXSTR PCXSTR; + typedef typename ChTraitsBase< BaseType >::YCHAR YCHAR; + typedef typename ChTraitsBase< BaseType >::PYSTR PYSTR; + typedef typename ChTraitsBase< BaseType >::PCYSTR PCYSTR; + +public: + explicit CMSimpleStringT() + { + CMStringData* pData = GetNilString(); + Attach(pData); + } + + CMSimpleStringT(const CMSimpleStringT& strSrc) + { + CMStringData* pSrcData = strSrc.GetData(); + CMStringData* pNewData = CloneData(pSrcData); + Attach(pNewData); + } + + CMSimpleStringT(PCXSTR pszSrc) + { + int nLength = StringLength(pszSrc); + CMStringData* pData = Allocate(nLength, sizeof(XCHAR)); + if (pData != NULL) + { + Attach(pData); + SetLength(nLength); + CopyChars(m_pszData, nLength, pszSrc, nLength); + } + } + CMSimpleStringT(const XCHAR* pchSrc, int nLength) + { + CMStringData* pData = Allocate(nLength, sizeof(XCHAR)); + if (pData != NULL) + { + Attach(pData); + SetLength(nLength); + CopyChars(m_pszData, nLength, pchSrc, nLength); + } + } + ~CMSimpleStringT() + { + CMStringData* pData = GetData(); + pData->Release(); + } + + operator CMSimpleStringT&() + { + return *(CMSimpleStringT*)this; + } + + CMSimpleStringT& operator=(const CMSimpleStringT& strSrc) + { + CMStringData* pSrcData = strSrc.GetData(); + CMStringData* pOldData = GetData(); + if (pSrcData != pOldData) + { + if (pOldData->IsLocked()) + SetString(strSrc.GetString(), strSrc.GetLength()); + else + { + CMStringData* pNewData = CloneData(pSrcData); + pOldData->Release(); + Attach(pNewData); + } + } + + return *this; + } + + CMSimpleStringT& operator=(PCXSTR pszSrc) + { + SetString(pszSrc); + return *this; + } + + CMSimpleStringT& operator+=(const CMSimpleStringT& strSrc) + { + Append(strSrc); + + return *this; + } + + CMSimpleStringT& operator+=(PCXSTR pszSrc) + { + Append(pszSrc); + + return *this; + } + CMSimpleStringT& operator+=(char ch) + { + AppendChar(XCHAR(ch)); + + return *this; + } + CMSimpleStringT& operator+=(unsigned char ch) + { + AppendChar(XCHAR(ch)); + + return *this; + } + CMSimpleStringT& operator+=(wchar_t ch) + { + AppendChar(XCHAR(ch)); + + return *this; + } + + XCHAR operator[](int iChar) const + { + return m_pszData[iChar]; + } + + operator PCXSTR() const + { + return m_pszData; + } + + PCXSTR c_str() const + { + 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(); + if (nOldLength < 0) + { + // protects from underflow + nOldLength = 0; + } + + //Make sure we don't read pass end of the terminating NULL + int nSrcLength = StringLength(pszSrc); + nLength = nLength > nSrcLength ? nSrcLength: nLength; + + 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, nLength, pszSrc, nLength); + ReleaseBufferSetLength(nNewLength); + } + void AppendChar(XCHAR ch) + { + UINT nOldLength = GetLength(); + int nNewLength = nOldLength+1; + PXSTR pszBuffer = GetBuffer(nNewLength); + pszBuffer[nOldLength] = ch; + ReleaseBufferSetLength(nNewLength); + } + void Append(const CMSimpleStringT& strSrc) + { + Append(strSrc.GetString(), strSrc.GetLength()); + } + void Empty() + { + CMStringData* pOldData = GetData(); + if (pOldData->nDataLength == 0) + return; + + if (pOldData->IsLocked()) + { + // Don't reallocate a locked buffer that's shrinking + SetLength(0); + } + else + { + pOldData->Release(); + CMStringData* pNewData = GetNilString(); + Attach(pNewData); + } + } + void FreeExtra() + { + CMStringData* pOldData = GetData(); + int nLength = pOldData->nDataLength; + if (pOldData->nAllocLength == nLength) + return; + + if ( !pOldData->IsLocked()) // Don't reallocate a locked buffer that's shrinking + { + CMStringData* pNewData = Allocate(nLength, sizeof(XCHAR)); + if (pNewData == NULL) { + SetLength(nLength); + return; + } + + CopyChars(PXSTR(pNewData->data()), nLength, PCXSTR(pOldData->data()), nLength); + + pOldData->Release(); + Attach(pNewData); + SetLength(nLength); + } + } + + int GetAllocLength() const + { + return GetData()->nAllocLength; + } + XCHAR GetAt(int iChar) const + { + return m_pszData[iChar]; + } + PXSTR GetBuffer() + { + CMStringData* 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 + { + return GetData()->nDataLength; + } + + PCXSTR GetString() const + { + return m_pszData; + } + bool IsEmpty() const + { + return GetLength() == 0; + } + PXSTR LockBuffer() + { + CMStringData* 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() + { + CMStringData* pData = GetData(); + pData->Unlock(); + } + void Preallocate(int nLength) + { + PrepareWrite(nLength); + } + void ReleaseBuffer(int nNewLength = -1) + { + if (nNewLength == -1) + { + int nAlloc = GetData()->nAllocLength; + nNewLength = StringLengthN(m_pszData, nAlloc); + } + SetLength(nNewLength); + } + void ReleaseBufferSetLength(int nNewLength) + { + SetLength(nNewLength); + } + void Truncate(int nNewLength) + { + GetBuffer(nNewLength); + ReleaseBufferSetLength(nNewLength); + } + void SetAt(int iChar, XCHAR ch) + { + int nLength = GetLength(); + PXSTR pszBuffer = GetBuffer(); + pszBuffer[iChar] = ch; + ReleaseBufferSetLength(nLength); + + } + void SetString(PCXSTR pszSrc) + { + SetString(pszSrc, StringLength(pszSrc)); + } + void SetString(PCXSTR pszSrc, int nLength) + { + if (nLength == 0) + { + Empty(); + } + else + { + + UINT nOldLength = GetLength(); + UINT_PTR nOffset = pszSrc - GetString(); + + PXSTR pszBuffer = GetBuffer(nLength); + if (nOffset <= nOldLength) + { + CopyCharsOverlapped(pszBuffer, GetAllocLength(), + pszBuffer+nOffset, nLength); + } + else + { + CopyChars(pszBuffer, GetAllocLength(), pszSrc, nLength); + } + ReleaseBufferSetLength(nLength); + } + } +public: + friend CMSimpleStringT __stdcall operator+(const CMSimpleStringT& str1, const CMSimpleStringT& str2) + { + CMSimpleStringT s; + + Concatenate(s, str1, str1.GetLength(), str2, str2.GetLength()); + + return s; + } + + friend CMSimpleStringT __stdcall operator+(const CMSimpleStringT& str1, PCXSTR psz2) + { + CMSimpleStringT s; + + Concatenate(s, str1, str1.GetLength(), psz2, StringLength(psz2)); + + return s; + } + + friend CMSimpleStringT __stdcall operator+(PCXSTR psz1, const CMSimpleStringT& str2) + { + CMSimpleStringT s; + + Concatenate(s, psz1, StringLength(psz1), str2, str2.GetLength()); + + return s; + } + + static void __stdcall CopyChars(XCHAR* pchDest, const XCHAR* pchSrc, int nChars) + { +#pragma warning (push) +#pragma warning(disable : 4996) + memcpy(pchDest, pchSrc, nChars * sizeof(XCHAR)); +#pragma warning (pop) + } + static void __stdcall CopyChars(XCHAR* pchDest, size_t nDestLen, const XCHAR* pchSrc, int nChars) + { + #if _MSC_VER >= 1400 + memcpy_s(pchDest, nDestLen * sizeof(XCHAR), pchSrc, nChars * sizeof(XCHAR)); + #else + memcpy(pchDest, pchSrc, nDestLen * sizeof(XCHAR)); + #endif + } + + static void __stdcall CopyCharsOverlapped(XCHAR* pchDest, const XCHAR* pchSrc, int nChars) + { +#pragma warning (push) +#pragma warning(disable : 4996) + memmove(pchDest, pchSrc, nChars * sizeof(XCHAR)); +#pragma warning (pop) + } + static void __stdcall CopyCharsOverlapped(XCHAR* pchDest, size_t nDestLen, const XCHAR* pchSrc, int nChars) + { + #if _MSC_VER >= 1400 + memmove_s(pchDest, nDestLen * sizeof(XCHAR), pchSrc, nChars * sizeof(XCHAR)); + #else + memmove(pchDest, pchSrc, nDestLen * sizeof(XCHAR)); + #endif + } + static int __stdcall StringLength(const char* psz) + { + if (psz == NULL) + { + return(0); + } + return (int(strlen(psz))); + } + static int __stdcall StringLength(const wchar_t* psz) + { + if (psz == NULL) + return 0; + + return int(wcslen(psz)); + } + static int __stdcall StringLengthN(const char* psz, size_t sizeInXChar) + { + if (psz == NULL) + return 0; + + return int(strnlen(psz, sizeInXChar)); + } + static int __stdcall StringLengthN(const wchar_t* psz, size_t sizeInXChar) + { + if (psz == NULL) + return 0; + + return int(wcsnlen(psz, sizeInXChar)); + } +protected: + static void __stdcall Concatenate(CMSimpleStringT& strResult, PCXSTR psz1, int nLength1, PCXSTR psz2, int nLength2) + { + int nNewLength = nLength1+nLength2; + PXSTR pszBuffer = strResult.GetBuffer(nNewLength); + CopyChars(pszBuffer, nLength1, psz1, nLength1); + CopyChars(pszBuffer + nLength1, nLength2, psz2, nLength2); + strResult.ReleaseBufferSetLength(nNewLength); + } + // Implementation +private: + void Attach(CMStringData* pData) + { + m_pszData = static_cast(pData->data()); + } + void Fork(int nLength) + { + CMStringData* pOldData = GetData(); + int nOldLength = pOldData->nDataLength; + CMStringData* pNewData = Allocate(nLength, sizeof(XCHAR)); + if (pNewData != NULL) + { + int nCharsToCopy = ((nOldLength < nLength) ? nOldLength : nLength)+1; // Copy '\0' + CopyChars(PXSTR(pNewData->data()), nCharsToCopy, PCXSTR(pOldData->data()), nCharsToCopy); + pNewData->nDataLength = nOldLength; + pOldData->Release(); + Attach(pNewData); + } + } + CMStringData* GetData() const + { + return (reinterpret_cast(m_pszData) - 1); + } + PXSTR PrepareWrite(int nLength) + { + CMStringData* 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; + } + void PrepareWrite2(int nLength) + { + CMStringData* 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); + } + } + void Reallocate(int nLength) + { + CMStringData* pOldData = GetData(); + if (pOldData->nAllocLength >= nLength || nLength <= 0) + return; + + CMStringData* pNewData = Realloc(pOldData, nLength, sizeof(XCHAR)); + if (pNewData != NULL) + Attach(pNewData); + } + + void SetLength(int nLength) + { + GetData()->nDataLength = nLength; + m_pszData[nLength] = 0; + } + + static CMStringData* __stdcall CloneData(CMStringData* pData) + { + CMStringData* pNewData = NULL; + + if ( !pData->IsLocked()) { + pNewData = pData; + pNewData->AddRef(); + } + + return pNewData; + } + +public : + // typedef CStrBufT CStrBuf; +private: + PXSTR m_pszData; +}; + + +template< typename _CharType = char > +class ChTraitsCRT : public ChTraitsBase< _CharType > +{ +public: + static char* __stdcall CharNext(const char* p) + { + return reinterpret_cast< char* >(_mbsinc(reinterpret_cast< const unsigned char* >(p))); + } + + static int __stdcall IsDigit(char ch) + { + return _ismbcdigit(ch); + } + + static int __stdcall IsSpace(char ch) + { + return _ismbcspace(ch); + } + + static int __stdcall StringCompare(LPCSTR pszA, LPCSTR pszB) + { + return _mbscmp(reinterpret_cast< const unsigned char* >(pszA), reinterpret_cast< const unsigned char* >(pszB)); + } + + static int __stdcall StringCompareIgnore(LPCSTR pszA, LPCSTR pszB) + { + return _mbsicmp(reinterpret_cast< const unsigned char* >(pszA), reinterpret_cast< const unsigned char* >(pszB)); + } + + static int __stdcall StringCollate(LPCSTR pszA, LPCSTR pszB) + { + return _mbscoll(reinterpret_cast< const unsigned char* >(pszA), reinterpret_cast< const unsigned char* >(pszB)); + } + + static int __stdcall StringCollateIgnore(LPCSTR pszA, LPCSTR pszB) + { + return _mbsicoll(reinterpret_cast< const unsigned char* >(pszA), reinterpret_cast< const unsigned char* >(pszB)); + } + + static LPCSTR __stdcall StringFindString(LPCSTR pszBlock, LPCSTR pszMatch) + { + return reinterpret_cast< LPCSTR >(_mbsstr(reinterpret_cast< const unsigned char* >(pszBlock), + reinterpret_cast< const unsigned char* >(pszMatch))); + } + + static LPSTR __stdcall StringFindString(LPSTR pszBlock, LPCSTR pszMatch) + { + return const_cast< LPSTR >(StringFindString(const_cast< LPCSTR >(pszBlock), pszMatch)); + } + + static LPCSTR __stdcall StringFindChar(LPCSTR pszBlock, char chMatch) + { + return reinterpret_cast< LPCSTR >(_mbschr(reinterpret_cast< const unsigned char* >(pszBlock), (unsigned char)chMatch)); + } + + static LPCSTR __stdcall StringFindCharRev(LPCSTR psz, char ch) + { + return reinterpret_cast< LPCSTR >(_mbsrchr(reinterpret_cast< const unsigned char* >(psz), (unsigned char)ch)); + } + + static LPCSTR __stdcall StringScanSet(LPCSTR pszBlock, LPCSTR pszMatch) + { + return reinterpret_cast< LPCSTR >(_mbspbrk(reinterpret_cast< const unsigned char* >(pszBlock), + reinterpret_cast< const unsigned char* >(pszMatch))); + } + + static int __stdcall StringSpanIncluding(LPCSTR pszBlock, LPCSTR pszSet) + { + return (int)_mbsspn(reinterpret_cast< const unsigned char* >(pszBlock), reinterpret_cast< const unsigned char* >(pszSet)); + } + + static int __stdcall StringSpanExcluding(LPCSTR pszBlock, LPCSTR pszSet) + { + return (int)_mbscspn(reinterpret_cast< const unsigned char* >(pszBlock), reinterpret_cast< const unsigned char* >(pszSet)); + } + + static LPSTR __stdcall StringUppercase(LPSTR psz) + { +#pragma warning (push) +#pragma warning(disable : 4996) + return reinterpret_cast< LPSTR >(_mbsupr(reinterpret_cast< unsigned char* >(psz))); +#pragma warning (pop) + } + + static LPSTR __stdcall StringLowercase(LPSTR psz) + { +#pragma warning (push) +#pragma warning(disable : 4996) + return reinterpret_cast< LPSTR >(_mbslwr(reinterpret_cast< unsigned char* >(psz))); +#pragma warning (pop) + } + + static LPSTR __stdcall StringUppercase(LPSTR psz, size_t size) + { + #if _MSC_VER >= 1400 + _mbsupr_s(reinterpret_cast< unsigned char* >(psz), size); + #else + _mbsupr(reinterpret_cast< unsigned char* >(psz)); + #endif + return psz; + } + + static LPSTR __stdcall StringLowercase(LPSTR psz, size_t size) + { + #if _MSC_VER >= 1400 + _mbslwr_s(reinterpret_cast< unsigned char* >(psz), size); + #else + _mbslwr(reinterpret_cast< unsigned char* >(psz)); + #endif + return psz; + } + + static LPSTR __stdcall StringReverse(LPSTR psz) + { + return reinterpret_cast< LPSTR >(_mbsrev(reinterpret_cast< unsigned char* >(psz))); + } + + static int __stdcall GetFormattedLength(LPCSTR pszFormat, va_list args); + + static int __stdcall Format(LPSTR pszBuffer, size_t nlength, LPCSTR pszFormat, va_list args); + + static int __stdcall GetBaseTypeLength(LPCSTR pszSrc) + { + // Returns required buffer length in XCHARs + return int(strlen(pszSrc)); + } + + static int __stdcall GetBaseTypeLength(LPCSTR pszSrc, int nLength) + { + (void)pszSrc; + // Returns required buffer length in XCHARs + return nLength; + } + + static int __stdcall GetBaseTypeLength(LPCWSTR pszSource) + { + // Returns required buffer length in XCHARs + return ::WideCharToMultiByte(_AtlGetConversionACP(), 0, pszSource, -1, NULL, 0, NULL, NULL)-1; + } + + static int __stdcall GetBaseTypeLength(LPCWSTR pszSource, int nLength) + { + // Returns required buffer length in XCHARs + return ::WideCharToMultiByte(_AtlGetConversionACP(), 0, pszSource, nLength, NULL, 0, NULL, NULL); + } + + static void __stdcall ConvertToBaseType(LPSTR pszDest, int nDestLength, LPCSTR pszSrc, int nSrcLength = -1) + { + if (nSrcLength == -1) { nSrcLength=1 + GetBaseTypeLength(pszSrc); } + // nLen is in XCHARs + memcpy_s(pszDest, nDestLength*sizeof(char), + pszSrc, nSrcLength*sizeof(char)); + } + + static void __stdcall ConvertToBaseType(LPSTR pszDest, int nDestLength, LPCWSTR pszSrc, int nSrcLength = -1) + { + // nLen is in XCHARs + ::WideCharToMultiByte(_AtlGetConversionACP(), 0, pszSrc, nSrcLength, pszDest, nDestLength, NULL, NULL); + } + + static void ConvertToOem(_CharType* pstrString) + { + BOOL fSuccess=::CharToOemA(pstrString, pstrString); + } + + static void ConvertToAnsi(_CharType* pstrString) + { + BOOL fSuccess=::OemToCharA(pstrString, pstrString); + } + + static void ConvertToOem(_CharType* pstrString, size_t size) + { + if (size>UINT_MAX) + { + return; + } + DWORD dwSize=static_cast(size); + BOOL fSuccess=::CharToOemBuffA(pstrString, pstrString, dwSize); + } + + static void ConvertToAnsi(_CharType* pstrString, size_t size) + { + if (size>UINT_MAX) + return; + + DWORD dwSize=static_cast(size); + BOOL fSuccess=::OemToCharBuffA(pstrString, pstrString, dwSize); + } + + static void __stdcall FloodCharacters(char ch, int nLength, char* pch) + { + // nLength is in XCHARs + memset(pch, ch, nLength); + } + + static BSTR __stdcall AllocSysString(const char* pchData, int nDataLength) + { + int nLen = ::MultiByteToWideChar(_AtlGetConversionACP(), 0, pchData, nDataLength, NULL, NULL); + BSTR bstr = ::SysAllocStringLen(NULL, nLen); + if (bstr != NULL) + ::MultiByteToWideChar(_AtlGetConversionACP(), 0, pchData, nDataLength, bstr, nLen); + + return bstr; + } + + static BOOL __stdcall ReAllocSysString(const char* pchData, BSTR* pbstr, int nDataLength) + { + int nLen = ::MultiByteToWideChar(_AtlGetConversionACP(), 0, pchData, nDataLength, NULL, NULL); + BOOL bSuccess = ::SysReAllocStringLen(pbstr, NULL, nLen); + if (bSuccess) + ::MultiByteToWideChar(_AtlGetConversionACP(), 0, pchData, nDataLength, *pbstr, nLen); + + return bSuccess; + } + + static int __stdcall SafeStringLen(LPCSTR psz) + { + // returns length in bytes + return (psz != NULL) ? int(strlen(psz)) : 0; + } + + static int __stdcall SafeStringLen(LPCWSTR psz) + { + // returns length in wchar_ts + return (psz != NULL) ? int(wcslen(psz)) : 0; + } + + static int __stdcall GetCharLen(const wchar_t* pch) + { + // returns char length + return 1; + } + + static int __stdcall GetCharLen(const char* pch) + { + // returns char length + return int(_mbclen(reinterpret_cast< const unsigned char* >(pch))); + } + + static DWORD __stdcall GetEnvironmentVariable(LPCSTR pszVar, LPSTR pszBuffer, DWORD dwSize) + { + return ::GetEnvironmentVariableA(pszVar, pszBuffer, dwSize); + } +}; + +// specialization for wchar_t +template<> +class ChTraitsCRT< wchar_t > : public ChTraitsBase< wchar_t > +{ + static DWORD __stdcall _GetEnvironmentVariableW(LPCWSTR pszName, LPWSTR pszBuffer, DWORD nSize) + { + return ::GetEnvironmentVariableW(pszName, pszBuffer, nSize); + } + +public: + static LPWSTR __stdcall CharNext(LPCWSTR psz) + { + return const_cast< LPWSTR >(psz+1); + } + + static int __stdcall IsDigit(wchar_t ch) + { + return iswdigit(static_cast(ch)); + } + + static int __stdcall IsSpace(wchar_t ch) + { + return iswspace(static_cast(ch)); + } + + static int __stdcall StringCompare(LPCWSTR pszA, LPCWSTR pszB) + { + return wcscmp(pszA, pszB); + } + + static int __stdcall StringCompareIgnore(LPCWSTR pszA, LPCWSTR pszB) + { + return _wcsicmp(pszA, pszB); + } + + static int __stdcall StringCollate(LPCWSTR pszA, LPCWSTR pszB) + { + return wcscoll(pszA, pszB); + } + + static int __stdcall StringCollateIgnore(LPCWSTR pszA, LPCWSTR pszB) + { + return _wcsicoll(pszA, pszB); + } + + static LPCWSTR __stdcall StringFindString(LPCWSTR pszBlock, LPCWSTR pszMatch) + { + return wcsstr(pszBlock, pszMatch); + } + + static LPWSTR __stdcall StringFindString(LPWSTR pszBlock, LPCWSTR pszMatch) + { + return const_cast< LPWSTR >(StringFindString(const_cast< LPCWSTR >(pszBlock), pszMatch)); + } + + static LPCWSTR __stdcall StringFindChar(LPCWSTR pszBlock, wchar_t chMatch) + { + return wcschr(pszBlock, chMatch); + } + + static LPCWSTR __stdcall StringFindCharRev(LPCWSTR psz, wchar_t ch) + { + return wcsrchr(psz, ch); + } + + static LPCWSTR __stdcall StringScanSet(LPCWSTR pszBlock, LPCWSTR pszMatch) + { + return wcspbrk(pszBlock, pszMatch); + } + + static int __stdcall StringSpanIncluding(LPCWSTR pszBlock, LPCWSTR pszSet) + { + return (int)wcsspn(pszBlock, pszSet); + } + + static int __stdcall StringSpanExcluding(LPCWSTR pszBlock, LPCWSTR pszSet) + { + return (int)wcscspn(pszBlock, pszSet); + } + + static LPWSTR __stdcall StringUppercase(LPWSTR psz) + { +#pragma warning (push) +#pragma warning(disable : 4996) + return _wcsupr(psz); +#pragma warning (pop) + } + + static LPWSTR __stdcall StringLowercase(LPWSTR psz) + { +#pragma warning (push) +#pragma warning(disable : 4996) + return _wcslwr(psz); +#pragma warning (pop) + } + + static LPWSTR __stdcall StringUppercase(LPWSTR psz, size_t) + { + return _wcsupr(psz); + } + + static LPWSTR __stdcall StringLowercase(LPWSTR psz, size_t) + { + return _wcslwr(psz); + } + + static LPWSTR __stdcall StringReverse(LPWSTR psz) + { + return _wcsrev(psz); + } + + static int __stdcall GetFormattedLength(LPCWSTR pszFormat, va_list args); + + static int __stdcall Format(LPWSTR pszBuffer, LPCWSTR pszFormat, va_list args) + { +#pragma warning (push) +#pragma warning(disable : 4996) + return vswprintf(pszBuffer, pszFormat, args); //!!!!!!!!! +#pragma warning (pop) + } + + static int __stdcall Format(LPWSTR pszBuffer, size_t nLength, LPCWSTR pszFormat, va_list args); + + static int __stdcall GetBaseTypeLength(LPCSTR pszSrc) + { + // Returns required buffer size in wchar_ts + return ::MultiByteToWideChar(CP_ACP, 0, pszSrc, -1, NULL, 0)-1; + } + + static int __stdcall GetBaseTypeLength(LPCSTR pszSrc, int nLength) + { + // Returns required buffer size in wchar_ts + return ::MultiByteToWideChar(CP_ACP, 0, pszSrc, nLength, NULL, 0); + } + + static int __stdcall GetBaseTypeLength(LPCWSTR pszSrc) + { + // Returns required buffer size in wchar_ts + return (int)wcslen(pszSrc); + } + + static int __stdcall GetBaseTypeLength(LPCWSTR pszSrc, int nLength) + { + (void)pszSrc; + // Returns required buffer size in wchar_ts + return nLength; + } + + static void __stdcall ConvertToBaseType(LPWSTR pszDest, int nDestLength, LPCSTR pszSrc, int nSrcLength = -1) + { + // nLen is in wchar_ts + ::MultiByteToWideChar(CP_ACP, 0, pszSrc, nSrcLength, pszDest, nDestLength); + } + + static void __stdcall ConvertToBaseType(LPWSTR pszDest, int nDestLength, LPCWSTR pszSrc, int nSrcLength = -1) + { + if (nSrcLength == -1) { nSrcLength=1 + GetBaseTypeLength(pszSrc); } + // nLen is in wchar_ts + #if _MSC_VER >= 1400 + wmemcpy_s(pszDest, nDestLength, pszSrc, nSrcLength); + #else + wmemcpy(pszDest, pszSrc, nDestLength); + #endif + } + + static void __stdcall FloodCharacters(wchar_t ch, int nLength, LPWSTR psz) + { + // nLength is in XCHARs + for (int i = 0; i < nLength; i++) + { + psz[i] = ch; + } + } + + static BSTR __stdcall AllocSysString(const wchar_t* pchData, int nDataLength) + { + return ::SysAllocStringLen(pchData, nDataLength); + } + + static BOOL __stdcall ReAllocSysString(const wchar_t* pchData, BSTR* pbstr, int nDataLength) + { + return ::SysReAllocStringLen(pbstr, pchData, nDataLength); + } + + static int __stdcall SafeStringLen(LPCSTR psz) + { + // returns length in bytes + return (psz != NULL) ? (int)strlen(psz) : 0; + } + + static int __stdcall SafeStringLen(LPCWSTR psz) + { + // returns length in wchar_ts + return (psz != NULL) ? (int)wcslen(psz) : 0; + } + + static int __stdcall GetCharLen(const wchar_t* pch) + { + (void)pch; + // returns char length + return 1; + } + + static int __stdcall GetCharLen(const char* pch) + { + // returns char length + return (int)(_mbclen(reinterpret_cast< const unsigned char* >(pch))); + } + + static DWORD __stdcall GetEnvironmentVariable(LPCWSTR pszVar, LPWSTR pszBuffer, DWORD dwSize) + { + return _GetEnvironmentVariableW(pszVar, pszBuffer, dwSize); + } + + static void __stdcall ConvertToOem(LPWSTR /*psz*/) + { + } + + static void __stdcall ConvertToAnsi(LPWSTR /*psz*/) + { + } + + static void __stdcall ConvertToOem(LPWSTR /*psz*/, size_t) + { + } + + static void __stdcall ConvertToAnsi(LPWSTR /*psz*/, size_t) + { + } +}; + +template< typename BaseType, class StringTraits > +class CMStringT : public CMSimpleStringT< BaseType > +{ +public: + typedef CMSimpleStringT< BaseType> CThisSimpleString; + typedef typename CThisSimpleString::XCHAR XCHAR; + typedef typename CThisSimpleString::PXSTR PXSTR; + typedef typename CThisSimpleString::PCXSTR PCXSTR; + typedef typename CThisSimpleString::YCHAR YCHAR; + typedef typename CThisSimpleString::PYSTR PYSTR; + typedef typename CThisSimpleString::PCYSTR PCYSTR; + +public: + CMStringT() : CThisSimpleString() + { + } + + static void __stdcall Construct(CMStringT* pString) + { + new(pString) CMStringT; + } + + // Copy constructor + CMStringT(const CMStringT& strSrc) : + CThisSimpleString(strSrc) + { + } + + CMStringT(const XCHAR* pszSrc) : + CThisSimpleString() + { + // nDestLength is in XCHARs + *this = pszSrc; + } + + CMStringT(const YCHAR* pszSrc) : + CThisSimpleString() + { + *this = pszSrc; + } + + + CMStringT(const unsigned char* pszSrc) : + CThisSimpleString() + { + *this = reinterpret_cast< const char* >(pszSrc); + } + + CMStringT(char ch, int nLength = 1) : + CThisSimpleString() + { + if (nLength > 0) + { + PXSTR pszBuffer = this->GetBuffer(nLength); + StringTraits::FloodCharacters(XCHAR(ch), nLength, pszBuffer); + this->ReleaseBufferSetLength(nLength); + } + } + + CMStringT(wchar_t ch, int nLength = 1) : + CThisSimpleString() + { + if (nLength > 0) + { + //Convert ch to the BaseType + wchar_t pszCh[2] = { ch , 0 }; + int nBaseTypeCharLen = 1; + + if (ch != L'\0') + { + nBaseTypeCharLen = StringTraits::GetBaseTypeLength(pszCh); + } + + XCHAR *buffBaseTypeChar = new XCHAR[nBaseTypeCharLen+1]; + StringTraits::ConvertToBaseType(buffBaseTypeChar, nBaseTypeCharLen+1, pszCh, 1); + //Allocate enough characters in String and flood (replicate) with the (converted character)*nLength + PXSTR pszBuffer = this->GetBuffer(nLength*nBaseTypeCharLen); + if (nBaseTypeCharLen == 1) + { //Optimization for a common case - wide char translates to 1 ansi/wide char. + StringTraits::FloodCharacters(buffBaseTypeChar[0], nLength, pszBuffer); + } else + { + XCHAR* p=pszBuffer; + for (int i=0 ; i < nLength ;i++) + { + for (int j=0 ; j < nBaseTypeCharLen ;++j) + { + *p=buffBaseTypeChar[j]; + ++p; + } + } + } + this->ReleaseBufferSetLength(nLength*nBaseTypeCharLen); + delete [] buffBaseTypeChar; + } + } + + CMStringT(const XCHAR* pch, int nLength) : + CThisSimpleString(pch, nLength) + { + } + + CMStringT(const YCHAR* pch, int nLength) : + CThisSimpleString() + { + if (nLength > 0) + { + int nDestLength = StringTraits::GetBaseTypeLength(pch, nLength); + PXSTR pszBuffer = this->GetBuffer(nDestLength); + StringTraits::ConvertToBaseType(pszBuffer, nDestLength, pch, nLength); + this->ReleaseBufferSetLength(nDestLength); + } + } + + // Destructor + ~CMStringT() + { + } + + // Assignment operators + CMStringT& operator=(const CMStringT& strSrc) + { + CThisSimpleString::operator=(strSrc); + + return *this; + } + + CMStringT& operator=(PCXSTR pszSrc) + { + CThisSimpleString::operator=(pszSrc); + + return *this; + } + + CMStringT& operator=(PCYSTR pszSrc) + { + // nDestLength is in XCHARs + int nDestLength = (pszSrc != NULL) ? StringTraits::GetBaseTypeLength(pszSrc) : 0; + if (nDestLength > 0) + { + PXSTR pszBuffer = this->GetBuffer(nDestLength); + StringTraits::ConvertToBaseType(pszBuffer, nDestLength, pszSrc); + this->ReleaseBufferSetLength(nDestLength); + } + else + { + this->Empty(); + } + + return *this; + } + + CMStringT& operator=(const unsigned char* pszSrc) + { + return operator=(reinterpret_cast< const char* >(pszSrc)); + } + + CMStringT& operator=(char ch) + { + char ach[2] = { ch, 0 }; + + return operator=(ach); + } + + CMStringT& operator=(wchar_t ch) + { + wchar_t ach[2] = { ch, 0 }; + + return operator=(ach); + } + +// CMStringT& operator=(const VARIANT& var); + + CMStringT& operator+=(const CMStringT& str) + { + CThisSimpleString::operator+=(str); + return *this; + } + + CMStringT& operator+=(const CThisSimpleString& str) + { + CThisSimpleString::operator+=(str); + return *this; + } + + CMStringT& operator+=(PCXSTR pszSrc) + { + CThisSimpleString::operator+=(pszSrc); + return *this; + } +// template< int t_nSize > +// CMStringT& operator+=(const CStaticString< XCHAR, t_nSize >& strSrc) +// { +// CThisSimpleString::operator+=(strSrc); +// +// return *this; +// } + CMStringT& operator+=(PCYSTR pszSrc) + { + CMStringT str(pszSrc); + + return operator+=(str); + } + + CMStringT& operator+=(char ch) + { + CThisSimpleString::operator+=(ch); + + return *this; + } + + CMStringT& operator+=(unsigned char ch) + { + CThisSimpleString::operator+=(ch); + + return *this; + } + + CMStringT& operator+=(wchar_t ch) + { + CThisSimpleString::operator+=(ch); + + return *this; + } + + // Comparison + + int Compare(PCXSTR psz) const + { + return StringTraits::StringCompare(this->GetString(), psz); + } + + int CompareNoCase(PCXSTR psz) const + { + return StringTraits::StringCompareIgnore(this->GetString(), psz); + } + + int Collate(PCXSTR psz) const + { + return StringTraits::StringCollate(this->GetString(), psz); + } + + int CollateNoCase(PCXSTR psz) const + { + return StringTraits::StringCollateIgnore(this->GetString(), psz); + } + + // Advanced manipulation + + // Delete 'nCount' characters, starting at index 'iIndex' + int Delete(int iIndex, int nCount = 1) + { + if (iIndex < 0) + iIndex = 0; + + if (nCount < 0) + nCount = 0; + + int nLength = this->GetLength(); + if (nCount + iIndex > nLength) + { + nCount = nLength-iIndex; + } + if (nCount > 0) + { + int nNewLength = nLength-nCount; + int nXCHARsToCopy = nLength-(iIndex+nCount)+1; + PXSTR pszBuffer = this->GetBuffer(); + #if _MSC_VER >= 1400 + memmove_s(pszBuffer+iIndex, nXCHARsToCopy*sizeof(XCHAR), pszBuffer+iIndex+nCount, nXCHARsToCopy*sizeof(XCHAR)); + #else + memmove(pszBuffer+iIndex, pszBuffer+iIndex+nCount, nXCHARsToCopy*sizeof(XCHAR)); + #endif + this->ReleaseBufferSetLength(nNewLength); + } + + return this->GetLength(); + } + + // Insert character 'ch' before index 'iIndex' + int Insert(int iIndex, XCHAR ch) + { + if (iIndex < 0) + iIndex = 0; + + if (iIndex > this->GetLength()) + iIndex = this->GetLength(); + + int nNewLength = this->GetLength()+1; + + PXSTR pszBuffer = this->GetBuffer(nNewLength); + + // move existing bytes down + #if _MSC_VER >= 1400 + memmove_s(pszBuffer+iIndex+1, (nNewLength-iIndex)*sizeof(XCHAR), pszBuffer+iIndex, (nNewLength-iIndex)*sizeof(XCHAR)); + #else + memmove(pszBuffer+iIndex+1, pszBuffer+iIndex, (nNewLength-iIndex)*sizeof(XCHAR)); + #endif + pszBuffer[iIndex] = ch; + + this->ReleaseBufferSetLength(nNewLength); + return nNewLength; + } + + // Insert string 'psz' before index 'iIndex' + int Insert(int iIndex, PCXSTR psz) + { + if (iIndex < 0) + iIndex = 0; + + if (iIndex > this->GetLength()) + { + iIndex = this->GetLength(); + } + + // nInsertLength and nNewLength are in XCHARs + int nInsertLength = StringTraits::SafeStringLen(psz); + int nNewLength = this->GetLength(); + if (nInsertLength > 0) + { + nNewLength += nInsertLength; + + PXSTR pszBuffer = this->GetBuffer(nNewLength); + // move existing bytes down + #if _MSC_VER >= 1400 + memmove_s(pszBuffer+iIndex+nInsertLength, (nNewLength-iIndex-nInsertLength+1)*sizeof(XCHAR), pszBuffer+iIndex, (nNewLength-iIndex-nInsertLength+1)*sizeof(XCHAR)); + memcpy_s(pszBuffer+iIndex, nInsertLength*sizeof(XCHAR), psz, nInsertLength*sizeof(XCHAR)); + #else + memmove(pszBuffer+iIndex+nInsertLength, pszBuffer+iIndex, (nNewLength-iIndex-nInsertLength+1)*sizeof(XCHAR)); + memcpy(pszBuffer+iIndex, psz, nInsertLength*sizeof(XCHAR)); + #endif + this->ReleaseBufferSetLength(nNewLength); + } + + return nNewLength; + } + + // Replace all occurrences of character 'chOld' with character 'chNew' + int Replace(XCHAR chOld, XCHAR chNew) + { + int nCount = 0; + + // short-circuit the nop case + if (chOld != chNew) + { + // otherwise modify each character that matches in the string + bool bCopied = false; + PXSTR pszBuffer = const_cast< PXSTR >(this->GetString()); // We don't actually write to pszBuffer until we've called GetBuffer(). + + int nLength = this->GetLength(); + int iChar = 0; + while(iChar < nLength) + { + // replace instances of the specified character only + if (pszBuffer[iChar] == chOld) + { + if ( !bCopied) + { + bCopied = true; + pszBuffer = this->GetBuffer(nLength); + } + pszBuffer[iChar] = chNew; + nCount++; + } + iChar = int(StringTraits::CharNext(pszBuffer+iChar)-pszBuffer); + } + if (bCopied) + { + this->ReleaseBufferSetLength(nLength); + } + } + + return nCount; + } + + // Replace all occurrences of string 'pszOld' with string 'pszNew' + int Replace(PCXSTR pszOld, PCXSTR pszNew) + { + // can't have empty or NULL lpszOld + + // nSourceLen is in XCHARs + int nSourceLen = StringTraits::SafeStringLen(pszOld); + if (nSourceLen == 0) + return 0; + // nReplacementLen is in XCHARs + int nReplacementLen = StringTraits::SafeStringLen(pszNew); + + // loop once to figure out the size of the result string + int nCount = 0; + { + PCXSTR pszStart = this->GetString(); + PCXSTR pszEnd = pszStart+this->GetLength(); + while(pszStart < pszEnd) + { + PCXSTR pszTarget; + while((pszTarget = StringTraits::StringFindString(pszStart, pszOld)) != NULL) + { + nCount++; + pszStart = pszTarget+nSourceLen; + } + pszStart += StringTraits::SafeStringLen(pszStart)+1; + } + } + + // if any changes were made, make them + if (nCount > 0) + { + // if the buffer is too small, just + // allocate a new buffer (slow but sure) + int nOldLength = this->GetLength(); + int nNewLength = nOldLength+(nReplacementLen-nSourceLen)*nCount; + + PXSTR pszBuffer = this->GetBuffer(__max(nNewLength, nOldLength)); + + PXSTR pszStart = pszBuffer; + PXSTR pszEnd = pszStart+nOldLength; + + // loop again to actually do the work + while(pszStart < pszEnd) + { + PXSTR pszTarget; + while((pszTarget = StringTraits::StringFindString(pszStart, pszOld)) != NULL) + { + int nBalance = nOldLength-int(pszTarget-pszBuffer+nSourceLen); + memmove_s(pszTarget+nReplacementLen, nBalance*sizeof(XCHAR), + pszTarget+nSourceLen, nBalance*sizeof(XCHAR)); + memcpy_s(pszTarget, nReplacementLen*sizeof(XCHAR), + pszNew, nReplacementLen*sizeof(XCHAR)); + pszStart = pszTarget+nReplacementLen; + pszTarget[nReplacementLen+nBalance] = 0; + nOldLength += (nReplacementLen-nSourceLen); + } + pszStart += StringTraits::SafeStringLen(pszStart)+1; + } + this->ReleaseBufferSetLength(nNewLength); + } + + return nCount; + } + + // Remove all occurrences of character 'chRemove' + int Remove(XCHAR chRemove) + { + int nLength = this->GetLength(); + PXSTR pszBuffer = this->GetBuffer(nLength); + + PXSTR pszSource = pszBuffer; + PXSTR pszDest = pszBuffer; + PXSTR pszEnd = pszBuffer+nLength; + + while(pszSource < pszEnd) + { + PXSTR pszNewSource = StringTraits::CharNext(pszSource); + if (*pszSource != chRemove) + { + // Copy the source to the destination. Remember to copy all bytes of an MBCS character + // Copy the source to the destination. Remember to copy all bytes of an MBCS character + size_t NewSourceGap = (pszNewSource-pszSource); + PXSTR pszNewDest = pszDest + NewSourceGap; + size_t i = 0; + for (i = 0; pszDest != pszNewDest && i < NewSourceGap; i++) + { + *pszDest = *pszSource; + pszSource++; + pszDest++; + } + } + pszSource = pszNewSource; + } + *pszDest = 0; + int nCount = int(pszSource-pszDest); + this->ReleaseBufferSetLength(nLength-nCount); + + return nCount; + } + + CMStringT Tokenize(PCXSTR pszTokens, int& iStart) const + { + if ((pszTokens == NULL) || (*pszTokens == (XCHAR)0)) + { + if (iStart < this->GetLength()) + return CMStringT(this->GetString()+iStart); + } + else + { + PCXSTR pszPlace = this->GetString()+iStart; + PCXSTR pszEnd = this->GetString()+this->GetLength(); + if (pszPlace < pszEnd) + { + int nIncluding = StringTraits::StringSpanIncluding(pszPlace, pszTokens); + + if ((pszPlace+nIncluding) < pszEnd) + { + pszPlace += nIncluding; + int nExcluding = StringTraits::StringSpanExcluding(pszPlace, pszTokens); + + int iFrom = iStart+nIncluding; + int nUntil = nExcluding; + iStart = iFrom+nUntil+1; + + return Mid(iFrom, nUntil); + } + } + } + + // return empty string, done tokenizing + iStart = -1; + + return CMStringT(); + } + + // find routines + + // Find the first occurrence of character 'ch', starting at index 'iStart' + int Find(XCHAR ch, int iStart = 0) const + { + // nLength is in XCHARs + int nLength = this->GetLength(); + if (iStart < 0 || iStart >= nLength) + return -1; + + // find first single character + PCXSTR psz = StringTraits::StringFindChar(this->GetString()+iStart, ch); + + // return -1 if not found and index otherwise + return (psz == NULL) ? -1 : int(psz-this->GetString()); + } + + // look for a specific sub-string + + // Find the first occurrence of string 'pszSub', starting at index 'iStart' + int Find(PCXSTR pszSub, int iStart = 0) const + { + // iStart is in XCHARs + if (pszSub == NULL) + return -1; + + // nLength is in XCHARs + int nLength = this->GetLength(); + if (iStart < 0 || iStart > nLength) + return -1; + + // find first matching substring + PCXSTR psz = StringTraits::StringFindString(this->GetString()+iStart, pszSub); + + // return -1 for not found, distance from beginning otherwise + return (psz == NULL) ? -1 : int(psz-this->GetString()); + } + + // Find the first occurrence of any of the characters in string 'pszCharSet' + int FindOneOf(PCXSTR pszCharSet) const + { + PCXSTR psz = StringTraits::StringScanSet(this->GetString(), pszCharSet); + return (psz == NULL) ? -1 : int(psz-this->GetString()); + } + + // Find the last occurrence of character 'ch' + int ReverseFind(XCHAR ch) const + { + // find last single character + PCXSTR psz = StringTraits::StringFindCharRev(this->GetString(), ch); + + // return -1 if not found, distance from beginning otherwise + return (psz == NULL) ? -1 : int(psz-this->GetString()); + } + + // manipulation + + // Convert the string to uppercase + CMStringT& MakeUpper() + { + int nLength = this->GetLength(); + PXSTR pszBuffer = this->GetBuffer(nLength); + StringTraits::StringUppercase(pszBuffer, nLength+1); + this->ReleaseBufferSetLength(nLength); + + return *this; + } + + // Convert the string to lowercase + CMStringT& MakeLower() + { + int nLength = this->GetLength(); + PXSTR pszBuffer = this->GetBuffer(nLength); + StringTraits::StringLowercase(pszBuffer, nLength+1); + this->ReleaseBufferSetLength(nLength); + + return *this; + } + + // Reverse the string + CMStringT& MakeReverse() + { + int nLength = this->GetLength(); + PXSTR pszBuffer = this->GetBuffer(nLength); + StringTraits::StringReverse(pszBuffer); + this->ReleaseBufferSetLength(nLength); + + return *this; + } + + // trimming + + // Remove all trailing whitespace + CMStringT& TrimRight() + { + // find beginning of trailing spaces by starting + // at beginning (DBCS aware) + + PCXSTR psz = this->GetString(); + PCXSTR pszLast = NULL; + + while(*psz != 0) + { + if (StringTraits::IsSpace(*psz)) + { + if (pszLast == NULL) + pszLast = psz; + } + else + { + pszLast = NULL; + } + psz = StringTraits::CharNext(psz); + } + + if (pszLast != NULL) + { + // truncate at trailing space start + int iLast = int(pszLast-this->GetString()); + + this->Truncate(iLast); + } + + return *this; + } + + // Remove all leading whitespace + CMStringT& TrimLeft() + { + // find first non-space character + + PCXSTR psz = this->GetString(); + + while(StringTraits::IsSpace(*psz)) + { + psz = StringTraits::CharNext(psz); + } + + if (psz != this->GetString()) + { + // fix up data and length + int iFirst = int(psz-this->GetString()); + PXSTR pszBuffer = this->GetBuffer(this->GetLength()); + psz = pszBuffer+iFirst; + int nDataLength = this->GetLength()-iFirst; + memmove_s(pszBuffer, (this->GetLength()+1)*sizeof(XCHAR), + psz, (nDataLength+1)*sizeof(XCHAR)); + this->ReleaseBufferSetLength(nDataLength); + } + + return *this; + } + + // Remove all leading and trailing whitespace + CMStringT& Trim() + { + return TrimRight().TrimLeft(); + } + + // Remove all leading and trailing occurrences of character 'chTarget' + CMStringT& Trim(XCHAR chTarget) + { + return TrimRight(chTarget).TrimLeft(chTarget); + } + + // Remove all leading and trailing occurrences of any of the characters in the string 'pszTargets' + CMStringT& Trim(PCXSTR pszTargets) + { + return TrimRight(pszTargets).TrimLeft(pszTargets); + } + + // trimming anything (either side) + + // Remove all trailing occurrences of character 'chTarget' + CMStringT& TrimRight(XCHAR chTarget) + { + // find beginning of trailing matches + // by starting at beginning (DBCS aware) + + PCXSTR psz = this->GetString(); + PCXSTR pszLast = NULL; + + while(*psz != 0) + { + if (*psz == chTarget) + { + if (pszLast == NULL) + { + pszLast = psz; + } + } + else + { + pszLast = NULL; + } + psz = StringTraits::CharNext(psz); + } + + if (pszLast != NULL) + { + // truncate at left-most matching character + int iLast = int(pszLast-this->GetString()); + this->Truncate(iLast); + } + + return *this; + } + + // Remove all trailing occurrences of any of the characters in string 'pszTargets' + CMStringT& TrimRight(PCXSTR pszTargets) + { + // if we're not trimming anything, we're not doing any work + if ((pszTargets == NULL) || (*pszTargets == 0)) + { + return *this; + } + + // find beginning of trailing matches + // by starting at beginning (DBCS aware) + + PCXSTR psz = this->GetString(); + PCXSTR pszLast = NULL; + + while(*psz != 0) + { + if (StringTraits::StringFindChar(pszTargets, *psz) != NULL) + { + if (pszLast == NULL) + { + pszLast = psz; + } + } + else + { + pszLast = NULL; + } + psz = StringTraits::CharNext(psz); + } + + if (pszLast != NULL) + { + // truncate at left-most matching character + int iLast = int(pszLast-this->GetString()); + this->Truncate(iLast); + } + + return *this; + } + + // Remove all leading occurrences of character 'chTarget' + CMStringT& TrimLeft(XCHAR chTarget) + { + // find first non-matching character + PCXSTR psz = this->GetString(); + + while(chTarget == *psz) + { + psz = StringTraits::CharNext(psz); + } + + if (psz != this->GetString()) + { + // fix up data and length + int iFirst = int(psz-this->GetString()); + PXSTR pszBuffer = this->GetBuffer(this->GetLength()); + psz = pszBuffer+iFirst; + int nDataLength = this->GetLength()-iFirst; + memmove_s(pszBuffer, (this->GetLength()+1)*sizeof(XCHAR), + psz, (nDataLength+1)*sizeof(XCHAR)); + this->ReleaseBufferSetLength(nDataLength); + } + + return *this; + } + + // Remove all leading occurrences of any of the characters in string 'pszTargets' + CMStringT& TrimLeft(PCXSTR pszTargets) + { + // if we're not trimming anything, we're not doing any work + if ((pszTargets == NULL) || (*pszTargets == 0)) + { + return *this; + } + + PCXSTR psz = this->GetString(); + while((*psz != 0) && (StringTraits::StringFindChar(pszTargets, *psz) != NULL)) + { + psz = StringTraits::CharNext(psz); + } + + if (psz != this->GetString()) + { + // fix up data and length + int iFirst = int(psz-this->GetString()); + PXSTR pszBuffer = this->GetBuffer(this->GetLength()); + psz = pszBuffer+iFirst; + int nDataLength = this->GetLength()-iFirst; + memmove_s(pszBuffer, (this->GetLength()+1)*sizeof(XCHAR), + psz, (nDataLength+1)*sizeof(XCHAR)); + this->ReleaseBufferSetLength(nDataLength); + } + + return *this; + } + + // Convert the string to the OEM character set + void AnsiToOem() + { + int nLength = this->GetLength(); + PXSTR pszBuffer = this->GetBuffer(nLength); + StringTraits::ConvertToOem(pszBuffer, nLength+1); + this->ReleaseBufferSetLength(nLength); + } + + // Convert the string to the ANSI character set + void OemToAnsi() + { + int nLength = this->GetLength(); + PXSTR pszBuffer = this->GetBuffer(nLength); + StringTraits::ConvertToAnsi(pszBuffer, nLength+1); + this->ReleaseBufferSetLength(nLength); + } + + // Very simple sub-string extraction + + // Return the substring starting at index 'iFirst' + CMStringT Mid(int iFirst) const + { + return Mid(iFirst, this->GetLength()-iFirst); + } + + // Return the substring starting at index 'iFirst', with length 'nCount' + CMStringT Mid(int iFirst, int nCount) const + { + // nCount is in XCHARs + + // out-of-bounds requests return sensible things + if (iFirst < 0) + iFirst = 0; + if (nCount < 0) + nCount = 0; + + if ((iFirst + nCount) > this->GetLength()) + nCount = this->GetLength()-iFirst; + + if (iFirst > this->GetLength()) + nCount = 0; + + // optimize case of returning entire string + if ((iFirst == 0) && ((iFirst+nCount) == this->GetLength())) + return *this; + + return CMStringT(this->GetString()+iFirst, nCount); + } + + // Return the substring consisting of the rightmost 'nCount' characters + CMStringT Right(int nCount) const + { + // nCount is in XCHARs + if (nCount < 0) + nCount = 0; + + int nLength = this->GetLength(); + if (nCount >= nLength) + { + return *this; + } + + return CMStringT(this->GetString()+nLength-nCount, nCount); + } + + // Return the substring consisting of the leftmost 'nCount' characters + CMStringT Left(int nCount) const + { + // nCount is in XCHARs + if (nCount < 0) + nCount = 0; + + int nLength = this->GetLength(); + if (nCount >= nLength) + return *this; + + return CMStringT(this->GetString(), nCount); + } + + // Return the substring consisting of the leftmost characters in the set 'pszCharSet' + CMStringT SpanIncluding(PCXSTR pszCharSet) const + { + return Left(StringTraits::StringSpanIncluding(this->GetString(), pszCharSet)); + } + + // Return the substring consisting of the leftmost characters not in the set 'pszCharSet' + CMStringT SpanExcluding(PCXSTR pszCharSet) const + { + return Left(StringTraits::StringSpanExcluding(this->GetString(), pszCharSet)); + } + + // Format data using format string 'pszFormat' + void Format(PCXSTR pszFormat, ...); + + // Append formatted data using format string 'pszFormat' + void AppendFormat(PCXSTR pszFormat, ...); + + void AppendFormatV(PCXSTR pszFormat, va_list args) + { + int nCurrentLength = this->GetLength(); + int nAppendLength = StringTraits::GetFormattedLength(pszFormat, args); + PXSTR pszBuffer = this->GetBuffer(nCurrentLength+nAppendLength); + StringTraits::Format(pszBuffer+nCurrentLength, + nAppendLength+1, pszFormat, args); + this->ReleaseBufferSetLength(nCurrentLength+nAppendLength); + } + + void FormatV(PCXSTR pszFormat, va_list args) + { + int nLength = StringTraits::GetFormattedLength(pszFormat, args); + PXSTR pszBuffer = this->GetBuffer(nLength); + StringTraits::Format(pszBuffer, nLength+1, pszFormat, args); + this->ReleaseBufferSetLength(nLength); + } + + // OLE BSTR support + + // Allocate a BSTR containing a copy of the string + BSTR AllocSysString() const + { + BSTR bstrResult = StringTraits::AllocSysString(this->GetString(), this->GetLength()); + return bstrResult; + } + + BSTR SetSysString(BSTR* pbstr) const + { + StringTraits::ReAllocSysString(this->GetString(), pbstr, this->GetLength()); + return *pbstr; + } + + // Set the string to the value of environment variable 'pszVar' + BOOL GetEnvironmentVariable(PCXSTR pszVar) + { + ULONG nLength = StringTraits::GetEnvironmentVariable(pszVar, NULL, 0); + BOOL bRetVal = FALSE; + + if (nLength == 0) + { + this->Empty(); + } + else + { + PXSTR pszBuffer = this->GetBuffer(nLength); + StringTraits::GetEnvironmentVariable(pszVar, pszBuffer, nLength); + this->ReleaseBuffer(); + bRetVal = TRUE; + } + + return bRetVal; + } + + // Load the string from resource 'nID' + BOOL LoadString(UINT nID) + { + HINSTANCE hInst = StringTraits::FindStringResourceInstance(nID); + if (hInst == NULL) + return FALSE; + + return LoadString(hInst, nID); + } + + friend CMStringT __stdcall operator+(const CMStringT& str1, const CMStringT& str2) + { + CMStringT strResult; + + Concatenate(strResult, str1, str1.GetLength(), str2, str2.GetLength()); + + return strResult; + } + + friend CMStringT __stdcall operator+(const CMStringT& str1, PCXSTR psz2) + { + CMStringT strResult; + + Concatenate(strResult, str1, str1.GetLength(), psz2, StringLength(psz2)); + + return strResult; + } + + friend CMStringT __stdcall operator+(PCXSTR psz1, const CMStringT& str2) + { + CMStringT strResult; + + Concatenate(strResult, psz1, StringLength(psz1), str2, str2.GetLength()); + + return strResult; + } + + friend CMStringT __stdcall operator+(const CMStringT& str1, wchar_t ch2) + { + CMStringT strResult; + XCHAR chTemp = XCHAR(ch2); + + Concatenate(strResult, str1, str1.GetLength(), &chTemp, 1); + + return strResult; + } + + friend CMStringT __stdcall operator+(const CMStringT& str1, char ch2) + { + CMStringT strResult; + XCHAR chTemp = XCHAR(ch2); + + Concatenate(strResult, str1, str1.GetLength(), &chTemp, 1); + + return strResult; + } + + friend CMStringT __stdcall operator+(wchar_t ch1, const CMStringT& str2) + { + CMStringT strResult; + XCHAR chTemp = XCHAR(ch1); + + Concatenate(strResult, &chTemp, 1, str2, str2.GetLength()); + + return strResult; + } + + friend CMStringT __stdcall operator+(char ch1, const CMStringT& str2) + { + CMStringT strResult; + XCHAR chTemp = XCHAR(ch1); + + Concatenate(strResult, &chTemp, 1, str2, str2.GetLength()); + + return strResult; + } + + friend bool __stdcall operator==(const CMStringT& str1, const CMStringT& str2) + { + return str1.Compare(str2) == 0; + } + + friend bool __stdcall operator==(const CMStringT& str1, PCXSTR psz2) + { + return str1.Compare(psz2) == 0; + } + + friend bool __stdcall operator==(PCXSTR psz1, const CMStringT& str2) + { + return str2.Compare(psz1) == 0; + } + + friend bool __stdcall operator==(const CMStringT& str1, PCYSTR psz2) + { + CMStringT str2(psz2); + + return str1 == str2; + } + + friend bool __stdcall operator==(PCYSTR psz1, const CMStringT& str2) + { + CMStringT str1(psz1); + + return str1 == str2; + } + + friend bool __stdcall operator!=(const CMStringT& str1, const CMStringT& str2) + { + return str1.Compare(str2) != 0; + } + + friend bool __stdcall operator!=(const CMStringT& str1, PCXSTR psz2) + { + return str1.Compare(psz2) != 0; + } + + friend bool __stdcall operator!=(PCXSTR psz1, const CMStringT& str2) + { + return str2.Compare(psz1) != 0; + } + + friend bool __stdcall operator!=(const CMStringT& str1, PCYSTR psz2) + { + CMStringT str2(psz2); + + return str1 != str2; + } + + friend bool __stdcall operator!=(PCYSTR psz1, const CMStringT& str2) + { + CMStringT str1(psz1); + + return str1 != str2; + } + + friend bool __stdcall operator<(const CMStringT& str1, const CMStringT& str2) + { + return str1.Compare(str2) < 0; + } + + friend bool __stdcall operator<(const CMStringT& str1, PCXSTR psz2) + { + return str1.Compare(psz2) < 0; + } + + friend bool __stdcall operator<(PCXSTR psz1, const CMStringT& str2) + { + return str2.Compare(psz1) > 0; + } + + friend bool __stdcall operator>(const CMStringT& str1, const CMStringT& str2) + { + return str1.Compare(str2) > 0; + } + + friend bool __stdcall operator>(const CMStringT& str1, PCXSTR psz2) + { + return str1.Compare(psz2) > 0; + } + + friend bool __stdcall operator>(PCXSTR psz1, const CMStringT& str2) + { + return str2.Compare(psz1) < 0; + } + + friend bool __stdcall operator<=(const CMStringT& str1, const CMStringT& str2) + { + return str1.Compare(str2) <= 0; + } + + friend bool __stdcall operator<=(const CMStringT& str1, PCXSTR psz2) + { + return str1.Compare(psz2) <= 0; + } + + friend bool __stdcall operator<=(PCXSTR psz1, const CMStringT& str2) + { + return str2.Compare(psz1) >= 0; + } + + friend bool __stdcall operator>=(const CMStringT& str1, const CMStringT& str2) + { + return str1.Compare(str2) >= 0; + } + + friend bool __stdcall operator>=(const CMStringT& str1, PCXSTR psz2) + { + return str1.Compare(psz2) >= 0; + } + + friend bool __stdcall operator>=(PCXSTR psz1, const CMStringT& str2) + { + return str2.Compare(psz1) <= 0; + } + + friend bool __stdcall operator==(XCHAR ch1, const CMStringT& str2) + { + return (str2.GetLength() == 1) && (str2[0] == ch1); + } + + friend bool __stdcall operator==(const CMStringT& str1, XCHAR ch2) + { + return (str1.GetLength() == 1) && (str1[0] == ch2); + } + + friend bool __stdcall operator!=(XCHAR ch1, const CMStringT& str2) + { + return (str2.GetLength() != 1) || (str2[0] != ch1); + } + + friend bool __stdcall operator!=(const CMStringT& str1, XCHAR ch2) + { + return (str1.GetLength() != 1) || (str1[0] != ch2); + } +}; + +template< typename BaseType, class StringTraits > +inline void CMStringT::Format(PCXSTR pszFormat, ...) +{ + va_list argList; + va_start(argList, pszFormat); + FormatV(pszFormat, argList); + va_end(argList); +} + +template< typename BaseType, class StringTraits > +inline void CMStringT::AppendFormat(PCXSTR pszFormat, ...) +{ + va_list argList; + va_start(argList, pszFormat); + AppendFormatV(pszFormat, argList); + va_end(argList); +} + +typedef CMStringT< wchar_t, ChTraitsCRT< wchar_t > > CMStringW; +typedef CMStringT< char, ChTraitsCRT< char > > CMStringA; +typedef CMStringT< TCHAR, ChTraitsCRT< TCHAR > > CMString; diff --git a/src/mir_core/mir_core.def b/src/mir_core/mir_core.def index f9e990ab40..bfd34cb47d 100644 --- a/src/mir_core/mir_core.def +++ b/src/mir_core/mir_core.def @@ -231,3 +231,10 @@ bin2hex @228 bin2hexW @229 mir_hmac_sha1 @230 mir_base64_encodebuf @231 +mirstr_allocate @232 +mirstr_free @233 +mirstr_realloc @234 +mirstr_getNil @235 +mirstr_lock @236 +mirstr_release @237 +mirstr_unlock @238 diff --git a/src/mir_core/mir_core_10.vcxproj b/src/mir_core/mir_core_10.vcxproj index 0b5efe22f5..cc805973b3 100644 --- a/src/mir_core/mir_core_10.vcxproj +++ b/src/mir_core/mir_core_10.vcxproj @@ -99,6 +99,7 @@ ..\commonheaders.h + Create diff --git a/src/mir_core/mir_core_10.vcxproj.filters b/src/mir_core/mir_core_10.vcxproj.filters index 1fafc175f9..b4fe537533 100644 --- a/src/mir_core/mir_core_10.vcxproj.filters +++ b/src/mir_core/mir_core_10.vcxproj.filters @@ -100,6 +100,9 @@ Source Files + + Source Files + diff --git a/src/mir_core/mir_core_11.vcxproj b/src/mir_core/mir_core_11.vcxproj index e30f2719cc..42572f7442 100644 --- a/src/mir_core/mir_core_11.vcxproj +++ b/src/mir_core/mir_core_11.vcxproj @@ -94,6 +94,7 @@ ../commonheaders.h + Create diff --git a/src/mir_core/mir_core_11.vcxproj.filters b/src/mir_core/mir_core_11.vcxproj.filters index 75eaf973e4..0b660b44ed 100644 --- a/src/mir_core/mir_core_11.vcxproj.filters +++ b/src/mir_core/mir_core_11.vcxproj.filters @@ -97,6 +97,9 @@ Source Files + + Source Files + diff --git a/src/mir_core/mstring.cpp b/src/mir_core/mstring.cpp new file mode 100644 index 0000000000..fc1b4d60f8 --- /dev/null +++ b/src/mir_core/mstring.cpp @@ -0,0 +1,144 @@ +/* + +Miranda IM: the free IM client for Microsoft* Windows* + +Copyright 2000-12 Miranda IM, 2012-13 Miranda NG project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "commonheaders.h" +#include "m_string.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// CMBaseString + +class CNilMStringData : public CMStringData +{ +public: + CNilMStringData(); + +public: + wchar_t achNil[2]; +}; + +CNilMStringData::CNilMStringData() +{ + nRefs = 2; // Never gets freed + nDataLength = 0; + nAllocLength = 0; + achNil[0] = 0; + achNil[1] = 0; +} + +static CNilMStringData m_nil; + +///////////////////////////////////////////////////////////////////////////////////////// +// CMBaseString + +MIR_CORE_DLL(CMStringData*) mirstr_allocate(int nChars, int nCharSize) +{ + nChars++; // nil char + size_t nDataBytes = nCharSize * nChars; + size_t nTotalSize = nDataBytes + sizeof(CMStringData); + + CMStringData *pData = static_cast(malloc(nTotalSize)); + if (pData == NULL) + return NULL; + + pData->nRefs = 1; + pData->nAllocLength = nChars - 1; + pData->nDataLength = 0; + return pData; +} + +MIR_CORE_DLL(void) mirstr_free(CMStringData *pData) +{ + free(pData); +} + +MIR_CORE_DLL(CMStringData*) mirstr_realloc(CMStringData* pData, int nChars, int nCharSize) +{ + nChars++; // nil char + ULONG nDataBytes = nCharSize * nChars; + ULONG nTotalSize = nDataBytes + sizeof(CMStringData); + + CMStringData *pNewData = static_cast(realloc(pData, nTotalSize)); + if (pNewData == NULL) + return NULL; + + pNewData->nAllocLength = nChars - 1; + return pNewData; +} + +MIR_CORE_DLL(CMStringData*) mirstr_getNil() +{ + m_nil.AddRef(); + return &m_nil; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// CMStringData + +MIR_CORE_DLL(void) mirstr_lock(CMStringData* pThis) +{ + pThis->nRefs--; // Locked buffers can't be shared, so no interlocked operation necessary + if (pThis->nRefs == 0) + pThis->nRefs = -1; +} + +MIR_CORE_DLL(void) mirstr_release(CMStringData* pThis) +{ + if (InterlockedDecrement(&pThis->nRefs) <= 0) + mirstr_free(pThis); +} + +MIR_CORE_DLL(void) mirstr_unlock(CMStringData* pThis) +{ + if (pThis->IsLocked()) + { + pThis->nRefs++; // Locked buffers can't be shared, so no interlocked operation necessary + if (pThis->nRefs == 0) + pThis->nRefs = 1; + } +} + +///////////////////////////////////////////////////////////////////////////////////////// +// ChTraitsCRT + +int __stdcall ChTraitsCRT::GetFormattedLength( LPCWSTR pszFormat, va_list args ) +{ + return _vscwprintf(pszFormat, args); +} + +int __stdcall ChTraitsCRT::Format( LPWSTR pszBuffer, size_t nLength, LPCWSTR pszFormat, va_list args) +{ + return _vsnwprintf(pszBuffer, nLength, pszFormat, args); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// ChTraitsCRT + +int __stdcall ChTraitsCRT::GetFormattedLength( LPCSTR pszFormat, va_list args ) +{ + return _vscprintf(pszFormat, args); +} + +int __stdcall ChTraitsCRT::Format( LPSTR pszBuffer, size_t nlength, LPCSTR pszFormat, va_list args ) +{ + return vsprintf_s(pszBuffer, nlength, pszFormat, args); +} -- cgit v1.2.3