#ifndef GOOGLE_PROTOBUF_JSON_INTERNAL_UNTYPED_MESSAGE_H__ #define GOOGLE_PROTOBUF_JSON_INTERNAL_UNTYPED_MESSAGE_H__ #include "absl/log/absl_check.h" // 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 #ifndef GOOGLE_PROTOBUF_UITL_UNTYPED_MESSAGE_H__ #define GOOGLE_PROTOBUF_UITL_UNTYPED_MESSAGE_H__ #include #include #include #include #include #include #include #include "google/protobuf/type.pb.h" #include "absl/container/flat_hash_map.h" #include "absl/status/status.h" #include "absl/strings/str_format.h" #include "absl/strings/string_view.h" #include "absl/types/optional.h" #include "absl/types/span.h" #include "google/protobuf/descriptor.h" #include "google/protobuf/dynamic_message.h" #include "google/protobuf/io/coded_stream.h" #include "google/protobuf/message.h" #include "google/protobuf/util/type_resolver.h" #include "google/protobuf/wire_format.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 { struct SizeVisitor { template size_t operator()(const std::vector& x) { return x.size(); } template size_t operator()(const T& x) { return 1; } }; // A DescriptorPool-like type for caching lookups from a TypeResolver. // // This type and all of its nested types are thread-hostile. class ResolverPool { public: class Message; class Enum; class Field { public: Field(const Field&) = delete; Field& operator=(const Field&) = delete; absl::StatusOr MessageType() const; absl::StatusOr EnumType() const; const Message& parent() const { return *parent_; } const google::protobuf::Field& proto() const { return *raw_; } private: friend class ResolverPool; Field() = default; ResolverPool* pool_ = nullptr; const google::protobuf::Field* raw_ = nullptr; const Message* parent_ = nullptr; mutable const void* type_ = nullptr; }; class Message { public: Message(const Message&) = delete; Message& operator=(const Message&) = delete; absl::Span FieldsByIndex() const; const Field* FindField(absl::string_view name) const; const Field* FindField(int32_t number) const; const google::protobuf::Type& proto() const { return raw_; } ResolverPool* pool() const { return pool_; } private: friend class ResolverPool; explicit Message(ResolverPool* pool) : pool_(pool) {} ResolverPool* pool_; google::protobuf::Type raw_; mutable std::unique_ptr fields_; mutable absl::flat_hash_map fields_by_name_; mutable absl::flat_hash_map fields_by_number_; }; class Enum { public: Enum(const Enum&) = delete; Enum& operator=(const Enum&) = delete; const google::protobuf::Enum& proto() const { return raw_; } ResolverPool* pool() const { return pool_; } private: friend class ResolverPool; explicit Enum(ResolverPool* pool) : pool_(pool) {} ResolverPool* pool_; google::protobuf::Enum raw_; mutable absl::flat_hash_map values_; }; explicit ResolverPool(google::protobuf::util::TypeResolver* resolver) : resolver_(resolver) {} ResolverPool(const ResolverPool&) = delete; ResolverPool& operator=(const ResolverPool&) = delete; absl::StatusOr FindMessage(absl::string_view url); absl::StatusOr FindEnum(absl::string_view url); private: absl::flat_hash_map> messages_; absl::flat_hash_map> enums_; google::protobuf::util::TypeResolver* resolver_; }; // A parsed wire-format proto that uses TypeReslover for parsing. // // This type is an implementation detail of the JSON parser. class UntypedMessage final { public: // New nominal type instead of `bool` to avoid vector shenanigans. enum Bool : unsigned char { kTrue, kFalse }; using Value = std::variant, std::vector, std::vector, std::vector, std::vector, std::vector, std::vector, std::vector, std::vector>; UntypedMessage(const UntypedMessage&) = delete; UntypedMessage& operator=(const UntypedMessage&) = delete; UntypedMessage(UntypedMessage&&) = default; UntypedMessage& operator=(UntypedMessage&&) = default; // Tries to parse a proto with the given descriptor from an input stream. static absl::StatusOr ParseFromStream( const ResolverPool::Message* desc, io::CodedInputStream& stream) { UntypedMessage msg(std::move(desc)); RETURN_IF_ERROR(msg.Decode(stream)); return std::move(msg); } // Returns the number of elements in a field by number. // // Optional fields are treated like repeated fields with one or zero elements. size_t Count(int32_t field_number) const { auto it = fields_.find(field_number); if (it == fields_.end()) { return 0; } return std::visit(SizeVisitor{}, *it->second); } // Returns the contents of a field by number. // // Optional fields are treated like repeated fields with one or zero elements. // If the field is not set, returns an empty span. // // If `T` is the wrong type, this function crashes. template absl::Span Get(int32_t field_number) const { auto it = fields_.find(field_number); if (it == fields_.end()) { return {}; } if (auto* val = std::get_if(it->second.get())) { return absl::Span(val, 1); } else if (auto* vec = std::get_if>(it->second.get())) { return *vec; } else { ABSL_CHECK(false) << "wrong type for UntypedMessage::Get(" << field_number << ")"; return {}; // avoid compiler warning. } } const ResolverPool::Message& desc() const { return *desc_; } private: enum Cardinality { kSingular, kRepeated }; explicit UntypedMessage(const ResolverPool::Message* desc) : desc_(desc) {} absl::Status Decode(io::CodedInputStream& stream, absl::optional current_group = absl::nullopt); absl::Status DecodeVarint(io::CodedInputStream& stream, const ResolverPool::Field& field); absl::Status Decode64Bit(io::CodedInputStream& stream, const ResolverPool::Field& field); absl::Status Decode32Bit(io::CodedInputStream& stream, const ResolverPool::Field& field); absl::Status DecodeDelimited(io::CodedInputStream& stream, const ResolverPool::Field& field); template absl::Status InsertField(const ResolverPool::Field& field, T&& value); const ResolverPool::Message* desc_; // We use std::unique_ptr here to work around a compiler error with older // versions of Clang when this is built for C++20. Once we can drop support // for Clang 14 and below, we should drop the extra indirection and have just // Value as the value type of the map. absl::flat_hash_map> fields_; }; } // namespace json_internal } // namespace protobuf } // namespace google #include "google/protobuf/port_undef.inc" #endif // GOOGLE_PROTOBUF_UITL_UNTYPED_MESSAGE_H__ #endif // GOOGLE_PROTOBUF_JSON_INTERNAL_UNTYPED_MESSAGE_H__