THRIFT-2870 - C++: JSON protocol will read & write doubles using "C" locale
Client: C++
Patch: Simon Falsig
diff --git a/lib/cpp/src/thrift/protocol/TJSONProtocol.cpp b/lib/cpp/src/thrift/protocol/TJSONProtocol.cpp
index 72cd28b..5e1d42b 100644
--- a/lib/cpp/src/thrift/protocol/TJSONProtocol.cpp
+++ b/lib/cpp/src/thrift/protocol/TJSONProtocol.cpp
@@ -19,8 +19,14 @@
#include <thrift/protocol/TJSONProtocol.h>
-#include <math.h>
+#include <limits>
+#include <locale>
+#include <sstream>
+#include <cmath>
+
+#include <boost/math/special_functions/fpclassify.hpp>
#include <boost/lexical_cast.hpp>
+
#include <thrift/protocol/TBase64Utils.h>
#include <thrift/transport/TTransportException.h>
@@ -503,30 +509,40 @@
return result;
}
+namespace
+{
+std::string doubleToString(double d)
+{
+ std::ostringstream str;
+ str.imbue(std::locale::classic());
+ str.precision(std::numeric_limits<double>::digits10 + 1);
+ str << d;
+ return str.str();
+}
+}
+
// Convert the given double to a JSON string, which is either the number,
// "NaN" or "Infinity" or "-Infinity".
uint32_t TJSONProtocol::writeJSONDouble(double num) {
uint32_t result = context_->write(*trans_);
- std::string val(boost::lexical_cast<std::string>(num));
+ std::string val;
- // Normalize output of boost::lexical_cast for NaNs and Infinities
bool special = false;
- switch (val[0]) {
- case 'N':
- case 'n':
+ switch (boost::math::fpclassify(num)) {
+ case FP_INFINITE:
+ if (boost::math::signbit(num)) {
+ val = kThriftNegativeInfinity;
+ } else {
+ val = kThriftInfinity;
+ }
+ special = true;
+ break;
+ case FP_NAN:
val = kThriftNan;
special = true;
break;
- case 'I':
- case 'i':
- val = kThriftInfinity;
- special = true;
- break;
- case '-':
- if ((val[1] == 'I') || (val[1] == 'i')) {
- val = kThriftNegativeInfinity;
- special = true;
- }
+ default:
+ val = doubleToString(num);
break;
}
@@ -801,6 +817,20 @@
return result;
}
+namespace
+{
+double stringToDouble(const std::string& s)
+{
+ double d;
+ std::istringstream str(s);
+ str.imbue(std::locale::classic());
+ str >> d;
+ if (str.bad() || !str.eof())
+ throw std::runtime_error(s);
+ return d;
+}
+}
+
// Reads a JSON number or string and interprets it as a double.
uint32_t TJSONProtocol::readJSONDouble(double& num) {
uint32_t result = context_->read(reader_);
@@ -821,8 +851,8 @@
"Numeric data unexpectedly quoted");
}
try {
- num = boost::lexical_cast<double>(str);
- } catch (boost::bad_lexical_cast e) {
+ num = stringToDouble(str);
+ } catch (std::runtime_error e) {
throw new TProtocolException(TProtocolException::INVALID_DATA,
"Expected numeric value; got \"" + str + "\"");
}
@@ -834,8 +864,8 @@
}
result += readJSONNumericChars(str);
try {
- num = boost::lexical_cast<double>(str);
- } catch (boost::bad_lexical_cast e) {
+ num = stringToDouble(str);
+ } catch (std::runtime_error e) {
throw new TProtocolException(TProtocolException::INVALID_DATA,
"Expected numeric value; got \"" + str + "\"");
}