Commit 7c1203d4 authored by Paul Reimer's avatar Paul Reimer Committed by Wouter van Oortmerssen

Add define/ifdef blocks, alternate sprintf implementation via…

Add define/ifdef blocks, alternate sprintf implementation via FLATBUFFERS_PREFER_PRINTF [C++] (#4700)

* Add define/ifdef blocks for FLATBUFFERS_PREFER_PRINTF to avoid using std::*streams for idl_parser

* Use string::size() as limit in snprintf

* Refactored FLATBUFFERS_PREFER_PRINTF guarded feature into NumToStringImplWrapper around sprintf

* Remove '.0' where not needed from IntToDigitCount

* Remove leading dot from name in GetFullyQualifiedName when FLATBUFFERS_PREFER_PRINTF is enabled

* Return string directly from conversion functions where possible when FLATBUFFERS_PREFER_PRINTF is enabled

* Use string instead of stringstream for GetFullyQualifiedName

* Revert removing leading dot from GetFullyQualifiedName, it does need to be there for parity with the stringstream implementation

* Dot is single char in Namespace::GetFullyQualifiedName

* Remove trailing (duplicate) null-byte from NumToStringImplWrapper when using FLATBUFFERS_PREFER_PRINTF.

* Update preprocessor indenting (and use clang-format off/on) for FLATBUFFERS_PREFER_PRINTF

* Reduce whitespace, unneeded braces in FLATBUFFERS_PREFER_PRINTF string width functions

* Remove unneeded use of iostream from idl_parser.cpp, std::string is used instead

* Tell snprintf to use the trailing null byte expected at the end of std::string buffer
parent a0a33d94
...@@ -22,7 +22,12 @@ ...@@ -22,7 +22,12 @@
#include <stdlib.h> #include <stdlib.h>
#include <fstream> #include <fstream>
#include <iomanip> #include <iomanip>
#include <sstream> #ifndef FLATBUFFERS_PREFER_PRINTF
# include <sstream>
#else // FLATBUFFERS_PREFER_PRINTF
# include <float.h>
# include <stdio.h>
#endif // FLATBUFFERS_PREFER_PRINTF
#include <string> #include <string>
#ifdef _WIN32 #ifdef _WIN32
# ifndef WIN32_LEAN_AND_MEAN # ifndef WIN32_LEAN_AND_MEAN
...@@ -45,13 +50,53 @@ ...@@ -45,13 +50,53 @@
namespace flatbuffers { namespace flatbuffers {
#ifdef FLATBUFFERS_PREFER_PRINTF
template<typename T> size_t IntToDigitCount(T t) {
size_t digit_count = 0;
// Count the sign for negative numbers
if (t < 0) digit_count++;
// Count a single 0 left of the dot for fractional numbers
if (-1 < t && t < 1) digit_count++;
// Count digits until fractional part
T eps = std::numeric_limits<float>::epsilon();
while (t <= (-1 + eps) || (1 - eps) <= t) {
t /= 10;
digit_count++;
}
return digit_count;
}
template<typename T> size_t NumToStringWidth(T t, int precision = 0) {
size_t string_width = IntToDigitCount(t);
// Count the dot for floating point numbers
if (precision) string_width += (precision + 1);
return string_width;
}
template<typename T> std::string NumToStringImplWrapper(T t, const char* fmt,
int precision = 0) {
size_t string_width = NumToStringWidth(t, precision);
std::string s(string_width, 0x00);
// Allow snprintf to use std::string trailing null to detect buffer overflow
snprintf(const_cast<char*>(s.data()), (s.size()+1), fmt, precision, t);
return s;
}
#endif // FLATBUFFERS_PREFER_PRINTF
// Convert an integer or floating point value to a string. // Convert an integer or floating point value to a string.
// In contrast to std::stringstream, "char" values are // In contrast to std::stringstream, "char" values are
// converted to a string of digits, and we don't use scientific notation. // converted to a string of digits, and we don't use scientific notation.
template<typename T> std::string NumToString(T t) { template<typename T> std::string NumToString(T t) {
std::stringstream ss; // clang-format off
ss << t; #ifndef FLATBUFFERS_PREFER_PRINTF
return ss.str(); std::stringstream ss;
ss << t;
return ss.str();
#else // FLATBUFFERS_PREFER_PRINTF
auto v = static_cast<long long>(t);
return NumToStringImplWrapper(v, "%.*lld");
#endif // FLATBUFFERS_PREFER_PRINTF
// clang-format on
} }
// Avoid char types used as character data. // Avoid char types used as character data.
template<> inline std::string NumToString<signed char>(signed char t) { template<> inline std::string NumToString<signed char>(signed char t) {
...@@ -77,15 +122,22 @@ inline std::string NumToString<unsigned long long>(unsigned long long t) { ...@@ -77,15 +122,22 @@ inline std::string NumToString<unsigned long long>(unsigned long long t) {
// Special versions for floats/doubles. // Special versions for floats/doubles.
template<typename T> std::string FloatToString(T t, int precision) { template<typename T> std::string FloatToString(T t, int precision) {
// to_string() prints different numbers of digits for floats depending on // clang-format off
// platform and isn't available on Android, so we use stringstream #ifndef FLATBUFFERS_PREFER_PRINTF
std::stringstream ss; // to_string() prints different numbers of digits for floats depending on
// Use std::fixed to surpress scientific notation. // platform and isn't available on Android, so we use stringstream
ss << std::fixed; std::stringstream ss;
// Default precision is 6, we want that to be higher for doubles. // Use std::fixed to suppress scientific notation.
ss << std::setprecision(precision); ss << std::fixed;
ss << t; // Default precision is 6, we want that to be higher for doubles.
auto s = ss.str(); ss << std::setprecision(precision);
ss << t;
auto s = ss.str();
#else // FLATBUFFERS_PREFER_PRINTF
auto v = static_cast<double>(t);
auto s = NumToStringImplWrapper(v, "%0.*f", precision);
#endif // FLATBUFFERS_PREFER_PRINTF
// clang-format on
// Sadly, std::fixed turns "1" into "1.00000", so here we undo that. // Sadly, std::fixed turns "1" into "1.00000", so here we undo that.
auto p = s.find_last_not_of('0'); auto p = s.find_last_not_of('0');
if (p != std::string::npos) { if (p != std::string::npos) {
...@@ -106,10 +158,16 @@ template<> inline std::string NumToString<float>(float t) { ...@@ -106,10 +158,16 @@ template<> inline std::string NumToString<float>(float t) {
// The returned string length is always xdigits long, prefixed by 0 digits. // The returned string length is always xdigits long, prefixed by 0 digits.
// For example, IntToStringHex(0x23, 8) returns the string "00000023". // For example, IntToStringHex(0x23, 8) returns the string "00000023".
inline std::string IntToStringHex(int i, int xdigits) { inline std::string IntToStringHex(int i, int xdigits) {
std::stringstream ss; // clang-format off
ss << std::setw(xdigits) << std::setfill('0') << std::hex << std::uppercase #ifndef FLATBUFFERS_PREFER_PRINTF
<< i; std::stringstream ss;
return ss.str(); ss << std::setw(xdigits) << std::setfill('0') << std::hex << std::uppercase
<< i;
return ss.str();
#else // FLATBUFFERS_PREFER_PRINTF
return NumToStringImplWrapper(i, "%.*X", xdigits);
#endif // FLATBUFFERS_PREFER_PRINTF
// clang-format on
} }
// Portable implementation of strtoll(). // Portable implementation of strtoll().
...@@ -353,6 +411,7 @@ inline int FromUTF8(const char **in) { ...@@ -353,6 +411,7 @@ inline int FromUTF8(const char **in) {
return ucc; return ucc;
} }
#ifndef FLATBUFFERS_PREFER_PRINTF
// Wraps a string to a maximum length, inserting new lines where necessary. Any // Wraps a string to a maximum length, inserting new lines where necessary. Any
// existing whitespace will be collapsed down to a single space. A prefix or // existing whitespace will be collapsed down to a single space. A prefix or
// suffix can be provided, which will be inserted before or after a wrapped // suffix can be provided, which will be inserted before or after a wrapped
...@@ -379,6 +438,7 @@ inline std::string WordWrap(const std::string in, size_t max_length, ...@@ -379,6 +438,7 @@ inline std::string WordWrap(const std::string in, size_t max_length,
return wrapped; return wrapped;
} }
#endif // !FLATBUFFERS_PREFER_PRINTF
inline bool EscapeString(const char *s, size_t length, std::string *_text, inline bool EscapeString(const char *s, size_t length, std::string *_text,
bool allow_non_utf8, bool natural_utf8) { bool allow_non_utf8, bool natural_utf8) {
......
...@@ -15,8 +15,8 @@ ...@@ -15,8 +15,8 @@
*/ */
#include <algorithm> #include <algorithm>
#include <iostream>
#include <list> #include <list>
#include <string>
#include <math.h> #include <math.h>
...@@ -177,13 +177,16 @@ std::string Namespace::GetFullyQualifiedName(const std::string &name, ...@@ -177,13 +177,16 @@ std::string Namespace::GetFullyQualifiedName(const std::string &name,
size_t max_components) const { size_t max_components) const {
// Early exit if we don't have a defined namespace. // Early exit if we don't have a defined namespace.
if (components.empty() || !max_components) { return name; } if (components.empty() || !max_components) { return name; }
std::stringstream stream; std::string stream_str;
for (size_t i = 0; i < std::min(components.size(), max_components); i++) { for (size_t i = 0; i < std::min(components.size(), max_components); i++) {
if (i) { stream << "."; } if (i) { stream_str += '.'; }
stream << components[i]; stream_str += std::string(components[i]);
} }
if (name.length()) stream << "." << name; if (name.length()) {
return stream.str(); stream_str += '.';
stream_str += name;
}
return stream_str;
} }
// Declare tokens we'll use. Single character tokens are represented by their // Declare tokens we'll use. Single character tokens are represented by their
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment