// Protocol Buffers - Google's data interchange format // Copyright 2024 Google LLC. 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_HPB_BACKEND_UPB_EXTENSION_H__ #define GOOGLE_PROTOBUF_HPB_BACKEND_UPB_EXTENSION_H__ #include #include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/strings/string_view.h" #include "hpb/backend/upb/interop.h" #include "hpb/internal/message_lock.h" #include "hpb/internal/template_help.h" #include "hpb/status.h" #include "upb/base/string_view.h" #include "upb/mem/arena.h" #include "upb/message/array.h" #include "upb/message/message.h" #include "upb/mini_table/extension.h" #include "upb/mini_table/extension_registry.h" namespace hpb { class ExtensionRegistry; template class RepeatedField; namespace internal { template class ExtensionIdentifier; absl::Status MoveExtension(upb_Message* message, upb_Arena* message_arena, const upb_MiniTableExtension* ext, upb_Message* extension, upb_Arena* extension_arena); absl::Status SetExtension(upb_Message* message, upb_Arena* message_arena, const upb_MiniTableExtension* ext, const upb_Message* extension); void SetAliasExtension(upb_Message* message, upb_Arena* message_arena, const upb_MiniTableExtension* ext, upb_Message* extension, upb_Arena* extension_arena); /** * Trait that maps upb extension types to the corresponding * return value: ubp_MessageValue. * * All partial specializations must have: * - DefaultType: the type of the default value. * - ReturnType: the type of the return value. * - kGetter: the corresponding upb_MessageValue upb_Message_GetExtension* func */ template struct UpbExtensionTrait; template struct UpbExtensionTrait> { using ReturnType = typename RepeatedField::CProxy; using DefaultType = std::false_type; template static constexpr ReturnType Get(Msg message, const Id& id) { auto upb_arr = upb_Message_GetExtensionArray( hpb::interop::upb::GetMessage(message), id.mini_table_ext()); return ReturnType(upb_arr, hpb::interop::upb::GetArena(message)); } }; #define UPB_EXT_PRIMITIVE(CppType, UpbFunc) \ template <> \ struct UpbExtensionTrait { \ using DefaultType = CppType; \ using ReturnType = CppType; \ \ template \ static constexpr ReturnType Get(Msg message, const Id& id) { \ auto default_val = internal::PrivateAccess::GetDefaultValue(id); \ return upb_Message_GetExtension##UpbFunc( \ interop::upb::GetMessage(message), id.mini_table_ext(), \ default_val); \ } \ template \ static absl::Status Set(Msg message, const Id& id, CppType value) { \ bool res = upb_Message_SetExtension##UpbFunc( \ interop::upb::GetMessage(message), id.mini_table_ext(), value, \ interop::upb::GetArena(message)); \ return res ? absl::OkStatus() : MessageAllocationError(); \ } \ } UPB_EXT_PRIMITIVE(bool, Bool); UPB_EXT_PRIMITIVE(int32_t, Int32); UPB_EXT_PRIMITIVE(int64_t, Int64); UPB_EXT_PRIMITIVE(uint32_t, UInt32); UPB_EXT_PRIMITIVE(uint64_t, UInt64); UPB_EXT_PRIMITIVE(float, Float); UPB_EXT_PRIMITIVE(double, Double); #undef UPB_EXT_PRIMITIVE template <> struct UpbExtensionTrait { using DefaultType = absl::string_view; using ReturnType = absl::string_view; template static constexpr ReturnType Get(Msg message, const Id& id) { auto default_val = hpb::internal::PrivateAccess::GetDefaultValue(id); upb_StringView result = upb_Message_GetExtensionString( hpb::interop::upb::GetMessage(message), id.mini_table_ext(), upb_StringView_FromDataAndSize(default_val.data(), default_val.size())); return absl::string_view(result.data, result.size); } template static absl::Status Set(Msg message, const Id& id, absl::string_view value) { auto upb_value = upb_StringView_FromDataAndSize(value.data(), value.size()); bool res = upb_Message_SetExtensionString(interop::upb::GetMessage(message), id.mini_table_ext(), upb_value, interop::upb::GetArena(message)); return res ? absl::OkStatus() : MessageAllocationError(); } }; // TODO: b/375460289 - flesh out non-promotional msg support that does // not return an error if missing but the default msg template struct UpbExtensionTrait { using DefaultType = std::false_type; using ReturnType = Ptr; template static constexpr absl::StatusOr Get(Msg message, const Id& id) { upb_MessageValue value; const bool ok = internal::GetOrPromoteExtension( interop::upb::GetMessage(message), id.mini_table_ext(), interop::upb::GetArena(message), &value); if (!ok) { return ExtensionNotFoundError( upb_MiniTableExtension_Number(id.mini_table_ext())); } return Ptr(interop::upb::MakeCHandle( value.msg_val, hpb::interop::upb::GetArena(message))); } template static absl::Status Set(Msg message, const Id& id, const T& value) { return Set(message, id, &value); } template static absl::Status Set(Msg message, const Id& id, T&& value) { T local = std::move(value); return internal::MoveExtension( interop::upb::GetMessage(message), interop::upb::GetArena(message), id.mini_table_ext(), interop::upb::GetMessage(&local), interop::upb::GetArena(&local)); } template static absl::Status Set(Msg message, const Id& id, Ptr value) { return hpb::internal::SetExtension( interop::upb::GetMessage(message), interop::upb::GetArena(message), id.mini_table_ext(), interop::upb::GetMessage(value)); } }; // ------------------------------------------------------------------- // ExtensionIdentifier // This is the type of actual extension objects. E.g. if you have: // extend Foo { // optional MyExtension bar = 1234; // } // then "bar" will be defined in C++ as: // ExtensionIdentifier bar(&namespace_bar_ext); template class ExtensionIdentifier { public: using Extension = ExtensionType; using Extendee = ExtendeeType; // Placeholder for extant legacy callers, avoid use if possible const upb_MiniTableExtension* mini_table_ext() const { return mini_table_ext_; } private: constexpr explicit ExtensionIdentifier( const upb_MiniTableExtension* mte, typename UpbExtensionTrait::DefaultType val, uint32_t number) : mini_table_ext_(mte), default_val_(val), number_(number) {} constexpr uint32_t number() const { return number_; } const upb_MiniTableExtension* mini_table_ext_; typename UpbExtensionTrait::ReturnType default_value() const { if constexpr (IsHpbClass) { return ExtensionType::default_instance(); } else { return default_val_; } } typename UpbExtensionTrait::DefaultType default_val_; uint32_t number_; friend struct PrivateAccess; }; upb_ExtensionRegistry* GetUpbExtensions( const ExtensionRegistry& extension_registry); } // namespace internal } // namespace hpb #endif // GOOGLE_PROTOBUF_HPB_BACKEND_UPB_EXTENSION_H__