| // Protocol Buffers - Google's data interchange format |
| // Copyright 2008 Google Inc. All rights reserved. |
| // https://developers.google.com/protocol-buffers/ |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // |
| // * Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above |
| // copyright notice, this list of conditions and the following disclaimer |
| // in the documentation and/or other materials provided with the |
| // distribution. |
| // * Neither the name of Google Inc. nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| #ifndef GOOGLE_PROTOBUF_JSON_INTERNAL_PARSER_TRAITS_H__ |
| #define GOOGLE_PROTOBUF_JSON_INTERNAL_PARSER_TRAITS_H__ |
| |
| #include <cfloat> |
| #include <cmath> |
| #include <cstdint> |
| #include <limits> |
| #include <string> |
| #include <utility> |
| |
| #include "google/protobuf/type.pb.h" |
| #include "google/protobuf/descriptor.h" |
| #include "google/protobuf/dynamic_message.h" |
| #include "google/protobuf/message.h" |
| #include "absl/base/attributes.h" |
| #include "absl/base/casts.h" |
| #include "absl/container/flat_hash_map.h" |
| #include "absl/container/flat_hash_set.h" |
| #include "absl/status/status.h" |
| #include "absl/strings/str_format.h" |
| #include "absl/strings/string_view.h" |
| #include "google/protobuf/io/coded_stream.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/wire_format_lite.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 { |
| using ::google::protobuf::internal::WireFormatLite; |
| |
| // See the comment in json_util2_parser.cc for more information. |
| // |
| // The type traits in this file describe how to parse to a protobuf |
| // representation used by the JSON API, either via proto reflection or by |
| // emitting wire format to an output stream. |
| |
| // Helper alias templates to avoid needing to write `typename` in function |
| // signatures. |
| template <typename Traits> |
| using Msg = typename Traits::Msg; |
| |
| struct ParseProto2Descriptor : Proto2Descriptor { |
| // A message value that fields can be written to, but not read from. |
| class Msg { |
| public: |
| explicit Msg(Message* msg) : msg_(msg) {} |
| |
| private: |
| friend ParseProto2Descriptor; |
| Message* msg_; |
| // Because `msg` might already have oneofs set, we need to track which were |
| // set *during* the parse separately. |
| absl::flat_hash_set<int> parsed_oneofs_indices_; |
| absl::flat_hash_set<int> parsed_fields_; |
| }; |
| |
| static bool HasParsed(Field f, const Msg& msg, |
| bool allow_repeated_non_oneof) { |
| if (f->real_containing_oneof()) { |
| return msg.parsed_oneofs_indices_.contains( |
| f->real_containing_oneof()->index()); |
| } |
| if (allow_repeated_non_oneof) { |
| return false; |
| } |
| return msg.parsed_fields_.contains(f->number()); |
| } |
| |
| /// Functions for writing fields. /// |
| |
| // Marks a field as having been "seen". This will clear the field if it is |
| // the first occurrence thereof. |
| // |
| // All setters call this function automatically, but it may also be called |
| // eagerly to clear a pre-existing value that might not be overwritten, such |
| // as when parsing a repeated field. |
| static void RecordAsSeen(Field f, Msg& msg) { |
| bool inserted = msg.parsed_fields_.insert(f->number()).second; |
| if (inserted) { |
| msg.msg_->GetReflection()->ClearField(msg.msg_, f); |
| } |
| |
| if (f->real_containing_oneof() != nullptr) { |
| msg.parsed_oneofs_indices_.insert(f->real_containing_oneof()->index()); |
| } |
| } |
| |
| // Adds a new message and calls body on it. |
| // |
| // Body should have a signature `absl::Status(const Desc&, Msg&)`. |
| template <typename F> |
| static absl::Status NewMsg(Field f, Msg& msg, F body) { |
| RecordAsSeen(f, msg); |
| |
| Message* new_msg; |
| if (f->is_repeated()) { |
| new_msg = msg.msg_->GetReflection()->AddMessage(msg.msg_, f); |
| } else { |
| new_msg = msg.msg_->GetReflection()->MutableMessage(msg.msg_, f); |
| } |
| Msg wrapper(new_msg); |
| return body(*f->message_type(), wrapper); |
| } |
| |
| // Adds a new dynamic message with the given type name and calls body on it. |
| // |
| // Body should have a signature `absl::Status(const Desc&, Msg&)`. |
| template <typename F> |
| static absl::Status NewDynamic(Field f, const std::string& type_url, Msg& msg, |
| F body) { |
| RecordAsSeen(f, msg); |
| return WithDynamicType( |
| *f->containing_type(), type_url, [&](const Desc& desc) -> absl::Status { |
| DynamicMessageFactory factory; |
| std::unique_ptr<Message> dynamic(factory.GetPrototype(&desc)->New()); |
| Msg wrapper(dynamic.get()); |
| RETURN_IF_ERROR(body(desc, wrapper)); |
| |
| if (f->is_repeated()) { |
| msg.msg_->GetReflection()->AddString(msg.msg_, f, |
| dynamic->SerializeAsString()); |
| } else { |
| msg.msg_->GetReflection()->SetString(msg.msg_, f, |
| dynamic->SerializeAsString()); |
| } |
| return absl::OkStatus(); |
| }); |
| } |
| |
| static void SetFloat(Field f, Msg& msg, float x) { |
| RecordAsSeen(f, msg); |
| if (f->is_repeated()) { |
| msg.msg_->GetReflection()->AddFloat(msg.msg_, f, x); |
| } else { |
| msg.msg_->GetReflection()->SetFloat(msg.msg_, f, x); |
| } |
| } |
| |
| static void SetDouble(Field f, Msg& msg, double x) { |
| if (f->is_repeated()) { |
| msg.msg_->GetReflection()->AddDouble(msg.msg_, f, x); |
| } else { |
| msg.msg_->GetReflection()->SetDouble(msg.msg_, f, x); |
| } |
| } |
| |
| static void SetInt64(Field f, Msg& msg, int64_t x) { |
| RecordAsSeen(f, msg); |
| if (f->is_repeated()) { |
| msg.msg_->GetReflection()->AddInt64(msg.msg_, f, x); |
| } else { |
| msg.msg_->GetReflection()->SetInt64(msg.msg_, f, x); |
| } |
| } |
| |
| static void SetUInt64(Field f, Msg& msg, uint64_t x) { |
| RecordAsSeen(f, msg); |
| if (f->is_repeated()) { |
| msg.msg_->GetReflection()->AddUInt64(msg.msg_, f, x); |
| } else { |
| msg.msg_->GetReflection()->SetUInt64(msg.msg_, f, x); |
| } |
| } |
| |
| static void SetInt32(Field f, Msg& msg, int32 x) { |
| RecordAsSeen(f, msg); |
| if (f->is_repeated()) { |
| msg.msg_->GetReflection()->AddInt32(msg.msg_, f, x); |
| } else { |
| msg.msg_->GetReflection()->SetInt32(msg.msg_, f, x); |
| } |
| } |
| |
| static void SetUInt32(Field f, Msg& msg, uint32 x) { |
| RecordAsSeen(f, msg); |
| if (f->is_repeated()) { |
| msg.msg_->GetReflection()->AddUInt32(msg.msg_, f, x); |
| } else { |
| msg.msg_->GetReflection()->SetUInt32(msg.msg_, f, x); |
| } |
| } |
| |
| static void SetBool(Field f, Msg& msg, bool x) { |
| RecordAsSeen(f, msg); |
| if (f->is_repeated()) { |
| msg.msg_->GetReflection()->AddBool(msg.msg_, f, x); |
| } else { |
| msg.msg_->GetReflection()->SetBool(msg.msg_, f, x); |
| } |
| } |
| |
| static void SetString(Field f, Msg& msg, absl::string_view x) { |
| RecordAsSeen(f, msg); |
| if (f->is_repeated()) { |
| msg.msg_->GetReflection()->AddString(msg.msg_, f, std::string(x)); |
| } else { |
| msg.msg_->GetReflection()->SetString(msg.msg_, f, std::string(x)); |
| } |
| } |
| |
| static void SetEnum(Field f, Msg& msg, int32_t x) { |
| RecordAsSeen(f, msg); |
| if (f->is_repeated()) { |
| msg.msg_->GetReflection()->AddEnumValue(msg.msg_, f, x); |
| } else { |
| msg.msg_->GetReflection()->SetEnumValue(msg.msg_, f, x); |
| } |
| } |
| }; |
| |
| // Traits for proto3-ish deserialization. |
| // |
| // This includes a rudimentary proto serializer, since message fields are |
| // written directly instead of being reflectively written to a proto field. |
| // |
| // See MessageTraits for API docs. |
| struct ParseProto3Type : Proto3Type { |
| class Msg { |
| public: |
| explicit Msg(io::ZeroCopyOutputStream* stream) : stream_(stream) {} |
| |
| private: |
| friend ParseProto3Type; |
| io::CodedOutputStream stream_; |
| absl::flat_hash_set<int32_t> parsed_oneofs_indices_; |
| absl::flat_hash_set<int32_t> parsed_fields_; |
| }; |
| |
| static bool HasParsed(Field f, const Msg& msg, |
| bool allow_repeated_non_oneof) { |
| if (f->proto().oneof_index() != 0) { |
| return msg.parsed_oneofs_indices_.contains(f->proto().oneof_index()); |
| } |
| if (allow_repeated_non_oneof) { |
| return false; |
| } |
| return msg.parsed_fields_.contains(f->proto().number()); |
| } |
| |
| /// Functions for writing fields. /// |
| |
| static void RecordAsSeen(Field f, Msg& msg) { |
| msg.parsed_fields_.insert(f->proto().number()); |
| if (f->proto().oneof_index() != 0) { |
| msg.parsed_oneofs_indices_.insert(f->proto().oneof_index()); |
| } |
| } |
| |
| template <typename F> |
| static absl::Status NewMsg(Field f, Msg& msg, F body) { |
| return NewDynamic(f, f->proto().type_url(), msg, body); |
| } |
| |
| template <typename F> |
| static absl::Status NewDynamic(Field f, const std::string& type_url, Msg& msg, |
| F body) { |
| RecordAsSeen(f, msg); |
| return WithDynamicType( |
| f->parent(), type_url, [&](const Desc& desc) -> absl::Status { |
| if (f->proto().kind() == google::protobuf::Field::TYPE_GROUP) { |
| msg.stream_.WriteTag(f->proto().number() << 3 | |
| WireFormatLite::WIRETYPE_START_GROUP); |
| RETURN_IF_ERROR(body(desc, msg)); |
| msg.stream_.WriteTag(f->proto().number() << 3 | |
| WireFormatLite::WIRETYPE_END_GROUP); |
| return absl::OkStatus(); |
| } |
| |
| std::string out; |
| io::StringOutputStream stream(&out); |
| Msg new_msg(&stream); |
| RETURN_IF_ERROR(body(desc, new_msg)); |
| |
| new_msg.stream_.Trim(); // Should probably be called "Flush()". |
| absl::string_view written( |
| out.data(), static_cast<size_t>(new_msg.stream_.ByteCount())); |
| SetString(f, msg, written); |
| return absl::OkStatus(); |
| }); |
| } |
| |
| static void SetFloat(Field f, Msg& msg, float x) { |
| RecordAsSeen(f, msg); |
| msg.stream_.WriteTag(f->proto().number() << 3 | |
| WireFormatLite::WIRETYPE_FIXED32); |
| msg.stream_.WriteLittleEndian32(absl::bit_cast<uint32_t>(x)); |
| } |
| |
| static void SetDouble(Field f, Msg& msg, double x) { |
| RecordAsSeen(f, msg); |
| msg.stream_.WriteTag(f->proto().number() << 3 | |
| WireFormatLite::WIRETYPE_FIXED64); |
| msg.stream_.WriteLittleEndian64(absl::bit_cast<uint64_t>(x)); |
| } |
| |
| static void SetInt64(Field f, Msg& msg, int64_t x) { |
| SetInt<int64_t, google::protobuf::Field::TYPE_INT64, |
| google::protobuf::Field::TYPE_SFIXED64, |
| google::protobuf::Field::TYPE_SINT64>(f, msg, x); |
| } |
| |
| static void SetUInt64(Field f, Msg& msg, uint64_t x) { |
| SetInt<uint64_t, google::protobuf::Field::TYPE_UINT64, |
| google::protobuf::Field::TYPE_FIXED64, |
| google::protobuf::Field::TYPE_UNKNOWN>(f, msg, x); |
| } |
| |
| static void SetInt32(Field f, Msg& msg, int32_t x) { |
| SetInt<int32_t, google::protobuf::Field::TYPE_INT32, |
| google::protobuf::Field::TYPE_SFIXED32, |
| google::protobuf::Field::TYPE_SINT32>(f, msg, x); |
| } |
| |
| static void SetUInt32(Field f, Msg& msg, uint32_t x) { |
| SetInt<uint32_t, google::protobuf::Field::TYPE_UINT32, |
| google::protobuf::Field::TYPE_FIXED32, |
| google::protobuf::Field::TYPE_UNKNOWN>(f, msg, x); |
| } |
| |
| static void SetBool(Field f, Msg& msg, bool x) { |
| RecordAsSeen(f, msg); |
| msg.stream_.WriteTag(f->proto().number() << 3); |
| char b = x ? 0x01 : 0x00; |
| msg.stream_.WriteRaw(&b, 1); |
| } |
| |
| static void SetString(Field f, Msg& msg, absl::string_view x) { |
| RecordAsSeen(f, msg); |
| msg.stream_.WriteTag(f->proto().number() << 3 | |
| WireFormatLite::WIRETYPE_LENGTH_DELIMITED); |
| msg.stream_.WriteVarint64(static_cast<uint64_t>(x.size())); |
| msg.stream_.WriteRaw(x.data(), x.size()); |
| } |
| |
| static void SetEnum(Field f, Msg& msg, int32_t x) { |
| RecordAsSeen(f, msg); |
| msg.stream_.WriteTag(f->proto().number() << 3); |
| // Sign extension is deliberate here. |
| msg.stream_.WriteVarint32(x); |
| } |
| |
| private: |
| using Kind = google::protobuf::Field::Kind; |
| // Sets a field of *some* integer type, with the given kinds for the possible |
| // encodings. This avoids quadruplicating this code in the helpers for the |
| // four major integer types. |
| template <typename Int, Kind varint, Kind fixed, Kind zigzag> |
| static void SetInt(Field f, Msg& msg, Int x) { |
| RecordAsSeen(f, msg); |
| switch (f->proto().kind()) { |
| case zigzag: |
| // Regardless of the integer type, ZigZag64 will do the right thing, |
| // because ZigZag is not dependent on the width of the integer: it is |
| // always `2 * abs(n) + (n < 0)`. |
| x = static_cast<Int>( |
| internal::WireFormatLite::ZigZagEncode64(static_cast<int64_t>(x))); |
| ABSL_FALLTHROUGH_INTENDED; |
| case varint: |
| msg.stream_.WriteTag(f->proto().number() << 3 | |
| WireFormatLite::WIRETYPE_VARINT); |
| if (sizeof(Int) == 4) { |
| msg.stream_.WriteVarint32(static_cast<uint32_t>(x)); |
| } else { |
| msg.stream_.WriteVarint64(static_cast<uint64_t>(x)); |
| } |
| break; |
| case fixed: { |
| if (sizeof(Int) == 4) { |
| msg.stream_.WriteTag(f->proto().number() << 3 | |
| WireFormatLite::WIRETYPE_FIXED32); |
| msg.stream_.WriteLittleEndian32(static_cast<uint32_t>(x)); |
| } else { |
| msg.stream_.WriteTag(f->proto().number() << 3 | |
| WireFormatLite::WIRETYPE_FIXED64); |
| msg.stream_.WriteLittleEndian64(static_cast<uint64_t>(x)); |
| } |
| break; |
| } |
| default: { // Unreachable. |
| } |
| } |
| } |
| }; |
| } // namespace json_internal |
| } // namespace protobuf |
| } // namespace google |
| |
| #include "google/protobuf/port_undef.inc" |
| #endif // GOOGLE_PROTOBUF_JSON_INTERNAL_PARSER_TRAITS_H__ |