|
|
|
@ -31,6 +31,7 @@ |
|
|
|
|
#include "google/protobuf/json/internal/unparser.h" |
|
|
|
|
|
|
|
|
|
#include <cfloat> |
|
|
|
|
#include <cmath> |
|
|
|
|
#include <complex> |
|
|
|
|
#include <cstdint> |
|
|
|
|
#include <cstring> |
|
|
|
@ -38,6 +39,7 @@ |
|
|
|
|
#include <memory> |
|
|
|
|
#include <sstream> |
|
|
|
|
#include <string> |
|
|
|
|
#include <type_traits> |
|
|
|
|
#include <utility> |
|
|
|
|
|
|
|
|
|
#include "google/protobuf/descriptor.h" |
|
|
|
@ -104,6 +106,33 @@ void WriteEnum(JsonWriter& writer, Field<Traits> field, int32_t value, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Returns true if x round-trips through being cast to a double, i.e., if
|
|
|
|
|
// x is represenable exactly as a double. This is a slightly weaker condition
|
|
|
|
|
// than x < 2^52.
|
|
|
|
|
template <typename Int> |
|
|
|
|
bool RoundTripsThroughDouble(Int x) { |
|
|
|
|
auto d = static_cast<double>(x); |
|
|
|
|
// d has guaranteed to be finite with no fractional part, because it came from
|
|
|
|
|
// an integer, so we only need to check that it is not outside of the
|
|
|
|
|
// representable range of `int`. The way to do this is somewhat not obvious:
|
|
|
|
|
// UINT64_MAX isn't representable, and what it gets rounded to when we go
|
|
|
|
|
// int->double is unspecified!
|
|
|
|
|
//
|
|
|
|
|
// Thus, we have to go through ldexp.
|
|
|
|
|
double min = 0; |
|
|
|
|
double max_plus_one = std::ldexp(1.0, sizeof(Int) * 8); |
|
|
|
|
if (std::is_signed<Int>::value) { |
|
|
|
|
max_plus_one /= 2; |
|
|
|
|
min = -max_plus_one; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (d < min || d >= max_plus_one) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return static_cast<Int>(d) == x; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Mutually recursive with functions that follow.
|
|
|
|
|
template <typename Traits> |
|
|
|
|
absl::Status WriteMessage(JsonWriter& writer, const Msg<Traits>& msg, |
|
|
|
@ -143,14 +172,24 @@ absl::Status WriteSingular(JsonWriter& writer, Field<Traits> field, |
|
|
|
|
case FieldDescriptor::TYPE_INT64: { |
|
|
|
|
auto x = Traits::GetInt64(field, std::forward<Args>(args)...); |
|
|
|
|
RETURN_IF_ERROR(x.status()); |
|
|
|
|
writer.Write(MakeQuoted(*x)); |
|
|
|
|
if (writer.options().unquote_int64_if_possible && |
|
|
|
|
RoundTripsThroughDouble(*x)) { |
|
|
|
|
writer.Write(*x); |
|
|
|
|
} else { |
|
|
|
|
writer.Write(MakeQuoted(*x)); |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
case FieldDescriptor::TYPE_FIXED64: |
|
|
|
|
case FieldDescriptor::TYPE_UINT64: { |
|
|
|
|
auto x = Traits::GetUInt64(field, std::forward<Args>(args)...); |
|
|
|
|
RETURN_IF_ERROR(x.status()); |
|
|
|
|
writer.Write(MakeQuoted(*x)); |
|
|
|
|
if (writer.options().unquote_int64_if_possible && |
|
|
|
|
RoundTripsThroughDouble(*x)) { |
|
|
|
|
writer.Write(*x); |
|
|
|
|
} else { |
|
|
|
|
writer.Write(MakeQuoted(*x)); |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
case FieldDescriptor::TYPE_SFIXED32: |
|
|
|
|