#ifndef GOOGLE_PROTOBUF_REFLECTION_VISIT_FIELDS_H__ #define GOOGLE_PROTOBUF_REFLECTION_VISIT_FIELDS_H__ #include #include #include #include "absl/base/attributes.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(static_cast(lhs) | static_cast(rhs)); } #ifdef __cpp_if_constexpr template void VisitFields(MessageT& message, CallbackFn&& func, FieldMask mask = FieldMask::kAll); class ReflectionVisit final { public: template static void VisitFields(MessageT& message, CallbackFn&& func, FieldMask mask); template static void VisitMessageFields(const Message& message, CallbackFn&& func); template 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 (PROTOBUF_PREDICT_TRUE(mask == FieldMask::kAll)) return true; return (static_cast(mask) & (1 << cpptype)) != 0; } template 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>(message, field); \ if (rep.size() == 0) continue; \ func(internal::Repeated##NAME##DynamicFieldInfo{ \ 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 (PROTOBUF_PREDICT_TRUE(!field->is_map())) { \ /* Handle repeated fields. */ \ const auto& rep = reflection->GetRawNonOneof>( \ message, field); \ if (rep.size() == 0) continue; \ func(internal::Repeated##NAME##DynamicFieldInfo{ \ reflection, message, field, rep}); \ } else { \ /* Handle map fields. */ \ const auto& map = \ reflection->GetRawNonOneof(message, field); \ if (map.size() == 0) continue; /* NOLINT */ \ const Descriptor* desc = field->message_type(); \ func(internal::MapDynamicFieldInfo{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>(message, field); \ if (rep.size() == 0) continue; \ func(internal::Repeated##NAME##DynamicFieldInfo{ \ reflection, message, field, rep}); \ } switch (cpp::EffectiveStringCType(field)) { default: case FieldOptions::STRING: 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( &message, schema.oneof_case_offset_); // Equivalent to: !HasOneofField(message, field) if (static_cast(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{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{ reflection, message, field}); break; case FieldDescriptor::TYPE_BYTES: case FieldDescriptor::TYPE_STRING: { auto ctype = cpp::EffectiveStringCType(field); if (ctype == FieldOptions::CORD) { func(CordDynamicFieldInfo{reflection, message, field}); } else { func(StringDynamicFieldInfo{reflection, message, field}); } break; } default: internal::Unreachable(); break; #undef PROTOBUF_HANDLE_CASE } } else { auto index = has_bits_indices[i]; bool check_hasbits = has_bits && index != static_cast(-1); if (PROTOBUF_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{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{ reflection, message, field}); break; case FieldDescriptor::TYPE_BYTES: case FieldDescriptor::TYPE_STRING: { auto ctype = cpp::EffectiveStringCType(field); if (ctype == FieldOptions::CORD) { func(CordDynamicFieldInfo{reflection, message, field}); } else { func(StringDynamicFieldInfo{reflection, message, field}); } 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(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{ \ 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{ 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{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{ ext, number, is_mset}); break; } case FieldDescriptor::TYPE_BYTES: case FieldDescriptor::TYPE_STRING: func(internal::StringDynamicExtensionInfo{ext, number}); break; default: internal::Unreachable(); break; #undef PROTOBUF_HANDLE_CASE } } }, ExtensionSet::Prefetch{}); } template 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(it)); } } else { func(info.Get()); } } }, FieldMask::kMessage); } template 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(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 void VisitFields(MessageT& message, CallbackFn&& func, FieldMask mask) { ReflectionVisit::VisitFields(message, std::forward(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 void VisitMessageFields(const Message& message, CallbackFn&& func) { ReflectionVisit::VisitMessageFields(message, std::forward(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 void VisitMutableMessageFields(Message& message, CallbackFn&& func) { ReflectionVisit::VisitMessageFields(message, std::forward(func)); } #endif // __cpp_if_constexpr } // namespace internal } // namespace protobuf } // namespace google #include "google/protobuf/port_undef.inc" #endif // GOOGLE_PROTOBUF_REFLECTION_VISIT_FIELDS_H__