This is an automated email from the ASF dual-hosted git repository.

qianzhang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mesos.git

commit ec82a516918ebd663816cb110f73bdee6e5268be
Author: Qian Zhang <[email protected]>
AuthorDate: Sun Feb 23 10:09:48 2020 +0800

    Accommodated the "Infinity" value in the JSON <-> Protobuf conversion.
    
    Review: https://reviews.apache.org/r/72162
---
 3rdparty/stout/include/stout/jsonify.hpp  |  1 +
 3rdparty/stout/include/stout/protobuf.hpp | 19 ++++++-
 3rdparty/stout/tests/protobuf_tests.cpp   | 82 +++++++++++++++++++++++++++++++
 3rdparty/stout/tests/protobuf_tests.proto |  9 ++++
 4 files changed, 110 insertions(+), 1 deletion(-)

diff --git a/3rdparty/stout/include/stout/jsonify.hpp 
b/3rdparty/stout/include/stout/jsonify.hpp
index 7a239d8..e18da8f 100644
--- a/3rdparty/stout/include/stout/jsonify.hpp
+++ b/3rdparty/stout/include/stout/jsonify.hpp
@@ -14,6 +14,7 @@
 #define __STOUT_JSONIFY__
 
 #define RAPIDJSON_HAS_STDSTRING 1
+#define RAPIDJSON_WRITE_DEFAULT_FLAGS kWriteNanAndInfFlag
 
 // TODO(bmahler): Consider enabling UTF-8 validation when writing
 // json. Prior to the introduction of rapidjson, we performed no
diff --git a/3rdparty/stout/include/stout/protobuf.hpp 
b/3rdparty/stout/include/stout/protobuf.hpp
index fcd91d5..9a82a30 100644
--- a/3rdparty/stout/include/stout/protobuf.hpp
+++ b/3rdparty/stout/include/stout/protobuf.hpp
@@ -540,7 +540,24 @@ struct Parser : boost::static_visitor<Try<Nothing>>
         break;
       }
       case google::protobuf::FieldDescriptor::TYPE_DOUBLE:
-      case google::protobuf::FieldDescriptor::TYPE_FLOAT:
+      case google::protobuf::FieldDescriptor::TYPE_FLOAT: {
+        if (string.value == "Infinity") {
+          return operator()(JSON::Number(
+              std::numeric_limits<double>::infinity()));
+        } else if (string.value == "-Infinity") {
+          return operator()(JSON::Number(
+              -std::numeric_limits<double>::infinity()));
+        } else {
+          Try<JSON::Number> number = JSON::parse<JSON::Number>(string.value);
+          if (number.isError()) {
+            return Error(
+                "Failed to parse '" + string.value + "' as a JSON number "
+                "for field '" + field->name() + "': " + number.error());
+          }
+
+          return operator()(number.get());
+        }
+      }
       case google::protobuf::FieldDescriptor::TYPE_INT64:
       case google::protobuf::FieldDescriptor::TYPE_SINT64:
       case google::protobuf::FieldDescriptor::TYPE_SFIXED64:
diff --git a/3rdparty/stout/tests/protobuf_tests.cpp 
b/3rdparty/stout/tests/protobuf_tests.cpp
index 55889dc..46249ea 100644
--- a/3rdparty/stout/tests/protobuf_tests.cpp
+++ b/3rdparty/stout/tests/protobuf_tests.cpp
@@ -15,6 +15,7 @@
 #include <gmock/gmock.h>
 
 #include <algorithm>
+#include <limits>
 #include <string>
 
 #include <google/protobuf/util/message_differencer.h>
@@ -53,6 +54,25 @@ bool operator!=(const SimpleMessage& left, const 
SimpleMessage& right)
   return !(left == right);
 }
 
+
+bool operator==(const FloatMessage& left, const FloatMessage& right)
+{
+  if (left.f1() == right.f1() &&
+      left.f2() == right.f2() &&
+      left.d1() == right.d1() &&
+      left.d2() == right.d2()) {
+    return true;
+  }
+
+  return false;
+}
+
+
+bool operator!=(const FloatMessage& left, const FloatMessage& right)
+{
+  return !(left == right);
+}
+
 } // namespace tests {
 
 
@@ -852,3 +872,65 @@ TEST(ProtobufTest, JsonifyMap)
   EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(
       message, *protoFromJson));
 }
+
+
+TEST(ProtobufTest, JsonInfinity)
+{
+  tests::FloatMessage message;
+  message.set_f1(std::numeric_limits<float>::infinity());
+  message.set_f2(-std::numeric_limits<float>::infinity());
+  message.set_d1(std::numeric_limits<double>::infinity());
+  message.set_d2(-std::numeric_limits<double>::infinity());
+
+  // The keys are in alphabetical order.
+  string expected =
+    R"~(
+    {
+      "d1": "Infinity",
+      "d2": "-Infinity",
+      "f1": "Infinity",
+      "f2": "-Infinity"
+    })~";
+
+  // Remove ' ' and '\n' from `expected` so that we can compare
+  // it with the JSON string parsed from protobuf message.
+  expected.erase(
+      std::remove_if(expected.begin(), expected.end(), ::isspace),
+      expected.end());
+
+  JSON::Object referenceObject;
+  referenceObject.values["d1"] = 
JSON::Number(std::numeric_limits<double>::infinity());
+  referenceObject.values["d2"] = 
JSON::Number(-std::numeric_limits<double>::infinity());
+  referenceObject.values["f1"] = 
JSON::Number(std::numeric_limits<double>::infinity());
+  referenceObject.values["f2"] = 
JSON::Number(-std::numeric_limits<double>::infinity());
+
+  // Check Protobuf -> JSON.
+  JSON::Object protobufObject = JSON::protobuf(message);
+  EXPECT_EQ(referenceObject, protobufObject);
+  EXPECT_EQ(expected, stringify(protobufObject));
+
+  // Check JSON -> Protobuf.
+  Try<tests::FloatMessage> parse =
+    protobuf::parse<tests::FloatMessage>(protobufObject);
+
+  ASSERT_SOME(parse);
+  EXPECT_EQ(message, parse.get());
+
+  // Check String -> JSON.
+  Try<JSON::Object> parsedObject = JSON::parse<JSON::Object>(expected);
+
+  // Unlike `JSON::protobuf()`, `JSON::parse()` does not have the
+  // field type information, so it will just parse "Infinity" as a
+  // `JSON::String` instead of `JSON::Number`.
+  referenceObject.values["d1"] = JSON::String("Infinity");
+  referenceObject.values["d2"] = JSON::String("-Infinity");
+  referenceObject.values["f1"] = JSON::String("Infinity");
+  referenceObject.values["f2"] = JSON::String("-Infinity");
+
+  EXPECT_SOME_EQ(referenceObject, parsedObject);
+
+  // Check JSON -> Protobuf.
+  parse = protobuf::parse<tests::FloatMessage>(parsedObject.get());
+  ASSERT_SOME(parse);
+  EXPECT_EQ(message, parse.get());
+}
diff --git a/3rdparty/stout/tests/protobuf_tests.proto 
b/3rdparty/stout/tests/protobuf_tests.proto
index 5e20215..3a6a458 100644
--- a/3rdparty/stout/tests/protobuf_tests.proto
+++ b/3rdparty/stout/tests/protobuf_tests.proto
@@ -130,3 +130,12 @@ message MapMessage {
   map<sint32, string> sint32_to_string = 19;
   map<sint64, string> sint64_to_string = 20;
 }
+
+
+// A message for testing infinite floating point numbers.
+message FloatMessage {
+  required float f1 = 1;
+  required float f2 = 2;
+  required double d1 = 3;
+  required double d2 = 4;
+}

Reply via email to