blob: 94303033002454c2b70b203b3a2f6642de837ee4 [file] [log] [blame]
#ifndef GOOGLE_PROTOBUF_REFLECTION_VISIT_FIELDS_H__
#define GOOGLE_PROTOBUF_REFLECTION_VISIT_FIELDS_H__
#include <cstdint>
#include <string>
#include <utility>
#include "absl/base/attributes.h"
#include "absl/base/optimization.h"
#include "absl/log/absl_check.h"
#include "absl/strings/cord.h"
#include "google/protobuf/descriptor.h"
#include "google/protobuf/descriptor.pb.h"
#include "google/protobuf/descriptor_lite.h"
#include "google/protobuf/extension_set.h"
#include "google/protobuf/generated_message_reflection.h"
#include "google/protobuf/message.h"
#include "google/protobuf/port.h"
#include "google/protobuf/reflection.h"
#include "google/protobuf/reflection_visit_field_info.h"
#include "google/protobuf/repeated_field.h"
#include "google/protobuf/repeated_ptr_field.h"
// Must be the last include.
#include "google/protobuf/port_def.inc" // NOLINT
namespace google {
namespace protobuf {
namespace internal {
enum class FieldMask : uint32_t {
kInt32 = 1 << FieldDescriptor::CPPTYPE_INT32,
kInt64 = 1 << FieldDescriptor::CPPTYPE_INT64,
kUInt32 = 1 << FieldDescriptor::CPPTYPE_UINT32,
kUInt64 = 1 << FieldDescriptor::CPPTYPE_UINT64,
kDouble = 1 << FieldDescriptor::CPPTYPE_DOUBLE,
kFloat = 1 << FieldDescriptor::CPPTYPE_FLOAT,
kBool = 1 << FieldDescriptor::CPPTYPE_BOOL,
kEnum = 1 << FieldDescriptor::CPPTYPE_ENUM,
kString = 1 << FieldDescriptor::CPPTYPE_STRING,
kMessage = 1 << FieldDescriptor::CPPTYPE_MESSAGE,
kPrimitive =
kInt32 | kInt64 | kUInt32 | kUInt64 | kDouble | kFloat | kBool | kEnum,
kAll = 0xFFFFFFFFu,
};
inline FieldMask operator|(FieldMask lhs, FieldMask rhs) {
return static_cast<FieldMask>(static_cast<uint32_t>(lhs) |
static_cast<uint32_t>(rhs));
}
#ifdef __cpp_if_constexpr
template <typename MessageT, typename CallbackFn>
void VisitFields(MessageT& message, CallbackFn&& func,
FieldMask mask = FieldMask::kAll);
class ReflectionVisit final {
public:
template <typename MessageT, typename CallbackFn>
static void VisitFields(MessageT& message, CallbackFn&& func, FieldMask mask);
template <typename CallbackFn>
static void VisitMessageFields(const Message& message, CallbackFn&& func);
template <typename CallbackFn>
static void VisitMessageFields(Message& message, CallbackFn&& func);
private:
static const internal::ReflectionSchema& GetSchema(
const Reflection* reflection) {
return reflection->schema_;
}
static const Descriptor* GetDescriptor(const Reflection* reflection) {
return reflection->descriptor_;
}
static const internal::ExtensionSet& ExtensionSet(
const Reflection* reflection, const Message& message) {
return reflection->GetExtensionSet(message);
}
static internal::ExtensionSet& ExtensionSet(const Reflection* reflection,
Message& message) {
return *reflection->MutableExtensionSet(&message);
}
};
inline bool ShouldVisit(FieldMask mask, FieldDescriptor::CppType cpptype) {
if (ABSL_PREDICT_TRUE(mask == FieldMask::kAll)) return true;
return (static_cast<uint32_t>(mask) & (1 << cpptype)) != 0;
}
template <typename MessageT, typename CallbackFn>
void ReflectionVisit::VisitFields(MessageT& message, CallbackFn&& func,
FieldMask mask) {
const Reflection* reflection = message.GetReflection();
const auto& schema = GetSchema(reflection);
ABSL_CHECK(!schema.HasWeakFields()) << "weak fields are not supported";
// See Reflection::ListFields for the optimization.
const uint32_t* const has_bits =
schema.HasHasbits() ? reflection->GetHasBits(message) : nullptr;
const uint32_t* const has_bits_indices = schema.has_bit_indices_;
const Descriptor* descriptor = GetDescriptor(reflection);
const int field_count = descriptor->field_count();
for (int i = 0; i < field_count; i++) {
const FieldDescriptor* field = descriptor->field(i);
ABSL_DCHECK(!field->options().weak()) << "weak fields are not supported";
if (!ShouldVisit(mask, field->cpp_type())) continue;
if (field->is_repeated()) {
switch (field->type()) {
#define PROTOBUF_HANDLE_REPEATED_CASE(TYPE, CPPTYPE, NAME) \
case FieldDescriptor::TYPE_##TYPE: { \
ABSL_DCHECK(!field->is_map()); \
const auto& rep = \
reflection->GetRawNonOneof<RepeatedField<CPPTYPE>>(message, field); \
if (rep.size() == 0) continue; \
func(internal::Repeated##NAME##DynamicFieldInfo<MessageT>{ \
reflection, message, field, rep}); \
break; \
}
PROTOBUF_HANDLE_REPEATED_CASE(DOUBLE, double, Double);
PROTOBUF_HANDLE_REPEATED_CASE(FLOAT, float, Float);
PROTOBUF_HANDLE_REPEATED_CASE(INT64, int64_t, Int64);
PROTOBUF_HANDLE_REPEATED_CASE(UINT64, uint64_t, UInt64);
PROTOBUF_HANDLE_REPEATED_CASE(INT32, int32_t, Int32);
PROTOBUF_HANDLE_REPEATED_CASE(FIXED64, uint64_t, Fixed64);
PROTOBUF_HANDLE_REPEATED_CASE(FIXED32, uint32_t, Fixed32);
PROTOBUF_HANDLE_REPEATED_CASE(BOOL, bool, Bool);
PROTOBUF_HANDLE_REPEATED_CASE(UINT32, uint32_t, UInt32);
PROTOBUF_HANDLE_REPEATED_CASE(ENUM, int, Enum);
PROTOBUF_HANDLE_REPEATED_CASE(SFIXED32, int32_t, SFixed32);
PROTOBUF_HANDLE_REPEATED_CASE(SFIXED64, int64_t, SFixed64);
PROTOBUF_HANDLE_REPEATED_CASE(SINT32, int32_t, SInt32);
PROTOBUF_HANDLE_REPEATED_CASE(SINT64, int64_t, SInt64);
#define PROTOBUF_HANDLE_REPEATED_PTR_CASE(TYPE, CPPTYPE, NAME) \
case FieldDescriptor::TYPE_##TYPE: { \
if (ABSL_PREDICT_TRUE(!field->is_map())) { \
/* Handle repeated fields. */ \
const auto& rep = reflection->GetRawNonOneof<RepeatedPtrField<CPPTYPE>>( \
message, field); \
if (rep.size() == 0) continue; \
func(internal::Repeated##NAME##DynamicFieldInfo<MessageT>{ \
reflection, message, field, rep}); \
} else { \
/* Handle map fields. */ \
const auto& map = \
reflection->GetRawNonOneof<MapFieldBase>(message, field); \
if (map.size() == 0) continue; /* NOLINT */ \
const Descriptor* desc = field->message_type(); \
func(internal::MapDynamicFieldInfo<MessageT>{reflection, message, field, \
desc->map_key(), \
desc->map_value(), map}); \
} \
break; \
}
PROTOBUF_HANDLE_REPEATED_PTR_CASE(MESSAGE, Message, Message);
PROTOBUF_HANDLE_REPEATED_PTR_CASE(GROUP, Message, Group);
case FieldDescriptor::TYPE_BYTES:
case FieldDescriptor::TYPE_STRING:
#define PROTOBUF_IMPL_STRING_CASE(CPPTYPE, NAME) \
{ \
const auto& rep = \
reflection->GetRawNonOneof<RepeatedPtrField<CPPTYPE>>(message, field); \
if (rep.size() == 0) continue; \
func(internal::Repeated##NAME##DynamicFieldInfo<MessageT>{ \
reflection, message, field, rep}); \
}
switch (field->cpp_string_type()) {
case FieldDescriptor::CppStringType::kCord:
case FieldDescriptor::CppStringType::kView:
case FieldDescriptor::CppStringType::kString:
PROTOBUF_IMPL_STRING_CASE(std::string, String);
break;
}
break;
default:
internal::Unreachable();
break;
}
#undef PROTOBUF_HANDLE_REPEATED_CASE
#undef PROTOBUF_HANDLE_REPEATED_PTR_CASE
#undef PROTOBUF_IMPL_STRING_CASE
} else if (schema.InRealOneof(field)) {
const OneofDescriptor* containing_oneof = field->containing_oneof();
const uint32_t* const oneof_case_array =
internal::GetConstPointerAtOffset<uint32_t>(
&message, schema.oneof_case_offset_);
// Equivalent to: !HasOneofField(message, field)
if (static_cast<int64_t>(oneof_case_array[containing_oneof->index()]) !=
field->number()) {
continue;
}
switch (field->type()) {
#define PROTOBUF_HANDLE_CASE(TYPE, NAME) \
case FieldDescriptor::TYPE_##TYPE: \
func(internal::NAME##DynamicFieldInfo<MessageT, true>{reflection, message, \
field}); \
break;
PROTOBUF_HANDLE_CASE(DOUBLE, Double);
PROTOBUF_HANDLE_CASE(FLOAT, Float);
PROTOBUF_HANDLE_CASE(INT64, Int64);
PROTOBUF_HANDLE_CASE(UINT64, UInt64);
PROTOBUF_HANDLE_CASE(INT32, Int32);
PROTOBUF_HANDLE_CASE(FIXED64, Fixed64);
PROTOBUF_HANDLE_CASE(FIXED32, Fixed32);
PROTOBUF_HANDLE_CASE(BOOL, Bool);
PROTOBUF_HANDLE_CASE(UINT32, UInt32);
PROTOBUF_HANDLE_CASE(ENUM, Enum);
PROTOBUF_HANDLE_CASE(SFIXED32, SFixed32);
PROTOBUF_HANDLE_CASE(SFIXED64, SFixed64);
PROTOBUF_HANDLE_CASE(SINT32, SInt32);
PROTOBUF_HANDLE_CASE(SINT64, SInt64);
case FieldDescriptor::TYPE_MESSAGE:
case FieldDescriptor::TYPE_GROUP:
func(internal::MessageDynamicFieldInfo<MessageT, true>{
reflection, message, field});
break;
case FieldDescriptor::TYPE_BYTES:
case FieldDescriptor::TYPE_STRING: {
switch (field->cpp_string_type()) {
case FieldDescriptor::CppStringType::kCord:
func(CordDynamicFieldInfo<MessageT, true>{reflection, message,
field});
break;
case FieldDescriptor::CppStringType::kString:
case FieldDescriptor::CppStringType::kView:
func(StringDynamicFieldInfo<MessageT, true>{reflection, message,
field});
break;
}
break;
}
default:
internal::Unreachable();
break;
#undef PROTOBUF_HANDLE_CASE
}
} else {
auto index = has_bits_indices[i];
bool check_hasbits = has_bits && index != static_cast<uint32_t>(-1);
if (ABSL_PREDICT_TRUE(check_hasbits)) {
if ((has_bits[index / 32] & (1u << (index % 32))) == 0) continue;
} else {
// Skip if it has default values.
if (!reflection->HasFieldSingular(message, field)) continue;
}
switch (field->type()) {
#define PROTOBUF_HANDLE_CASE(TYPE, NAME) \
case FieldDescriptor::TYPE_##TYPE: \
func(internal::NAME##DynamicFieldInfo<MessageT, false>{reflection, \
message, field}); \
break;
PROTOBUF_HANDLE_CASE(DOUBLE, Double);
PROTOBUF_HANDLE_CASE(FLOAT, Float);
PROTOBUF_HANDLE_CASE(INT64, Int64);
PROTOBUF_HANDLE_CASE(UINT64, UInt64);
PROTOBUF_HANDLE_CASE(INT32, Int32);
PROTOBUF_HANDLE_CASE(FIXED64, Fixed64);
PROTOBUF_HANDLE_CASE(FIXED32, Fixed32);
PROTOBUF_HANDLE_CASE(BOOL, Bool);
PROTOBUF_HANDLE_CASE(UINT32, UInt32);
PROTOBUF_HANDLE_CASE(ENUM, Enum);
PROTOBUF_HANDLE_CASE(SFIXED32, SFixed32);
PROTOBUF_HANDLE_CASE(SFIXED64, SFixed64);
PROTOBUF_HANDLE_CASE(SINT32, SInt32);
PROTOBUF_HANDLE_CASE(SINT64, SInt64);
case FieldDescriptor::TYPE_MESSAGE:
case FieldDescriptor::TYPE_GROUP:
func(internal::MessageDynamicFieldInfo<MessageT, false>{
reflection, message, field});
break;
case FieldDescriptor::TYPE_BYTES:
case FieldDescriptor::TYPE_STRING: {
switch (field->cpp_string_type()) {
case FieldDescriptor::CppStringType::kCord:
func(CordDynamicFieldInfo<MessageT, false>{reflection, message,
field});
break;
case FieldDescriptor::CppStringType::kString:
case FieldDescriptor::CppStringType::kView:
func(StringDynamicFieldInfo<MessageT, false>{reflection, message,
field});
break;
}
break;
}
default:
internal::Unreachable();
break;
#undef PROTOBUF_HANDLE_CASE
}
}
}
if (!schema.HasExtensionSet()) return;
auto& set = ExtensionSet(reflection, message);
auto* extendee = reflection->descriptor_;
auto* pool = reflection->descriptor_pool_;
set.ForEach(
[&](int number, auto& ext) {
ABSL_DCHECK_GT(ext.type, 0);
ABSL_DCHECK_LE(ext.type, FieldDescriptor::MAX_TYPE);
if (!ShouldVisit(mask,
FieldDescriptor::TypeToCppType(
static_cast<FieldDescriptor::Type>(ext.type)))) {
return;
}
if (ext.is_repeated) {
if (ext.GetSize() == 0) return;
switch (ext.type) {
#define PROTOBUF_HANDLE_CASE(TYPE, NAME) \
case FieldDescriptor::TYPE_##TYPE: \
func(internal::Repeated##NAME##DynamicExtensionInfo<decltype(ext)>{ \
ext, number}); \
break;
PROTOBUF_HANDLE_CASE(DOUBLE, Double);
PROTOBUF_HANDLE_CASE(FLOAT, Float);
PROTOBUF_HANDLE_CASE(INT64, Int64);
PROTOBUF_HANDLE_CASE(UINT64, UInt64);
PROTOBUF_HANDLE_CASE(INT32, Int32);
PROTOBUF_HANDLE_CASE(FIXED64, Fixed64);
PROTOBUF_HANDLE_CASE(FIXED32, Fixed32);
PROTOBUF_HANDLE_CASE(BOOL, Bool);
PROTOBUF_HANDLE_CASE(UINT32, UInt32);
PROTOBUF_HANDLE_CASE(ENUM, Enum);
PROTOBUF_HANDLE_CASE(SFIXED32, SFixed32);
PROTOBUF_HANDLE_CASE(SFIXED64, SFixed64);
PROTOBUF_HANDLE_CASE(SINT32, SInt32);
PROTOBUF_HANDLE_CASE(SINT64, SInt64);
PROTOBUF_HANDLE_CASE(MESSAGE, Message);
PROTOBUF_HANDLE_CASE(GROUP, Group);
case FieldDescriptor::TYPE_BYTES:
case FieldDescriptor::TYPE_STRING:
func(internal::RepeatedStringDynamicExtensionInfo<decltype(ext)>{
ext, number});
break;
default:
internal::Unreachable();
break;
#undef PROTOBUF_HANDLE_CASE
}
} else {
if (ext.is_cleared) return;
switch (ext.type) {
#define PROTOBUF_HANDLE_CASE(TYPE, NAME) \
case FieldDescriptor::TYPE_##TYPE: \
func(internal::NAME##DynamicExtensionInfo<decltype(ext)>{ext, number}); \
break;
PROTOBUF_HANDLE_CASE(DOUBLE, Double);
PROTOBUF_HANDLE_CASE(FLOAT, Float);
PROTOBUF_HANDLE_CASE(INT64, Int64);
PROTOBUF_HANDLE_CASE(UINT64, UInt64);
PROTOBUF_HANDLE_CASE(INT32, Int32);
PROTOBUF_HANDLE_CASE(FIXED64, Fixed64);
PROTOBUF_HANDLE_CASE(FIXED32, Fixed32);
PROTOBUF_HANDLE_CASE(BOOL, Bool);
PROTOBUF_HANDLE_CASE(UINT32, UInt32);
PROTOBUF_HANDLE_CASE(ENUM, Enum);
PROTOBUF_HANDLE_CASE(SFIXED32, SFixed32);
PROTOBUF_HANDLE_CASE(SFIXED64, SFixed64);
PROTOBUF_HANDLE_CASE(SINT32, SInt32);
PROTOBUF_HANDLE_CASE(SINT64, SInt64);
PROTOBUF_HANDLE_CASE(GROUP, Group);
case FieldDescriptor::TYPE_MESSAGE: {
const FieldDescriptor* field =
ext.descriptor != nullptr
? ext.descriptor
: pool->FindExtensionByNumber(extendee, number);
ABSL_DCHECK_EQ(field->number(), number);
bool is_mset =
field->containing_type()->options().message_set_wire_format();
func(internal::MessageDynamicExtensionInfo<decltype(ext)>{
ext, number, is_mset});
break;
}
case FieldDescriptor::TYPE_BYTES:
case FieldDescriptor::TYPE_STRING:
func(internal::StringDynamicExtensionInfo<decltype(ext)>{ext,
number});
break;
default:
internal::Unreachable();
break;
#undef PROTOBUF_HANDLE_CASE
}
}
},
ExtensionSet::Prefetch{});
}
template <typename CallbackFn>
void ReflectionVisit::VisitMessageFields(const Message& message,
CallbackFn&& func) {
ReflectionVisit::VisitFields(
message,
[&](auto info) {
if constexpr (info.is_map) {
auto value_type = info.value_type();
if (value_type != FieldDescriptor::TYPE_MESSAGE &&
value_type != FieldDescriptor::TYPE_GROUP) {
return;
}
info.VisitElements([&](auto key, auto val) {
if constexpr (val.cpp_type == FieldDescriptor::CPPTYPE_MESSAGE) {
func(val.Get());
}
});
} else if constexpr (info.cpp_type ==
FieldDescriptor::CPPTYPE_MESSAGE) {
if constexpr (info.is_repeated) {
for (const auto& it : info.Get()) {
func(DownCastMessage<Message>(it));
}
} else {
func(info.Get());
}
}
},
FieldMask::kMessage);
}
template <typename CallbackFn>
void ReflectionVisit::VisitMessageFields(Message& message, CallbackFn&& func) {
ReflectionVisit::VisitFields(
message,
[&](auto info) {
if constexpr (info.is_map) {
auto value_type = info.value_type();
if (value_type != FieldDescriptor::TYPE_MESSAGE &&
value_type != FieldDescriptor::TYPE_GROUP) {
return;
}
info.VisitElements([&](auto key, auto val) {
if constexpr (val.cpp_type == FieldDescriptor::CPPTYPE_MESSAGE) {
func(*val.Mutable());
}
});
} else if constexpr (info.cpp_type ==
FieldDescriptor::CPPTYPE_MESSAGE) {
if constexpr (info.is_repeated) {
for (auto& it : info.Mutable()) {
func(DownCastMessage<Message>(it));
}
} else {
func(info.Mutable());
}
}
},
FieldMask::kMessage);
}
// Visits present fields of "message" and calls the callback function "func".
// Skips fields whose ctypes are missing in "mask".
template <typename MessageT, typename CallbackFn>
void VisitFields(MessageT& message, CallbackFn&& func, FieldMask mask) {
ReflectionVisit::VisitFields(message, std::forward<CallbackFn>(func), mask);
}
// Visits message fields of "message" and calls "func". Expects "func" to
// accept const Message&. Note the following divergence from VisitFields.
//
// --Each of N elements of a repeated message field is visited (total N).
// --Each of M elements of a map field whose value type is message are visited
// (total M).
// --A map field whose value type is not message is ignored.
//
// This is a helper API built on top of VisitFields to hide specifics about
// extensions, repeated fields, etc.
template <typename CallbackFn>
void VisitMessageFields(const Message& message, CallbackFn&& func) {
ReflectionVisit::VisitMessageFields(message, std::forward<CallbackFn>(func));
}
// Same as VisitMessageFields above but expects "func" to accept Message&. This
// is useful when mutable access is required. As mutable access can be
// expensive, use it only if it's necessary.
template <typename CallbackFn>
void VisitMutableMessageFields(Message& message, CallbackFn&& func) {
ReflectionVisit::VisitMessageFields(message, std::forward<CallbackFn>(func));
}
#endif // __cpp_if_constexpr
} // namespace internal
} // namespace protobuf
} // namespace google
#include "google/protobuf/port_undef.inc"
#endif // GOOGLE_PROTOBUF_REFLECTION_VISIT_FIELDS_H__