Michael Hansen
13 years ago
3 changed files with 1062 additions and 0 deletions
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
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 <cstring> |
||||||
|
#include <cstdlib> |
||||||
|
|
||||||
|
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<char>::Steal(utf8, size) |
||||||
|
: plStringBuffer<char>(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<char>::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<char>::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<char>::Steal(utf8, convlen); |
||||||
|
} |
||||||
|
|
||||||
|
plStringBuffer<UInt16> plString::ToUtf16() const |
||||||
|
{ |
||||||
|
if (IsNull()) |
||||||
|
return plStringBuffer<UInt16>(); |
||||||
|
|
||||||
|
// 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<UInt16>::Steal(ustr, convlen); |
||||||
|
} |
||||||
|
|
||||||
|
plStringBuffer<wchar_t> plString::ToWchar() const |
||||||
|
{ |
||||||
|
#if WCHAR_BYTES == 2 |
||||||
|
// We assume that if sizeof(wchar_t) == 2, the data is UTF-16 already
|
||||||
|
plStringBuffer<UInt16> utf16 = ToUtf16(); |
||||||
|
return *reinterpret_cast<plStringBuffer<wchar_t>*>(&utf16); |
||||||
|
#else |
||||||
|
if (IsNull()) |
||||||
|
return plStringBuffer<wchar_t>(); |
||||||
|
|
||||||
|
// 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<wchar_t>::Steal(wstr, convlen); |
||||||
|
#endif |
||||||
|
} |
||||||
|
|
||||||
|
plStringBuffer<char> plString::ToIso8859_1() const |
||||||
|
{ |
||||||
|
if (IsNull()) |
||||||
|
return plStringBuffer<char>(); |
||||||
|
|
||||||
|
// 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<char>::Steal(astr, convlen); |
||||||
|
} |
||||||
|
|
||||||
|
plStringBuffer<UniChar> plString::GetUnicodeArray() const |
||||||
|
{ |
||||||
|
static UniChar empty[1] = {0}; |
||||||
|
|
||||||
|
if (IsNull()) |
||||||
|
return plStringBuffer<UniChar>(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<UniChar>::Steal(ustr, convlen); |
||||||
|
} |
||||||
|
|
||||||
|
int plString::ToInt(int base) const |
||||||
|
{ |
||||||
|
return static_cast<int>(strtol(s_str(), nil, base)); |
||||||
|
} |
||||||
|
|
||||||
|
unsigned int plString::ToUInt(int base) const |
||||||
|
{ |
||||||
|
return static_cast<unsigned int>(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); |
||||||
|
} |
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
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 <stddef.h> |
||||||
|
#include <vector> |
||||||
|
#include <functional> |
||||||
|
|
||||||
|
#define _TEMP_CONVERT_FROM_LITERAL(x) plString::FromUtf8((x)) |
||||||
|
#define _TEMP_CONVERT_FROM_WCHAR_T(x) plString::FromWchar((x)) |
||||||
|
|
||||||
|
typedef unsigned int UniChar; |
||||||
|
|
||||||
|
template <typename _Ch> |
||||||
|
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<char> 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<char> ToUtf8() const { return fUtf8Buffer; } |
||||||
|
plStringBuffer<UInt16> ToUtf16() const; |
||||||
|
plStringBuffer<wchar_t> ToWchar() const; |
||||||
|
plStringBuffer<char> ToIso8859_1() const; |
||||||
|
|
||||||
|
// For use in displaying characters in a GUI
|
||||||
|
plStringBuffer<UniChar> 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<plString, plString, bool> |
||||||
|
{ |
||||||
|
bool operator()(const plString &_L, const plString &_R) const |
||||||
|
{ return _L.Compare(_R, kCaseSensitive) < 0; } |
||||||
|
}; |
||||||
|
|
||||||
|
struct less_i : public std::binary_function<plString, plString, bool> |
||||||
|
{ |
||||||
|
bool operator()(const plString &_L, const plString &_R) const |
||||||
|
{ return _L.Compare(_R, kCaseInsensitive) < 0; } |
||||||
|
}; |
||||||
|
|
||||||
|
struct equal : public std::binary_function<plString, plString, bool> |
||||||
|
{ |
||||||
|
bool operator()(const plString &_L, const plString &_R) const |
||||||
|
{ return _L.Compare(_R, kCaseSensitive) == 0; } |
||||||
|
}; |
||||||
|
|
||||||
|
struct equal_i : public std::binary_function<plString, plString, bool> |
||||||
|
{ |
||||||
|
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
|
Loading…
Reference in new issue