Michael Hansen
11 years ago
3 changed files with 365 additions and 0 deletions
@ -0,0 +1,187 @@ |
|||||||
|
/*==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==*/ |
||||||
|
|
||||||
|
#include "plFormat.h" |
||||||
|
|
||||||
|
#include "HeadSpin.h" |
||||||
|
|
||||||
|
namespace plFormat_Private |
||||||
|
{ |
||||||
|
static const char *_scanNextFormat(IFormatDataObject &data) |
||||||
|
{ |
||||||
|
hsAssert(data.fFormatStr, "Passed a null format string!"); |
||||||
|
|
||||||
|
const char *ptr = data.fFormatStr; |
||||||
|
while (*ptr) { |
||||||
|
if (*ptr == '{') |
||||||
|
return ptr; |
||||||
|
++ptr; |
||||||
|
} |
||||||
|
|
||||||
|
return ptr; |
||||||
|
} |
||||||
|
|
||||||
|
static void _fetchPrefixChunk(IFormatDataObject &data) |
||||||
|
{ |
||||||
|
do { |
||||||
|
const char *next = _scanNextFormat(data); |
||||||
|
if (*next && *(next + 1) == '{') { |
||||||
|
// Escaped '{'
|
||||||
|
data.fOutput.push_back(plStringBuffer<char>(data.fFormatStr, 1 + next - data.fFormatStr)); |
||||||
|
data.fFormatStr = next + 2; |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
if (next != data.fFormatStr) |
||||||
|
data.fOutput.push_back(plStringBuffer<char>(data.fFormatStr, next - data.fFormatStr)); |
||||||
|
data.fFormatStr = next; |
||||||
|
} while (0); |
||||||
|
} |
||||||
|
|
||||||
|
FormatSpec FetchNextFormat(IFormatDataObject &data) |
||||||
|
{ |
||||||
|
_fetchPrefixChunk(data); |
||||||
|
hsAssert(*data.fFormatStr == '{', "Too many actual parameters for format string"); |
||||||
|
|
||||||
|
FormatSpec spec; |
||||||
|
const char *ptr = data.fFormatStr; |
||||||
|
for ( ;; ) { |
||||||
|
++ptr; |
||||||
|
|
||||||
|
switch (*ptr) { |
||||||
|
case 0: |
||||||
|
hsAssert(0, "Unterminated format specifier."); |
||||||
|
abort(); |
||||||
|
case '}': |
||||||
|
// Done with format spec
|
||||||
|
data.fFormatStr = ptr + 1; |
||||||
|
return spec; |
||||||
|
break; |
||||||
|
|
||||||
|
case '<': |
||||||
|
spec.fAlignment = kAlignLeft; |
||||||
|
break; |
||||||
|
case '>': |
||||||
|
spec.fAlignment = kAlignRight; |
||||||
|
break; |
||||||
|
case '_': |
||||||
|
spec.fPadChar = *(ptr + 1); |
||||||
|
hsAssert(spec.fPadChar, "Unterminated format specifier"); |
||||||
|
++ptr; |
||||||
|
break; |
||||||
|
case 'x': |
||||||
|
spec.fDigitClass = kDigitHex; |
||||||
|
break; |
||||||
|
case 'X': |
||||||
|
spec.fDigitClass = kDigitHexUpper; |
||||||
|
break; |
||||||
|
case 'd': |
||||||
|
spec.fDigitClass = kDigitDec; |
||||||
|
break; |
||||||
|
case 'o': |
||||||
|
spec.fDigitClass = kDigitOct; |
||||||
|
break; |
||||||
|
case 'b': |
||||||
|
spec.fDigitClass = kDigitBin; |
||||||
|
break; |
||||||
|
case 'c': |
||||||
|
spec.fDigitClass = kDigitChar; |
||||||
|
break; |
||||||
|
case 'f': |
||||||
|
spec.fFloatClass = kFloatF; |
||||||
|
break; |
||||||
|
case 'g': |
||||||
|
spec.fFloatClass = kFloatG; |
||||||
|
break; |
||||||
|
case 'e': |
||||||
|
spec.fFloatClass = kFloatE; |
||||||
|
break; |
||||||
|
case '0': case '1': case '2': case '3': case '4': |
||||||
|
case '5': case '6': case '7': case '8': case '9': |
||||||
|
{ |
||||||
|
char *end = nullptr; |
||||||
|
spec.fPrecisionLeft = strtol(ptr, &end, 10); |
||||||
|
ptr = end - 1; |
||||||
|
} |
||||||
|
break; |
||||||
|
case '.': |
||||||
|
{ |
||||||
|
hsAssert(*(ptr + 1), "Unterminated format specifier"); |
||||||
|
char *end = nullptr; |
||||||
|
spec.fPrecisionRight = strtol(ptr + 1, &end, 10); |
||||||
|
ptr = end - 1; |
||||||
|
} |
||||||
|
break; |
||||||
|
default: |
||||||
|
hsAssert(0, "Unexpected character in format string"); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
plString _IFormat(plFormat_Private::IFormatDataObject &data) |
||||||
|
{ |
||||||
|
_fetchPrefixChunk(data); |
||||||
|
hsAssert(*data.fFormatStr == 0, "Not enough actual parameters for format string"); |
||||||
|
|
||||||
|
size_t outsize = 0; |
||||||
|
for (const plStringBuffer<char> &buf : data.fOutput) |
||||||
|
outsize += buf.GetSize(); |
||||||
|
|
||||||
|
plStringBuffer<char> outbuf; |
||||||
|
char *out_ptr = outbuf.CreateWritableBuffer(outsize); |
||||||
|
for (const plStringBuffer<char> &buf : data.fOutput) { |
||||||
|
memcpy(out_ptr, buf.GetData(), buf.GetSize()); |
||||||
|
out_ptr += buf.GetSize(); |
||||||
|
} |
||||||
|
*out_ptr = 0; |
||||||
|
|
||||||
|
return outbuf; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
PL_FORMAT_IMPL(int) |
||||||
|
{ |
||||||
|
char buffer[32]; |
||||||
|
int size = snprintf(buffer, 32, "%d", value); |
||||||
|
return plStringBuffer<char>(buffer, size); |
||||||
|
} |
@ -0,0 +1,176 @@ |
|||||||
|
/*==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 plFormat_Defined |
||||||
|
#define plFormat_Defined |
||||||
|
|
||||||
|
#include "plString.h" |
||||||
|
#include <list> |
||||||
|
#include <string> |
||||||
|
|
||||||
|
/* (TODO: Make this table doxygen-friendly)
|
||||||
|
* |
||||||
|
* FORMAT SPECIFICATION |
||||||
|
* |
||||||
|
* {} - format a value (defaults) |
||||||
|
* {{ - Escape for a single '{' char |
||||||
|
* {options} - Format a value, with the specified options (see below) |
||||||
|
* |
||||||
|
* Options: |
||||||
|
* < - Align left |
||||||
|
* > - Align right |
||||||
|
* NNN - Pad to NNN characters (minimum - can be more) |
||||||
|
* _C - Use C as the pad character (only '\001'..'\177' supported for now) |
||||||
|
* x - Hex (lower-case) |
||||||
|
* X - Hex (upper-case) |
||||||
|
* o - Octal |
||||||
|
* b - Binary |
||||||
|
* d - Decimal (default) -- when used with char types, outputs a |
||||||
|
* number instead of the UTF representation of the char |
||||||
|
* c - UTF character (default for character types) |
||||||
|
* FFF.EEE - Use FFF.EEE floating point precision |
||||||
|
* f - Use 'f' format for floating point numbers |
||||||
|
* g - Use 'g' format for floating point numbers |
||||||
|
* e - Use 'e' format for floating point numbers |
||||||
|
*/ |
||||||
|
|
||||||
|
// For internal use by plFormat and its helper function
|
||||||
|
namespace plFormat_Private |
||||||
|
{ |
||||||
|
enum Alignment : unsigned char |
||||||
|
{ |
||||||
|
kAlignDefault, kAlignLeft, kAlignRight |
||||||
|
}; |
||||||
|
|
||||||
|
enum DigitClass : unsigned char |
||||||
|
{ |
||||||
|
kDigitDefault, kDigitDec, kDigitHex, kDigitHexUpper, |
||||||
|
kDigitOct, kDigitBin, kDigitChar |
||||||
|
}; |
||||||
|
|
||||||
|
enum FloatClass : unsigned char |
||||||
|
{ |
||||||
|
kFloatDefault, kFloatE, kFloatF, kFloatG |
||||||
|
}; |
||||||
|
|
||||||
|
struct FormatSpec |
||||||
|
{ |
||||||
|
const char *fEnd; |
||||||
|
|
||||||
|
int fPrecisionLeft = 0; |
||||||
|
int fPrecisionRight = 0; |
||||||
|
|
||||||
|
char fPadChar = 0; |
||||||
|
Alignment fAlignment = kAlignDefault; |
||||||
|
DigitClass fDigitClass = kDigitDefault; |
||||||
|
FloatClass fFloatClass = kFloatDefault; |
||||||
|
}; |
||||||
|
|
||||||
|
struct IFormatDataObject |
||||||
|
{ |
||||||
|
const char *fFormatStr; |
||||||
|
std::list<plStringBuffer<char>> fOutput; |
||||||
|
}; |
||||||
|
|
||||||
|
extern FormatSpec FetchNextFormat(IFormatDataObject &data); |
||||||
|
} |
||||||
|
|
||||||
|
// Fun fact: You can add your own formatters by declaring
|
||||||
|
// PL_FORMAT_TYPE(mytype) in a header, and
|
||||||
|
// PL_FORMAT_IMPL(mytype) { ... } in a source file
|
||||||
|
|
||||||
|
#define PL_FORMAT_TYPE(_type) \ |
||||||
|
extern plStringBuffer<char> _impl_plFormat_DataHandler( \
|
||||||
|
plFormat_Private::FormatSpec &format, _type value); \
|
||||||
|
namespace plFormat_Private \
|
||||||
|
{ \
|
||||||
|
template <typename... _Args> \
|
||||||
|
plString _IFormat(IFormatDataObject &data, _type value, _Args... args) \
|
||||||
|
{ \
|
||||||
|
plFormat_Private::FormatSpec format = plFormat_Private::FetchNextFormat(data); \
|
||||||
|
data.fOutput.push_back(_impl_plFormat_DataHandler(format, value)); \
|
||||||
|
return _IFormat(data, args...); \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
template <typename... _Args> \
|
||||||
|
plString plFormat(const char *fmt_str, _type value, _Args... args) \
|
||||||
|
{ \
|
||||||
|
plFormat_Private::IFormatDataObject data; \
|
||||||
|
data.fFormatStr = fmt_str; \
|
||||||
|
plFormat_Private::FormatSpec format = plFormat_Private::FetchNextFormat(data); \
|
||||||
|
data.fOutput.push_back(_impl_plFormat_DataHandler(format, value)); \
|
||||||
|
return plFormat_Private::_IFormat(data, args...); \
|
||||||
|
} |
||||||
|
|
||||||
|
#define PL_FORMAT_IMPL(_type) \ |
||||||
|
plStringBuffer<char> _impl_plFormat_DataHandler( \
|
||||||
|
plFormat_Private::FormatSpec &format, _type value) |
||||||
|
|
||||||
|
PL_FORMAT_TYPE(char) |
||||||
|
PL_FORMAT_TYPE(wchar_t) |
||||||
|
PL_FORMAT_TYPE(signed char) |
||||||
|
PL_FORMAT_TYPE(unsigned char) |
||||||
|
PL_FORMAT_TYPE(short) |
||||||
|
PL_FORMAT_TYPE(unsigned short) |
||||||
|
PL_FORMAT_TYPE(int) |
||||||
|
PL_FORMAT_TYPE(unsigned) |
||||||
|
PL_FORMAT_TYPE(long) |
||||||
|
PL_FORMAT_TYPE(unsigned long) |
||||||
|
PL_FORMAT_TYPE(int64_t) |
||||||
|
PL_FORMAT_TYPE(uint64_t) |
||||||
|
PL_FORMAT_TYPE(float) |
||||||
|
PL_FORMAT_TYPE(double) |
||||||
|
PL_FORMAT_TYPE(const char *) |
||||||
|
PL_FORMAT_TYPE(const wchar_t *) |
||||||
|
PL_FORMAT_TYPE(const plString &) |
||||||
|
|
||||||
|
// TODO: Remove these when they're no longer needed
|
||||||
|
PL_FORMAT_TYPE(const std::string &) |
||||||
|
PL_FORMAT_TYPE(const std::wstring &) |
||||||
|
|
||||||
|
// End of the chain -- emits the last piece (if any) and builds the final string
|
||||||
|
namespace plFormat_Private |
||||||
|
{ |
||||||
|
plString _IFormat(IFormatDataObject &data); |
||||||
|
} |
||||||
|
|
||||||
|
#endif // plFormat_Defined
|
Loading…
Reference in new issue