/
redgpu
/
ezEngine
Обзор
Документация
Войти
/
redgpu
/
ezEngine
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
dev
Code/Engine/Foundation/Strings/Implementation/snprintf.cpp
1 092 строки
33 KB
Jan Krassnigg
Remove circular includes in Basics.h (#1318)
04 июн 2024, 22:07
Не верифицирован
04 июн 2024, 22:07
dab7d29
Код
Авторство
О чём код?
#include <Foundation/FoundationPCH.h> #include <Foundation/Math/Math.h> #include <Foundation/Strings/StringUtils.h> #include <stdarg.h> // This is an implementation of the sprintf function, with an additional buffer size // On some systems this is implemented under the name 'snprintf' // It tries to be as true to the specification as possible, where possible, // though there are some ambiguities in the specification, which other implementations // also interpret differently // Spec taken from here: http://www.cplusplus.com/reference/clibrary/cstdio/sprintf/ // So far there is no support for wide character strings (and chars) // There is no support for locales, the floating point dot is always a '.' even on layouts // that would otherwise use a ',' (e.g. German) // Not Implemented: // Wide Character support for %s // Everything about this function works with standard ASCII characters, // what sense does it make to embed a wide-character string into it? Shouldn't the formatting string then // also be wide-character ? // // Length Specifier (h/l/L) for d/i/u/x/X/o // I don't get it what these things are useful for. Everything is promoted to int / double. // Small values (bytes / shorts) don't get any larger by this, I don't see anything that could be done // differently knowing these values are supposed to be bytes or shorts. // #define USE_STRICT_SPECIFICATION struct sprintfFlags { enum Enum { None, LeftJustify = EZ_BIT(0), // - ForceSign = EZ_BIT(1), // + BlankSign = EZ_BIT(2), // (space) Hash = EZ_BIT(3), // # PadZeros = EZ_BIT(4), // 0 ForceZeroSign = EZ_BIT(5), // [internal] Prints a '+' even for zero }; }; struct sprintfLength { enum Enum { Default, // int, double, char, char* ShortInt, // short LongInt, // long int (?) LongDouble, // long double (?) LongLongInt, // long long int (64 Bit) }; }; // Reads all the 'Flags' from the formatting string static unsigned int ReadFlags(const char* szFormat, unsigned int& ref_uiReadPos, char& ref_iNext) { unsigned int Flags = sprintfFlags::None; bool bContinue = true; while (bContinue) { bContinue = false; switch (ref_iNext) { case '-': Flags |= sprintfFlags::LeftJustify; bContinue = true; break; case '+': Flags |= sprintfFlags::ForceSign; bContinue = true; break; case ' ': Flags |= sprintfFlags::BlankSign; bContinue = true; break; case '#': Flags |= sprintfFlags::Hash; bContinue = true; break; case '0': Flags |= sprintfFlags::PadZeros; bContinue = true; break; } if (bContinue) ref_iNext = szFormat[++ref_uiReadPos]; } return Flags; } // Reads the 'width' parameter from the formatting string static int ReadWidth(const char* szFormat, unsigned int& ref_uiReadPos, char& ref_iNext) { int iWidth = 0; if (ref_iNext == '*') { iWidth = -1; ref_iNext = szFormat[++ref_uiReadPos]; return iWidth; } if ((ref_iNext >= '1') && (ref_iNext <= '9')) // do not allow widths of zero, because that would collide with the flags specifier { iWidth = ref_iNext - '0'; ref_iNext = szFormat[++ref_uiReadPos]; while ((ref_iNext >= '0') && (ref_iNext <= '9')) // also breaks upon '\0', so this is safe { iWidth *= 10; iWidth += ref_iNext - '0'; ref_iNext = szFormat[++ref_uiReadPos]; } } return iWidth; } // Reads the 'precision' parameter from the formatting string. static int ReadPrecision(const char* szFormat, unsigned int& ref_uiReadPos, char& ref_iNext) { if (ref_iNext != '.') return -1; // default if no precision is specified ref_iNext = szFormat[++ref_uiReadPos]; if (ref_iNext == '*') { ref_iNext = szFormat[++ref_uiReadPos]; return -2; } // the default if no precision is specified, but the '.' is present int iPrecision = 0; while ((ref_iNext >= '0') && (ref_iNext <= '9')) // also breaks upon '\0', so this is safe { iPrecision *= 10; iPrecision += ref_iNext - '0'; ref_iNext = szFormat[++ref_uiReadPos]; } return iPrecision; } // Reads the 'length' parameter from the formatting string static sprintfLength::Enum ReadLength(const char* szFormat, unsigned int& ref_uiReadPos, char& ref_iNext) { sprintfLength::Enum res = sprintfLength::Default; switch (ref_iNext) { case 'z': res = sizeof(size_t) == 8 ? sprintfLength::LongInt : sprintfLength::Default; ref_iNext = szFormat[++ref_uiReadPos]; break; case 'h': res = sprintfLength::ShortInt; ref_iNext = szFormat[++ref_uiReadPos]; break; case 'l': res = sprintfLength::LongInt; ref_iNext = szFormat[++ref_uiReadPos]; if (ref_iNext == 'l') { res = sprintfLength::LongLongInt; ref_iNext = szFormat[++ref_uiReadPos]; } break; case 'L': res = sprintfLength::LongDouble; ref_iNext = szFormat[++ref_uiReadPos]; break; } return res; } // Reads the 'specifier' parameter from the formatting string. static char ReadSpecifier(const char* szFormat, unsigned int& ref_uiReadPos, char& ref_iNext, bool& ref_bError) { const char cRet = ref_iNext; switch (ref_iNext) { case 'c': case 'd': case 'i': case 'e': case 'E': case 'f': case 'g': case 'G': case 'o': case 's': case 'u': case 'x': case 'X': case 'p': case 'n': case '%': // 'b' for 'binary' does not exist in the original specification // However, for debugging, it is sometime quite useful, so I added it case 'b': ref_iNext = szFormat[++ref_uiReadPos]; break; default: ref_bError = true; break; } return cRet; } static void OutputChar(char* szOutputBuffer, unsigned int uiBufferSize, unsigned int& ref_uiWritePos, char c) { if ((szOutputBuffer) && (ref_uiWritePos + 1 < uiBufferSize)) szOutputBuffer[ref_uiWritePos] = c; // always increase the write position to know how much would have been written ++ref_uiWritePos; } static void OutputPadding(char* szOutputBuffer, unsigned int uiBufferSize, unsigned int& ref_uiWritePos, int iPadding, char iPad) { for (int i = 0; i < iPadding; ++i) OutputChar(szOutputBuffer, uiBufferSize, ref_uiWritePos, iPad); } static void OutputPaddingByFlags(char* szOutputBuffer, unsigned int uiBufferSize, unsigned int& ref_uiWritePos, int iPadding, unsigned int uiFlags) { if (uiFlags & sprintfFlags::PadZeros) OutputPadding(szOutputBuffer, uiBufferSize, ref_uiWritePos, iPadding, '0'); else OutputPadding(szOutputBuffer, uiBufferSize, ref_uiWritePos, iPadding, ' '); } static void OutputNullPtr(char* szOutputBuffer, unsigned int uiBufferSize, unsigned int& ref_uiWritePos) { OutputChar(szOutputBuffer, uiBufferSize, ref_uiWritePos, '('); OutputChar(szOutputBuffer, uiBufferSize, ref_uiWritePos, 'n'); OutputChar(szOutputBuffer, uiBufferSize, ref_uiWritePos, 'u'); OutputChar(szOutputBuffer, uiBufferSize, ref_uiWritePos, 'l'); OutputChar(szOutputBuffer, uiBufferSize, ref_uiWritePos, 'l'); OutputChar(szOutputBuffer, uiBufferSize, ref_uiWritePos, ')'); } static void OutputNaN(char* szOutputBuffer, unsigned int uiBufferSize, unsigned int& ref_uiWritePos) { OutputChar(szOutputBuffer, uiBufferSize, ref_uiWritePos, 'N'); OutputChar(szOutputBuffer, uiBufferSize, ref_uiWritePos, 'a'); OutputChar(szOutputBuffer, uiBufferSize, ref_uiWritePos, 'N'); } static void OutputInf(char* szOutputBuffer, unsigned int uiBufferSize, unsigned int& ref_uiWritePos) { OutputChar(szOutputBuffer, uiBufferSize, ref_uiWritePos, 'I'); OutputChar(szOutputBuffer, uiBufferSize, ref_uiWritePos, 'n'); OutputChar(szOutputBuffer, uiBufferSize, ref_uiWritePos, 'f'); OutputChar(szOutputBuffer, uiBufferSize, ref_uiWritePos, 'i'); OutputChar(szOutputBuffer, uiBufferSize, ref_uiWritePos, 'n'); OutputChar(szOutputBuffer, uiBufferSize, ref_uiWritePos, 'i'); OutputChar(szOutputBuffer, uiBufferSize, ref_uiWritePos, 't'); OutputChar(szOutputBuffer, uiBufferSize, ref_uiWritePos, 'y'); } static void OutputString( char* szOutputBuffer, unsigned int uiBufferSize, unsigned int& ref_uiWritePos, const char* szString, unsigned int uiFlags, int iMinSize, int iMaxSize) { if (!szString) { OutputNullPtr(szOutputBuffer, uiBufferSize, ref_uiWritePos); return; } int iLen = 0; // if the string is supposed to be right-justified if (((uiFlags & sprintfFlags::LeftJustify) == 0) && (iMinSize > 0)) { // get the string length while (szString[iLen] != '\0') ++iLen; if ((iMaxSize >= 0) && (iLen > iMaxSize)) iLen = iMaxSize; // output as much padding as necessary OutputPaddingByFlags(szOutputBuffer, uiBufferSize, ref_uiWritePos, iMinSize - iLen, uiFlags); } // a negative max size will have no effect (disabled) iLen = 0; while ((szString[iLen] != '\0') && (iMaxSize != 0)) { OutputChar(szOutputBuffer, uiBufferSize, ref_uiWritePos, szString[iLen]); --iMaxSize; ++iLen; } // if the text is supposed to be left justified, add required padding if (((uiFlags & sprintfFlags::LeftJustify) != 0) && (iMinSize > 0)) OutputPadding(szOutputBuffer, uiBufferSize, ref_uiWritePos, iMinSize - iLen, ' '); } static void OutputReverseString(char* szOutputBuffer, unsigned int uiBufferSize, unsigned int& ref_uiWritePos, const char* szString, int iStringLength) { while (iStringLength > 0) { --iStringLength; OutputChar(szOutputBuffer, uiBufferSize, ref_uiWritePos, szString[iStringLength]); // NOLINT: False positive from clang-tidy } } static void OutputIntSign(char* szOutputBuffer, unsigned int uiBufferSize, unsigned int& ref_uiWritePos, long long int value, unsigned int uiFlags, int iBase, bool bUpperCase, int iPrecision) { if (value < 0) OutputChar(szOutputBuffer, uiBufferSize, ref_uiWritePos, '-'); else if (uiFlags & sprintfFlags::ForceSign) { if (value > 0) OutputChar(szOutputBuffer, uiBufferSize, ref_uiWritePos, '+'); else OutputChar(szOutputBuffer, uiBufferSize, ref_uiWritePos, ' '); } else if (uiFlags & sprintfFlags::BlankSign) OutputChar(szOutputBuffer, uiBufferSize, ref_uiWritePos, ' '); else if (uiFlags & sprintfFlags::ForceZeroSign) OutputChar(szOutputBuffer, uiBufferSize, ref_uiWritePos, '+'); if (uiFlags & sprintfFlags::Hash) { // With precision set to 'zero', nothing shall be printed if the value is zero if ((value == 0) && (iPrecision == 0)) return; // in octal mode, print a '0' in front of it if (iBase == 8) OutputChar(szOutputBuffer, uiBufferSize, ref_uiWritePos, '0'); // in hexadecimal mode, print a '0x' or '0X' in front of it if (iBase == 16) { OutputChar(szOutputBuffer, uiBufferSize, ref_uiWritePos, '0'); if (bUpperCase) OutputChar(szOutputBuffer, uiBufferSize, ref_uiWritePos, 'X'); else OutputChar(szOutputBuffer, uiBufferSize, ref_uiWritePos, 'x'); } } } static void OutputIntSignAndPadding(char* szOutputBuffer, unsigned int uiBufferSize, unsigned int& ref_uiWritePos, long long int value, unsigned int uiFlags, int iPadding, int iBase, bool bUpperCase, int iPrecision) { if (uiFlags & sprintfFlags::PadZeros) { // if we pad with zeros, FIRST write the sign OutputIntSign(szOutputBuffer, uiBufferSize, ref_uiWritePos, value, uiFlags, iBase, bUpperCase, iPrecision); OutputPadding(szOutputBuffer, uiBufferSize, ref_uiWritePos, iPadding, '0'); } else { // if we pad with spaces, pad first, THEN write the sign OutputPadding(szOutputBuffer, uiBufferSize, ref_uiWritePos, iPadding, ' '); OutputIntSign(szOutputBuffer, uiBufferSize, ref_uiWritePos, value, uiFlags, iBase, bUpperCase, iPrecision); } } static void FormatUInt(char* szOutputBuffer, int& ref_iNumDigits, unsigned long long int uiValue, unsigned int uiBase, bool bUpperCase, int iPrecision) { // this function will write the number in reverse order to the string buffer ref_iNumDigits = 0; // if no precision is given, we store this as '-1' // this means that at least one digit should be written, thus change it to 1 (default by the spec) if (iPrecision == -1) iPrecision = 1; // if value is zero and precision is zero, do not write anything if ((iPrecision == 0) && (uiValue == 0)) { szOutputBuffer[0] = '\0'; return; } // make sure we do not write too much data into the temporary buffer if (iPrecision > 64) iPrecision = 64; while (uiValue > 0) { const unsigned int digit = uiValue % uiBase; if (digit <= 9) szOutputBuffer[ref_iNumDigits] = static_cast<char>('0' + digit); else if (digit <= 15) { if (bUpperCase) szOutputBuffer[ref_iNumDigits] = static_cast<char>('A' + (digit - 10)); else szOutputBuffer[ref_iNumDigits] = static_cast<char>('a' + (digit - 10)); } uiValue /= uiBase; ++ref_iNumDigits; } // if too few digits were written, pad the result with zero (precision modifier) while (ref_iNumDigits < iPrecision) { szOutputBuffer[ref_iNumDigits] = '0'; ++ref_iNumDigits; } // write the final zero terminator szOutputBuffer[ref_iNumDigits] = '\0'; } static int GetSignSize(long long int value, unsigned int uiFlags, int iBase, int iPrecision) { int iSize = 0; if (value < 0) iSize = 1; if (uiFlags & (sprintfFlags::ForceSign | sprintfFlags::BlankSign)) iSize = 1; if ((uiFlags & sprintfFlags::Hash) && ((value != 0) || (iPrecision != 0))) { if (iBase == 8) iSize += 1; if (iBase == 16) iSize += 2; } return iSize; } static void OutputInt(char* szOutputBuffer, unsigned int uiBufferSize, unsigned int& ref_uiWritePos, long long int value, int iWidth, int iPrecision, unsigned int uiFlags, int iBase) { // for a 32 Bit Integer one needs at most 10 digits // for a 64 Bit Integer one needs at most 20 digits // However, FormatUInt allows to add extra padding, so make the buffer a bit larger char s[128]; int iNumDigits = 0; unsigned long long int absval = value < 0 ? -value : value; FormatUInt(s, iNumDigits, absval, iBase, false, iPrecision); int iNumberWidth = GetSignSize(value, uiFlags, iBase, iPrecision) + iNumDigits; if (uiFlags & sprintfFlags::LeftJustify) { OutputIntSign(szOutputBuffer, uiBufferSize, ref_uiWritePos, value, uiFlags, iBase, false, iPrecision); OutputReverseString(szOutputBuffer, uiBufferSize, ref_uiWritePos, s, iNumDigits); OutputPadding(szOutputBuffer, uiBufferSize, ref_uiWritePos, iWidth - iNumberWidth, ' '); } else { OutputIntSignAndPadding(szOutputBuffer, uiBufferSize, ref_uiWritePos, value, uiFlags, iWidth - iNumberWidth, iBase, false, iPrecision); OutputReverseString(szOutputBuffer, uiBufferSize, ref_uiWritePos, s, iNumDigits); } } static void OutputUInt(char* szOutputBuffer, unsigned int uiBufferSize, unsigned int& ref_uiWritePos, unsigned long long int value, int iWidth, int iPrecision, unsigned int uiFlags, int iBase, bool bUpperCase) { #ifndef USE_STRICT_SPECIFICATION // In the non-strict implementation unsigned values will never be preceded by signs (+ or space) uiFlags &= ~(sprintfFlags::BlankSign | sprintfFlags::ForceSign); #endif // for a 32 Bit Integer one needs at most 10 digits // for a 64 Bit Integer one needs at most 20 digits // However, FormatUInt allows to add extra padding, so make the buffer a bit larger char s[128]; int iNumDigits = 0; FormatUInt(s, iNumDigits, value, iBase, bUpperCase, iPrecision); // since the uint is casted to an int, we must make sure very large values are not mapped to negative ints // therefore do the ' > 0 ? 1 : 0 ' test to just pass on the proper sign, instead of casting it to an int int iNumberWidth = GetSignSize(value > 0 ? 1 : 0, uiFlags, iBase, iPrecision) + iNumDigits; if (uiFlags & sprintfFlags::LeftJustify) { OutputIntSign(szOutputBuffer, uiBufferSize, ref_uiWritePos, value > 0 ? 1 : 0, uiFlags, iBase, bUpperCase, iPrecision); OutputReverseString(szOutputBuffer, uiBufferSize, ref_uiWritePos, s, iNumDigits); OutputPadding(szOutputBuffer, uiBufferSize, ref_uiWritePos, iWidth - iNumberWidth, ' '); } else { OutputIntSignAndPadding(szOutputBuffer, uiBufferSize, ref_uiWritePos, value > 0 ? 1 : 0, uiFlags, iWidth - iNumberWidth, iBase, bUpperCase, iPrecision); OutputReverseString(szOutputBuffer, uiBufferSize, ref_uiWritePos, s, iNumDigits); } } static bool RoundUpDigits(char* szBuffer, int iLastDigits) { for (int i = iLastDigits - 1; i >= 0; --i) { szBuffer[i] = szBuffer[i] + 1; if (szBuffer[i] == '9' + 1) szBuffer[i] = '0'; else return false; } return true; } static void RemoveTrailingZeros(char* szBuffer, int& ref_iWritePos) { const int iEnd = ref_iWritePos; for (int i = iEnd - 1; i >= 0; --i) { if (szBuffer[i] == '0') { szBuffer[i] = '\0'; --ref_iWritePos; } else break; } } union Int64DoubleUnion { unsigned long long int i; double f; }; static bool IsFinite(double value) { // Check the 11 exponent bits. // NAN -> (exponent = all 1, mantissa = non-zero) // INF -> (exponent = all 1, mantissa = zero) Int64DoubleUnion i2f; i2f.f = value; return ((i2f.i & 0x7FF0000000000000LL) != 0x7FF0000000000000LL); } static bool IsNaN(double value) { // Check the 11 exponent bits. // NAN -> (exponent = all 1, mantissa = non-zero) // INF -> (exponent = all 1, mantissa = zero) Int64DoubleUnion i2f; i2f.f = value; return (((i2f.i & 0x7FF0000000000000LL) == 0x7FF0000000000000LL) && ((i2f.i & 0xFFFFFFFFFFFFFLL) != 0)); } static bool FormatUFloat( char* szOutputBuffer, unsigned int uiBufferSize, unsigned int& ref_uiWritePos, double& ref_fValue0, int iPrecision, unsigned int uiFlags, bool bRemoveZeroes) { if (IsNaN(ref_fValue0)) { ref_fValue0 = 0; OutputNaN(szOutputBuffer, uiBufferSize, ref_uiWritePos); return false; } else if (!IsFinite(ref_fValue0)) { OutputInf(szOutputBuffer, uiBufferSize, ref_uiWritePos); return false; } double value = ref_fValue0; if (value < 0) value = -value; bool bRoundedUp = false; long long int uiIntPart = (long long int)value; char szFraction[128]; int iFractionDigits = 0; double dRemainder = value - (double)uiIntPart; // make sure we don't write outside our buffer if (iPrecision > 64) iPrecision = 64; // When no precision is given a maximum of 6 fractional digits is written // However, all trailing zeros will be removed again, to shorten the number as much as possible const bool bRemoveTrailingZeros = bRemoveZeroes || (iPrecision < 0); if (iPrecision < 0) iPrecision = 6; for (int i = 0; i < iPrecision; ++i) { dRemainder *= 10.0f; long long int digit = (long long int)dRemainder; char cDigit = '0' + (char)(digit); szFraction[iFractionDigits++] = cDigit; dRemainder -= (double)digit; } // zero terminate the string szFraction[iFractionDigits] = '\0'; // if the NEXT (not written digit) is 5 or larger, round the whole number up if (dRemainder >= 0.5) { if (RoundUpDigits(szFraction, iFractionDigits)) { bRoundedUp = true; ++uiIntPart; } } if (bRemoveTrailingZeros) RemoveTrailingZeros(szFraction, iFractionDigits); { char szBuffer[128]; int iNumDigits = 0; // for a 32 Bit Integer one needs at most 10 digits // for a 64 Bit Integer one needs at most 20 digits // However, FormatUInt allows to add extra padding, so make the buffer a bit larger FormatUInt(szBuffer, iNumDigits, uiIntPart, 10, false, 1); OutputReverseString(szOutputBuffer, uiBufferSize, ref_uiWritePos, szBuffer, iNumDigits); } // if there is any fractional digit, or the user specifically requested it, // add the '.' if ((iFractionDigits > 0) || (uiFlags & sprintfFlags::Hash)) OutputChar(szOutputBuffer, uiBufferSize, ref_uiWritePos, '.'); OutputString(szOutputBuffer, uiBufferSize, ref_uiWritePos, szFraction, 0, 0, -1); return bRoundedUp; } static bool WouldRoundToTen(double value, int iPrecision) { if (value == 0.0) return false; // get the absolute value value = value < 0.0 ? -value : value; // change the number until it is at least n.xxx with n > 0 while (value < 1.0) value *= 10.0; // now remove the whole part // the number will only be rounded up to the next digit, if all digits are '9' // otherwise some digit will just increase by one but no continuous overflow will happen { long long int iWhole = (long long int)value; // remove the whole part value -= (double)iWhole; // get the next digit // if it is not '9', no overflow will occur while (iWhole > 0) { int iDigit = iWhole % 10; if (iDigit < 9) return false; iWhole /= 10; } } // now check iPrecision digits of the fractional part, whether they are all '9' for (int i = 0; i < iPrecision; ++i) { value *= 10.0; int iDigit = (int)value; if (iDigit != 9) return false; value -= (double)iDigit; } // finally check the next digit (which would not be printed anymore) value *= 10.0; int iDigit = (int)value; // if it is >= 5, the number will be rounded up, thus triggering a cascade of overflows (everything else is '9') if (iDigit < 5) return false; return true; } static void FormatUFloatScientific(char* szOutputBuffer, unsigned int uiBufferSize, unsigned int& ref_uiWritePos, double& ref_fValue0, int iPrecision, unsigned int uiFlags, bool bUpperCase, bool bRemoveZeroes) { if (IsNaN(ref_fValue0)) { ref_fValue0 = 0; OutputNaN(szOutputBuffer, uiBufferSize, ref_uiWritePos); return; } else if (!IsFinite(ref_fValue0)) { OutputInf(szOutputBuffer, uiBufferSize, ref_uiWritePos); return; } double value = ref_fValue0; double dSci = value > 0.0 ? value : -value; int exp = 0; if (dSci != 0.0) { while (dSci >= 10.0) { dSci /= 10.0; exp++; } while (dSci < 1.0) { dSci *= 10.0; exp--; } } if (WouldRoundToTen(value, iPrecision)) { dSci /= 10.0; exp++; } FormatUFloat(szOutputBuffer, uiBufferSize, ref_uiWritePos, dSci, iPrecision, uiFlags, bRemoveZeroes); OutputChar(szOutputBuffer, uiBufferSize, ref_uiWritePos, bUpperCase ? 'E' : 'e'); OutputInt(szOutputBuffer, uiBufferSize, ref_uiWritePos, exp, 0, 3, sprintfFlags::ForceZeroSign, 10); } static bool IsAllZero(char* szBuffer) { while (*szBuffer != '\0') { if (*szBuffer != '0' && *szBuffer != '.') { return false; } ++szBuffer; } return true; } static void OutputFloat(char* szOutputBuffer, unsigned int uiBufferSize, unsigned int& ref_uiWritePos, double value, int iWidth, int iPrecision, unsigned int uiFlags, bool bUpperCase, bool bScientific, bool bRemoveZeroes) { char szBuffer[128]; unsigned int iNumDigits = 0; // Input values that are outside the int64 range cannot be output in non-scientific form if (bScientific || value >= 9223372036854775807.0 || value <= -9223372036854775808.0) FormatUFloatScientific(szBuffer, 128, iNumDigits, value, iPrecision, uiFlags, bUpperCase, bRemoveZeroes); else FormatUFloat(szBuffer, 128, iNumDigits, value, iPrecision, uiFlags, bRemoveZeroes); szBuffer[iNumDigits] = '\0'; double signValue = value; // if the stringification of a float value with the given precision resulted in only zeros, do not print a minus sign if (IsAllZero(szBuffer)) signValue = 0; int iNumberWidth = GetSignSize((long long int)signValue, uiFlags, 10, iPrecision) + iNumDigits; // when right justifying, first output the padding if ((uiFlags & sprintfFlags::LeftJustify) == 0) OutputIntSignAndPadding(szOutputBuffer, uiBufferSize, ref_uiWritePos, signValue < 0 ? -1 : 1, uiFlags, iWidth - iNumberWidth, 10, false, iPrecision); else OutputIntSign(szOutputBuffer, uiBufferSize, ref_uiWritePos, signValue < 0 ? -1 : 1, uiFlags, 10, false, 1); OutputString(szOutputBuffer, uiBufferSize, ref_uiWritePos, szBuffer, uiFlags, 0, -1); // when left justifying, first output the string, then output the padding if ((uiFlags & sprintfFlags::LeftJustify) != 0) OutputPadding(szOutputBuffer, uiBufferSize, ref_uiWritePos, iWidth - iNumberWidth, ' '); } static void OutputFloat_Short(char* szOutputBuffer, unsigned int uiBufferSize, unsigned int& ref_uiWritePos, double value, int iWidth, int iPrecision, unsigned int uiFlags, bool bUpperCase) { // bool bScientific = false; // double absval = value < 0.0 ? -value : value; // if (absval >= 1000000) // bScientific = true; // if (absval <= 0.00001) // bScientific = true; // OutputFloat (szOutputBuffer, uiBufferSize, uiWritePos, value, iWidth, iPrecision, Flags, bUpperCase, bScientific, false); char szBuffer[128], szBuffer2[128]; unsigned int iNumDigitsF = 0; unsigned int iNumDigitsE = 0; const int iPrecF = iPrecision < 0 ? 12 : iPrecision; const int iPrecE = iPrecision < 0 ? 5 : iPrecision; FormatUFloatScientific(szBuffer, 128, iNumDigitsE, value, iPrecE, uiFlags, bUpperCase, iPrecision < 0); FormatUFloat(szBuffer2, 128, iNumDigitsF, value, iPrecF, uiFlags, iPrecision < 0); bool bScientific = (iNumDigitsE < iNumDigitsF); if (value != 0.0) { bool bAllZero = true; for (int i = 0; i < (int)iNumDigitsF; ++i) { if ((szBuffer2[i] != '0') && (szBuffer2[i] != '.')) { bAllZero = false; break; } } if (bAllZero) bScientific = true; } // Input values that are outside the int64 range cannot be output in non-scientific form if (bScientific || value >= 9223372036854775807.0 || value <= -9223372036854775808.0) OutputFloat(szOutputBuffer, uiBufferSize, ref_uiWritePos, value, iWidth, iPrecE, uiFlags, bUpperCase, bScientific, iPrecision < 0); else OutputFloat(szOutputBuffer, uiBufferSize, ref_uiWritePos, value, iWidth, iPrecF, uiFlags, bUpperCase, bScientific, iPrecision < 0); } int ezStringUtils::vsnprintf(char* szOutputBuffer, unsigned int uiBufferSize, const char* szFormat, va_list szArgs0) { va_list args; va_copy(args, szArgs0); EZ_ASSERT_DEBUG(ezUnicodeUtils::IsValidUtf8(szFormat), "The sprintf format string must be valid Utf8."); // make sure the last character is a \0 if ((szOutputBuffer) && (uiBufferSize > 0)) szOutputBuffer[uiBufferSize - 1] = '\0'; unsigned int uiReadPos = 0; unsigned int uiWritePos = 0; bool bError = false; while (szFormat[uiReadPos] != '\0') { const char c = szFormat[uiReadPos]; ++uiReadPos; // if c is not %, just print it out and be done with it if (c != '%') { OutputChar(szOutputBuffer, uiBufferSize, uiWritePos, c); continue; } // otherwise parse the formatting string to find out what has to be done char cNext = szFormat[uiReadPos]; // *** parse the format string *** // first read the flags (as many as there are) unsigned int Flags = ReadFlags(szFormat, uiReadPos, cNext); // read the width of the field int iWidth = ReadWidth(szFormat, uiReadPos, cNext); // read the precision specifier int iPrecision = ReadPrecision(szFormat, uiReadPos, cNext); // read the input data 'length' (short / int / long it, float / double) const sprintfLength::Enum Length = ReadLength(szFormat, uiReadPos, cNext); // read the 'specifier' type const char cSpecifier = ReadSpecifier(szFormat, uiReadPos, cNext, bError); if (bError) { snprintf(szOutputBuffer, uiBufferSize, "Error in formatting string at position %u ('%c').", uiReadPos, cSpecifier); va_end(args); return -1; } // if 'width' was specified as '*', read it from an extra parameter if (iWidth < 0) iWidth = va_arg(args, int); // if 'precision' was specified as '*', read it from an extra parameter if (iPrecision == -2) iPrecision = va_arg(args, int); // % Sign if (cSpecifier == '%') { OutputChar(szOutputBuffer, uiBufferSize, uiWritePos, '%'); continue; } // Nothing: Writes the current write position back to an int pointer if (cSpecifier == 'n') { int* p = va_arg(args, int*); if (p) *p = (int)uiWritePos; continue; } // String if (cSpecifier == 's') { const char* s = va_arg(args, const char*); OutputString(szOutputBuffer, uiBufferSize, uiWritePos, s, Flags, iWidth, iPrecision); continue; } // Character if (cSpecifier == 'c') { char c2 = static_cast<char>(va_arg(args, int)); char s[2] = {c2, 0}; OutputString(szOutputBuffer, uiBufferSize, uiWritePos, s, Flags, iWidth, iPrecision); continue; } // Signed Decimal if ((cSpecifier == 'i') || (cSpecifier == 'd')) { long long int i = 0; if (Length == sprintfLength::LongLongInt) i = va_arg(args, long long int); else if (Length == sprintfLength::LongInt) i = va_arg(args, long int); else i = va_arg(args, int); OutputInt(szOutputBuffer, uiBufferSize, uiWritePos, i, iWidth, iPrecision, Flags, 10); continue; } // Unsigned Decimal, Octal, Hexadecimal or Binary if ((cSpecifier == 'u') || (cSpecifier == 'o') || (cSpecifier == 'b') || (cSpecifier == 'x') || (cSpecifier == 'X')) { // the spec says that octal values are signed, but hexadecimal values are unsigned // I believe this is a typo in the specification, other implementations also always // assume unsigned values for octal numbers // therefore this implementation does this too unsigned long long int i = 0; if (Length == sprintfLength::LongLongInt) i = va_arg(args, unsigned long long int); else if (Length == sprintfLength::LongInt) i = va_arg(args, unsigned long int); else i = va_arg(args, unsigned int); int iBase = 10; switch (cSpecifier) { case 'o': iBase = 8; break; case 'b': iBase = 2; break; case 'x': case 'X': iBase = 16; break; } OutputUInt(szOutputBuffer, uiBufferSize, uiWritePos, i, iWidth, iPrecision, Flags, iBase, (cSpecifier == 'X')); continue; } // Float if ((cSpecifier == 'f') || (cSpecifier == 'e') || (cSpecifier == 'E')) { double f = va_arg(args, double); OutputFloat(szOutputBuffer, uiBufferSize, uiWritePos, f, iWidth, iPrecision, Flags, (cSpecifier == 'E'), (cSpecifier != 'f'), false); continue; } // Float, shorter Version either 'f' or 'e' if ((cSpecifier == 'g') || (cSpecifier == 'G')) { double f = va_arg(args, double); OutputFloat_Short(szOutputBuffer, uiBufferSize, uiWritePos, f, iWidth, iPrecision, Flags, (cSpecifier == 'G')); continue; } // pointer address if (cSpecifier == 'p') { iPrecision = (int)sizeof(void*) * 2; #ifndef USE_STRICT_SPECIFICATION // In the non-strict implementation, pointer addresses will not be preceded by additional signs, hex-indicators (0x..) // or padded with zeros Flags &= ~(sprintfFlags::BlankSign | sprintfFlags::ForceSign | sprintfFlags::PadZeros | sprintfFlags::Hash); #endif void* p = va_arg(args, void*); OutputUInt(szOutputBuffer, uiBufferSize, uiWritePos, (unsigned long long int)p, iWidth, iPrecision, Flags, 16, true); continue; } } // write the final \0 OutputChar(szOutputBuffer, uiBufferSize, uiWritePos, '\0'); va_end(args); // return the number of characters that would have been written // minus the terminating zero return uiWritePos - 1; } int ezStringUtils::snprintf(char* szOutputBuffer, unsigned int uiBufferSize, const char* szFormat, ...) { va_list args; va_start(args, szFormat); int ret = vsnprintf(szOutputBuffer, uiBufferSize, szFormat, args); va_end(args); return ret; } void ezStringUtils::OutputFormattedInt( char* szOutputBuffer, ezUInt32 uiBufferSize, ezUInt32& ref_uiWritePos, ezInt64 value, ezUInt8 uiWidth, bool bPadZeros, ezUInt8 uiBase) { OutputInt(szOutputBuffer, uiBufferSize, ref_uiWritePos, value, uiWidth, -1, bPadZeros ? sprintfFlags::PadZeros : 0, uiBase); } void ezStringUtils::OutputFormattedUInt( char* szOutputBuffer, ezUInt32 uiBufferSize, ezUInt32& ref_uiWritePos, ezUInt64 value, ezUInt8 uiWidth, bool bPadZeros, ezUInt8 uiBase, bool bUpperCase) { OutputUInt(szOutputBuffer, uiBufferSize, ref_uiWritePos, value, uiWidth, -1, bPadZeros ? sprintfFlags::PadZeros : 0, uiBase, bUpperCase); } void ezStringUtils::OutputFormattedFloat(char* szOutputBuffer, ezUInt32 uiBufferSize, ezUInt32& ref_uiWritePos, double value, ezUInt8 uiWidth, bool bPadZeros, ezInt8 iPrecision, bool bScientific, bool bRemoveTrailingZeroes) { OutputFloat(szOutputBuffer, uiBufferSize, ref_uiWritePos, value, uiWidth, ezMath::Max<int>(-1, iPrecision), bPadZeros ? sprintfFlags::PadZeros : 0, false, bScientific, bRemoveTrailingZeroes); }