/////////////////////////////////////////////////////////////////////////////////////////
//
// Please refer to "COPYRIGHT.md" for the relevant copyright statement of this software.
//
/////////////////////////////////////////////////////////////////////////////////////////
//
#ifndef _Gc_String_h_
#define _Gc_String_h_

#include "gcbasedefs.h"
#include "gsoft.h"
#include "GcHeapOpers.h"
#include "GdGChar.h"
#include <cstddef>
#include <cstdarg>
#include <cctype>
#include <cwctype>
#include <string>
#include <functional>
#ifndef OBJECTPARAM_ALLOWED
#  pragma warning(error : 4840)
#endif
#if (__cplusplus >= 201703L)
#  if (__cplusplus < 202002L)
#    include <ciso646>
#  else
#    include <version>
#  endif
#  include <string_view>
#endif

#define GcStringSSO

#define GcStringMBCS

class GcDbHandle;
class GcRxResourceInstance;

#if !defined(NDEBUG)
#  define GcStrASSERT(x) (void)((!!(x)) || (GcString::iASSERTMsg(GCRX_T(#x), GCRX_T(__FILE__), __LINE__)))
#else
#  define GcStrASSERT(x)
#endif

#if defined(GcStrLogging)
#  define GcStrLOG(...) GcString::iLogMsg iMsg(__VA_ARGS__)
#  define GcStrLOGADD(...) iMsg.addDMsg(__VA_ARGS__)
#else
#  define GcStrLOG(...)
#  define GcStrLOGADD(...)
#endif

class GcString : public GcHeapOperators
{
public:
  enum Encoding
  {
    Utf8
  };

  GCBASE_PORT GcString();

  GCBASE_PORT GcString(wchar_t wch);

  GCBASE_PORT GcString(std::nullptr_t);

#ifdef GcStringMBCS
  GCBASE_PORT GcString(const char* psz);
#endif

  GCBASE_PORT GcString(const char* psz, Encoding encoding);

  GCBASE_PORT GcString(char ch, Encoding);

  GCBASE_PORT GcString(const char* psz, Encoding encoding, Gsoft::UInt32 nByteCount);

  GCBASE_PORT GcString(const wchar_t* pwsz);

  GCBASE_PORT GcString(const wchar_t* pwsz, Gsoft::UInt32 count);

  GCBASE_PORT GcString(const GcString& gcs);

  GCBASE_PORT GcString(GcString&& gcs) noexcept;

  enum eFormat
  {
    kSigned = 1,
    kUnSigned,
    kHex,
    kHexSigned
  };

  GCBASE_PORT GcString(eFormat nCtorFlags, Gsoft::Int32 nArg);

  GCBASE_PORT GcString(eFormat nCtorFlags, Gsoft::UInt32 nArg);

  GCBASE_PORT GcString(eFormat nCtorFlags, Gsoft::Int64 nArg);

  GCBASE_PORT GcString(eFormat nCtorFlags, Gsoft::UInt64 nArg);

  GCBASE_PORT GcString(wchar_t ch, Gsoft::UInt32 nRepeatTimes);

  GCBASE_PORT GcString(const GcDbHandle& h);

  GCBASE_PORT GcString(const GcRxResourceInstance& hDll, Gsoft::UInt32 nId);

  GCBASE_PORT ~GcString();

  GCBASE_PORT const char* utf8Ptr() const;

  GCBASE_PORT std::string utf8Str() const;

  GCBASE_PORT const wchar_t* c_str() const;

  GCBASE_PORT const wchar_t* kwszPtr() const;

  GCBASE_PORT const wchar_t* constPtr() const;

  GCBASE_PORT const wchar_t* kTCharPtr() const;

  GCBASE_PORT const wchar_t* kGCharPtr() const;

  GCBASE_PORT operator const wchar_t*() const;

  GCBASE_PORT bool isEmpty() const;

  GCBASE_PORT bool empty() const;

  GCBASE_PORT Gsoft::UInt32 length() const;

  GCBASE_PORT Gsoft::UInt32 tcharLength() const;

  GCBASE_PORT Gsoft::UInt32 capacity() const;

  GCBASE_PORT bool reserve(Gsoft::UInt32 nCapacity);

  GCBASE_PORT bool isAscii() const;

  GCBASE_PORT bool is7Bit() const;

  GCBASE_PORT static constexpr Gsoft::UInt32 kParseZero = 0;

  GCBASE_PORT static constexpr Gsoft::UInt32 kParseMinus1 = 0x01;

  GCBASE_PORT static constexpr Gsoft::UInt32 kParseAssert = 0x02;

  GCBASE_PORT static constexpr Gsoft::UInt32 kParseExcept = 0x04;

  GCBASE_PORT static constexpr Gsoft::UInt32 kParseNoEmpty = 0x08;

  GCBASE_PORT static constexpr Gsoft::UInt32 kParseDefault = (kParseAssert | kParseZero);

  GCBASE_PORT Gsoft::Int32 asDeci(Gsoft::UInt32 nFlags = kParseDefault) const;

  GCBASE_PORT Gsoft::Int32 asHex(Gsoft::UInt32 nFlags = kParseDefault) const;

  GCBASE_PORT Gsoft::UInt32 asUDeci(Gsoft::UInt32 nFlags = kParseDefault) const;

  GCBASE_PORT Gsoft::UInt32 asUHex(Gsoft::UInt32 nFlags = kParseDefault) const;

  GCBASE_PORT Gsoft::Int64 asDeci64(Gsoft::UInt32 nFlags = kParseDefault) const;

  GCBASE_PORT Gsoft::Int64 asHex64(Gsoft::UInt32 nFlags = kParseDefault) const;

  GCBASE_PORT Gsoft::UInt64 asUDeci64(Gsoft::UInt32 nFlags = kParseDefault) const;

  GCBASE_PORT Gsoft::UInt64 asUHex64(Gsoft::UInt32 nFlags = kParseDefault) const;

  GCBASE_PORT GcDbHandle asGcDbHandle(Gsoft::UInt32 nFlags = kParseDefault) const;

  GCBASE_PORT Gsoft::Int32 find(GCHAR ch) const;

  GCBASE_PORT Gsoft::Int32 find(GCHAR ch, Gsoft::Int32 nStartPos) const;

  GCBASE_PORT Gsoft::Int32 find(const GCHAR* pwsz, Gsoft::Int32 nStartPos = 0) const;

  GCBASE_PORT Gsoft::Int32 find(const GcString& gcs) const;

  GCBASE_PORT Gsoft::Int32 findNoCase(const GCHAR* pwsz, Gsoft::Int32 nStartPos = 0) const;

  GCBASE_PORT Gsoft::Int32 findOneOf(const GCHAR* pwsz, Gsoft::Int32 nStartPos = 0) const;

  GCBASE_PORT Gsoft::Int32 findNoneOf(const GCHAR* pwsz, Gsoft::Int32 nStartPos = 0) const;

  GCBASE_PORT Gsoft::Int32 findRev(GCHAR ch) const;

  GCBASE_PORT Gsoft::Int32 findRev(const GCHAR* pwsz) const;

  GCBASE_PORT Gsoft::Int32 findRev(const GcString& gcs) const;

  GCBASE_PORT Gsoft::Int32 findOneOfRev(const GCHAR* pwsz) const;

  GCBASE_PORT Gsoft::Int32 findLast(GCHAR ch, Gsoft::Int32 nEndPos = -1) const;

  GCBASE_PORT Gsoft::Int32 findLast(const GCHAR* pwsz, Gsoft::Int32 nEndPos = -1) const;

  GCBASE_PORT Gsoft::Int32 findLastOneOf(const GCHAR* pwsz, Gsoft::Int32 nEndPos = -1) const;

  GCBASE_PORT Gsoft::Int32 findLastNoneOf(const GCHAR* pwsz, Gsoft::Int32 nEndPos = -1) const;

  GCBASE_PORT GcString mid(Gsoft::Int32 nStart) const;

  GCBASE_PORT GcString mid(Gsoft::Int32 nStart, Gsoft::Int32 nNumChars) const;

  GCBASE_PORT GcString substr(Gsoft::Int32 numChars) const;

  GCBASE_PORT GcString substr(Gsoft::Int32 nStart, Gsoft::Int32 nNumChars) const;

  GCBASE_PORT GcString substrRev(Gsoft::Int32 numChars) const;

  GCBASE_PORT GcString left(Gsoft::Int32 nNumChars) const;

  GCBASE_PORT GcString right(Gsoft::Int32 nNumChars) const;

  GCBASE_PORT GcString& assign(wchar_t wch);

  GCBASE_PORT GcString& assign(std::nullptr_t);

#ifdef GcStringMBCS
  GCBASE_PORT GcString& assign(const char* psz);
#endif

  GCBASE_PORT GcString& assign(const char* psz, Encoding encoding);

  GCBASE_PORT GcString& assign(const char* psz, Encoding encoding, Gsoft::UInt32 nLen);

  GCBASE_PORT GcString& assign(const wchar_t* pwsz);

  GCBASE_PORT GcString& assign(const wchar_t* pwsz, Gsoft::UInt32 len);

  GCBASE_PORT GcString& assign(const GcString& gcs);

  GCBASE_PORT GcString& assign(GcString&& gcs) noexcept;

  GCBASE_PORT GcString& assign(const GcDbHandle& h);

  GCBASE_PORT GcString& operator=(wchar_t wch);

  GCBASE_PORT GcString& operator=(std::nullptr_t);

#ifdef GcStringMBCS
  GCBASE_PORT GcString& operator=(const char* psz);
#endif

  GCBASE_PORT GcString& operator=(const wchar_t* pwsz);

  GCBASE_PORT GcString& operator=(const GcString& gcs);

  GCBASE_PORT GcString& operator=(GcString&& gcs) noexcept;

  GCBASE_PORT GcString& operator=(const GcDbHandle& h);

  GCBASE_PORT GcString& setEmpty();

  GCBASE_PORT bool loadString(const GcRxResourceInstance& hDll, unsigned nId);

  GCBASE_PORT GcString& format(const GCHAR* pszFmt, ...);

  GCBASE_PORT GcString& formatV(const GCHAR* pszFmt, va_list args);

  GCBASE_PORT GcString& appendFormat(const GCHAR* pszFmt, ...);

  GCBASE_PORT GcString& operator+=(wchar_t wch);

  GCBASE_PORT GcString& operator+=(std::nullptr_t);

#ifdef GcStringMBCS
  GCBASE_PORT GcString& operator+=(const char* psz);
#endif

  GCBASE_PORT GcString& operator+=(const wchar_t* pwsz);

  GCBASE_PORT GcString& operator+=(const GcString& gcs);

  GCBASE_PORT GcString& append(wchar_t wch);

  GCBASE_PORT GcString& append(std::nullptr_t);

#ifdef GcStringMBCS
  GCBASE_PORT GcString& append(const char* psz);
#endif
  GCBASE_PORT GcString& append(const char* psz, Encoding encoding);

  GCBASE_PORT GcString& append(const wchar_t* pwsz);

  GCBASE_PORT GcString& append(const GcString& gcs);

  GCBASE_PORT GcString operator+(wchar_t wch) const;

  GCBASE_PORT GcString operator+(std::nullptr_t) const;

#ifdef GcStringMBCS
  GCBASE_PORT GcString operator+(const char* psz) const;
#endif

  GCBASE_PORT GcString operator+(const wchar_t* pwsz) const;

  GCBASE_PORT GcString operator+(const GcString& gcs) const;

  GCBASE_PORT GcString concat(wchar_t wch) const;

  GCBASE_PORT GcString concat(std::nullptr_t) const;

  GCBASE_PORT GcString concat(const char* psz, Encoding encoding) const;

  GCBASE_PORT GcString concat(const wchar_t* pwsz) const;

  GCBASE_PORT GcString concat(const GcString& gcs) const;

  GCBASE_PORT GcString precat(GCHAR ch) const;

  GCBASE_PORT GcString precat(std::nullptr_t) const;

  GCBASE_PORT GcString precat(const char* psz, Encoding encoding) const;

  GCBASE_PORT GcString precat(const wchar_t* pwsz) const;

  GCBASE_PORT Gsoft::Int32 compare(wchar_t wch) const;

  GCBASE_PORT Gsoft::Int32 compare(std::nullptr_t) const;

#ifdef GcStringMBCS
  GCBASE_PORT Gsoft::Int32 compare(const char* psz) const;
#endif

  GCBASE_PORT Gsoft::Int32 compare(const char* psz, Encoding encoding) const;

  GCBASE_PORT Gsoft::Int32 compare(const wchar_t* pwsz) const;

  GCBASE_PORT Gsoft::Int32 compare(const GcString& gcs) const;

  GCBASE_PORT Gsoft::Int32 collate(const wchar_t* pwsz) const;

  GCBASE_PORT Gsoft::Int32 collate(const GcString& gcs) const;

  GCBASE_PORT static Gsoft::Int32 compareNoCase(const wchar_t* pwsz1, const wchar_t* pwsz2);

  GCBASE_PORT static Gsoft::Int32 compareNoCase(const wchar_t* pwsz1, const wchar_t* pwsz2, size_t nLen);

  GCBASE_PORT Gsoft::Int32 compareNoCase(wchar_t wch) const;

  GCBASE_PORT Gsoft::Int32 compareNoCase(std::nullptr_t) const;

#ifdef GcStringMBCS
  GCBASE_PORT Gsoft::Int32 compareNoCase(const char* psz) const;
#endif

  GCBASE_PORT Gsoft::Int32 compareNoCase(const char* psz, Encoding encoding) const;

  GCBASE_PORT Gsoft::Int32 compareNoCase(const wchar_t* pwsz) const;

  GCBASE_PORT Gsoft::Int32 compareNoCase(const wchar_t* pwsz, size_t nLen) const;

  GCBASE_PORT Gsoft::Int32 compareNoCase(const GcString& gcs) const;

  GCBASE_PORT Gsoft::Int32 compareNoCase(const GcString& gcs, size_t nLen) const;

  GCBASE_PORT static bool equalsNoCase(const GcString& left, const GcString& right);

  GCBASE_PORT Gsoft::Int32 collateNoCase(const wchar_t* pwsz) const;

  GCBASE_PORT Gsoft::Int32 collateNoCase(const GcString& gcs) const;

  GCBASE_PORT bool operator==(wchar_t wch) const;

  GCBASE_PORT bool operator==(std::nullptr_t) const;

#ifdef GcStringMBCS
  GCBASE_PORT bool operator==(const char* psz) const;
#endif

  GCBASE_PORT bool operator==(const wchar_t* pwsz) const;

  GCBASE_PORT bool operator==(const GcString& gcs) const;

  GCBASE_PORT bool operator!=(wchar_t wch) const;

  GCBASE_PORT bool operator!=(std::nullptr_t) const;

#ifdef GcStringMBCS
  GCBASE_PORT bool operator!=(const char* psz) const;
#endif

  GCBASE_PORT bool operator!=(const wchar_t* pwsz) const;

  GCBASE_PORT bool operator!=(const GcString& gcs) const;

  GCBASE_PORT bool operator>(wchar_t wch) const;

  GCBASE_PORT bool operator>(std::nullptr_t) const;

#ifdef GcStringMBCS
  GCBASE_PORT bool operator>(const char* psz) const;
#endif

  GCBASE_PORT bool operator>(const wchar_t* pwsz) const;

  GCBASE_PORT bool operator>(const GcString& gcs) const;

  GCBASE_PORT bool operator>=(wchar_t wch) const;

  GCBASE_PORT bool operator>=(std::nullptr_t) const;

#ifdef GcStringMBCS
  GCBASE_PORT bool operator>=(const char* psz) const;
#endif

  GCBASE_PORT bool operator>=(const wchar_t* pwsz) const;

  GCBASE_PORT bool operator>=(const GcString& gcs) const;

  GCBASE_PORT bool operator<(wchar_t wch) const;

  GCBASE_PORT bool operator<(std::nullptr_t) const;

#ifdef GcStringMBCS
  GCBASE_PORT bool operator<(const char* psz) const;
#endif

  GCBASE_PORT bool operator<(const wchar_t* pwsz) const;

  GCBASE_PORT bool operator<(const GcString& gcs) const;

  GCBASE_PORT bool operator<=(wchar_t wch) const;

  GCBASE_PORT bool operator<=(std::nullptr_t) const;

#ifdef GcStringMBCS
  GCBASE_PORT bool operator<=(const char* psz) const;
#endif

  GCBASE_PORT bool operator<=(const wchar_t* pwsz) const;

  GCBASE_PORT bool operator<=(const GcString& gcs) const;

  GCBASE_PORT Gsoft::Int32 match(const char* psz, Encoding encoding) const;

  GCBASE_PORT Gsoft::Int32 match(const wchar_t* pwsz) const;

  GCBASE_PORT Gsoft::Int32 match(const GcString& gcs) const;

  GCBASE_PORT Gsoft::Int32 matchNoCase(const char* psz, Encoding encoding) const;

  GCBASE_PORT Gsoft::Int32 matchNoCase(const wchar_t* pwsz) const;

  GCBASE_PORT Gsoft::Int32 matchNoCase(const GcString& gcs) const;

  GCBASE_PORT GcString& makeUpper();

  GCBASE_PORT GcString& makeLower();

  GCBASE_PORT GcString& makeReverse();

  GCBASE_PORT GcString& trimLeft(wchar_t wch);

  GCBASE_PORT GcString& trimRight(wchar_t wch);

  GCBASE_PORT GcString& trim(wchar_t wch);

  GCBASE_PORT GcString& trimLeft();

  GCBASE_PORT GcString& trimLeft(const wchar_t* pwszChars);

  GCBASE_PORT GcString& trimRight();

  GCBASE_PORT GcString& trimRight(const wchar_t* pwszChars);

  GCBASE_PORT GcString& trim();

  GCBASE_PORT GcString& trim(const wchar_t* pwszChars);

  GCBASE_PORT Gsoft::Int32 remove(wchar_t wch);

  GCBASE_PORT Gsoft::Int32 remove();

  GCBASE_PORT GcString spanExcluding(const wchar_t* pwszChars) const;

#if defined(_AFX) || defined(__OSX_WINAPI_UNIX_STRING_H__) || defined(__ATLSTR_H__)
#  if !defined(_GSOFT_CROSS_PLATFORM_)
#    define ENABLE_GCSTRING_CSTRING_OPERATORS
#  endif
#endif

#if defined(ENABLE_GCSTRING_CSTRING_OPERATORS)

  GcString(const CStringW& csw) : GcString((const wchar_t*)csw)
  {
    GcStrLOG(this, iLogMsg::dStr, L"GcString(const CStringW &) : \"%ls\"", (const wchar_t*)csw);
  }

  GcString& operator=(const CStringW& csw)
  {
    GcStrLOG(this, iLogMsg::dStr, L"operator = (const CStringW &) : \"%ls\"", (const wchar_t*)csw);
    assign((const wchar_t*)csw);
    return *this;
  }

  GcString& operator+=(const CStringW& csw)
  {
    GcStrLOG(this, iLogMsg::dStr, L"operator += (const CStringW &) : \"%ls\"", (const wchar_t*)csw);
    append((const wchar_t*)csw);
    return *this;
  }

  Gsoft::Int32 compare(const CStringW& csw) const
  {
    GcStrLOG(this, iLogMsg::dStr, L"compare(const CStringW &) : \"%ls\"", (const wchar_t*)csw);
    Gsoft::Int32 retval = compare((const wchar_t*)csw);
    GcStrLOGADD(L"%d", (int)retval);
    return retval;
  }

  Gsoft::Int32 compareNoCase(const CStringW& csw) const
  {
    GcStrLOG(this, iLogMsg::dStr, L"compareNoCase(const CStringW &) : \"%ls\"", (const wchar_t*)csw);
    Gsoft::Int32 retval = compareNoCase((const wchar_t*)csw);
    GcStrLOGADD(L"%d", (int)retval);
    return retval;
  }

  bool operator==(const CStringW& csw) const
  {
    GcStrLOG(this, iLogMsg::dStr, L"operator == (const CStringW &) : \"%ls\"", (const wchar_t*)csw);
    bool retval = operator==((const wchar_t*)csw);
    GcStrLOGADD(L"%d", (int)retval);
    return retval;
  }

  bool operator!=(const CStringW& csw) const
  {
    GcStrLOG(this, iLogMsg::dStr, L"operator != (const CStringW &) : \"%ls\"", (const wchar_t*)csw);
    bool retval = operator!=((const wchar_t*)csw);
    GcStrLOGADD(L"%d", (int)retval);
    return retval;
  }

  bool operator<(const CStringW& csw) const
  {
    GcStrLOG(this, iLogMsg::dStr, L"operator < (const CStringW &) : \"%ls\"", (const wchar_t*)csw);
    bool retval = operator<((const wchar_t*)csw);
    GcStrLOGADD(L"%d", (int)retval);
    return retval;
  }

  bool operator<=(const CStringW& csw) const
  {
    GcStrLOG(this, iLogMsg::dStr, L"operator <= (const CStringW &) : \"%ls\"", (const wchar_t*)csw);
    bool retval = operator<=((const wchar_t*)csw);
    GcStrLOGADD(L"%d", (int)retval);
    return retval;
  }

  bool operator>(const CStringW& csw) const
  {
    GcStrLOG(this, iLogMsg::dStr, L"operator > (const CStringW &) : \"%ls\"", (const wchar_t*)csw);
    bool retval = operator>((const wchar_t*)csw);
    GcStrLOGADD(L"%d", (int)retval);
    return retval;
  }

  bool operator>=(const CStringW& csw) const
  {
    GcStrLOG(this, iLogMsg::dStr, L"operator >=(const CStringW &) : \"%ls\"", (const wchar_t*)csw);
    bool retval = operator>=((const wchar_t*)csw);
    GcStrLOGADD(L"%d", (int)retval);
    return retval;
  }

  Gsoft::Int32 match(const CStringW& csw) const
  {
    GcStrLOG(this, iLogMsg::dStr, L"match(const CStringW &) : \"%ls\"", (const wchar_t*)csw);
    Gsoft::Int32 retval = match((const wchar_t*)csw);
    GcStrLOGADD(L"%d", retval);
    return retval;
  }

  Gsoft::Int32 matchNoCase(const CStringW& csw) const
  {
    GcStrLOG(this, iLogMsg::dStr, L"matchNoCase(const CStringW &) : \"%ls\"", (const wchar_t*)csw);
    Gsoft::Int32 retval = matchNoCase((const wchar_t*)csw);
    GcStrLOGADD(L"%d", retval);
    return retval;
  }
#endif

#ifdef __cpp_lib_string_view
  explicit GcString(std::wstring_view str) : GcString(str.data(), (Gsoft::UInt32)str.length())
  {
    GcStrLOG(this, iLogMsg::dStr, L"GcString(std::wstring_view)");
  }

  GcString& operator=(std::wstring_view csw)
  {
    GcStrLOG(this, iLogMsg::dStr, L"operator = (std::wstring_view)");
    assign(csw.data(), (Gsoft::UInt32)csw.length());
    return *this;
  }

#  ifdef GcStringMBCS
  explicit GcString(std::string_view str) : GcString(str.data(), Utf8, (Gsoft::UInt32)str.length())
  {
    GcStrLOG(this, iLogMsg::dStr, L"GcString(std::string_view)");
  }

  explicit GcString(const std::string& str) : GcString(str.c_str(), Utf8)
  {
    GcStrLOG(this, iLogMsg::dStr, L"GcString(const std::string &)");
  }
#  endif

#endif

  GCBASE_PORT explicit GcString(const std::wstring& str);

  GCBASE_PORT GcString& operator=(const std::wstring& csw);

  GCBASE_PORT Gsoft::Int32 replace(const wchar_t* pwszOld, const wchar_t* pwszNew);

  GCBASE_PORT Gsoft::Int32 replace(wchar_t wchOld, wchar_t wchNew);

  GCBASE_PORT Gsoft::Int32 deleteAtIndex(Gsoft::Int32 iIndex, Gsoft::Int32 nCount = 1);

  GCBASE_PORT GcString& truncate(Gsoft::Int32 nIndex);

  GCBASE_PORT GcString tokenize(const wchar_t* pszTokens, Gsoft::Int32& iStart) const;

  GCBASE_PORT GcString& setAt(Gsoft::Int32 nIndex, GCHAR ch);

  GCBASE_PORT wchar_t getAt(Gsoft::Int32 nIndex) const;

  template<class T>
  const wchar_t& operator[](T nIndex) const
  {
    GcStrLOG(this, iLogMsg::dStr, L"operator [](T): %d", (int)nIndex);
    const wchar_t& ref = refAt((Gsoft::UInt32)nIndex);
    GcStrLOGADD(L"%d", (int)ref);
    return ref;
  }

  GCBASE_PORT GcString& insert(Gsoft::Int32 nIndex, wchar_t ch);

  GCBASE_PORT GcString& insert(Gsoft::Int32 nIndex, const wchar_t* pwsz);

  GCBASE_PORT wchar_t* getBuffer(Gsoft::Int32 nMinBufferLength = 0);

  GCBASE_PORT bool releaseBuffer(Gsoft::Int32 nNewLength = -1);

  GCBASE_PORT Gsoft::UInt64 hash() const noexcept;

#if defined(GcStrLogging)
  bool hasUTF8() const
  {
    return (!isSSO() && m_utf8) ? true : false;
  }

  class iLogMsg
  {
  public:
    static constexpr Gsoft::UInt32 dStr = 0x01;
    static constexpr Gsoft::UInt32 destructor = 0x02;

    GCBASE_PORT iLogMsg(const GcString* p, Gsoft::UInt32 flags, const wchar_t* fmt, ...);
    GCBASE_PORT ~iLogMsg();

    GCBASE_PORT void addDMsg(const wchar_t* fmt, ...);

  private:
    const GcString* m_gcstring = nullptr;
    std::wstring m_addmsg;
    Gsoft::UInt32 m_flags = 0;
  };
#endif

private:
  static constexpr Gsoft::UInt32 m_ClassSize = (sizeof(wchar_t) == 4) ? 64 : 48;
  static constexpr Gsoft::UInt32 m_ssoSize = (m_ClassSize - sizeof(Gsoft::UInt64)) / sizeof(wchar_t);
  static constexpr Gsoft::UInt32 m_alignAlloc = 32;
  static constexpr Gsoft::UInt32 m_bufferMode = 0x80000000;
  static constexpr Gsoft::UInt32 m_getBuffMode = 0x40000000;
  static constexpr Gsoft::UInt32 m_maxStrLen = m_getBuffMode - 1;

#ifdef GcStringBADSTRDebug
  union
  {
    Gsoft::UInt64 m_bad64;
    wchar_t m_badstr[sizeof(Gsoft::UInt64) / sizeof(wchar_t)];
  };
#endif
#pragma warning(push)
#pragma warning(disable : 4201)
  union
  {
    Gsoft::UInt64 m_empty;
    struct
    {
      Gsoft::UInt32 m_nullstr;
      Gsoft::UInt32 m_len;
    };
  };
  union
  {
    wchar_t m_ssoBuff[m_ssoSize];
    struct
    {
      wchar_t* m_buffer;
      Gsoft::UInt32* m_utf8;
      Gsoft::UInt32 m_allocated;
    };
  };
#pragma warning(pop)

  void checkSize();

  static bool iASSERTMsg(const wchar_t* msg, const wchar_t* file, Gsoft::Int32 linenum);

  static constexpr bool canSSO(Gsoft::UInt32 newlen) noexcept;

  Gsoft::UInt32 iGetLen() const noexcept;

  void setBuffLen(Gsoft::UInt32 len);

  void iSetLen(Gsoft::UInt32 len);

  bool isSelf(const wchar_t* p) const noexcept;

  bool isSelf(const GcString& object) const noexcept;

  void setNull() noexcept;

  const wchar_t* iGetBuff() const noexcept;

  wchar_t* iGetBuff() noexcept;

  bool isSSO() const noexcept;

  void releaseUTF8();

  static constexpr bool isOneOf(wchar_t ch, const wchar_t* __restrict pwsz) noexcept;

  static constexpr bool isSubStr(const wchar_t* __restrict src, const wchar_t* __restrict sub) noexcept;

  static constexpr bool isSubStrLC(const wchar_t* __restrict src, const wchar_t* __restrict sub) noexcept;

  static constexpr bool is7Bit(const wchar_t* pwsz, Gsoft::UInt32 len) noexcept;

  static constexpr bool isAscii(const wchar_t* pwsz, Gsoft::UInt32 len) noexcept;

  static constexpr Gsoft::UInt32 cstrLen(const wchar_t* __restrict str, Gsoft::UInt32 maxlen) noexcept;

  static constexpr Gsoft::UInt32 cstrLen(const uint8_t* __restrict str, Gsoft::UInt32 maxlen) noexcept;

  static constexpr Gsoft::UInt32 cstrLen(const wchar_t* __restrict str) noexcept;

  static constexpr Gsoft::UInt32 alignLen(Gsoft::UInt32 len) noexcept;

  bool ClrGetBuffer();

  bool SetGetBuffer();

  bool isGetBuffer() const;

  static constexpr void fstrcpy(wchar_t* __restrict dest, const wchar_t* __restrict src, Gsoft::UInt32 len) noexcept;

  static constexpr void fstrcpy(uint8_t* __restrict dest, const wchar_t* __restrict src, Gsoft::UInt32 len) noexcept;

  static constexpr void fstrcpy(uint8_t* __restrict dest, const uint8_t* __restrict src, Gsoft::UInt32 len) noexcept;

  static constexpr void fstrcpy(wchar_t* __restrict dest, const wchar_t* __restrict src) noexcept;

  GCBASE_PORT const wchar_t& refAt(Gsoft::UInt32 nIndex) const;

  void iMove(GcString& gcs);

  void emptyReserve(Gsoft::UInt32 nCapacity);

  void convertSSOtoBuffer(Gsoft::UInt32 size);

  void makeAndCopy(const wchar_t* src, Gsoft::UInt32 len, Gsoft::UInt32 iallocSize);

  void reserveBufferFromEmpty(Gsoft::UInt32 size);

  void bufferResize(Gsoft::UInt32 size);

  static constexpr Gsoft::UInt32 kHowSigned = 0x01;
  static constexpr Gsoft::UInt32 kHowDeci = 0x02;
  static constexpr Gsoft::UInt32 kHowHex = 0x04;
  static constexpr Gsoft::UInt32 kHowBinary = 0x08;
  static constexpr Gsoft::UInt32 kHow64 = 0x10;

  void initWithLen(const wchar_t* __restrict pwsz, Gsoft::UInt32 len, Gsoft::UInt32 allocLen);

  void copyWithLen(const wchar_t* __restrict pwsz, Gsoft::UInt32 len);

  void iappend(const wchar_t* src, Gsoft::UInt32 len);

  void iappendWillFit(const wchar_t* src, Gsoft::UInt32 addlen);

  const char* iutf8Ptr();

  static constexpr Gsoft::UInt32 toLower(Gsoft::UInt32 ch) noexcept;

  static constexpr Gsoft::UInt32 toUpper(Gsoft::UInt32 ch) noexcept;

  static constexpr Gsoft::Int32 compareLCtoLC(Gsoft::UInt32 left, Gsoft::UInt32 right) noexcept;

  static constexpr Gsoft::UInt32 mapeFormat(eFormat flags) noexcept;

  static constexpr Gsoft::UInt64 computeHash(const wchar_t* pws) noexcept;

  static constexpr Gsoft::UInt64 fnv1a64char(Gsoft::UInt64 ch, Gsoft::UInt64 hash) noexcept;

#if !defined(NDEBUG)
  bool DBGverify(void) const;
#endif

  class UTF8converter
  {
  public:
    UTF8converter(const Gsoft::UInt8* __restrict src, Gsoft::UInt32 maxlen, const GcString& GcS);

    ~UTF8converter();

    const wchar_t* getBuff() const;
    Gsoft::UInt32 getLen() const;

  private:
    GCBASE_PORT void genericConverter(const Gsoft::UInt8* src, Gsoft::UInt32 maxlen, const GcString& GcS);

    static constexpr Gsoft::UInt32 m_maxOutSize = 512;
    Gsoft::UInt8* m_in8ptr = nullptr;
    wchar_t* m_outputPtr = nullptr;
    Gsoft::UInt32 m_outlen = 0;

    wchar_t m_outputBuff[m_maxOutSize];
    Gsoft::UInt8 m_in8buff[m_maxOutSize];
  };

  class valToString
  {
  public:
    constexpr valToString(Gsoft::UInt32 nFlags, Gsoft::Int64 sval);

    constexpr const wchar_t* getBuff() const;

    constexpr Gsoft::UInt32 getLen() const;

  private:
    static constexpr Gsoft::Int32 kBufSiz = 32;
    wchar_t m_outBuf[kBufSiz] = {0};
    Gsoft::UInt32 m_StartIndex = kBufSiz - 1;
  };

  Gsoft::Int64 parseVal(Gsoft::UInt32 nHow, Gsoft::UInt32 nFlags) const;
  Gsoft::Int64 parseError(const char* pszErrString, Gsoft::UInt32 nFlags) const;
  Gsoft::Int64 parseEmpty(Gsoft::UInt32 nflags) const;
  Gsoft::Int64 parseInvalidChar(Gsoft::UInt32 nflags) const;

  static Gsoft::Int32 fmtHelper(void* pCookie, const wchar_t* pszFrom, size_t nLen);
  GcString iformatV(const wchar_t* pszFmt, va_list args) const;

  static Gsoft::Int32 collateNoCaseGeneric(const wchar_t* left, const wchar_t* right);

  Gsoft::UInt64 convertHandleToU64(const GcDbHandle& h);

  const char* genericUTF8Ptr();
};

#ifdef GC_GCARRAY_H
typedef GcArray<GcString, GcArrayObjectCopyReallocator<GcString>> GcStringArray;
#endif

inline bool operator==(wchar_t wch, const GcString& gcs)
{
  GcStrLOG(nullptr, 0, L"operator == (wchar_t, const GcString &): %d, \"%ls\"", wch, gcs.kwszPtr());
  bool retval = gcs.compare(wch) == 0;
  GcStrLOGADD(L"%d", (int)retval);
  return retval;
}

inline bool operator==(const wchar_t* pwsz, const GcString& gcs)
{
  GcStrLOG(nullptr,
           0,
           L"operator == (const wchar_t *, const GcString &): \"%ls\", \"%ls\"",
           pwsz ? pwsz : L"(nullptr)",
           gcs.kwszPtr());
  bool retval = gcs.compare(pwsz) == 0;
  GcStrLOGADD(L"%d", (int)retval);
  return retval;
}

inline bool operator!=(wchar_t wch, const GcString& gcs)
{
  GcStrLOG(nullptr, 0, L"operator != (wchar_t, const GcString &): %d, \"%ls\"", wch, gcs.kwszPtr());
  bool retval = gcs.compare(wch) != 0;
  GcStrLOGADD(L"%d", (int)retval);
  return retval;
}

inline bool operator!=(const wchar_t* pwsz, const GcString& gcs)
{
  GcStrLOG(nullptr,
           0,
           L"operator != (const wchar_t *, const GcString &): \"%ls\", \"%ls\"",
           pwsz ? pwsz : L"(nullptr)",
           gcs.kwszPtr());
  bool retval = gcs.compare(pwsz) != 0;
  GcStrLOGADD(L"%d", (int)retval);
  return retval;
}

inline bool operator>(wchar_t wch, const GcString& gcs)
{
  GcStrLOG(nullptr, 0, L"operator > (wchar_t, const GcString &): %d, \"%ls\"", wch, gcs.kwszPtr());
  bool retval = gcs.compare(wch) < 0;
  GcStrLOGADD(L"%d", (int)retval);
  return retval;
}

inline bool operator>(const wchar_t* pwsz, const GcString& gcs)
{
  GcStrLOG(nullptr,
           0,
           L"operator > (const wchar_t *, const GcString &): \"%ls\", \"%ls\"",
           pwsz ? pwsz : L"(nullptr)",
           gcs.kwszPtr());
  bool retval = gcs.compare(pwsz) < 0;
  GcStrLOGADD(L"%d", (int)retval);
  return retval;
}

inline bool operator>=(wchar_t wch, const GcString& gcs)
{
  GcStrLOG(nullptr, 0, L"operator >= (wchar_t, const GcString &): %d, \"%ls\"", wch, gcs.kwszPtr());
  bool retval = gcs.compare(wch) <= 0;
  GcStrLOGADD(L"%d", (int)retval);
  return retval;
}

inline bool operator>=(const wchar_t* pwsz, const GcString& gcs)
{
  GcStrLOG(nullptr,
           0,
           L"operator >= (const wchar_t *, const GcString &): \"%ls\", \"%ls\"",
           pwsz ? pwsz : L"(nullptr)",
           gcs.kwszPtr());
  bool retval = gcs.compare(pwsz) <= 0;
  GcStrLOGADD(L"%d", (int)retval);
  return retval;
}

inline bool operator<(wchar_t wch, const GcString& gcs)
{
  GcStrLOG(nullptr, 0, L"operator < (wchar_t, const GcString &): %d, \"%ls\"", wch, gcs.kwszPtr());
  bool retval = gcs.compare(wch) > 0;
  GcStrLOGADD(L"%d", (int)retval);
  return retval;
}

inline bool operator<(const wchar_t* pwsz, const GcString& gcs)
{
  GcStrLOG(nullptr,
           0,
           L"operator < (const wchar_t *, const GcString &): \"%ls\", \"%ls\"",
           pwsz ? pwsz : L"(nullptr)",
           gcs.kwszPtr());
  bool retval = gcs.compare(pwsz) > 0;
  GcStrLOGADD(L"%d", (int)retval);
  return retval;
}

inline bool operator<=(wchar_t wch, const GcString& gcs)
{
  GcStrLOG(nullptr, 0, L"operator <= (wchar_t, const GcString &): %d, \"%ls\"", wch, gcs.kwszPtr());
  bool retval = gcs.compare(wch) >= 0;
  GcStrLOGADD(L"%d", (int)retval);
  return retval;
}

inline bool operator<=(const wchar_t* pwsz, const GcString& gcs)
{
  GcStrLOG(nullptr,
           0,
           L"operator <= (const wchar_t *, const GcString &): \"%ls\", \"%ls\"",
           pwsz ? pwsz : L"(nullptr)",
           gcs.kwszPtr());
  bool retval = gcs.compare(pwsz) >= 0;
  GcStrLOGADD(L"%d", (int)retval);
  return retval;
}

inline GcString operator+(wchar_t wch, const GcString& gcs)
{
  GcStrLOG(nullptr, 0, L"operator + (wchar_t, const GcString &): %d, \"%ls\"", wch, gcs.kwszPtr());
  GcString retval = gcs.precat(wch);
  GcStrLOGADD(L"\"%ls\"", retval.kwszPtr());
  return retval;
}

inline GcString operator+(const wchar_t* pwsz, const GcString& gcs)
{
  GcStrLOG(nullptr,
           0,
           L"operator + (const wchar_t *, const GcString &): \"%ls\", \"%ls\"",
           pwsz ? pwsz : L"(nullptr)",
           gcs.kwszPtr());
  GcString retval = gcs.precat(pwsz);
  GcStrLOGADD(L"\"%ls\"", retval.kwszPtr());
  return retval;
}

#if defined(ENABLE_GCSTRING_CSTRING_OPERATORS)
#  if defined(_AFX) && !defined(__cplusplus_cli)
inline bool operator==(const CStringW& csw, const GcString& gcs)
{
  GcStrLOG(nullptr,
           0,
           L"operator == (const CStringW &, const GcString &): \"%ls\", \"%ls\"",
           (const wchar_t*)csw,
           gcs.kwszPtr());
  bool retval = gcs.compare(csw) == 0;
  GcStrLOGADD(L"%d", (int)retval);
  return retval;
}

inline bool operator!=(const CStringW& csw, const GcString& gcs)
{
  GcStrLOG(nullptr,
           0,
           L"operator != (const CStringW &, const GcString &): \"%ls\", \"%ls\"",
           (const wchar_t*)csw,
           gcs.kwszPtr());
  bool retval = gcs.compare(csw) != 0;
  GcStrLOGADD(L"%d", (int)retval);
  return retval;
}

inline bool operator>(const CStringW& csw, const GcString& gcs)
{
  GcStrLOG(nullptr,
           0,
           L"operator > (const CStringW &, const GcString &): \"%ls\", \"%ls\"",
           (const wchar_t*)csw,
           gcs.kwszPtr());
  bool retval = gcs.compare(csw) < 0;
  GcStrLOGADD(L"%d", (int)retval);
  return retval;
}

inline bool operator>=(const CStringW& csw, const GcString& gcs)
{
  GcStrLOG(nullptr,
           0,
           L"operator >= (const CStringW &, const GcString &): \"%ls\", \"%ls\"",
           (const wchar_t*)csw,
           gcs.kwszPtr());
  bool retval = gcs.compare(csw) <= 0;
  GcStrLOGADD(L"%d", (int)retval);
  return retval;
}

inline bool operator<(const CStringW& csw, const GcString& gcs)
{
  GcStrLOG(nullptr,
           0,
           L"operator < (const CStringW &, const GcString &): \"%ls\", \"%ls\"",
           (const wchar_t*)csw,
           gcs.kwszPtr());
  bool retval = gcs.compare(csw) > 0;
  GcStrLOGADD(L"%d", (int)retval);
  return retval;
}

inline bool operator<=(const CStringW& csw, const GcString& gcs)
{
  GcStrLOG(nullptr,
           0,
           L"operator <= (const CStringW &, const GcString &): \"%ls\", \"%ls\"",
           (const wchar_t*)csw,
           gcs.kwszPtr());
  bool retval = gcs.compare(csw) >= 0;
  GcStrLOGADD(L"%d", (int)retval);
  return retval;
}

#    ifndef DISABLE_CSTRING_PLUS_GCSTRING
inline GcString operator+(const CStringW& csw, const GcString& gcs)
{
  GcStrLOG(nullptr,
           0,
           L"operator + (const CStringW &, const GcString &): \"%ls\", \"%ls\"",
           (const wchar_t*)csw,
           gcs.kwszPtr());
  GcString retval = gcs.precat((const wchar_t*)csw);
  GcStrLOGADD(L"\"%ls\"", retval.kwszPtr());
  return retval;
}
#    endif
#  endif
#endif

template<>
struct std::hash<GcString>
{
  std::size_t operator()(const GcString& key) const noexcept
  {
    return static_cast<std::size_t>(key.hash());
  }
};

#endif
