blob: cfe74a26cffc58c77a0f250cd592db4b04272d29 [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_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__