"Fossies" - the Fresh Open Source Software Archive

Member "AutoHotkey_L-1.1.33.09/source/KuString.h" (8 May 2021, 31483 Bytes) of package /windows/misc/AutoHotkey_L-1.1.33.09.zip:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "KuString.h" see the Fossies "Dox" file reference documentation.

    1 /* This file is part of KuShellExtension
    2  * Copyright (C) 2008-2009 Kai-Chieh Ku (kjackie@gmail.com)
    3  *
    4  * This program is free software; you can redistribute it and/or
    5  * modify it under the terms of the GNU General Public License
    6  * as published by the Free Software Foundation; either
    7  * version 3 of the License, or (at your option) any later version.
    8  *
    9  * This program is distributed in the hope that it will be useful,
   10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
   12  * GNU General Public License for more details.
   13  *
   14  * You should have received a copy of the GNU General Public License
   15  * along with this program; if not, write to the Free Software
   16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
   17  */
   18 
   19 #pragma once
   20 
   21 /*
   22     CKuStringT: A copy on write CString-like string class.
   23 
   24     The "copy on write" means,
   25         CKuString s1, s2, s3, s4;
   26         s1 = s2 = s3 = s4 = "this is a string";
   27     will share the same buffer, which saves spaces and does faster when copying strings.
   28 
   29     WARNING: It is not thread safe, to copy a string from a thread to another thread, use following syntax
   30     s2 = s1.GetString(); // s2 will have its own buffer, which is not shared by other instances.
   31 */
   32 
   33 #ifdef _WIN32
   34 #define WIN32_LEAN_AND_MEAN
   35 #define _CRT_SECURE_NO_WARNINGS
   36 #define _CRT_NON_CONFORMING_SWPRINTFS
   37 #include <windows.h>
   38 #define strcasecmp stricmp
   39 #define wcscasecmp wcsicmp
   40 #else
   41 #define _vsnwprintf vswprintf
   42 #endif
   43 
   44 #include <stdio.h>
   45 #include <stdlib.h>
   46 #include <stdarg.h>
   47 #include <string.h>
   48 #include <ctype.h>
   49 #ifndef NO_WCHAR // Most systems has wchar_t now, including *nix and Windows.
   50 #include <wchar.h>
   51 #include <wctype.h>
   52 #endif
   53 
   54 #ifndef ASSERT
   55 #ifdef _DEBUG
   56 #include <assert.h>
   57 #define ASSERT(expr) assert(expr)
   58 #else
   59 #define ASSERT(expr)
   60 #endif
   61 #endif
   62 
   63 #ifndef VERIFY
   64 #ifdef _DEBUG
   65 #define VERIFY(expr) ASSERT(expr)
   66 #else
   67 #define VERIFY(expr) expr
   68 #endif
   69 #endif
   70 
   71 #ifndef va_copy
   72     #ifdef _WIN32
   73         #define va_copy(dst, src) { (dst) = (src); }
   74     #endif
   75 #endif
   76 
   77 template<typename T, typename U> class CKuStringT;
   78 class CKuStringUtilA;
   79 class CKuStringUtilW;
   80 
   81 typedef CKuStringT<char, CKuStringUtilA> CKuStringA;
   82 #ifndef NO_WCHAR
   83 typedef CKuStringT<wchar_t, CKuStringUtilW> CKuStringW;
   84 #endif
   85 
   86 #if defined(_WIN32) && defined(_UNICODE) && !defined(NO_WCHAR)
   87 typedef CKuStringW CKuString;
   88 typedef CKuStringUtilW CKuStringUtil;
   89 #else
   90 typedef CKuStringA CKuString;
   91 typedef CKuStringUtilA CKuStringUtil;
   92 #endif
   93 
   94 #ifndef __CSTRINGT_H__
   95 typedef CKuStringA CStringA;
   96 #ifndef NO_WCHAR
   97 typedef CKuStringW CStringW;
   98 #endif
   99 typedef CKuString CString;
  100 #endif
  101 
  102 #ifdef _WIN32
  103 extern LPCWSTR StringCharToWChar(LPCSTR sChar, CStringW &sWChar, int iChars/* = -1*/, UINT codepage/* = CP_ACP*/);
  104 extern LPCSTR StringWCharToChar(LPCWSTR sWChar, CStringA &sChar, int iChars/* = -1*/, char chDef/* = '?'*/, UINT codepage/* = CP_ACP*/);
  105 #endif
  106 
  107 #ifdef _MSC_VER
  108 #pragma warning(push)
  109 #pragma warning(disable: 4996)
  110 #endif
  111 
  112 class CKuStringUtilA
  113 {
  114 public:
  115     inline static int FormatV(char *dest, size_t cch, const char *fmt, va_list ap)
  116     { return vsnprintf(dest, cch, fmt, ap); }
  117     inline static int Compare(const char *a, const char *b)
  118     { return strcmp(a, b); }
  119     inline static int CompareNoCase(const char *a, const char *b)
  120     { return strcasecmp(a, b); }
  121     inline static size_t SpanIncluding(const char *str, const char *strCharSet)
  122     { return strspn(str, strCharSet); }
  123     inline static size_t SpanExcluding(const char *str, const char *strCharSet)
  124     { return strcspn(str, strCharSet); }
  125     inline static char * FindString(char *haystack, const char *needle)
  126     { return strstr(haystack, needle); }
  127     inline static bool IsSpace(char c)
  128     { return !!isspace((unsigned char) c); }
  129     inline static bool IsLower(char c)
  130     { return !!islower((unsigned char) c); }
  131     inline static bool IsUpper(char c)
  132     { return !!isupper((unsigned char) c); }
  133     inline static char ToLower(char c)
  134     { return IsUpper(c) ? (char) tolower((unsigned char) c) : c; }
  135     inline static char ToUpper(char c)
  136     { return IsLower(c) ? (char) toupper((unsigned char) c) : c; }
  137     inline static DWORD GetEnvironmentVariable(const char *lpName, char *lpBuffer, DWORD nSize)
  138 #ifdef _WIN32
  139     { return ::GetEnvironmentVariableA(lpName, lpBuffer, nSize); }
  140 #else
  141     {
  142         if (lpBuffer) {
  143             int ret = 0;
  144             const char *env = getenv(lpName);
  145             while (ret < nSize && (*lpBuffer++ = *env++))
  146                 ret++;
  147             return ret;
  148         }
  149         return strlen(getenv(lpName));
  150     }
  151 #endif
  152 #ifdef _WIN32
  153     inline static bool LoadString(CKuStringA &str, HINSTANCE hInstance, UINT nID, WORD wLanguage);
  154 #endif
  155 };
  156 
  157 #ifndef NO_WCHAR
  158 class CKuStringUtilW
  159 {
  160 public:
  161     inline static int FormatV(wchar_t *dest, size_t cch, const wchar_t *fmt, va_list ap)
  162     { return _vsnwprintf(dest, cch, fmt, ap); }
  163     inline static int Compare(const wchar_t *a, const wchar_t *b)
  164     { return wcscmp(a, b); }
  165     inline static int CompareNoCase(const wchar_t *a, const wchar_t *b)
  166     { return wcscasecmp(a, b); }
  167     inline static size_t SpanIncluding(const wchar_t *str, const wchar_t *strCharSet)
  168     { return wcsspn(str, strCharSet); }
  169     inline static size_t SpanExcluding(const wchar_t *str, const wchar_t *strCharSet)
  170     { return wcscspn(str, strCharSet); }
  171     inline static wchar_t * FindString(wchar_t *haystack, const wchar_t *needle)
  172     { return wcsstr(haystack, needle); }
  173     inline static bool IsSpace(wchar_t c)
  174     { return !!iswspace((wint_t) c); }
  175     inline static bool IsLower(wchar_t c)
  176     { return !!iswlower((wint_t) c); }
  177     inline static bool IsUpper(wchar_t c)
  178     { return !!iswupper((wint_t) c); }
  179     inline static wchar_t ToLower(wchar_t c)
  180     { return IsUpper(c) ? (wchar_t) towlower((wint_t) c) : c; }
  181     inline static wchar_t ToUpper(wchar_t c)
  182     { return IsLower(c) ? (wchar_t) towupper((wint_t) c) : c; }
  183     inline static DWORD GetEnvironmentVariable(const wchar_t *lpName, wchar_t *lpBuffer, DWORD nSize)
  184 #ifdef _WIN32
  185     { return ::GetEnvironmentVariableW(lpName, lpBuffer, nSize); }
  186 #else
  187     {
  188         int ret = wcstombs(NULL, lpName, 0);
  189         if (ret < 0)
  190             return ret;
  191         char *sName = (char *) malloc(ret + 1);
  192         wcstombs(sName, lpName, ret);
  193         ret = mbstowcs(lpBuffer, getenv(sName), nSize);
  194         free(sName);
  195         return ret;
  196     }
  197 #endif
  198 #ifdef _WIN32
  199     inline static bool LoadString(CKuStringW &str, HINSTANCE hInstance, UINT nID, WORD wLanguage);
  200 #endif
  201 };
  202 #endif
  203 
  204 #ifdef _MSC_VER
  205 #pragma warning(pop) // C4996
  206 #endif
  207 
  208 template<typename T, typename U>
  209 class CKuStringT
  210 {
  211     typedef T * STRT;
  212     typedef const T * CSTRT;
  213     typedef INT_PTR SIZET;
  214 public:
  215     inline static SIZET StringLength(CSTRT str)
  216     {
  217         ASSERT(str);
  218         SIZET len = 0;
  219         while (*str++)
  220             len++;
  221         return len;
  222     }
  223     inline static void CopyChars(STRT dst, CSTRT src, SIZET len = -1)
  224     {
  225         ASSERT(src);
  226         ASSERT(dst);
  227         if (len < 0)
  228             while (*dst = *src++)
  229                 dst++;
  230         else {
  231             if (len > 0)
  232                 memcpy(dst, src, len * sizeof(T));
  233             dst[len] = 0;
  234         }
  235     }
  236     inline static void CopyCharsOverlapped(STRT dst, CSTRT src, SIZET len = -1)
  237     {
  238         ASSERT(src);
  239         ASSERT(dst);
  240         if (len < 0)
  241             len = StringLength(src);
  242         if (len > 0)
  243             memmove(dst, src, len * sizeof(T));
  244         dst[len] = 0;
  245     }
  246     inline static SIZET StringLengthSafe(CSTRT str)
  247     { return str ? StringLength(str) : 0; }
  248     inline static STRT Find(STRT str, T ch)
  249     {
  250         ASSERT(str);
  251         for (;*str;*str++)
  252             if (*str == ch)
  253                 return str;
  254         return NULL;
  255     }
  256     inline static CSTRT Find(CSTRT str, T ch)
  257     { return Find((STRT) str, ch); }
  258 
  259     inline static CSTRT GetStringSafe(CSTRT str)
  260     { return str ? str : &m_sNULL; }
  261 
  262     typedef bool (*IsFunc)(T ch, CSTRT str);
  263     inline static bool IsSpace(T ch, CSTRT sSet)
  264     { return !!U::IsSpace(ch); }
  265     inline static bool IsInSet(T ch, CSTRT sSet)
  266     { return !!Find(sSet, ch); }
  267     inline static bool IsTheChar(T ch, CSTRT sSet)
  268     { return ch == *sSet; }
  269     inline static bool IsLower(T ch, CSTRT sSet)
  270     { return U::IsLower(ch); }
  271     inline static bool IsUpper(T ch, CSTRT sSet)
  272     { return U::IsUpper(ch); }
  273 
  274     inline static CSTRT Find(CSTRT str, IsFunc isfunc, CSTRT sSet)
  275     {
  276         for (;*str;str++)
  277             if (isfunc(*str, sSet))
  278                 return str;
  279         return NULL;
  280     }
  281 private:
  282     class CKuStringDataT
  283     {
  284         friend class CKuStringT;
  285 
  286         CKuStringDataT()
  287         { Init(); }
  288         CKuStringDataT(const CKuStringDataT &d)
  289         { Init(); Copy(d); }
  290         CKuStringDataT(CSTRT src, SIZET len = -1)
  291         { Init(); Copy(src, len); }
  292         CKuStringDataT(T ch)
  293         { Init(); Alloc(1); m_sString[0] = ch; m_iLength = 1; }
  294         ~CKuStringDataT()
  295         {
  296             if (m_sData)
  297                 free(m_sData);
  298         }
  299 
  300         unsigned int AddRef() { return ++m_iRefCount; }
  301         unsigned int Release()
  302         {
  303             unsigned int uRefCount = --m_iRefCount;
  304             if (uRefCount == 0)
  305                 delete this;
  306             return uRefCount;
  307         }
  308 
  309         CKuStringDataT &Copy(CSTRT src, SIZET len = -1)
  310         {
  311             if (len == -1)
  312                 len = StringLength(src);
  313             Alloc(len);
  314             CopyChars(m_sString, src, len);
  315             m_iLength = len;
  316             return *this;
  317         }
  318         CKuStringDataT &Copy(const CKuStringDataT &d)
  319         { return Copy(d.m_sString, d.m_iLength); }
  320         bool Alloc(SIZET iCount)
  321         {
  322             ASSERT(iCount >= 0);
  323             ASSERT(m_sString >= m_sData);
  324             if (m_sData == m_sString) {
  325                 if (!m_sData)
  326                     m_sData = m_sString = (STRT) malloc((iCount + 1) * sizeof(T));
  327                 else if (iCount > m_iSize)
  328                     m_sData = m_sString = (STRT) realloc(m_sData, (iCount + 1) * sizeof(T));
  329                 if (!m_sData)
  330                     return false;
  331                 m_sData[iCount] = 0;
  332                 m_iSize = iCount;
  333             }
  334             else if (((SIZET)(m_sString - m_sData)) + iCount > m_iSize) {
  335                 STRT sNew = (STRT) malloc((iCount + 1) * sizeof(T));
  336                 if (!sNew)
  337                     return false;
  338                 CopyChars(sNew, m_sString, m_iLength);
  339                 free(m_sData);
  340                 m_sData = m_sString = sNew;
  341                 m_iSize = iCount;
  342             }
  343             return true;
  344         }
  345         void Normalize() // let offset be zero
  346         {
  347             if (m_sString != m_sData) {
  348                 ASSERT(m_sString);
  349                 ASSERT(m_sData);
  350                 if (m_iLength > 0)
  351                     CopyCharsOverlapped(m_sData, m_sString, m_iLength);
  352                 m_sString = m_sData;
  353             }
  354         }
  355         void FreeExtra(bool bForced = false)
  356         {
  357             if (!m_sData)
  358                 return;
  359             if (m_iLength == 0) {
  360                 free(m_sData);
  361                 m_sData = m_sString = NULL;
  362                 m_iSize = 0;
  363                 Alloc(0);
  364             }
  365             else if (m_iSize > (m_iLength << 1) || bForced && m_iSize > m_iLength) { // free extra space only if unused > used or forced flag is specified.
  366                 Normalize();
  367                 VERIFY( m_sData = m_sString = (STRT) realloc(m_sData, (m_iLength + 1) * sizeof(T)) );
  368                 m_iSize = m_iLength;
  369             }
  370         }
  371         void Offset(SIZET iOffset = 1)
  372         {
  373             m_sString += iOffset;
  374             m_iLength -= iOffset;
  375         }
  376         void Count()
  377         {
  378             m_sData[m_iSize] = 0;
  379             m_iLength = StringLength(m_sString);
  380         }
  381         void Init()
  382         {
  383             m_sData = m_sString = NULL;
  384             m_iSize = m_iLength = 0;
  385             m_iRefCount = 1;
  386         }
  387 
  388         STRT m_sString;     // pointer to the string
  389         STRT m_sData;       // allocated buffer
  390         SIZET m_iLength;    // current string length
  391         SIZET m_iSize;      // allocated size in chars w/o '\0'
  392         unsigned int volatile m_iRefCount; // reference counter
  393     };
  394 
  395     void Init()
  396     {
  397         m_pData = NULL;
  398         m_bDirty = false;
  399     }
  400 protected:
  401     // Make sure data is ready for write. i.e. refcount = 1
  402     void New(bool bCopy = true)
  403     {
  404         ASSERT(!m_bDirty);
  405         if (!m_pData)
  406             m_pData = new CKuStringDataT;
  407         else if (m_pData->m_iRefCount > 1) {
  408             CKuStringDataT *pData = m_pData;
  409             if (bCopy)
  410                 m_pData = new CKuStringDataT(*pData);
  411             else
  412                 m_pData = new CKuStringDataT;
  413             pData->Release();
  414         }
  415         else if (!bCopy)
  416             m_pData->m_iLength = 0;
  417     }
  418 private:
  419     CKuStringT& TrimLeft(IsFunc isfunc, CSTRT sSet)
  420     {
  421         ASSERT(isfunc);
  422         if (!IsEmpty() && isfunc(m_pData->m_sString[0], sSet)) {
  423             New();
  424             m_pData->Offset();
  425             while (isfunc(m_pData->m_sString[0], sSet))
  426                 m_pData->Offset();
  427         }
  428         return *this;
  429     }
  430     CKuStringT& TrimRight(IsFunc isfunc, CSTRT sSet)
  431     {
  432         ASSERT(isfunc);
  433         if (!IsEmpty() && isfunc(m_pData->m_sString[m_pData->m_iLength - 1] , sSet)) {
  434             New();
  435             CSTRT str = m_pData->m_sString;
  436             STRT ptr = m_pData->m_sString + m_pData->m_iLength - 2;
  437             for (;ptr >= str && isfunc(*ptr, sSet);)
  438                 ptr--;
  439             *++ptr = 0;
  440             m_pData->m_iLength = (SIZET)(ptr - str);
  441         }
  442         return *this;
  443     }
  444     CKuStringT& Trim(IsFunc isfunc, CSTRT sSet)
  445     { TrimRight(isfunc, sSet); return TrimLeft(isfunc, sSet); }
  446 public:
  447     CKuStringT()
  448     { Init(); }
  449     virtual ~CKuStringT()
  450     {
  451         if (m_pData)
  452             m_pData->Release();
  453     }
  454 
  455     SIZET GetLength() const
  456     { return m_pData ? m_pData->m_iLength : 0; }
  457 
  458     CKuStringT& Empty()
  459     {
  460         if (m_pData) {
  461             m_pData->Release();
  462             Init();
  463         }
  464         return *this;
  465     }
  466     bool IsEmpty() const
  467     { return GetLength() == 0; }
  468 
  469     CKuStringT& SetString(const CKuStringT& str)
  470     {
  471         ASSERT(!str.m_bDirty);
  472         if (str.IsEmpty())
  473             Empty();
  474         else if (m_pData != str.m_pData) {
  475             if (m_pData)
  476                 m_pData->Release();
  477             m_pData = str.m_pData;
  478             m_pData->AddRef();
  479         }
  480         return *this;
  481     }
  482     CKuStringT(const CKuStringT& str)
  483     { Init(); SetString(str); }
  484     CKuStringT&  operator = (const CKuStringT& str)
  485     { return SetString(str); }
  486 
  487     CKuStringT& SetString(CSTRT str, SIZET len = -1)
  488     {
  489         if (!str || !*str || !len)
  490             Empty();
  491         else {
  492             if (m_pData)
  493                 m_pData->Release();
  494             m_pData = new CKuStringDataT(str, len);
  495             m_bDirty = false;
  496         }
  497         return *this;
  498     }
  499     CKuStringT(CSTRT str, SIZET len = -1)
  500     { Init(); SetString(str, len); }
  501     CKuStringT&  operator = (CSTRT str)
  502     { return SetString(str); }
  503 
  504     CKuStringT& SetString(T ch)
  505     {
  506         if (m_pData)
  507             m_pData->Release();
  508         m_pData = new CKuStringDataT(ch);
  509         m_bDirty = false;
  510         return *this;
  511     }
  512     explicit CKuStringT(T ch)
  513     { Init(); SetString(ch); }
  514     CKuStringT&  operator = (T ch)
  515     { return SetString(ch); }
  516 
  517     CSTRT GetString() const
  518     { return m_pData ? GetStringSafe(m_pData->m_sString) : &m_sNULL; }
  519     operator CSTRT () const
  520     { return GetString(); }
  521 
  522     CKuStringT& SetAt(SIZET i, T ch)
  523     { ASSERT(i < GetLength()); m_pData->m_sString[i] = ch; return *this; }
  524     T GetAt(SIZET i) const
  525     { ASSERT(i < GetLength()); return m_pData->m_sString[i]; }
  526     T &GetAt(SIZET i)
  527     { ASSERT(i < GetLength()); return m_pData->m_sString[i]; }
  528     T operator [] (SIZET i) const
  529     { return GetAt(i); }
  530     T &operator [] (SIZET i)
  531     { return GetAt(i); }
  532 
  533     SIZET GetAllocLength() const
  534     { return m_pData ? m_pData->m_iSize : 0; }
  535     CKuStringT& Preallocate(int nLength)
  536     {
  537         if (!m_pData)
  538             m_pData = new CKuStringDataT;
  539         m_pData->Alloc(nLength);
  540         return *this;
  541     }
  542     CKuStringT& FreeExtra(bool bForced = false)
  543     { ASSERT(!m_bDirty); if (m_pData) m_pData->FreeExtra(bForced); return *this; }
  544 
  545 
  546     STRT GetBuffer()
  547     {
  548         New();
  549         m_bDirty = true;
  550         return m_pData->m_sString;
  551     }
  552     STRT GetBufferSetLength(SIZET len)
  553     {
  554         GetBuffer();
  555         m_pData->Alloc(len);
  556         return m_pData->m_sString;
  557     }
  558     STRT GetBuffer(SIZET len)
  559     { return GetBufferSetLength(len); }
  560     CKuStringT& ReleaseBufferSetLength(SIZET len)
  561     {
  562         if (m_bDirty) {
  563             m_pData->m_iLength = len;
  564             m_pData->m_sString[m_pData->m_iLength] = 0;
  565             m_bDirty = false;
  566             FreeExtra();
  567         }
  568         return *this;
  569     }
  570     CKuStringT& ReleaseBuffer(SIZET len = -1)
  571     {
  572         if (len >= 0)
  573             ReleaseBufferSetLength(len);
  574         else if (m_bDirty) {
  575             m_pData->Count();
  576             m_bDirty = false;
  577             FreeExtra();
  578         }
  579         return *this;
  580     }
  581 
  582     CKuStringT& AttachBuffer(STRT sBuffer, SIZET iCapacity = -1, SIZET iLength = -1)
  583     {
  584         ASSERT(sBuffer);
  585         ASSERT(iCapacity != 0);
  586         ASSERT(iCapacity >= iLength);
  587         if (m_pData)
  588             m_pData->Release();
  589         m_pData = new CKuStringDataT;
  590         if (iLength < 0)
  591             iLength = StringLength(sBuffer);
  592         if (iCapacity < 0)
  593             iCapacity = iLength;
  594         m_pData->m_sData = m_pData->m_sString = sBuffer;
  595         m_pData->m_iSize = iCapacity;
  596         m_pData->m_iLength = iLength;
  597         return *this;
  598     }
  599     STRT DetachBuffer()
  600     {
  601         if (!m_pData)
  602             return NULL;
  603         New();
  604         m_pData->Normalize();
  605         STRT sRet = m_pData->m_sData;
  606         m_pData->m_sData = NULL;
  607         Empty();
  608         return sRet;
  609     }
  610 
  611     CKuStringT& Append(CSTRT str, SIZET len = -1)
  612     {
  613         ASSERT(!m_bDirty);
  614         ASSERT(str);
  615         if (*str) {
  616             New();
  617             if (len < 0)
  618                 len = StringLength(str);
  619             m_pData->Alloc(m_pData->m_iLength + len);
  620             CopyChars(m_pData->m_sString + m_pData->m_iLength, str, len);
  621             m_pData->m_iLength += len;
  622         }
  623         return *this;
  624     }
  625     CKuStringT& operator += (CSTRT str)
  626     { return Append(str); }
  627 
  628     CKuStringT& Append(T ch)
  629     {
  630         ASSERT(!m_bDirty);
  631         New();
  632         m_pData->Alloc(m_pData->m_iLength + 1);
  633         m_pData->m_sString[m_pData->m_iLength++] = ch;
  634         m_pData->m_sString[m_pData->m_iLength] = 0;
  635         return *this;
  636     }
  637     CKuStringT& operator += (T ch)
  638     { return Append(ch); }
  639 
  640     friend CKuStringT operator + (const CKuStringT& a, const CKuStringT& b)
  641     { CKuStringT ret(a); return ret.Append(b); }
  642     friend CKuStringT operator + (const CKuStringT& a, CSTRT b)
  643     { CKuStringT ret(a); return ret.Append(b); }
  644     friend CKuStringT operator + (CSTRT a, const CKuStringT& b)
  645     { CKuStringT ret(a); return ret.Append(b); }
  646     friend CKuStringT operator + (const CKuStringT& a, T b)
  647     { CKuStringT ret(a); return ret.Append(b); }
  648     friend CKuStringT operator + (T a, const CKuStringT& b)
  649     { CKuStringT ret(a); return ret.Append(b); }
  650 
  651     CKuStringT& Insert(CSTRT str, SIZET idx = 0, SIZET len = -1)
  652     {
  653         ASSERT(str);
  654         ASSERT(idx >= 0);
  655         if (IsEmpty())
  656             return SetString(str, len);
  657         if (idx >= GetLength())
  658             return Append(str, len);
  659         if (len)
  660             len = StringLength(str);
  661         if (len > 0) {
  662             New();
  663             m_pData->Alloc(m_pData->m_iLength + len);
  664             CopyCharsOverlapped(m_pData->m_sString + idx + len, m_pData->m_sString + idx, m_pData->m_iLength - idx); // including '\0'
  665             memcpy(m_pData->m_sString + idx, str, len * sizeof(T)); // excluding '\0'
  666             m_pData->m_iLength += len;
  667         }
  668         return *this;
  669     }
  670     CKuStringT& Insert(T ch, SIZET idx = 0)
  671     {
  672         ASSERT(idx >= 0);
  673         ASSERT(ch);
  674         if (IsEmpty())
  675             return SetString(ch);
  676         if (idx >= GetLength())
  677             return Append(ch);
  678         New();
  679         m_pData->Alloc(m_pData->m_iLength + 1);
  680         CopyCharsOverlapped(m_pData->m_sString + idx + 1, m_pData->m_sString + idx, m_pData->m_iLength - idx); // including '\0'
  681         m_pData->m_sString[idx] = ch;
  682         m_pData->m_iLength++;
  683         return *this;
  684     }
  685 
  686     CKuStringT& Delete(SIZET idx, SIZET len = 1)
  687     {
  688         ASSERT(idx >= 0 && idx < GetLength());
  689         ASSERT(len >= 0);
  690         if (!IsEmpty() && len > 0) {
  691             New();
  692             if (idx + len >= m_pData->m_iLength)
  693                 Truncate(idx);
  694             else if (idx == 0)
  695                 m_pData->Offset(len);
  696             else {
  697                 CopyCharsOverlapped(m_pData->m_sString + idx, m_pData->m_sString + idx + len, m_pData->m_iLength - idx - len); // including '\0'
  698                 m_pData->m_iLength -= len;
  699                 FreeExtra();
  700             }
  701         }
  702         return *this;
  703     }
  704 
  705     CKuStringT& FormatV(CSTRT fmt, va_list ap)
  706     {
  707         ASSERT(fmt);
  708         New(false);
  709         va_list ap2;
  710         va_copy(ap2, ap);
  711         int len = U::FormatV(NULL, 0, fmt, ap);
  712         ASSERT(len >= 0);
  713         // NOTE: The length argument of format function has different implementations.
  714         // Use safe one, though it may waste 1 character space.
  715         len++;
  716         m_pData->Alloc(len);
  717         m_pData->m_iLength = U::FormatV(m_pData->m_sString, len, fmt, ap2);
  718         return *this;
  719     }
  720     CKuStringT& Format(CSTRT fmt, ...)
  721     {
  722         va_list ap;
  723         va_start(ap, fmt);
  724         return FormatV(fmt, ap);
  725     }
  726 
  727     CKuStringT& AppendFormatV(CSTRT fmt, va_list ap)
  728     {
  729         ASSERT(fmt);
  730         New();
  731         va_list ap2;
  732         va_copy(ap2, ap);
  733         int len = U::FormatV(NULL, 0, fmt, ap);
  734         ASSERT(len >= 0);
  735         // NOTE: The length argument of format function has different implementations.
  736         // Use safe one, though it may waste 1 character space.
  737         len++;
  738         m_pData->Alloc(m_pData->m_iLength + len);
  739         m_pData->m_iLength += U::FormatV(m_pData->m_sString + m_pData->m_iLength, len, fmt, ap2);
  740         return *this;
  741     }
  742     CKuStringT& AppendFormat(CSTRT fmt, ...)
  743     {
  744         va_list ap;
  745         va_start(ap, fmt);
  746         return AppendFormatV(fmt, ap);
  747     }
  748 
  749     CKuStringT& GetEnvironmentVariable(CSTRT sName)
  750     {
  751         ASSERT(sName);
  752         DWORD len = U::GetEnvironmentVariable(sName, NULL, 0);
  753         if (len == 0)
  754             Empty();
  755         else {
  756             New(false);
  757             m_pData->Alloc(len);
  758             m_pData->m_iLength = U::GetEnvironmentVariable(sName, m_pData->m_sString, len);
  759         }
  760         return *this;
  761     }
  762 
  763     CKuStringT Mid(SIZET iFirst, SIZET iLen) const
  764     { ASSERT(iFirst + iLen <= GetLength()); return CKuStringT(GetString() + iFirst, iLen); }
  765     CKuStringT Mid(SIZET iFirst) const
  766     { ASSERT(iFirst <= GetLength()); return CKuStringT(GetString() + iFirst); }
  767     CKuStringT Left(SIZET iLen) const
  768     { ASSERT(iLen <= GetLength()); return CKuStringT(GetString(), iLen); }
  769     CKuStringT Right(SIZET iLen) const
  770     { ASSERT(iLen <= GetLength()); return CKuStringT(GetString() + GetLength() - iLen); }
  771 
  772     CKuStringT& Truncate(SIZET nNewLength)
  773     {
  774         ASSERT(!m_bDirty);
  775         ASSERT(nNewLength >= 0 && nNewLength <= GetLength());
  776         if (nNewLength < GetLength()) {
  777             New();
  778             m_pData->m_sString[nNewLength] = 0;
  779             m_pData->m_iLength = nNewLength;
  780             FreeExtra();
  781         }
  782         return *this;
  783     }
  784 
  785     CKuStringT& CutMid(SIZET iFirst)
  786     {
  787         ASSERT(!m_bDirty);
  788         ASSERT(iFirst >= 0 && iFirst <= GetLength());
  789         if (iFirst > 0) {
  790             New();
  791             m_pData->Offset(iFirst);
  792         }
  793         return *this;
  794     }
  795     CKuStringT& CutMid(SIZET iFirst, SIZET iLen)
  796     {
  797         if (iLen == 0)
  798             Empty();
  799         else {
  800             CutMid(iFirst);
  801             Truncate(iLen);
  802         }
  803         return *this;
  804     }
  805     CKuStringT& CutLeft(SIZET iLen)
  806     { Truncate(iLen); return *this; }
  807     CKuStringT& CutRight(SIZET iLen)
  808     { return CutMid(GetLength() - iLen); }
  809 
  810     CKuStringT& TrimLeft()
  811     { return TrimLeft(IsSpace, NULL); }
  812     CKuStringT& TrimLeft(T ch)
  813     { return TrimLeft(IsTheChar, &ch); }
  814     CKuStringT& TrimLeft(CSTRT sSet)
  815     { ASSERT(sSet && *sSet); return TrimLeft(IsInSet, sSet); }
  816     CKuStringT& TrimRight()
  817     { return TrimRight(IsSpace, NULL); }
  818     CKuStringT& TrimRight(T ch)
  819     { return TrimRight(IsTheChar, &ch); }
  820     CKuStringT& TrimRight(CSTRT sSet)
  821     { ASSERT(sSet && *sSet); return TrimRight(IsInSet, sSet); }
  822     CKuStringT& Trim()
  823     { return Trim(IsSpace, NULL); }
  824     CKuStringT& Trim(T ch)
  825     { return Trim(IsTheChar, &ch); }
  826     CKuStringT& Trim(CSTRT sSet)
  827     { ASSERT(sSet && *sSet); return Trim(IsInSet, sSet); }
  828 
  829     CKuStringT SpanIncluding(CSTRT pszCharSet) const
  830     {
  831         ASSERT(pszCharSet);
  832         CKuStringT sRet;
  833         if (!IsEmpty()) {
  834             size_t len = U::SpanIncluding(m_pData->m_sString, pszCharSet);
  835             if (len > 0) {
  836                 STRT ptr = sRet.GetBufferSetLength(len);
  837                 CopyChars(ptr, m_pData->m_sString, len);
  838                 sRet.ReleaseBuffer();
  839             }
  840         }
  841         return sRet;
  842     }
  843 
  844     CKuStringT SpanExcluding(CSTRT pszCharSet) const
  845     {
  846         ASSERT(pszCharSet);
  847         CKuStringT sRet;
  848         if (!IsEmpty()) {
  849             size_t len = U::SpanExcluding(m_pData->m_sString, pszCharSet);
  850             if (len > 0) {
  851                 STRT ptr = sRet.GetBufferSetLength(len);
  852                 CopyChars(ptr, m_pData->m_sString, len);
  853                 sRet.ReleaseBuffer();
  854             }
  855         }
  856         return sRet;
  857     }
  858 
  859     CKuStringT Tokenize(CSTRT pszTokens, SIZET& iStart) const
  860     {
  861         ASSERT(!m_bDirty);
  862         ASSERT(pszTokens);
  863         ASSERT(iStart >= 0);
  864         CKuStringT sRet;
  865         if (iStart < GetLength()) {
  866             iStart += U::SpanIncluding(m_pData->m_sString + iStart, pszTokens);
  867             if (iStart < GetLength()) {
  868                 size_t len = U::SpanExcluding(m_pData->m_sString + iStart, pszTokens);
  869                 if (len > 0) {
  870                     STRT ptr = sRet.GetBufferSetLength(len);
  871                     CopyChars(ptr, m_pData->m_sString + iStart, len);
  872                     sRet.ReleaseBuffer();
  873                 }
  874                 iStart += len;
  875             }
  876         }
  877         return sRet;
  878     }
  879 
  880     SIZET Find(CSTRT sFind, SIZET iStart = 0) const
  881     {
  882         ASSERT(sFind);
  883         ASSERT(iStart >= 0 && iStart <= GetLength());
  884         if (IsEmpty() || iStart == GetLength())
  885             return -1;
  886         CSTRT s = U::FindString(m_pData->m_sString + iStart, sFind);
  887         return s ? (SIZET)(s - m_pData->m_sString) : -1;
  888     }
  889     SIZET Find(T ch, SIZET iStart = 0) const
  890     {
  891         ASSERT(ch);
  892         ASSERT(iStart >= 0 && iStart <= GetLength());
  893         if (IsEmpty() || iStart == GetLength())
  894             return -1;
  895         CSTRT s = Find(m_pData->m_sString + iStart, ch);
  896         return s ? (SIZET)(s - m_pData->m_sString) : -1;
  897     }
  898 
  899     SIZET ReverseFind(T ch) const
  900     {
  901         ASSERT(ch);
  902         if (IsEmpty())
  903             return -1;
  904         SIZET i = m_pData->m_iLength - 1;
  905         for (;i >= 0;i--)
  906             if (m_pData->m_sString[i] == ch)
  907                 return i;
  908         return -1;
  909     }
  910 
  911     CKuStringT& Replace(T chOld, T chNew)
  912     {
  913         ASSERT(chOld);
  914         ASSERT(chNew);
  915         SIZET iPos = Find(chOld);
  916         if (!IsEmpty() && iPos >= 0) {
  917             New();
  918             for (STRT ptr = m_pData->m_sString + iPos;*ptr;ptr++)
  919                 if (*ptr == chOld) {
  920                     *ptr = chNew;
  921                 }
  922         }
  923         return *this;
  924     }
  925     CKuStringT& Replace(CSTRT sOld, CSTRT sNew)
  926     {
  927         ASSERT(sOld && *sOld);
  928         SIZET iPos = Find(sOld);
  929 
  930         if (!IsEmpty() && iPos >= 0) {
  931             SIZET iOld = StringLength(sOld);
  932             SIZET iNew = StringLengthSafe(sNew);
  933             SIZET iDiff = iNew - iOld;
  934             SIZET iStart = iPos, iNext;
  935 
  936             if (iNew > iOld) { // will need grow size
  937                 SIZET iNewLen = m_pData->m_iLength + iDiff;
  938                 
  939                 iPos += iOld;
  940                 while ((iPos = Find(sOld, iPos)) > 0) {
  941                     iNewLen += iDiff;
  942                     iPos += iOld;
  943                 }
  944 
  945                 iPos = iStart;
  946                 CKuStringDataT *pNew = new CKuStringDataT;
  947                 pNew->Alloc(iNewLen);
  948                 pNew->m_iLength = iNewLen; // we have known the new string length
  949 
  950                 STRT str = pNew->m_sString;
  951                 if (iPos > 0)
  952                     memcpy(str, m_pData->m_sString, iPos * sizeof(T)); // copy beginning mismatched string
  953 
  954                 do {
  955                     memcpy(str + iPos, sNew, iNew * sizeof(T));
  956                     iStart += iOld; // next search position
  957                     iPos += iNew; // next copy position
  958                     iNext = Find(sOld, iStart);
  959                     if (iNext > iStart) {
  960                         memcpy(str + iPos, m_pData->m_sString + iStart, (iNext - iStart) * sizeof(T));
  961                         iPos += (iNext - iStart);
  962                         iStart = iNext;
  963                     }
  964                     else if (iNext < 0 && m_pData->m_iLength > iStart)
  965                         memcpy(str + iPos, m_pData->m_sString + iStart, (m_pData->m_iLength - iStart) * sizeof(T)); // last loop, can't find more 'sOld'
  966                 } while (iNext >= 0);
  967                 str[iNewLen] = 0;
  968 
  969                 m_pData->Release();
  970                 m_pData = pNew;
  971             }
  972             else {
  973                 New();
  974                 SIZET iNewLen = m_pData->m_iLength;
  975                 STRT str = m_pData->m_sString;
  976                 do {
  977                     if (iNew > 0)
  978                         memcpy(str + iPos, sNew, iNew * sizeof(T));
  979                     iNewLen += iDiff;
  980                     iStart += iOld; // next search position
  981                     iPos += iNew; // next copy position
  982                     iNext = Find(sOld, iStart);
  983                     if (iNext > iStart) {
  984                         if (iStart > iPos)
  985                             memmove(str + iPos, str + iStart, (iNext - iStart) * sizeof(T));
  986                         iPos += (iNext - iStart);
  987                         iStart = iNext;
  988                     }
  989                     else if (iNext < 0 && m_pData->m_iLength > iStart && iStart > iPos)
  990                         memmove(str + iPos, str + iStart, (m_pData->m_iLength - iStart) * sizeof(T)); // last loop, can't find more 'sOld'
  991                 } while (iNext >= 0);
  992                 str[iNewLen] = 0;
  993                 m_pData->m_iLength = iNewLen;
  994                 FreeExtra();
  995             }
  996         }
  997         return *this;
  998     }
  999 
 1000     CKuStringT& MakeLower()
 1001     {
 1002         if (!IsEmpty()) {
 1003             STRT str  = (STRT) Find(m_pData->m_sString, IsUpper, NULL);
 1004             if (str) {
 1005                 New();
 1006                 for (STRT ptr = str;*ptr;ptr++)
 1007                     *ptr = U::ToLower(*ptr);
 1008             }
 1009         }
 1010         return *this;
 1011     }
 1012     CKuStringT& MakeUpper()
 1013     {
 1014         if (!IsEmpty()) {
 1015             STRT str  = (STRT) Find(m_pData->m_sString, IsLower, NULL);
 1016             if (str) {
 1017                 New();
 1018                 for (STRT ptr = str;*ptr;ptr++)
 1019                     *ptr = U::ToUpper(*ptr);
 1020             }
 1021         }
 1022         return *this;
 1023     }
 1024 
 1025     int Compare(CSTRT str) const
 1026     { ASSERT(str); return U::Compare(GetString(), str); }
 1027     int CompareNoCase(CSTRT str) const
 1028     { ASSERT(str); return U::CompareNoCase(GetString(), str); }
 1029 
 1030     friend bool operator == (const CKuStringT& a, const CKuStringT& b)
 1031     { return a.Compare(b) == 0; }
 1032     friend bool operator != (const CKuStringT& a, const CKuStringT& b)
 1033     { return a.Compare(b) != 0; }
 1034     friend bool operator > (const CKuStringT& a, const CKuStringT& b)
 1035     { return a.Compare(b) > 0; }
 1036     friend bool operator < (const CKuStringT& a, const CKuStringT& b)
 1037     { return a.Compare(b) < 0; }
 1038     friend bool operator >= (const CKuStringT& a, const CKuStringT& b)
 1039     { return a.Compare(b) >= 0; }
 1040     friend bool operator <= (const CKuStringT& a, const CKuStringT& b)
 1041     { return a.Compare(b) <= 0; }
 1042 
 1043     friend bool operator == (const CKuStringT& a, CSTRT b)
 1044     { return a.Compare(b) == 0; }
 1045     friend bool operator != (const CKuStringT& a, CSTRT b)
 1046     { return a.Compare(b) != 0; }
 1047     friend bool operator > (const CKuStringT& a, CSTRT b)
 1048     { return a.Compare(b) > 0; }
 1049     friend bool operator < (const CKuStringT& a, CSTRT b)
 1050     { return a.Compare(b) < 0; }
 1051     friend bool operator >= (const CKuStringT& a, CSTRT b)
 1052     { return a.Compare(b) >= 0; }
 1053     friend bool operator <= (const CKuStringT& a, CSTRT b)
 1054     { return a.Compare(b) <= 0; }
 1055 
 1056     friend bool operator == (CSTRT a, const CKuStringT& b)
 1057     { return b.Compare(a) == 0; }
 1058     friend bool operator != (CSTRT a, const CKuStringT& b)
 1059     { return b.Compare(a) != 0; }
 1060     friend bool operator > (CSTRT a, const CKuStringT& b)
 1061     { return b.Compare(a) < 0; }
 1062     friend bool operator < (CSTRT a, const CKuStringT& b)
 1063     { return b.Compare(a) > 0; }
 1064     friend bool operator >= (CSTRT a, const CKuStringT& b)
 1065     { return b.Compare(a) <= 0; }
 1066     friend bool operator <= (CSTRT a, const CKuStringT& b)
 1067     { return b.Compare(a) >= 0; }
 1068 
 1069     int Compare(T ch) const
 1070     { T str[2] = {ch, 0}; return Compare(str); }
 1071     int CompareNoCase(T ch) const
 1072     { T str[2] = {ch, 0}; return CompareNoCase(str); }
 1073 
 1074     friend bool operator == (const CKuStringT& a, T b)
 1075     { return a.Compare(b) == 0; }
 1076     friend bool operator != (const CKuStringT& a, T b)
 1077     { return a.Compare(b) != 0; }
 1078     friend bool operator > (const CKuStringT& a, T b)
 1079     { return a.Compare(b) > 0; }
 1080     friend bool operator < (const CKuStringT& a, T b)
 1081     { return a.Compare(b) < 0; }
 1082     friend bool operator >= (const CKuStringT& a, T b)
 1083     { return a.Compare(b) >= 0; }
 1084     friend bool operator <= (const CKuStringT& a, T b)
 1085     { return a.Compare(b) <= 0; }
 1086 
 1087     friend bool operator == (T a, const CKuStringT& b)
 1088     { return b.Compare(a) == 0; }
 1089     friend bool operator != (T a, const CKuStringT& b)
 1090     { return b.Compare(a) != 0; }
 1091     friend bool operator > (T a, const CKuStringT& b)
 1092     { return b.Compare(a) < 0; }
 1093     friend bool operator < (T a, const CKuStringT& b)
 1094     { return b.Compare(a) > 0; }
 1095     friend bool operator >= (T a, const CKuStringT& b)
 1096     { return b.Compare(a) <= 0; }
 1097     friend bool operator <= (T a, const CKuStringT& b)
 1098     { return b.Compare(a) >= 0; }
 1099 
 1100 #ifdef _WIN32
 1101     bool LoadString(HINSTANCE hInstance, UINT nID, WORD wLanguage)
 1102     { return U::LoadString(*this, hInstance, nID, wLanguage); }
 1103     bool LoadString(HINSTANCE hInstance, UINT nID)
 1104     { return LoadString(hInstance, nID, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)); }
 1105     bool LoadString(UINT nID)
 1106     { return LoadString(GetModuleHandle(NULL), nID); }
 1107 
 1108     explicit CKuStringT(HINSTANCE hInstance, UINT nID, WORD wLanguage)
 1109     { Init(); LoadString(hInstance, nID, wLanguage); }
 1110     explicit CKuStringT(HINSTANCE hInstance, UINT nID)
 1111     { Init(); LoadString(hInstance, nID); }
 1112     explicit CKuStringT(UINT nID)
 1113     { Init(); LoadString(nID); }
 1114 #endif
 1115 private:
 1116     static const T m_sNULL;
 1117     CKuStringDataT *m_pData;
 1118     bool m_bDirty;
 1119 };
 1120 template<typename T, typename U> const T CKuStringT<T, U>::m_sNULL = 0;
 1121 
 1122 #ifdef _WIN32
 1123 inline bool CKuStringUtilW::LoadString(CKuStringW &str, HINSTANCE hInstance, UINT nID, WORD wLanguage)
 1124 {
 1125     HRSRC hResource = ::FindResourceEx(hInstance, RT_STRING, MAKEINTRESOURCE(((nID >> 4) + 1)), wLanguage);
 1126     if( hResource == NULL )
 1127         return false;
 1128     HGLOBAL hGlobal = ::LoadResource(hInstance, hResource);
 1129     if (hGlobal) {
 1130         const WORD *pImage;
 1131         const WORD *pImageEnd;
 1132         pImage = (const WORD *) ::LockResource(hGlobal);
 1133         if (pImage) {
 1134             pImageEnd = (const WORD *)(((UINT_PTR) pImage) + ::SizeofResource(hInstance, hResource));
 1135             UINT iIndex = nID & 0xF;
 1136 
 1137             while(iIndex > 0 && pImage < pImageEnd)
 1138             {
 1139                 pImage = (const WORD *)(((UINT_PTR) pImage) + (sizeof(WORD) + (*pImage * sizeof(wchar_t))));
 1140                 iIndex--;
 1141             }
 1142             if (pImage < pImageEnd && *pImage > 0) {
 1143                 memcpy(str.GetBufferSetLength(*pImage), pImage + 1, *pImage * sizeof(wchar_t));
 1144                 str.ReleaseBufferSetLength(*pImage);
 1145                 FreeResource(hGlobal);
 1146                 return true;
 1147             }
 1148         }
 1149         FreeResource(hGlobal);
 1150     }
 1151 
 1152     return false;
 1153 }
 1154 
 1155 inline bool CKuStringUtilA::LoadString(CKuStringA &str, HINSTANCE hInstance, UINT nID, WORD wLanguage)
 1156 {
 1157     CKuStringW s;
 1158     if (!s.LoadString(hInstance, nID, wLanguage))
 1159         return false;
 1160     return !!StringWCharToChar(s, str, -1, '?', CP_ACP);
 1161 }
 1162 #endif