diff --git a/Sources/Plasma/CoreLib/CMakeLists.txt b/Sources/Plasma/CoreLib/CMakeLists.txt index 2196d9a6..c6ad4945 100644 --- a/Sources/Plasma/CoreLib/CMakeLists.txt +++ b/Sources/Plasma/CoreLib/CMakeLists.txt @@ -1,5 +1,16 @@ add_definitions(-D_LIB) +if(NOT WCHAR_BYTES) + include(CheckTypeSize) + + check_type_size("wchar_t" WCHAR_BYTES) + if(NOT WCHAR_BYTES) + message(FATAL_ERROR "Could not determine sizeof(wchar_t)") + set(WCHAR_BYTES 0) + endif(NOT WCHAR_BYTES) +endif(NOT WCHAR_BYTES) +add_definitions(-DWCHAR_BYTES=${WCHAR_BYTES}) + set(CoreLib_SOURCES HeadSpin.cpp hsBitVector.cpp @@ -26,6 +37,7 @@ set(CoreLib_SOURCES pcSmallRect.cpp plGeneric.cpp plLoadMask.cpp + plString.cpp plViewTransform.cpp ) @@ -82,6 +94,7 @@ set(CoreLib_HEADERS plLoadMask.h plQuality.h plRefCnt.h + plString.h plTweak.h plViewTransform.h ) diff --git a/Sources/Plasma/CoreLib/plString.cpp b/Sources/Plasma/CoreLib/plString.cpp new file mode 100644 index 00000000..9db20ea8 --- /dev/null +++ b/Sources/Plasma/CoreLib/plString.cpp @@ -0,0 +1,662 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +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 3 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, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "plString.h" + +#include +#include + +const plString plString::Null; + +#if !defined(WCHAR_BYTES) || (WCHAR_BYTES != 2) && (WCHAR_BYTES != 4) +#error "WCHAR_BYTES must be either 2 (16-bit) or 4 (32-bit)!" +#endif + +#if WCHAR_BYTES == 2 +#define u16slen(str, max) wcsnlen((const wchar_t *)(str), (max)) +#else +static inline size_t u16slen(const UInt16 *ustr, size_t max) +{ + size_t length = 0; + for ( ; *ustr++ && max--; ++length) + ; + return length; +} +#endif + +#define BADCHAR_REPLACEMENT (0xFFFDul) + +void plString::IConvertFromUtf8(const char *utf8, size_t size, bool steal) +{ + if ((long)size < 0) + size = strnlen(utf8, -(long)size); + +#ifdef _DEBUG + // Check to make sure the string is actually valid UTF-8 + const char *sp = utf8; + while (sp < utf8 + size) { + unsigned char unichar = *sp++; + if ((unichar & 0xF8) == 0xF0) { + // Four bytes + hsAssert((*sp++) & 0x80, "Invalid UTF-8 sequence byte (1)"); + hsAssert((*sp++) & 0x80, "Invalid UTF-8 sequence byte (2)"); + hsAssert((*sp++) & 0x80, "Invalid UTF-8 sequence byte (3)"); + } else if ((unichar & 0xF0) == 0xE0) { + // Three bytes + hsAssert((*sp++) & 0x80, "Invalid UTF-8 sequence byte (1)"); + hsAssert((*sp++) & 0x80, "Invalid UTF-8 sequence byte (2)"); + } else if ((unichar & 0xE0) == 0xC0) { + // Two bytes + hsAssert((*sp++) & 0x80, "Invalid UTF-8 sequence byte (1)"); + } else if ((unichar & 0xC0) == 0x80) { + hsAssert(0, "Invalid UTF-8 marker byte"); + } else if ((unichar & 0x80) != 0) { + hsAssert(0, "UTF-8 character out of range"); + } + } +#endif + + fUtf8Buffer = steal ? plStringBuffer::Steal(utf8, size) + : plStringBuffer(utf8, size); +} + +void plString::IConvertFromUtf16(const UInt16 *utf16, size_t size) +{ + if ((long)size < 0) + size = u16slen(utf16, -(long)size); + + // Calculate the UTF-8 size + size_t convlen = 0; + const UInt16 *sp = utf16; + while (sp < utf16 + size) { + if (*sp >= 0xD800 && *sp <= 0xDFFF) { + // Surrogate pair + convlen += 4; + ++sp; + } + else if (*sp > 0x7FF) + convlen += 3; + else if (*sp > 0x7F) + convlen += 2; + else + convlen += 1; + ++sp; + } + + // And perform the actual conversion + char *utf8 = TRACKED_NEW char[convlen + 1]; + char *dp = utf8; + sp = utf16; + while (sp < utf16 + size) { + if (*sp >= 0xD800 && *sp <= 0xDFFF) { + // Surrogate pair + unsigned int unichar = 0x10000; + + if (sp + 1 >= utf16 + size) { + hsAssert(0, "Incomplete surrogate pair in UTF-16 data"); + unichar = BADCHAR_REPLACEMENT; + } else if (*sp < 0xDC00) { + unichar += (*sp++ & 0x3FF) << 10; + hsAssert(*sp >= 0xDC00 && *sp <= 0xDFFF, + "Invalid surrogate pair in UTF-16 data"); + unichar += (*sp & 0x3FF); + } else { + unichar += (*sp++ & 0x3FF); + hsAssert(*sp >= 0xD800 && *sp < 0xDC00, + "Invalid surrogate pair in UTF-16 data"); + unichar += (*sp & 0x3FF) << 10; + } + *dp++ = 0xF0 | ((unichar >> 18) & 0x07); + *dp++ = 0x80 | ((unichar >> 12) & 0x3F); + *dp++ = 0x80 | ((unichar >> 6) & 0x3F); + *dp++ = 0x80 | ((unichar ) & 0x3F); + } else if (*sp > 0x7FF) { + *dp++ = 0xF0 | ((*sp >> 12) & 0x0F); + *dp++ = 0x80 | ((*sp >> 6) & 0x3F); + *dp++ = 0x80 | ((*sp ) & 0x3F); + } else if (*sp > 0x7F) { + *dp++ = 0xF0 | ((*sp >> 6) & 0x1F); + *dp++ = 0x80 | ((*sp ) & 0x3F); + } else { + *dp++ = (char)(*sp); + } + ++sp; + } + utf8[convlen] = 0; + + fUtf8Buffer = plStringBuffer::Steal(utf8, convlen); +} + +void plString::IConvertFromWchar(const wchar_t *wstr, size_t size) +{ +#if WCHAR_BYTES == 2 + // We assume that if sizeof(wchar_t) == 2, the data is UTF-16 already + IConvertFromUtf16((const UInt16 *)wstr, size); +#else + if ((long)size < 0) + size = wcsnlen(wstr, -(long)size); + + // Calculate the UTF-8 size + size_t convlen = 0; + const wchar_t *sp = wstr; + while (sp < wstr + size) { + if (*sp > 0x10FFFF) { + hsAssert(0, "UCS-4 character out of range"); + convlen += 3; // Use U+FFFD for release builds + } + else if (*sp > 0xFFFF) + convlen += 4; + else if (*sp > 0x7FF) + convlen += 3; + else if (*sp > 0x7F) + convlen += 2; + else + convlen += 1; + ++sp; + } + + // And perform the actual conversion + char *utf8 = TRACKED_NEW char[convlen + 1]; + char *dp = utf8; + sp = wstr; + while (sp < wstr + size) { + if (*sp > 0x10FFFF) { + // Character out of range; Use U+FFFD instead + *dp++ = 0xE0 | ((BADCHAR_REPLACEMENT >> 12) & 0x0F); + *dp++ = 0x80 | ((BADCHAR_REPLACEMENT >> 6) & 0x3F); + *dp++ = 0x80 | ((BADCHAR_REPLACEMENT ) & 0x3F); + } else if (*sp > 0xFFFF) { + *dp++ = 0xF0 | ((*sp >> 18) & 0x07); + *dp++ = 0x80 | ((*sp >> 12) & 0x3F); + *dp++ = 0x80 | ((*sp >> 6) & 0x3F); + *dp++ = 0x80 | ((*sp ) & 0x3F); + } else if (*sp > 0x7FF) { + *dp++ = 0xF0 | ((*sp >> 12) & 0x0F); + *dp++ = 0x80 | ((*sp >> 6) & 0x3F); + *dp++ = 0x80 | ((*sp ) & 0x3F); + } else if (*sp > 0x7F) { + *dp++ = 0xF0 | ((*sp >> 6) & 0x1F); + *dp++ = 0x80 | ((*sp ) & 0x3F); + } else { + *dp++ = (char)(*sp); + } + ++sp; + } + utf8[convlen] = 0; + + fUtf8Buffer = plStringBuffer::Steal(utf8, convlen); +#endif +} + +void plString::IConvertFromIso8859_1(const char *astr, size_t size) +{ + if ((long)size < 0) + size = strnlen(astr, -(long)size); + + // Calculate the UTF-8 size + size_t convlen = 0; + const char *sp = astr; + while (sp < astr + size) { + if ((*sp++) & 0x80) + convlen += 2; + else + convlen += 1; + } + + // And perform the actual conversion + char *utf8 = TRACKED_NEW char[convlen + 1]; + char *dp = utf8; + sp = astr; + while (sp < astr + size) { + if (*astr & 0x80) { + *dp++ = 0xC0 | ((*sp >> 6) & 0x1F); + *dp++ = 0x80 | ((*sp ) & 0x3F); + } else { + *dp++ = *sp; + } + ++sp; + } + utf8[convlen] = 0; + + fUtf8Buffer = plStringBuffer::Steal(utf8, convlen); +} + +plStringBuffer plString::ToUtf16() const +{ + if (IsNull()) + return plStringBuffer(); + + // Calculate the UTF-16 size + size_t convlen = 0; + const char *utf8 = fUtf8Buffer.GetData(); + const char *sp = utf8; + size_t srcSize = fUtf8Buffer.GetSize(); + while (sp < utf8 + srcSize) { + if ((*sp & 0xF8) == 0xF0) { + // Will require a surrogate pair + ++convlen; + sp += 4; + } + else if ((*sp & 0xF0) == 0xE0) + sp += 3; + else if ((*sp & 0xE0) == 0xC0) + sp += 2; + else + sp += 1; + ++convlen; + } + + // And perform the actual conversion + UInt16 *ustr = TRACKED_NEW UInt16[convlen + 1]; + UInt16 *dp = ustr; + sp = utf8; + while (sp < utf8 + srcSize) { + unsigned int unichar; + if ((*sp & 0xF8) == 0xF0) { + unichar = (*sp++ & 0x07) << 18; + unichar |= (*sp++ & 0x3F) << 12; + unichar |= (*sp++ & 0x3F) << 6; + unichar |= (*sp++ & 0x3F); + + *dp++ = 0xD800 | ((unichar >> 10) & 0x3FF); + *dp++ = 0xDC00 | ((unichar ) & 0x3FF); + } else if ((*sp & 0xF0) == 0xE0) { + unichar = (*sp++ & 0x0F) << 12; + unichar |= (*sp++ & 0x3F) << 6; + unichar |= (*sp++ & 0x3F); + *dp++ = unichar; + } else if ((*sp & 0xE0) == 0xC0) { + unichar = (*sp++ & 0x1F) << 6; + unichar |= (*sp++ & 0x3F); + *dp++ = unichar; + } else { + *dp++ = *sp++; + } + } + ustr[convlen] = 0; + + return plStringBuffer::Steal(ustr, convlen); +} + +plStringBuffer plString::ToWchar() const +{ +#if WCHAR_BYTES == 2 + // We assume that if sizeof(wchar_t) == 2, the data is UTF-16 already + plStringBuffer utf16 = ToUtf16(); + return *reinterpret_cast*>(&utf16); +#else + if (IsNull()) + return plStringBuffer(); + + // Calculate the UCS-4 size + size_t convlen = 0; + const char *utf8 = fUtf8Buffer.GetData(); + const char *sp = utf8; + size_t srcSize = fUtf8Buffer.GetSize(); + while (sp < utf8 + srcSize) { + if ((*sp & 0xF8) == 0xF0) + sp += 4; + else if ((*sp & 0xF0) == 0xE0) + sp += 3; + else if ((*sp & 0xE0) == 0xC0) + sp += 2; + else + sp += 1; + ++convlen; + } + + // And perform the actual conversion + wchar_t *wstr = TRACKED_NEW wchar_t[convlen + 1]; + wchar_t *dp = wstr; + sp = utf8; + while (sp < utf8 + srcSize) { + unsigned int unichar; + if ((*sp & 0xF8) == 0xF0) { + unichar = (*sp++ & 0x07) << 18; + unichar |= (*sp++ & 0x3F) << 12; + unichar |= (*sp++ & 0x3F) << 6; + unichar |= (*sp++ & 0x3F); + } else if ((*sp & 0xF0) == 0xE0) { + unichar = (*sp++ & 0x0F) << 12; + unichar |= (*sp++ & 0x3F) << 6; + unichar |= (*sp++ & 0x3F); + } else if ((*sp & 0xE0) == 0xC0) { + unichar = (*sp++ & 0x1F) << 6; + unichar |= (*sp++ & 0x3F); + } else { + unichar = *sp++; + } + *dp++ = unichar; + } + wstr[convlen] = 0; + + return plStringBuffer::Steal(wstr, convlen); +#endif +} + +plStringBuffer plString::ToIso8859_1() const +{ + if (IsNull()) + return plStringBuffer(); + + // Calculate the ASCII size + size_t convlen = 0; + const char *utf8 = fUtf8Buffer.GetData(); + const char *sp = utf8; + size_t srcSize = fUtf8Buffer.GetSize(); + while (sp < utf8 + srcSize) { + if ((*sp & 0xF8) == 0xF0) + sp += 4; + else if ((*sp & 0xF0) == 0xE0) + sp += 3; + else if ((*sp & 0xE0) == 0xC0) + sp += 2; + else + sp += 1; + ++convlen; + } + + // And perform the actual conversion + char *astr = TRACKED_NEW char[convlen + 1]; + char *dp = astr; + sp = utf8; + while (sp < utf8 + srcSize) { + unsigned int unichar; + if ((*sp & 0xF8) == 0xF0) { + unichar = (*sp++ & 0x07) << 18; + unichar |= (*sp++ & 0x3F) << 12; + unichar |= (*sp++ & 0x3F) << 6; + unichar |= (*sp++ & 0x3F); + } else if ((*sp & 0xF0) == 0xE0) { + unichar = (*sp++ & 0x0F) << 12; + unichar |= (*sp++ & 0x3F) << 6; + unichar |= (*sp++ & 0x3F); + } else if ((*sp & 0xE0) == 0xC0) { + unichar = (*sp++ & 0x1F) << 6; + unichar |= (*sp++ & 0x3F); + } else { + unichar = *sp++; + } + *dp++ = (unichar < 0xFF) ? unichar : '?'; + } + astr[convlen] = 0; + + return plStringBuffer::Steal(astr, convlen); +} + +plStringBuffer plString::GetUnicodeArray() const +{ + static UniChar empty[1] = {0}; + + if (IsNull()) + return plStringBuffer(empty, 0); + + size_t convlen = GetUniCharCount(); + UniChar *ustr = new UniChar[convlen + 1]; + iterator iter = GetIterator(); + size_t dp = 0; + while (!iter.AtEnd()) + ustr[dp++] = *iter++; + ustr[convlen] = 0; + return plStringBuffer::Steal(ustr, convlen); +} + +int plString::ToInt(int base) const +{ + return static_cast(strtol(s_str(), nil, base)); +} + +unsigned int plString::ToUInt(int base) const +{ + return static_cast(strtoul(s_str(), nil, base)); +} + +float plString::ToFloat() const +{ + // strtof is C99, which MS doesn't support... + return (float)strtod(s_str(), nil); +} + +double plString::ToDouble() const +{ + return strtod(s_str(), nil); +} + +// Microsoft doesn't provide this for us +#ifdef _MSC_VER +#define va_copy(dest, src) (dest) = (src) +#endif + +plString plString::IFormat(const char *fmt, va_list vptr) +{ + char buffer[256]; + va_list vptr_save; + va_copy(vptr_save, vptr); + + int chars = vsnprintf(buffer, 256, fmt, vptr); + if (chars < 0) { + // We will need to try this multiple times until we get a + // large enough buffer :( + int size = 4096; + for ( ;; ) { + va_copy(vptr, vptr_save); + char *bigbuffer = TRACKED_NEW char[size]; + chars = vsnprintf(bigbuffer, size, fmt, vptr); + if (chars >= 0) + return plString::Steal(bigbuffer); + + delete [] bigbuffer; + size *= 2; + } + } else if (chars >= 256) { + va_copy(vptr, vptr_save); + char *bigbuffer = TRACKED_NEW char[chars+1]; + vsnprintf(bigbuffer, chars+1, fmt, vptr); + return plString::Steal(bigbuffer); + } + + return plString::FromUtf8(buffer); +} + +plString plString::Format(const char *fmt, ...) +{ + va_list vptr; + va_start(vptr, fmt); + plString str = IFormat(fmt, vptr); + va_end(vptr); + return str; +} + +int plString::Find(char ch, CaseSense sense) const +{ + if (sense == kCaseSensitive) { + const char *cp = strchr(s_str(), ch); + return cp ? (cp - c_str()) : -1; + } else { + // No need to check for null, since s_str() will return { 0 } if it is null + const char *cp = s_str(); + while (*cp) { + if (tolower(*cp) == tolower(ch)) + return cp - c_str(); + } + return -1; + } +} + +int plString::FindReverse(char ch, CaseSense sense) const +{ + if (IsEmpty()) + return -1; + + if (sense == kCaseSensitive) { + const char *cp = strrchr(s_str(), ch); + return cp ? (cp - c_str()) : -1; + } else { + const char *cp = c_str(); + cp += strlen(cp); + + while (--cp >= c_str()) { + if (tolower(*cp) == tolower(ch)) + return cp - c_str(); + } + return -1; + } +} + +static bool in_set(char key, const char *charset) +{ + for (const char *cs = charset; *cs; ++cs) { + if (*cs == key) + return true; + } + return false; +} + +plString plString::TrimLeft(const char *charset) const +{ + if (IsEmpty()) + return Null; + + const char *cp = c_str(); + while (*cp && in_set(*cp, charset)) + ++cp; + + return Substr(cp - c_str()); +} + +plString plString::TrimRight(const char *charset) const +{ + if (IsEmpty()) + return Null; + + const char *cp = c_str(); + cp += strlen(cp); + + while (--cp >= c_str() && in_set(*cp, charset)) + ; + + return Substr(0, cp - c_str() + 1); +} + +plString plString::Trim(const char *charset) const +{ + if (IsEmpty()) + return Null; + + const char *lp = c_str(); + const char *rp = lp + strlen(lp); + + while (*lp && in_set(*lp, charset)) + ++lp; + while (--rp >= lp && in_set(*rp, charset)) + ; + + return Substr(lp - c_str(), rp - lp + 1); +} + +plString plString::Substr(int start, size_t size) const +{ + size_t maxSize = GetSize(); + + if (start > maxSize) + return Null; + if (start < 0) + start = 0; + if (start + size > maxSize) + size = maxSize - start; + + if (start == 0 && size == maxSize) + return *this; + + char *substr = TRACKED_NEW char[size + 1]; + memcpy(substr, c_str() + start, size); + substr[size] = 0; + + // Don't re-check UTF-8 on this + plString str; + str.fUtf8Buffer.Steal(substr, size); + return str; +} + +plString &plString::operator+=(const plString &str) +{ + size_t catsize = GetSize() + str.GetSize(); + char *catstr = TRACKED_NEW char[catsize + 1]; + memcpy(catstr, s_str(), GetSize()); + memcpy(catstr + GetSize(), str.s_str(), str.GetSize()); + catstr[catsize] = 0; + fUtf8Buffer.Steal(catstr, catsize); + return *this; +} + +plString operator+(const plString &left, const plString &right) +{ + size_t catsize = left.GetSize() + right.GetSize(); + char *catstr = TRACKED_NEW char[catsize + 1]; + memcpy(catstr, left.s_str(), left.GetSize()); + memcpy(catstr + left.GetSize(), right.s_str(), right.GetSize()); + catstr[catsize] = 0; + + // Don't re-check UTF-8 on this + plString str; + str.fUtf8Buffer.Steal(catstr, catsize); + return str; +} + +plStringStream &plStringStream::operator<<(const char *text) +{ + size_t length = strlen(text); + if (fLength + length > fBufSize) { + char *bigger = new char[fBufSize * 2]; + memcpy(bigger, fBuffer, fBufSize); + delete [] fBuffer; + fBuffer = bigger; + fBufSize *= 2; + } + memcpy(fBuffer + fLength, text, length); + fLength += length; + return *this; +} + +plStringStream &plStringStream::operator<<(int num) +{ + char buffer[12]; + snprintf(buffer, 12, "%d", num); + return operator<<(buffer); +} + +plStringStream &plStringStream::operator<<(unsigned int num) +{ + char buffer[12]; + snprintf(buffer, 12, "%u", num); + return operator<<(buffer); +} + +plStringStream &plStringStream::operator<<(char ch) +{ + char buffer[2] = { ch, 0 }; + return operator<<(buffer); +} diff --git a/Sources/Plasma/CoreLib/plString.h b/Sources/Plasma/CoreLib/plString.h new file mode 100644 index 00000000..368b4a96 --- /dev/null +++ b/Sources/Plasma/CoreLib/plString.h @@ -0,0 +1,387 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +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 3 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, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plString_Defined +#define plString_Defined + +#include "hsTypes.h" +#include "hsUtils.h" +#include +#include +#include + +#define _TEMP_CONVERT_FROM_LITERAL(x) plString::FromUtf8((x)) +#define _TEMP_CONVERT_FROM_WCHAR_T(x) plString::FromWchar((x)) + +typedef unsigned int UniChar; + +template +class plStringBuffer +{ +private: + struct StringRef + { + unsigned int fRefs; + const _Ch *fStringData; + const size_t fSize; + + StringRef(const _Ch *data, const size_t size) + : fRefs(1), fStringData(data), fSize(size) { } + + inline void AddRef() { ++fRefs; } + inline void DecRef() + { + if (--fRefs == 0) { + delete [] fStringData; + delete this; + } + } + }; + + StringRef *fData; + +public: + plStringBuffer() : fData(0) { } + + plStringBuffer(const plStringBuffer<_Ch> ©) + { + fData = copy.fData; + if (fData) + fData->AddRef(); + } + + plStringBuffer(const _Ch *data, size_t size) + { + _Ch *copyData = new _Ch[size + 1]; + memcpy(copyData, data, size); + copyData[size] = 0; + + fData = new StringRef(copyData, size); + } + + ~plStringBuffer<_Ch>() + { + if (fData) + fData->DecRef(); + } + + static plStringBuffer<_Ch> Steal(const _Ch *data, size_t size) + { + plStringBuffer<_Ch> string; + string.fData = new StringRef(data, size); + return string; + } + + plStringBuffer<_Ch> &operator=(const plStringBuffer<_Ch> ©) + { + if (copy.fData) + copy.fData->AddRef(); + if (fData) + fData->DecRef(); + fData = copy.fData; + return *this; + } + + const _Ch *GetData() const { return fData ? fData->fStringData : 0; } + const size_t GetSize() const { return fData ? fData->fSize : 0; } +}; + + +class plString +{ +#pragma warning(push) +#pragma warning(disable : 4146) + enum { + kSizeAuto = (size_t)(-2147483648L) + }; +#pragma warning(pop) + + enum CaseSense { + kCaseSensitive, kCaseInsensitive + }; + +public: + static const plString Null; + +private: + plStringBuffer fUtf8Buffer; + + void IConvertFromUtf8(const char *utf8, size_t size, bool steal); + void IConvertFromUtf16(const UInt16 *utf16, size_t size); + void IConvertFromWchar(const wchar_t *wstr, size_t size); + void IConvertFromIso8859_1(const char *astr, size_t size); + +public: + plString() { } + + //plString(const char *utf8) { IConvertFromUtf8(utf8, kSizeAuto, false); } + //plString(const wchar_t *wstr) { IConvertFromWchar(wstr, kSizeAuto); } + plString(const plString ©) : fUtf8Buffer(copy.fUtf8Buffer) { } + + //plString &operator=(const char *utf8) { IConvertFromUtf8(utf8, kSizeAuto, false); return *this; } + //plString &operator=(const wchar_t *wstr) { IConvertFromWchar(wstr, kSizeAuto); return *this; } + plString &operator=(const plString ©) { fUtf8Buffer = copy.fUtf8Buffer; return *this; } + + plString &operator+=(const plString &str); + + static inline plString FromUtf8(const char *utf8, size_t size = kSizeAuto) + { + plString str; + str.IConvertFromUtf8(utf8, size, false); + return str; + } + + static inline plString FromUtf16(const UInt16 *utf16, size_t size = kSizeAuto) + { + plString str; + str.IConvertFromUtf16(utf16, size); + return str; + } + + static inline plString FromWchar(const wchar_t *wstr, size_t size = kSizeAuto) + { + plString str; + str.IConvertFromWchar(wstr, size); + return str; + } + + static inline plString FromIso8859_1(const char *astr, size_t size = kSizeAuto) + { + plString str; + str.IConvertFromIso8859_1(astr, size); + return str; + } + + const char *c_str() const { return fUtf8Buffer.GetData(); } + const char *s_str() const { return c_str() ? c_str() : ""; } + plStringBuffer ToUtf8() const { return fUtf8Buffer; } + plStringBuffer ToUtf16() const; + plStringBuffer ToWchar() const; + plStringBuffer ToIso8859_1() const; + + // For use in displaying characters in a GUI + plStringBuffer GetUnicodeArray() const; + + size_t GetSize() const { return fUtf8Buffer.GetSize(); } + bool IsEmpty() const { return fUtf8Buffer.GetSize() == 0; } + bool IsNull() const { return fUtf8Buffer.GetData() == 0; } + + int ToInt(int base = 0) const; + unsigned int ToUInt(int base = 0) const; + float ToFloat() const; + double ToDouble() const; + + static plString Format(const char *fmt, ...); + static plString IFormat(const char *fmt, va_list vptr); + static plString Steal(const char *utf8, size_t size = kSizeAuto) + { + plString str; + str.IConvertFromUtf8(utf8, size, true); + return str; + } + + int Compare(const plString &str, CaseSense sense = kCaseSensitive) const + { + return (sense == kCaseSensitive) ? strcmp(s_str(), str.s_str()) + : stricmp(s_str(), str.s_str()); + } + + int Compare(const char *str, CaseSense sense = kCaseSensitive) const + { + return (sense == kCaseSensitive) ? strcmp(s_str(), str) + : stricmp(s_str(), str); + } + + bool operator==(const plString &other) const { return Compare(other) == 0; } + bool operator!=(const plString &other) const { return Compare(other) != 0; } + + int Find(char ch, CaseSense sense = kCaseSensitive) const; + int FindReverse(char ch, CaseSense sense = kCaseSensitive) const; + + plString TrimLeft(const char *charset = " \t\n\r") const; + plString TrimRight(const char *charset = " \t\n\r") const; + plString Trim(const char *charset = " \t\n\r") const; + + plString Substr(int start, size_t size = kSizeAuto) const; + plString Left(size_t size) const { return Substr(0, size); } + plString Right(size_t size) const { return Substr(GetSize() - size, size); } + +public: + struct less : public std::binary_function + { + bool operator()(const plString &_L, const plString &_R) const + { return _L.Compare(_R, kCaseSensitive) < 0; } + }; + + struct less_i : public std::binary_function + { + bool operator()(const plString &_L, const plString &_R) const + { return _L.Compare(_R, kCaseInsensitive) < 0; } + }; + + struct equal : public std::binary_function + { + bool operator()(const plString &_L, const plString &_R) const + { return _L.Compare(_R, kCaseSensitive) == 0; } + }; + + struct equal_i : public std::binary_function + { + bool operator()(const plString &_L, const plString &_R) const + { return _L.Compare(_R, kCaseInsensitive) == 0; } + }; + +public: + struct iterator + { + iterator() : m_ptr(nil), m_end(nil) { } + iterator(const iterator ©) : m_ptr(copy.m_ptr), m_end(copy.m_end) { } + + iterator &operator=(const iterator ©) + { m_ptr = copy.m_ptr; m_end = copy.m_end; return *this; } + + iterator &operator++() + { + if ((*m_ptr & 0xF8) == 0xF0) + m_ptr += 4; + else if ((*m_ptr & 0xF0) == 0xE0) + m_ptr += 3; + else if ((*m_ptr & 0xE0) == 0xC0) + m_ptr += 2; + else + m_ptr += 1; + return *this; + } + + iterator &operator+=(size_t delta) + { + while (delta) { + operator++(); + --delta; + } + return *this; + } + + iterator operator+(size_t delta) const + { + iterator copy(*this); + copy += delta; + return copy; + } + + int operator-(const iterator &other) const + { + return (int)(m_ptr - other.m_ptr); + } + + bool operator==(const iterator &other) const { return m_ptr == other.m_ptr; } + bool operator!=(const iterator &other) const { return m_ptr != other.m_ptr; } + bool operator<(const iterator &other) const { return m_ptr < other.m_ptr; } + bool operator>(const iterator &other) const { return m_ptr > other.m_ptr; } + bool operator<=(const iterator &other) const { return m_ptr <= other.m_ptr; } + bool operator>=(const iterator &other) const { return m_ptr >= other.m_ptr; } + + UniChar operator*() const + { + UniChar ch; + if ((*m_ptr & 0xF8) == 0xF0) { + ch = (m_ptr[0] & 0x07) << 18; + ch |= (m_ptr[1] & 0x3F) << 12; + ch |= (m_ptr[2] & 0x3F) << 6; + ch |= (m_ptr[3] & 0x3F); + } else if ((*m_ptr & 0xF0) == 0xE0) { + ch = (m_ptr[0] & 0x0F) << 12; + ch |= (m_ptr[1] & 0x3F) << 6; + ch |= (m_ptr[2] & 0x3F); + } else if ((*m_ptr & 0xE0) == 0xC0) { + ch = (m_ptr[0] & 0x1F) << 6; + ch |= (m_ptr[1] & 0x3F); + } else { + ch = m_ptr[0]; + } + return ch; + } + + bool AtEnd() const { return m_ptr >= m_end; } + bool IsValid() const { return m_ptr != 0; } + + private: + friend class plString; + iterator(const char *ptr, size_t size) : m_ptr(ptr), m_end(ptr + size) { } + + const char *m_ptr; + const char *m_end; + }; + + iterator GetIterator() const { return iterator(s_str(), GetSize()); } + + size_t GetUniCharCount() const + { + iterator iter = GetIterator(); + size_t count = 0; + while (!iter.AtEnd()) { + ++iter; + ++count; + } + return count; + } + +private: + friend plString operator+(const plString &left, const plString &right); +}; + +plString operator+(const plString &left, const plString &right); + + +class plStringStream +{ +public: + plStringStream() : fBufSize(256), fLength(0) + { + fBuffer = new char[fBufSize]; + } + ~plStringStream() { delete [] fBuffer; } + + plStringStream &operator<<(const char *text); + plStringStream &operator<<(int num); + plStringStream &operator<<(unsigned int num); + plStringStream &operator<<(char ch); + + plStringStream &operator<<(const plString &text) + { + return operator<<(text.s_str()); + } + + size_t GetLength() const { return fLength; } + plString GetString() { return plString::FromUtf8(fBuffer, fLength); } + +private: + char *fBuffer; + size_t fBufSize; + size_t fLength; +}; + +#endif //plString_Defined