|
|
|
/*==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/>.
|
|
|
|
|
|
|
|
Additional permissions under GNU GPL version 3 section 7
|
|
|
|
|
|
|
|
If you modify this Program, or any covered work, by linking or
|
|
|
|
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
|
|
|
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
|
|
|
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
|
|
|
(or a modified version of those libraries),
|
|
|
|
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
|
|
|
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
|
|
|
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
|
|
|
licensors of this Program grant you additional
|
|
|
|
permission to convey the resulting work. Corresponding Source for a
|
|
|
|
non-source form of such a combination shall include the source code for
|
|
|
|
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
|
|
|
work.
|
|
|
|
|
|
|
|
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 "HeadSpin.h"
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
typedef unsigned int UniChar;
|
|
|
|
|
|
|
|
#define SSO_CHARS (16)
|
|
|
|
#define WHITESPACE_CHARS " \t\n\r"
|
|
|
|
|
|
|
|
template <typename _Ch>
|
|
|
|
class plStringBuffer
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
struct StringRef
|
|
|
|
{
|
|
|
|
unsigned int fRefs;
|
|
|
|
const _Ch *fStringData;
|
|
|
|
|
|
|
|
StringRef(const _Ch *data)
|
|
|
|
: fRefs(1), fStringData(data) { }
|
|
|
|
|
|
|
|
inline void AddRef() { ++fRefs; }
|
|
|
|
inline void DecRef()
|
|
|
|
{
|
|
|
|
if (--fRefs == 0) {
|
|
|
|
delete [] fStringData;
|
|
|
|
delete this;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
union {
|
|
|
|
StringRef *fData;
|
|
|
|
_Ch fShort[SSO_CHARS];
|
|
|
|
};
|
|
|
|
size_t fSize;
|
|
|
|
|
|
|
|
bool IHaveACow() const { return fSize >= SSO_CHARS; }
|
|
|
|
|
|
|
|
public:
|
|
|
|
plStringBuffer() : fSize(0) { memset(fShort, 0, sizeof(fShort)); }
|
|
|
|
|
|
|
|
plStringBuffer(const plStringBuffer<_Ch> ©) : fSize(copy.fSize)
|
|
|
|
{
|
|
|
|
memcpy(fShort, copy.fShort, sizeof(fShort));
|
|
|
|
if (IHaveACow())
|
|
|
|
fData->AddRef();
|
|
|
|
}
|
|
|
|
|
|
|
|
plStringBuffer(const _Ch *data, size_t size) : fSize(size)
|
|
|
|
{
|
|
|
|
memset(fShort, 0, sizeof(fShort));
|
|
|
|
_Ch *copyData = IHaveACow() ? new _Ch[size + 1] : fShort;
|
|
|
|
memcpy(copyData, data, size);
|
|
|
|
copyData[size] = 0;
|
|
|
|
|
|
|
|
if (IHaveACow())
|
|
|
|
fData = new StringRef(copyData);
|
|
|
|
}
|
|
|
|
|
|
|
|
~plStringBuffer<_Ch>()
|
|
|
|
{
|
|
|
|
if (IHaveACow())
|
|
|
|
fData->DecRef();
|
|
|
|
}
|
|
|
|
|
|
|
|
plStringBuffer<_Ch> &operator=(const plStringBuffer<_Ch> ©)
|
|
|
|
{
|
|
|
|
if (copy.IHaveACow())
|
|
|
|
copy.fData->AddRef();
|
|
|
|
if (IHaveACow())
|
|
|
|
fData->DecRef();
|
|
|
|
|
|
|
|
memcpy(fShort, copy.fShort, sizeof(fShort));
|
|
|
|
fSize = copy.fSize;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
const _Ch *GetData() const { return IHaveACow() ? fData->fStringData : fShort; }
|
|
|
|
size_t GetSize() const { return fSize; }
|
|
|
|
operator const _Ch *() const { return GetData(); }
|
|
|
|
|
|
|
|
// From Haxxia with love
|
|
|
|
// NOTE: The client is expected to nul-terminate the returned buffer!
|
|
|
|
_Ch *CreateWritableBuffer(size_t size)
|
|
|
|
{
|
|
|
|
if (IHaveACow())
|
|
|
|
fData->DecRef();
|
|
|
|
|
|
|
|
fSize = size;
|
|
|
|
if (IHaveACow()) {
|
|
|
|
_Ch *writable = new _Ch[fSize + 1];
|
|
|
|
fData = new StringRef(writable);
|
|
|
|
return writable;
|
|
|
|
} else {
|
|
|
|
return fShort;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef plStringBuffer<UniChar> plUnicodeBuffer;
|
|
|
|
|
|
|
|
|
|
|
|
class plString
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
enum {
|
|
|
|
kSizeAuto = (size_t)(0x80000000)
|
|
|
|
};
|
|
|
|
static const plString Null;
|
|
|
|
|
|
|
|
private:
|
|
|
|
plStringBuffer<char> fUtf8Buffer;
|
|
|
|
|
|
|
|
void IConvertFromUtf8(const char *utf8, size_t size);
|
|
|
|
void IConvertFromUtf16(const uint16_t *utf16, size_t size);
|
|
|
|
void IConvertFromWchar(const wchar_t *wstr, size_t size);
|
|
|
|
void IConvertFromUtf32(const UniChar *ustr, size_t size);
|
|
|
|
void IConvertFromIso8859_1(const char *astr, size_t size);
|
|
|
|
|
|
|
|
public:
|
|
|
|
plString() { }
|
|
|
|
|
|
|
|
plString(const char *cstr) { IConvertFromUtf8(cstr, kSizeAuto); }
|
|
|
|
plString(const plString ©) : fUtf8Buffer(copy.fUtf8Buffer) { }
|
|
|
|
plString(const plStringBuffer<char> &init) { operator=(init); }
|
|
|
|
plString(const plUnicodeBuffer &init) { IConvertFromUtf32(init.GetData(), init.GetSize()); }
|
|
|
|
|
|
|
|
plString &operator=(const char *cstr) { IConvertFromUtf8(cstr, kSizeAuto); return *this; }
|
|
|
|
plString &operator=(const plString ©) { fUtf8Buffer = copy.fUtf8Buffer; return *this; }
|
|
|
|
plString &operator=(const plStringBuffer<char> &init);
|
|
|
|
plString &operator=(const plUnicodeBuffer &init) { IConvertFromUtf32(init.GetData(), init.GetSize()); return *this; }
|
|
|
|
|
|
|
|
plString &operator+=(const char *cstr) { return operator=(*this + cstr); }
|
|
|
|
plString &operator+=(const plString &str) { return operator=(*this + str); }
|
|
|
|
|
|
|
|
static inline plString FromUtf8(const char *utf8, size_t size = kSizeAuto)
|
|
|
|
{
|
|
|
|
plString str;
|
|
|
|
str.IConvertFromUtf8(utf8, size);
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline plString FromUtf16(const uint16_t *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 char *substitute = "") const
|
|
|
|
{ return IsEmpty() ? substitute : fUtf8Buffer.GetData(); }
|
|
|
|
|
|
|
|
char CharAt(size_t position) const { return c_str()[position]; }
|
|
|
|
|
|
|
|
plStringBuffer<char> ToUtf8() const { return fUtf8Buffer; }
|
|
|
|
plStringBuffer<uint16_t> ToUtf16() const;
|
|
|
|
plStringBuffer<wchar_t> ToWchar() const;
|
|
|
|
plStringBuffer<char> ToIso8859_1() const;
|
|
|
|
|
|
|
|
// For use in displaying characters in a GUI
|
|
|
|
plUnicodeBuffer GetUnicodeArray() const;
|
|
|
|
|
|
|
|
size_t GetSize() const { return fUtf8Buffer.GetSize(); }
|
|
|
|
bool IsEmpty() const { return fUtf8Buffer.GetSize() == 0; }
|
|
|
|
|
|
|
|
// TODO: Evaluate whether Plasma actually needs to distinguish between
|
|
|
|
// empty and NULL strings. Ideally, only IsEmpty should be required.
|
|
|
|
bool IsNull() const { return IsEmpty(); }
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
enum CaseSensitivity {
|
|
|
|
kCaseSensitive, kCaseInsensitive
|
|
|
|
};
|
|
|
|
|
|
|
|
int Compare(const plString &str, CaseSensitivity sense = kCaseSensitive) const
|
|
|
|
{
|
|
|
|
return (sense == kCaseSensitive) ? strcmp(c_str(), str.c_str())
|
|
|
|
: stricmp(c_str(), str.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
int Compare(const char *str, CaseSensitivity sense = kCaseSensitive) const
|
|
|
|
{
|
|
|
|
return (sense == kCaseSensitive) ? strcmp(c_str(), str)
|
|
|
|
: stricmp(c_str(), str);
|
|
|
|
}
|
|
|
|
|
|
|
|
int CompareN(const plString &str, size_t count, CaseSensitivity sense = kCaseSensitive) const
|
|
|
|
{
|
|
|
|
return (sense == kCaseSensitive) ? strncmp(c_str(), str.c_str(), count)
|
|
|
|
: strnicmp(c_str(), str.c_str(), count);
|
|
|
|
}
|
|
|
|
|
|
|
|
int CompareN(const char *str, size_t count, CaseSensitivity sense = kCaseSensitive) const
|
|
|
|
{
|
|
|
|
return (sense == kCaseSensitive) ? strncmp(c_str(), str, count)
|
|
|
|
: strnicmp(c_str(), str, count);
|
|
|
|
}
|
|
|
|
|
|
|
|
int CompareI(const plString &str) const { return Compare(str, kCaseInsensitive); }
|
|
|
|
int CompareI(const char *str) const { return Compare(str, kCaseInsensitive); }
|
|
|
|
int CompareNI(const plString &str, size_t count) const { return CompareN(str, count, kCaseInsensitive); }
|
|
|
|
int CompareNI(const char *str, size_t count) const { return CompareN(str, count, kCaseInsensitive); }
|
|
|
|
|
|
|
|
bool operator<(const plString &other) const { return Compare(other) < 0; }
|
|
|
|
bool operator==(const char *other) const { return Compare(other) == 0; }
|
|
|
|
bool operator==(const plString &other) const { return Compare(other) == 0; }
|
|
|
|
bool operator!=(const char *other) const { return Compare(other) != 0; }
|
|
|
|
bool operator!=(const plString &other) const { return Compare(other) != 0; }
|
|
|
|
|
|
|
|
int Find(char ch, CaseSensitivity sense = kCaseSensitive) const;
|
|
|
|
int FindLast(char ch, CaseSensitivity sense = kCaseSensitive) const;
|
|
|
|
|
|
|
|
int Find(const char *str, CaseSensitivity sense = kCaseSensitive) const;
|
|
|
|
int Find(const plString &str, CaseSensitivity sense = kCaseSensitive) const
|
|
|
|
{ return Find(str.c_str(), sense); }
|
|
|
|
|
|
|
|
plString TrimLeft(const char *charset = WHITESPACE_CHARS) const;
|
|
|
|
plString TrimRight(const char *charset = WHITESPACE_CHARS) const;
|
|
|
|
plString Trim(const char *charset = WHITESPACE_CHARS) 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); }
|
|
|
|
|
|
|
|
// NOTE: Does Compare(blah, kCaseInsensitive) make more sense? If
|
|
|
|
// so, use that instead -- it's faster and more efficient!
|
|
|
|
plString ToUpper() const;
|
|
|
|
plString ToLower() const;
|
|
|
|
|
|
|
|
// Should replace other tokenization methods. The difference between Split
|
|
|
|
// and Tokenize is that Tokenize never returns a blank string (it strips
|
|
|
|
// all delimiters and only returns the pieces left between them), whereas
|
|
|
|
// Split will split on a full string, returning whatever is left between.
|
|
|
|
std::vector<plString> Split(const char *split, size_t maxSplits = kSizeAuto) const;
|
|
|
|
std::vector<plString> Tokenize(const char *delims = WHITESPACE_CHARS) const;
|
|
|
|
|
|
|
|
static plString Fill(size_t count, char c);
|
|
|
|
|
|
|
|
public:
|
|
|
|
struct less
|
|
|
|
{
|
|
|
|
bool operator()(const plString &_L, const plString &_R) const
|
|
|
|
{ return _L.Compare(_R, kCaseSensitive) < 0; }
|
|
|
|
};
|
|
|
|
|
|
|
|
struct less_i
|
|
|
|
{
|
|
|
|
bool operator()(const plString &_L, const plString &_R) const
|
|
|
|
{ return _L.Compare(_R, kCaseInsensitive) < 0; }
|
|
|
|
};
|
|
|
|
|
|
|
|
struct equal
|
|
|
|
{
|
|
|
|
bool operator()(const plString &_L, const plString &_R) const
|
|
|
|
{ return _L.Compare(_R, kCaseSensitive) == 0; }
|
|
|
|
};
|
|
|
|
|
|
|
|
struct equal_i
|
|
|
|
{
|
|
|
|
bool operator()(const plString &_L, const plString &_R) const
|
|
|
|
{ return _L.Compare(_R, kCaseInsensitive) == 0; }
|
|
|
|
};
|
|
|
|
|
|
|
|
private:
|
|
|
|
friend plString operator+(const plString &left, const plString &right);
|
|
|
|
friend plString operator+(const plString &left, const char *right);
|
|
|
|
friend plString operator+(const char *left, const plString &right);
|
|
|
|
};
|
|
|
|
|
|
|
|
plString operator+(const plString &left, const plString &right);
|
|
|
|
plString operator+(const plString &left, const char *right);
|
|
|
|
plString operator+(const char *left, const plString &right);
|
|
|
|
|
|
|
|
|
|
|
|
class plStringStream
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
plStringStream() : fBufSize(256), fLength(0)
|
|
|
|
{
|
|
|
|
fBuffer = new char[fBufSize];
|
|
|
|
}
|
|
|
|
~plStringStream() { delete [] fBuffer; }
|
|
|
|
|
|
|
|
plStringStream &append(const char *data, size_t length);
|
|
|
|
|
|
|
|
plStringStream &operator<<(const char *text);
|
|
|
|
plStringStream &operator<<(int num);
|
|
|
|
plStringStream &operator<<(unsigned int num);
|
|
|
|
plStringStream &operator<<(char ch) { return append(&ch, 1); }
|
|
|
|
|
|
|
|
plStringStream &operator<<(const plString &text)
|
|
|
|
{
|
|
|
|
return append(text.c_str(), text.GetSize());
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t GetLength() const { return fLength; }
|
|
|
|
plString GetString() { return plString::FromUtf8(fBuffer, fLength); }
|
|
|
|
|
|
|
|
private:
|
|
|
|
char *fBuffer;
|
|
|
|
size_t fBufSize;
|
|
|
|
size_t fLength;
|
|
|
|
};
|
|
|
|
|
|
|
|
size_t ustrlen(const UniChar *ustr, size_t max = plString::kSizeAuto);
|
|
|
|
|
|
|
|
#endif //plString_Defined
|