| // Protocol Buffers - Google's data interchange format | |
| // Copyright 2008 Google Inc. All rights reserved. | |
| // | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file or at | |
| // https://developers.google.com/open-source/licenses/bsd | |
| #include "google/protobuf/json/internal/unparser.h" | |
| #include <cmath> | |
| #include <cstdint> | |
| #include <cstring> | |
| #include <limits> | |
| #include <string> | |
| #include <type_traits> | |
| #include <utility> | |
| #include <vector> | |
| #include "absl/log/absl_check.h" | |
| #include "absl/log/absl_log.h" | |
| #include "absl/status/status.h" | |
| #include "absl/status/statusor.h" | |
| #include "absl/strings/ascii.h" | |
| #include "absl/strings/escaping.h" | |
| #include "absl/strings/str_cat.h" | |
| #include "absl/strings/str_format.h" | |
| #include "absl/strings/string_view.h" | |
| #include "absl/types/optional.h" | |
| #include "google/protobuf/descriptor.h" | |
| #include "google/protobuf/dynamic_message.h" | |
| #include "google/protobuf/io/coded_stream.h" | |
| #include "google/protobuf/io/zero_copy_sink.h" | |
| #include "google/protobuf/io/zero_copy_stream.h" | |
| #include "google/protobuf/io/zero_copy_stream_impl_lite.h" | |
| #include "google/protobuf/json/internal/descriptor_traits.h" | |
| #include "google/protobuf/json/internal/unparser_traits.h" | |
| #include "google/protobuf/json/internal/writer.h" | |
| #include "google/protobuf/message.h" | |
| #include "google/protobuf/util/type_resolver.h" | |
| #include "google/protobuf/stubs/status_macros.h" | |
| // Must be included last. | |
| #include "google/protobuf/port_def.inc" | |
| namespace google { | |
| namespace protobuf { | |
| namespace json_internal { | |
| namespace { | |
| template <typename Traits> | |
| bool IsEmpty(const Msg<Traits>& msg, const Desc<Traits>& desc) { | |
| size_t count = Traits::FieldCount(desc); | |
| for (size_t i = 0; i < count; ++i) { | |
| if (Traits::GetSize(Traits::FieldByIndex(desc, i), msg) > 0) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| enum class IntegerEnumStyle { | |
| kQuoted, | |
| kUnquoted, | |
| }; | |
| template <typename Traits> | |
| void WriteEnum(JsonWriter& writer, Field<Traits> field, int32_t value, | |
| IntegerEnumStyle int_style = IntegerEnumStyle::kUnquoted) { | |
| if (ClassifyMessage(Traits::FieldTypeName(field)) == MessageType::kNull) { | |
| writer.Write("null"); | |
| return; | |
| } | |
| if (!writer.options().always_print_enums_as_ints) { | |
| auto name = Traits::EnumNameByNumber(field, value); | |
| if (name.ok()) { | |
| writer.Write(MakeQuoted(*name)); | |
| return; | |
| } | |
| } | |
| if (int_style == IntegerEnumStyle::kQuoted) { | |
| writer.Write("\"", value, "\""); | |
| } else { | |
| writer.Write(value); | |
| } | |
| } | |
| // Returns true if x round-trips through being cast to a double, i.e., if | |
| // x is representable 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_v<Int>) { | |
| 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, | |
| const Desc<Traits>& desc, bool is_top_level = false); | |
| // This is templatized so that defaults, singular, and repeated fields can both | |
| // use the same enormous switch-case. | |
| template <typename Traits, typename... Args> | |
| absl::Status WriteSingular(JsonWriter& writer, Field<Traits> field, | |
| Args&&... args) { | |
| // When the pack `args` is empty, the caller has requested printing the | |
| // default value. | |
| bool is_default = sizeof...(Args) == 0; | |
| switch (Traits::FieldType(field)) { | |
| case FieldDescriptor::TYPE_FLOAT: { | |
| auto x = Traits::GetFloat(field, std::forward<Args>(args)...); | |
| RETURN_IF_ERROR(x.status()); | |
| if (writer.options().allow_legacy_nonconformant_behavior && is_default && | |
| !std::isfinite(*x)) { | |
| *x = 0; | |
| } | |
| writer.Write(*x); | |
| break; | |
| } | |
| case FieldDescriptor::TYPE_DOUBLE: { | |
| auto x = Traits::GetDouble(field, std::forward<Args>(args)...); | |
| RETURN_IF_ERROR(x.status()); | |
| if (writer.options().allow_legacy_nonconformant_behavior && is_default && | |
| !std::isfinite(*x)) { | |
| *x = 0; | |
| } | |
| writer.Write(*x); | |
| break; | |
| } | |
| case FieldDescriptor::TYPE_SFIXED64: | |
| case FieldDescriptor::TYPE_SINT64: | |
| case FieldDescriptor::TYPE_INT64: { | |
| auto x = Traits::GetInt64(field, std::forward<Args>(args)...); | |
| RETURN_IF_ERROR(x.status()); | |
| 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()); | |
| if (writer.options().unquote_int64_if_possible && | |
| RoundTripsThroughDouble(*x)) { | |
| writer.Write(*x); | |
| } else { | |
| writer.Write(MakeQuoted(*x)); | |
| } | |
| break; | |
| } | |
| case FieldDescriptor::TYPE_SFIXED32: | |
| case FieldDescriptor::TYPE_SINT32: | |
| case FieldDescriptor::TYPE_INT32: { | |
| auto x = Traits::GetInt32(field, std::forward<Args>(args)...); | |
| RETURN_IF_ERROR(x.status()); | |
| writer.Write(*x); | |
| break; | |
| } | |
| case FieldDescriptor::TYPE_FIXED32: | |
| case FieldDescriptor::TYPE_UINT32: { | |
| auto x = Traits::GetUInt32(field, std::forward<Args>(args)...); | |
| RETURN_IF_ERROR(x.status()); | |
| writer.Write(*x); | |
| break; | |
| } | |
| case FieldDescriptor::TYPE_BOOL: { | |
| auto x = Traits::GetBool(field, std::forward<Args>(args)...); | |
| RETURN_IF_ERROR(x.status()); | |
| writer.Write(*x ? "true" : "false"); | |
| break; | |
| } | |
| case FieldDescriptor::TYPE_STRING: { | |
| auto x = Traits::GetString(field, writer.ScratchBuf(), | |
| std::forward<Args>(args)...); | |
| RETURN_IF_ERROR(x.status()); | |
| writer.Write(MakeQuoted(*x)); | |
| break; | |
| } | |
| case FieldDescriptor::TYPE_BYTES: { | |
| auto x = Traits::GetString(field, writer.ScratchBuf(), | |
| std::forward<Args>(args)...); | |
| RETURN_IF_ERROR(x.status()); | |
| if (writer.options().allow_legacy_nonconformant_behavior && is_default) { | |
| // Although difficult to verify, it appears that the original ESF parser | |
| // fails to unescape the contents of a | |
| // google.protobuf.Field.default_value, which may potentially be | |
| // escaped if it is for a `bytes` field (note that default_value is a | |
| // `string` regardless of what type the field is). | |
| // | |
| // However, our parser's type.proto guts actually know to do this | |
| // correctly, so this bug must be manually re-introduced. | |
| writer.WriteBase64(absl::CEscape(*x)); | |
| } else { | |
| writer.WriteBase64(*x); | |
| } | |
| break; | |
| } | |
| case FieldDescriptor::TYPE_ENUM: { | |
| auto x = Traits::GetEnumValue(field, std::forward<Args>(args)...); | |
| RETURN_IF_ERROR(x.status()); | |
| WriteEnum<Traits>(writer, field, *x); | |
| break; | |
| } | |
| case FieldDescriptor::TYPE_MESSAGE: | |
| case FieldDescriptor::TYPE_GROUP: { | |
| auto x = Traits::GetMessage(field, std::forward<Args>(args)...); | |
| RETURN_IF_ERROR(x.status()); | |
| return WriteMessage<Traits>(writer, **x, Traits::GetDesc(**x)); | |
| } | |
| default: | |
| return absl::InvalidArgumentError( | |
| absl::StrCat("unsupported field type: ", Traits::FieldType(field))); | |
| } | |
| return absl::OkStatus(); | |
| } | |
| template <typename Traits> | |
| absl::Status WriteRepeated(JsonWriter& writer, const Msg<Traits>& msg, | |
| Field<Traits> field) { | |
| writer.Write("["); | |
| writer.Push(); | |
| size_t count = Traits::GetSize(field, msg); | |
| bool first = true; | |
| for (size_t i = 0; i < count; ++i) { | |
| if (ClassifyMessage(Traits::FieldTypeName(field)) == MessageType::kValue) { | |
| bool empty = false; | |
| RETURN_IF_ERROR(Traits::WithFieldType( | |
| field, [&](const Desc<Traits>& desc) -> absl::Status { | |
| auto inner = Traits::GetMessage(field, msg, i); | |
| RETURN_IF_ERROR(inner.status()); | |
| empty = IsEmpty<Traits>(**inner, desc); | |
| return absl::OkStatus(); | |
| })); | |
| // Empty google.protobuf.Values are silently discarded. | |
| if (empty) { | |
| continue; | |
| } | |
| } | |
| writer.WriteComma(first); | |
| writer.NewLine(); | |
| RETURN_IF_ERROR(WriteSingular<Traits>(writer, field, msg, i)); | |
| } | |
| writer.Pop(); | |
| if (!first) { | |
| writer.NewLine(); | |
| } | |
| writer.Write("]"); | |
| return absl::OkStatus(); | |
| } | |
| template <typename Traits, typename Entry> | |
| absl::Status WriteMapKey(JsonWriter& writer, const Entry& entry, | |
| Field<Traits> field) { | |
| switch (Traits::FieldType(field)) { | |
| case FieldDescriptor::TYPE_SFIXED64: | |
| case FieldDescriptor::TYPE_SINT64: | |
| case FieldDescriptor::TYPE_INT64: { | |
| auto x = Traits::GetInt64(field, entry); | |
| RETURN_IF_ERROR(x.status()); | |
| writer.Write(MakeQuoted(*x)); | |
| break; | |
| } | |
| case FieldDescriptor::TYPE_FIXED64: | |
| case FieldDescriptor::TYPE_UINT64: { | |
| auto x = Traits::GetUInt64(field, entry); | |
| RETURN_IF_ERROR(x.status()); | |
| writer.Write(MakeQuoted(*x)); | |
| break; | |
| } | |
| case FieldDescriptor::TYPE_SFIXED32: | |
| case FieldDescriptor::TYPE_SINT32: | |
| case FieldDescriptor::TYPE_INT32: { | |
| auto x = Traits::GetInt32(field, entry); | |
| RETURN_IF_ERROR(x.status()); | |
| writer.Write(MakeQuoted(*x)); | |
| break; | |
| } | |
| case FieldDescriptor::TYPE_FIXED32: | |
| case FieldDescriptor::TYPE_UINT32: { | |
| auto x = Traits::GetUInt32(field, entry); | |
| RETURN_IF_ERROR(x.status()); | |
| writer.Write(MakeQuoted(*x)); | |
| break; | |
| } | |
| case FieldDescriptor::TYPE_BOOL: { | |
| auto x = Traits::GetBool(field, entry); | |
| RETURN_IF_ERROR(x.status()); | |
| writer.Write(MakeQuoted(*x ? "true" : "false")); | |
| break; | |
| } | |
| case FieldDescriptor::TYPE_STRING: { | |
| auto x = Traits::GetString(field, writer.ScratchBuf(), entry); | |
| RETURN_IF_ERROR(x.status()); | |
| writer.Write(MakeQuoted(*x)); | |
| break; | |
| } | |
| case FieldDescriptor::TYPE_ENUM: { | |
| auto x = Traits::GetEnumValue(field, entry); | |
| RETURN_IF_ERROR(x.status()); | |
| WriteEnum<Traits>(writer, field, *x, IntegerEnumStyle::kQuoted); | |
| break; | |
| } | |
| default: | |
| return absl::InvalidArgumentError( | |
| absl::StrCat("unsupported map key type: ", Traits::FieldType(field))); | |
| } | |
| return absl::OkStatus(); | |
| } | |
| template <typename Traits> | |
| absl::StatusOr<bool> IsEmptyValue(const Msg<Traits>& msg, Field<Traits> field) { | |
| if (ClassifyMessage(Traits::FieldTypeName(field)) != MessageType::kValue) { | |
| return false; | |
| } | |
| bool empty = false; | |
| RETURN_IF_ERROR(Traits::WithFieldType( | |
| field, [&](const Desc<Traits>& desc) -> absl::Status { | |
| auto inner = Traits::GetMessage(field, msg); | |
| RETURN_IF_ERROR(inner.status()); | |
| empty = IsEmpty<Traits>(**inner, desc); | |
| return absl::OkStatus(); | |
| })); | |
| return empty; | |
| } | |
| template <typename Traits> | |
| absl::StatusOr<bool> IsEmptyValue(const MapValueConstRef& value, | |
| Field<Traits> field) { | |
| if (ClassifyMessage(Traits::FieldTypeName(field)) != MessageType::kValue) { | |
| return false; | |
| } | |
| const auto& inner = value.GetMessageValue(); | |
| return IsEmpty<Traits>(inner, *field->message_type()); | |
| } | |
| template <typename Traits> | |
| absl::Status WriteMap(JsonWriter& writer, const Msg<Traits>& msg, | |
| Field<Traits> field) { | |
| writer.Write("{"); | |
| writer.Push(); | |
| bool first = true; | |
| if (Traits::MapFieldUseMapReflection(field, msg)) { | |
| RETURN_IF_ERROR(Traits::ForEachMapEntry( | |
| field, msg, [&](auto it, const auto& type) -> absl::Status { | |
| auto is_empty = | |
| IsEmptyValue<Traits>(it.GetValueRef(), Traits::ValueField(type)); | |
| RETURN_IF_ERROR(is_empty.status()); | |
| if (*is_empty) { | |
| // Empty google.protobuf.Values are silently discarded. | |
| return absl::OkStatus(); | |
| } | |
| writer.WriteComma(first); | |
| writer.NewLine(); | |
| RETURN_IF_ERROR( | |
| WriteMapKey<Traits>(writer, it.GetKey(), Traits::KeyField(type))); | |
| writer.Write(":"); | |
| writer.Whitespace(" "); | |
| RETURN_IF_ERROR(WriteSingular<Traits>( | |
| writer, Traits::ValueField(type), it.GetValueRef())); | |
| return absl::OkStatus(); | |
| })); | |
| } else { | |
| const size_t count = Traits::GetSize(field, msg); | |
| for (size_t i = 0; i < count; ++i) { | |
| absl::StatusOr<const Msg<Traits>*> entry = | |
| Traits::GetMessage(field, msg, i); | |
| RETURN_IF_ERROR(entry.status()); | |
| const Desc<Traits>& type = Traits::GetDesc(**entry); | |
| auto is_empty = IsEmptyValue<Traits>(**entry, Traits::ValueField(type)); | |
| RETURN_IF_ERROR(is_empty.status()); | |
| if (*is_empty) { | |
| // Empty google.protobuf.Values are silently discarded. | |
| continue; | |
| } | |
| writer.WriteComma(first); | |
| writer.NewLine(); | |
| RETURN_IF_ERROR( | |
| WriteMapKey<Traits>(writer, **entry, Traits::KeyField(type))); | |
| writer.Write(":"); | |
| writer.Whitespace(" "); | |
| RETURN_IF_ERROR( | |
| WriteSingular<Traits>(writer, Traits::ValueField(type), **entry)); | |
| } | |
| } | |
| writer.Pop(); | |
| if (!first) { | |
| writer.NewLine(); | |
| } | |
| writer.Write("}"); | |
| return absl::OkStatus(); | |
| } | |
| template <typename Traits> | |
| absl::Status WriteField(JsonWriter& writer, const Msg<Traits>& msg, | |
| Field<Traits> field, bool& first) { | |
| if (!Traits::IsRepeated(field)) { // Repeated case is handled in | |
| // WriteRepeated. | |
| auto is_empty = IsEmptyValue<Traits>(msg, field); | |
| RETURN_IF_ERROR(is_empty.status()); | |
| if (*is_empty) { | |
| // Empty google.protobuf.Values are silently discarded. | |
| return absl::OkStatus(); | |
| } | |
| } | |
| writer.WriteComma(first); | |
| writer.NewLine(); | |
| if (Traits::IsExtension(field)) { | |
| writer.Write(MakeQuoted("[", Traits::FieldFullName(field), "]"), ":"); | |
| } else if (writer.options().preserve_proto_field_names) { | |
| writer.Write(MakeQuoted(Traits::FieldName(field)), ":"); | |
| } else { | |
| // The generator for type.proto and the internals of descriptor.cc disagree | |
| // on what the json name of a PascalCase field is supposed to be; type.proto | |
| // seems to (incorrectly?) capitalize the first letter, which is the | |
| // behavior ESF defaults to. To fix this, if the original field name starts | |
| // with an uppercase letter, and the Json name does not, we uppercase it. | |
| absl::string_view original_name = Traits::FieldName(field); | |
| absl::string_view json_name = Traits::FieldJsonName(field); | |
| if (writer.options().allow_legacy_nonconformant_behavior && | |
| absl::ascii_isupper(original_name[0]) && | |
| !absl::ascii_isupper(json_name[0])) { | |
| writer.Write(MakeQuoted(absl::ascii_toupper(original_name[0]), | |
| original_name.substr(1)), | |
| ":"); | |
| } else { | |
| writer.Write(MakeQuoted(json_name), ":"); | |
| } | |
| } | |
| writer.Whitespace(" "); | |
| if (Traits::IsMap(field)) { | |
| return WriteMap<Traits>(writer, msg, field); | |
| } else if (Traits::IsRepeated(field)) { | |
| return WriteRepeated<Traits>(writer, msg, field); | |
| } else if (Traits::GetSize(field, msg) == 0) { | |
| if (Traits::FieldType(field) == FieldDescriptor::TYPE_GROUP) { | |
| // We do not yet have full group support, but this is required so that we | |
| // pass the same tests as the ESF parser. | |
| writer.Write("null"); | |
| return absl::OkStatus(); | |
| } | |
| return WriteSingular<Traits>(writer, field); | |
| } | |
| return WriteSingular<Traits>(writer, field, msg); | |
| } | |
| template <typename Traits> | |
| absl::Status WriteFields(JsonWriter& writer, const Msg<Traits>& msg, | |
| const Desc<Traits>& desc, bool& first) { | |
| std::vector<Field<Traits>> fields; | |
| size_t total = Traits::FieldCount(desc); | |
| fields.reserve(total); | |
| for (size_t i = 0; i < total; ++i) { | |
| Field<Traits> field = Traits::FieldByIndex(desc, i); | |
| bool has = Traits::GetSize(field, msg) > 0; | |
| if (writer.options().always_print_fields_with_no_presence) { | |
| has |= Traits::IsRepeated(field) || Traits::IsImplicitPresence(field); | |
| } | |
| if (has) { | |
| fields.push_back(field); | |
| } | |
| } | |
| // Add extensions *before* sorting. | |
| Traits::FindAndAppendExtensions(msg, fields); | |
| // Fields are guaranteed to be serialized in field number order. | |
| absl::c_sort(fields, [](const auto& a, const auto& b) { | |
| return Traits::FieldNumber(a) < Traits::FieldNumber(b); | |
| }); | |
| for (auto field : fields) { | |
| RETURN_IF_ERROR(WriteField<Traits>(writer, msg, field, first)); | |
| } | |
| return absl::OkStatus(); | |
| } | |
| template <typename Traits> | |
| absl::Status WriteStructValue(JsonWriter& writer, const Msg<Traits>& msg, | |
| const Desc<Traits>& desc); | |
| template <typename Traits> | |
| absl::Status WriteListValue(JsonWriter& writer, const Msg<Traits>& msg, | |
| const Desc<Traits>& desc); | |
| template <typename Traits> | |
| absl::Status WriteValue(JsonWriter& writer, const Msg<Traits>& msg, | |
| const Desc<Traits>& desc, bool is_top_level) { | |
| // NOTE: The field numbers 1 through 6 are the numbers of the oneof fields in | |
| // google.protobuf.Value. Conformance tests verify the correctness of these | |
| // numbers. | |
| if (Traits::GetSize(Traits::MustHaveField(desc, 1), msg) > 0) { | |
| writer.Write("null"); | |
| return absl::OkStatus(); | |
| } | |
| auto number_field = Traits::MustHaveField(desc, 2); | |
| if (Traits::GetSize(number_field, msg) > 0) { | |
| auto x = Traits::GetDouble(number_field, msg); | |
| RETURN_IF_ERROR(x.status()); | |
| if (std::isnan(*x)) { | |
| return absl::InvalidArgumentError( | |
| "google.protobuf.Value cannot encode double values for nan, " | |
| "because it would be parsed as a string"); | |
| } | |
| if (*x == std::numeric_limits<double>::infinity() || | |
| *x == -std::numeric_limits<double>::infinity()) { | |
| return absl::InvalidArgumentError( | |
| "google.protobuf.Value cannot encode double values for " | |
| "infinity, because it would be parsed as a string"); | |
| } | |
| writer.Write(*x); | |
| return absl::OkStatus(); | |
| } | |
| auto string_field = Traits::MustHaveField(desc, 3); | |
| if (Traits::GetSize(string_field, msg) > 0) { | |
| auto x = Traits::GetString(string_field, writer.ScratchBuf(), msg); | |
| RETURN_IF_ERROR(x.status()); | |
| writer.Write(MakeQuoted(*x)); | |
| return absl::OkStatus(); | |
| } | |
| auto bool_field = Traits::MustHaveField(desc, 4); | |
| if (Traits::GetSize(bool_field, msg) > 0) { | |
| auto x = Traits::GetBool(bool_field, msg); | |
| RETURN_IF_ERROR(x.status()); | |
| writer.Write(*x ? "true" : "false"); | |
| return absl::OkStatus(); | |
| } | |
| auto struct_field = Traits::MustHaveField(desc, 5); | |
| if (Traits::GetSize(struct_field, msg) > 0) { | |
| auto x = Traits::GetMessage(struct_field, msg); | |
| RETURN_IF_ERROR(x.status()); | |
| return Traits::WithFieldType(struct_field, [&](const Desc<Traits>& type) { | |
| return WriteStructValue<Traits>(writer, **x, type); | |
| }); | |
| } | |
| auto list_field = Traits::MustHaveField(desc, 6); | |
| if (Traits::GetSize(list_field, msg) > 0) { | |
| auto x = Traits::GetMessage(list_field, msg); | |
| RETURN_IF_ERROR(x.status()); | |
| return Traits::WithFieldType(list_field, [&](const Desc<Traits>& type) { | |
| return WriteListValue<Traits>(writer, **x, type); | |
| }); | |
| } | |
| ABSL_CHECK(is_top_level) | |
| << "empty, non-top-level Value must be handled one layer " | |
| "up, since it prints an empty string; reaching this " | |
| "statement is always a bug"; | |
| return absl::OkStatus(); | |
| } | |
| template <typename Traits> | |
| absl::Status WriteStructValue(JsonWriter& writer, const Msg<Traits>& msg, | |
| const Desc<Traits>& desc) { | |
| return WriteMap<Traits>(writer, msg, Traits::MustHaveField(desc, 1)); | |
| } | |
| template <typename Traits> | |
| absl::Status WriteListValue(JsonWriter& writer, const Msg<Traits>& msg, | |
| const Desc<Traits>& desc) { | |
| return WriteRepeated<Traits>(writer, msg, Traits::MustHaveField(desc, 1)); | |
| } | |
| template <typename Traits> | |
| absl::Status WriteTimestamp(JsonWriter& writer, const Msg<Traits>& msg, | |
| const Desc<Traits>& desc) { | |
| auto secs_field = Traits::MustHaveField(desc, 1); | |
| auto status_or_secs = Traits::GetSize(secs_field, msg) > 0 | |
| ? Traits::GetInt64(secs_field, msg) | |
| : 0; | |
| RETURN_IF_ERROR(status_or_secs.status()); | |
| int64_t secs = *status_or_secs; | |
| if (secs < -62135596800) { | |
| return absl::InvalidArgumentError( | |
| "minimum acceptable time value is 0001-01-01T00:00:00Z"); | |
| } else if (secs > 253402300799) { | |
| return absl::InvalidArgumentError( | |
| "maximum acceptable time value is 9999-12-31T23:59:59Z"); | |
| } | |
| // Ensure seconds is positive. | |
| secs += 62135596800; | |
| auto nanos_field = Traits::MustHaveField(desc, 2); | |
| auto status_or_nanos = Traits::GetSize(nanos_field, msg) > 0 | |
| ? Traits::GetInt32(nanos_field, msg) | |
| : 0; | |
| RETURN_IF_ERROR(status_or_nanos.status()); | |
| int32_t nanos = *status_or_nanos; | |
| if (nanos < 0 || nanos > 999999999) { | |
| return absl::InvalidArgumentError( | |
| "nanos must be in range [0, +999,999,999]"); | |
| } | |
| // Julian Day -> Y/M/D, Algorithm from: | |
| // Fliegel, H. F., and Van Flandern, T. C., "A Machine Algorithm for | |
| // Processing Calendar Dates," Communications of the Association of | |
| // Computing Machines, vol. 11 (1968), p. 657. | |
| int32_t L, N, I, J, K; | |
| L = static_cast<int32_t>(secs / 86400) - 719162 + 68569 + 2440588; | |
| N = 4 * L / 146097; | |
| L = L - (146097 * N + 3) / 4; | |
| I = 4000 * (L + 1) / 1461001; | |
| L = L - 1461 * I / 4 + 31; | |
| J = 80 * L / 2447; | |
| K = L - 2447 * J / 80; | |
| L = J / 11; | |
| J = J + 2 - 12 * L; | |
| I = 100 * (N - 49) + I + L; | |
| int32_t sec = secs % 60; | |
| int32_t min = (secs / 60) % 60; | |
| int32_t hour = (secs / 3600) % 24; | |
| if (nanos == 0) { | |
| writer.Write(absl::StrFormat(R"("%04d-%02d-%02dT%02d:%02d:%02dZ")", I, J, K, | |
| hour, min, sec)); | |
| return absl::OkStatus(); | |
| } | |
| size_t digits = 9; | |
| uint32_t frac_seconds = std::abs(nanos); | |
| while (frac_seconds % 1000 == 0) { | |
| frac_seconds /= 1000; | |
| digits -= 3; | |
| } | |
| writer.Write(absl::StrFormat(R"("%04d-%02d-%02dT%02d:%02d:%02d.%.*dZ")", I, J, | |
| K, hour, min, sec, digits, frac_seconds)); | |
| return absl::OkStatus(); | |
| } | |
| template <typename Traits> | |
| absl::Status WriteDuration(JsonWriter& writer, const Msg<Traits>& msg, | |
| const Desc<Traits>& desc) { | |
| constexpr int64_t kMaxSeconds = int64_t{3652500} * 86400; | |
| constexpr int32_t kMaxNanos = 999999999; | |
| auto secs_field = Traits::MustHaveField(desc, 1); | |
| auto status_or_secs = Traits::GetSize(secs_field, msg) > 0 | |
| ? Traits::GetInt64(secs_field, msg) | |
| : 0; | |
| RETURN_IF_ERROR(status_or_secs.status()); | |
| int64_t secs = *status_or_secs; | |
| if (secs > kMaxSeconds || secs < -kMaxSeconds) { | |
| return absl::InvalidArgumentError("duration out of range"); | |
| } | |
| auto nanos_field = Traits::MustHaveField(desc, 2); | |
| auto status_or_nanos = Traits::GetSize(nanos_field, msg) > 0 | |
| ? Traits::GetInt32(nanos_field, msg) | |
| : 0; | |
| RETURN_IF_ERROR(status_or_nanos.status()); | |
| int32_t nanos = *status_or_nanos; | |
| if (nanos > kMaxNanos || nanos < -kMaxNanos) { | |
| return absl::InvalidArgumentError("duration out of range"); | |
| } | |
| if ((secs != 0) && (nanos != 0) && ((secs < 0) != (nanos < 0))) { | |
| return absl::InvalidArgumentError("nanos and seconds signs do not match"); | |
| } | |
| if (nanos == 0) { | |
| writer.Write(absl::StrFormat(R"("%ds")", secs)); | |
| return absl::OkStatus(); | |
| } | |
| size_t digits = 9; | |
| uint32_t frac_seconds = std::abs(nanos); | |
| while (frac_seconds % 1000 == 0) { | |
| frac_seconds /= 1000; | |
| digits -= 3; | |
| } | |
| absl::string_view sign = ((secs < 0) || (nanos < 0)) ? "-" : ""; | |
| writer.Write(absl::StrFormat(R"("%s%d.%.*ds")", sign, std::abs(secs), digits, | |
| frac_seconds)); | |
| return absl::OkStatus(); | |
| } | |
| template <typename Traits> | |
| absl::Status WriteFieldMask(JsonWriter& writer, const Msg<Traits>& msg, | |
| const Desc<Traits>& desc) { | |
| // google.protobuf.FieldMask has a single field with number 1. | |
| auto paths_field = Traits::MustHaveField(desc, 1); | |
| size_t paths = Traits::GetSize(paths_field, msg); | |
| writer.Write('"'); | |
| bool first = true; | |
| for (size_t i = 0; i < paths; ++i) { | |
| writer.WriteComma(first); | |
| auto path = Traits::GetString(paths_field, writer.ScratchBuf(), msg, i); | |
| RETURN_IF_ERROR(path.status()); | |
| bool saw_under = false; | |
| for (char c : *path) { | |
| if (absl::ascii_islower(c) && saw_under) { | |
| writer.Write(absl::ascii_toupper(c)); | |
| } else if (absl::ascii_isdigit(c) || absl::ascii_islower(c) || c == '.') { | |
| writer.Write(c); | |
| } else if (c == '_' && | |
| (!saw_under || | |
| writer.options().allow_legacy_nonconformant_behavior)) { | |
| saw_under = true; | |
| continue; | |
| } else if (!writer.options().allow_legacy_nonconformant_behavior) { | |
| return absl::InvalidArgumentError("unexpected character in FieldMask"); | |
| } else { | |
| if (saw_under) { | |
| writer.Write('_'); | |
| } | |
| writer.Write(c); | |
| } | |
| saw_under = false; | |
| } | |
| } | |
| writer.Write('"'); | |
| return absl::OkStatus(); | |
| } | |
| template <typename Traits> | |
| absl::Status WriteAny(JsonWriter& writer, const Msg<Traits>& msg, | |
| const Desc<Traits>& desc) { | |
| auto type_url_field = Traits::MustHaveField(desc, 1); | |
| auto value_field = Traits::MustHaveField(desc, 2); | |
| bool has_type_url = Traits::GetSize(type_url_field, msg) > 0; | |
| bool has_value = Traits::GetSize(value_field, msg) > 0; | |
| if (!has_type_url && !has_value) { | |
| writer.Write("{}"); | |
| return absl::OkStatus(); | |
| } else if (!has_type_url) { | |
| return absl::InvalidArgumentError("broken Any: missing type URL"); | |
| } | |
| writer.Write("{"); | |
| writer.Push(); | |
| auto type_url = Traits::GetString(type_url_field, writer.ScratchBuf(), msg); | |
| RETURN_IF_ERROR(type_url.status()); | |
| writer.NewLine(); | |
| writer.Write("\"@type\":"); | |
| writer.Whitespace(" "); | |
| writer.Write(MakeQuoted(*type_url)); | |
| return Traits::WithDynamicType( | |
| desc, std::string(*type_url), | |
| [&](const Desc<Traits>& any_desc) -> absl::Status { | |
| absl::string_view any_bytes; | |
| if (has_value) { | |
| absl::StatusOr<absl::string_view> bytes = | |
| Traits::GetString(value_field, writer.ScratchBuf(), msg); | |
| RETURN_IF_ERROR(bytes.status()); | |
| any_bytes = *bytes; | |
| } | |
| return Traits::WithDecodedMessage( | |
| any_desc, any_bytes, | |
| [&](const Msg<Traits>& unerased) -> absl::Status { | |
| bool first = false; | |
| if (ClassifyMessage(Traits::TypeName(any_desc)) != | |
| MessageType::kNotWellKnown) { | |
| if (ClassifyMessage(Traits::TypeName(any_desc)) == | |
| MessageType::kValue && | |
| IsEmpty<Traits>(unerased, any_desc)) { | |
| // Omit "value" field for empty Value. | |
| } else { | |
| writer.WriteComma(first); | |
| writer.NewLine(); | |
| writer.Write("\"value\":"); | |
| writer.Whitespace(" "); | |
| RETURN_IF_ERROR( | |
| WriteMessage<Traits>(writer, unerased, any_desc)); | |
| } | |
| } else { | |
| RETURN_IF_ERROR( | |
| WriteFields<Traits>(writer, unerased, any_desc, first)); | |
| } | |
| writer.Pop(); | |
| if (!first) { | |
| writer.NewLine(); | |
| } | |
| writer.Write("}"); | |
| return absl::OkStatus(); | |
| }); | |
| }); | |
| } | |
| template <typename Traits> | |
| absl::Status WriteMessage(JsonWriter& writer, const Msg<Traits>& msg, | |
| const Desc<Traits>& desc, bool is_top_level) { | |
| switch (ClassifyMessage(Traits::TypeName(desc))) { | |
| case MessageType::kAny: | |
| return WriteAny<Traits>(writer, msg, desc); | |
| case MessageType::kWrapper: { | |
| auto field = Traits::MustHaveField(desc, 1); | |
| if (Traits::GetSize(field, msg) == 0) { | |
| return WriteSingular<Traits>(writer, field); | |
| } | |
| return WriteSingular<Traits>(writer, field, msg); | |
| } | |
| case MessageType::kValue: | |
| return WriteValue<Traits>(writer, msg, desc, is_top_level); | |
| case MessageType::kStruct: | |
| return WriteStructValue<Traits>(writer, msg, desc); | |
| case MessageType::kList: | |
| return WriteListValue<Traits>(writer, msg, desc); | |
| case MessageType::kTimestamp: | |
| return WriteTimestamp<Traits>(writer, msg, desc); | |
| case MessageType::kDuration: | |
| return WriteDuration<Traits>(writer, msg, desc); | |
| case MessageType::kFieldMask: | |
| return WriteFieldMask<Traits>(writer, msg, desc); | |
| default: { | |
| writer.Write("{"); | |
| writer.Push(); | |
| bool first = true; | |
| RETURN_IF_ERROR(WriteFields<Traits>(writer, msg, desc, first)); | |
| writer.Pop(); | |
| if (!first) { | |
| writer.NewLine(); | |
| } | |
| writer.Write("}"); | |
| return absl::OkStatus(); | |
| } | |
| } | |
| } | |
| } // namespace | |
| absl::Status MessageToJsonStream(const Message& message, | |
| io::ZeroCopyOutputStream* json_output, | |
| json_internal::WriterOptions options) { | |
| if (PROTOBUF_DEBUG) { | |
| ABSL_DLOG(INFO) << "json2/input: " << message.DebugString(); | |
| } | |
| JsonWriter writer(json_output, options); | |
| absl::Status s = WriteMessage<UnparseProto2Descriptor>( | |
| writer, message, *message.GetDescriptor(), /*is_top_level=*/true); | |
| if (PROTOBUF_DEBUG) ABSL_DLOG(INFO) << "json2/status: " << s; | |
| RETURN_IF_ERROR(s); | |
| writer.NewLine(); | |
| return absl::OkStatus(); | |
| } | |
| absl::Status BinaryToJsonStream(google::protobuf::util::TypeResolver* resolver, | |
| const std::string& type_url, | |
| io::ZeroCopyInputStream* binary_input, | |
| io::ZeroCopyOutputStream* json_output, | |
| json_internal::WriterOptions options) { | |
| // NOTE: Most of the contortions in this function are to allow for capture of | |
| // input and output of the parser in ABSL_DLOG mode. Destruction order is very | |
| // critical in this function, because io::ZeroCopy*Stream types usually only | |
| // flush on destruction. | |
| // For ABSL_DLOG, we would like to print out the input and output, which | |
| // requires buffering both instead of doing "zero copy". This block, and the | |
| // one at the end of the function, set up and tear down interception of the | |
| // input and output streams. | |
| std::string copy; | |
| std::string out; | |
| absl::optional<io::ArrayInputStream> tee_input; | |
| absl::optional<io::StringOutputStream> tee_output; | |
| if (PROTOBUF_DEBUG) { | |
| const void* data; | |
| int len; | |
| while (binary_input->Next(&data, &len)) { | |
| copy.resize(copy.size() + len); | |
| std::memcpy(©[copy.size() - len], data, len); | |
| } | |
| tee_input.emplace(copy.data(), copy.size()); | |
| tee_output.emplace(&out); | |
| ABSL_DLOG(INFO) << "json2/input: " << absl::BytesToHexString(copy); | |
| } | |
| ResolverPool pool(resolver); | |
| auto desc = pool.FindMessage(type_url); | |
| RETURN_IF_ERROR(desc.status()); | |
| io::CodedInputStream stream(tee_input.has_value() ? &*tee_input | |
| : binary_input); | |
| auto msg = UntypedMessage::ParseFromStream(*desc, stream); | |
| RETURN_IF_ERROR(msg.status()); | |
| JsonWriter writer(tee_output.has_value() ? &*tee_output : json_output, | |
| options); | |
| absl::Status s = WriteMessage<UnparseProto3Type>( | |
| writer, *msg, UnparseProto3Type::GetDesc(*msg), | |
| /*is_top_level=*/true); | |
| if (PROTOBUF_DEBUG) ABSL_DLOG(INFO) << "json2/status: " << s; | |
| RETURN_IF_ERROR(s); | |
| if (PROTOBUF_DEBUG) { | |
| tee_output.reset(); // Flush the output stream. | |
| io::zc_sink_internal::ZeroCopyStreamByteSink(json_output) | |
| .Append(out.data(), out.size()); | |
| ABSL_DLOG(INFO) << "json2/output: " << absl::CHexEscape(out); | |
| } | |
| writer.NewLine(); | |
| return absl::OkStatus(); | |
| } | |
| } // namespace json_internal | |
| } // namespace protobuf | |
| } // namespace google | |
| #include "google/protobuf/port_undef.inc" |