blob: 1b4ad8fbaffe159ba7b811c24f0a111fd109a224 [file] [log] [blame]
// 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_UITL_UNTYPED_MESSAGE_H__
#define GOOGLE_PROTOBUF_UITL_UNTYPED_MESSAGE_H__
#include <cstddef>
#include <cstdint>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "google/protobuf/type.pb.h"
#include "google/protobuf/descriptor.h"
#include "google/protobuf/dynamic_message.h"
#include "google/protobuf/message.h"
#include "google/protobuf/wire_format.h"
#include "google/protobuf/wire_format_lite.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 "absl/types/variant.h"
#include "google/protobuf/io/coded_stream.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 {
struct SizeVisitor {
template <typename T>
size_t operator()(const std::vector<T>& x) {
return x.size();
}
template <typename T>
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<const Message*> MessageType() const;
absl::StatusOr<const Enum*> 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<const Field> 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<Field[]> fields_;
mutable absl::flat_hash_map<absl::string_view, const Field*>
fields_by_name_;
mutable absl::flat_hash_map<int32_t, const Field*> 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<absl::string_view, google::protobuf::EnumValue*>
values_;
};
explicit ResolverPool(google::protobuf::util::TypeResolver* resolver)
: resolver_(resolver) {}
ResolverPool(const ResolverPool&) = delete;
ResolverPool& operator=(const ResolverPool&) = delete;
absl::StatusOr<const Message*> FindMessage(absl::string_view url);
absl::StatusOr<const Enum*> FindEnum(absl::string_view url);
private:
absl::flat_hash_map<std::string, std::unique_ptr<Message>> messages_;
absl::flat_hash_map<std::string, std::unique_ptr<Enum>> 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<bool> shenanigans.
enum Bool : unsigned char { kTrue, kFalse };
using Value = absl::variant<Bool, int32_t, uint32_t, int64_t, uint64_t, float,
double, std::string, UntypedMessage,
//
std::vector<Bool>, std::vector<int32_t>,
std::vector<uint32_t>, std::vector<int64_t>,
std::vector<uint64_t>, std::vector<float>,
std::vector<double>, std::vector<std::string>,
std::vector<UntypedMessage>>;
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<UntypedMessage> 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 absl::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 <typename T>
absl::Span<const T> Get(int32_t field_number) const {
auto it = fields_.find(field_number);
if (it == fields_.end()) {
return {};
}
if (auto* val = absl::get_if<T>(&it->second)) {
return absl::Span<const T>(val, 1);
} else if (auto* vec = absl::get_if<std::vector<T>>(&it->second)) {
return *vec;
} else {
GOOGLE_CHECK(false) << "wrong type for UntypedMessage::Get(" << field_number
<< ")";
}
}
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<int32_t> 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 <typename T>
absl::Status InsertField(const ResolverPool::Field& field, T value);
const ResolverPool::Message* desc_;
absl::flat_hash_map<int32_t, Value> fields_;
};
} // namespace json_internal
} // namespace protobuf
} // namespace google
#include "google/protobuf/port_undef.inc"
#endif // GOOGLE_PROTOBUF_UITL_UNTYPED_MESSAGE_H__