Skip to content

Commit 920d5c3

Browse files
tonyliaosscopybara-github
authored andcommitted
Remove the AnyMetadata class and use free functions instead.
The way AnyMetadata works is by holding two pointers to the fields of google.protobuf.Any, and its methods `PackFrom` and `UnpackTo` mutates these pointers directly. This works but is a bit overcomplicated. A whole new object is created to mutate members of Any directly, breaking isolation. Each Any object will also need to unnecessarily create room to hold an AnyMetadata object, which is at minimum the size of two pointers. By removing AnyMetadata and using free functions, the call site passes `const Value& foo()` and `Value* mutable_foo()` directly into `PackFrom` and `UnpackTo`. This is more idiomatic and will play well with hasbits. The only catch here is that in some templated implementations of `PackFrom` and `UnpackTo`, caller will need access to T::FullMessageName(), which is in private scope. The workaround here is to have a single templated helper function that is made into a friend of google.protobuf.Any, so that it has the right visibility. PiperOrigin-RevId: 670722407
1 parent 1f237ef commit 920d5c3

File tree

10 files changed

+333
-239
lines changed

10 files changed

+333
-239
lines changed

src/google/protobuf/any.cc

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
#include "google/protobuf/any.h"
99

10+
#include "absl/strings/cord.h"
1011
#include "absl/strings/string_view.h"
1112
#include "google/protobuf/descriptor.h"
1213
#include "google/protobuf/generated_message_util.h"
@@ -19,21 +20,24 @@ namespace google {
1920
namespace protobuf {
2021
namespace internal {
2122

22-
bool AnyMetadata::PackFrom(Arena* arena, const Message& message) {
23-
return PackFrom(arena, message, kTypeGoogleApisComPrefix);
23+
using UrlType = std::string;
24+
using ValueType = std::string;
25+
26+
bool InternalPackFrom(const Message& message, UrlType* dst_url,
27+
ValueType* dst_value) {
28+
return InternalPackFromLite(message, kTypeGoogleApisComPrefix,
29+
message.GetTypeName(), dst_url, dst_value);
2430
}
2531

26-
bool AnyMetadata::PackFrom(Arena* arena, const Message& message,
27-
absl::string_view type_url_prefix) {
28-
type_url_->Set(GetTypeUrl(message.GetTypeName(), type_url_prefix), arena);
29-
return message.SerializeToString(value_->Mutable(arena));
32+
bool InternalPackFrom(const Message& message, absl::string_view type_url_prefix,
33+
UrlType* dst_url, ValueType* dst_value) {
34+
return InternalPackFromLite(message, type_url_prefix, message.GetTypeName(),
35+
dst_url, dst_value);
3036
}
3137

32-
bool AnyMetadata::UnpackTo(Message* message) const {
33-
if (!InternalIs(message->GetTypeName())) {
34-
return false;
35-
}
36-
return message->ParseFromString(value_->Get());
38+
bool InternalUnpackTo(absl::string_view type_url, const ValueType& value,
39+
Message* message) {
40+
return InternalUnpackToLite(message->GetTypeName(), type_url, value, message);
3741
}
3842

3943
bool GetAnyFieldDescriptors(const Message& message,

src/google/protobuf/any.h

Lines changed: 74 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
#include "absl/strings/string_view.h"
1414
#include "google/protobuf/port.h"
15-
#include "google/protobuf/arenastring.h"
15+
#include "absl/strings/cord.h"
1616
#include "google/protobuf/message_lite.h"
1717

1818
// Must be included last.
@@ -36,75 +36,80 @@ PROTOBUF_EXPORT extern const char kTypeGoogleProdComPrefix[];
3636
std::string GetTypeUrl(absl::string_view message_name,
3737
absl::string_view type_url_prefix);
3838

39+
template <typename T>
40+
absl::string_view GetAnyMessageName() {
41+
return T::FullMessageName();
42+
}
43+
3944
// Helper class used to implement google::protobuf::Any.
40-
class PROTOBUF_EXPORT AnyMetadata {
41-
typedef ArenaStringPtr UrlType;
42-
typedef ArenaStringPtr ValueType;
43-
public:
44-
// AnyMetadata does not take ownership of "type_url" and "value".
45-
constexpr AnyMetadata(UrlType* type_url, ValueType* value)
46-
: type_url_(type_url), value_(value) {}
47-
AnyMetadata(const AnyMetadata&) = delete;
48-
AnyMetadata& operator=(const AnyMetadata&) = delete;
49-
50-
// Packs a message using the default type URL prefix: "type.googleapis.com".
51-
// The resulted type URL will be "type.googleapis.com/<message_full_name>".
52-
// Returns false if serializing the message failed.
53-
template <typename T>
54-
bool PackFrom(Arena* arena, const T& message) {
55-
return InternalPackFrom(arena, message, kTypeGoogleApisComPrefix,
56-
T::FullMessageName());
57-
}
58-
59-
bool PackFrom(Arena* arena, const Message& message);
60-
61-
// Packs a message using the given type URL prefix. The type URL will be
62-
// constructed by concatenating the message type's full name to the prefix
63-
// with an optional "/" separator if the prefix doesn't already end with "/".
64-
// For example, both PackFrom(message, "type.googleapis.com") and
65-
// PackFrom(message, "type.googleapis.com/") yield the same result type
66-
// URL: "type.googleapis.com/<message_full_name>".
67-
// Returns false if serializing the message failed.
68-
template <typename T>
69-
bool PackFrom(Arena* arena, const T& message,
70-
absl::string_view type_url_prefix) {
71-
return InternalPackFrom(arena, message, type_url_prefix,
72-
T::FullMessageName());
73-
}
74-
75-
bool PackFrom(Arena* arena, const Message& message,
76-
absl::string_view type_url_prefix);
77-
78-
// Unpacks the payload into the given message. Returns false if the message's
79-
// type doesn't match the type specified in the type URL (i.e., the full
80-
// name after the last "/" of the type URL doesn't match the message's actual
81-
// full name) or parsing the payload has failed.
82-
template <typename T>
83-
bool UnpackTo(T* message) const {
84-
return InternalUnpackTo(T::FullMessageName(), message);
85-
}
86-
87-
bool UnpackTo(Message* message) const;
88-
89-
// Checks whether the type specified in the type URL matches the given type.
90-
// A type is considered matching if its full name matches the full name after
91-
// the last "/" in the type URL.
92-
template <typename T>
93-
bool Is() const {
94-
return InternalIs(T::FullMessageName());
95-
}
96-
97-
private:
98-
bool InternalPackFrom(Arena* arena, const MessageLite& message,
99-
absl::string_view type_url_prefix,
100-
absl::string_view type_name);
101-
bool InternalUnpackTo(absl::string_view type_name,
102-
MessageLite* message) const;
103-
bool InternalIs(absl::string_view type_name) const;
104-
105-
UrlType* type_url_;
106-
ValueType* value_;
107-
};
45+
#define URL_TYPE std::string
46+
#define VALUE_TYPE std::string
47+
48+
// Helper functions that only require 'lite' messages to work.
49+
PROTOBUF_EXPORT bool InternalPackFromLite(const MessageLite& message,
50+
absl::string_view type_url_prefix,
51+
absl::string_view type_name,
52+
URL_TYPE* dst_url,
53+
VALUE_TYPE* dst_value);
54+
PROTOBUF_EXPORT bool InternalUnpackToLite(absl::string_view type_name,
55+
absl::string_view type_url,
56+
const VALUE_TYPE& value,
57+
MessageLite* dst_message);
58+
PROTOBUF_EXPORT bool InternalIsLite(absl::string_view type_name,
59+
absl::string_view type_url);
60+
61+
// Packs a message using the default type URL prefix: "type.googleapis.com".
62+
// The resulted type URL will be "type.googleapis.com/<message_full_name>".
63+
// Returns false if serializing the message failed.
64+
template <typename T>
65+
bool InternalPackFrom(const T& message, URL_TYPE* dst_url,
66+
VALUE_TYPE* dst_value) {
67+
return InternalPackFromLite(message, kTypeGoogleApisComPrefix,
68+
GetAnyMessageName<T>(), dst_url, dst_value);
69+
}
70+
PROTOBUF_EXPORT bool InternalPackFrom(const Message& message, URL_TYPE* dst_url,
71+
VALUE_TYPE* dst_value);
72+
73+
// Packs a message using the given type URL prefix. The type URL will be
74+
// constructed by concatenating the message type's full name to the prefix
75+
// with an optional "/" separator if the prefix doesn't already end with "/".
76+
// For example, both InternalPackFrom(message, "type.googleapis.com") and
77+
// InternalPackFrom(message, "type.googleapis.com/") yield the same result type
78+
// URL: "type.googleapis.com/<message_full_name>".
79+
// Returns false if serializing the message failed.
80+
template <typename T>
81+
bool InternalPackFrom(const T& message, absl::string_view type_url_prefix,
82+
URL_TYPE* dst_url, VALUE_TYPE* dst_value) {
83+
return InternalPackFromLite(message, type_url_prefix, GetAnyMessageName<T>(),
84+
dst_url, dst_value);
85+
}
86+
PROTOBUF_EXPORT bool InternalPackFrom(const Message& message,
87+
absl::string_view type_url_prefix,
88+
URL_TYPE* dst_url, VALUE_TYPE* dst_value);
89+
90+
// Unpacks the payload into the given message. Returns false if the message's
91+
// type doesn't match the type specified in the type URL (i.e., the full
92+
// name after the last "/" of the type URL doesn't match the message's actual
93+
// full name) or parsing the payload has failed.
94+
template <typename T>
95+
bool InternalUnpackTo(absl::string_view type_url, const VALUE_TYPE& value,
96+
T* message) {
97+
return InternalUnpackToLite(GetAnyMessageName<T>(), type_url, value, message);
98+
}
99+
PROTOBUF_EXPORT bool InternalUnpackTo(absl::string_view type_url,
100+
const VALUE_TYPE& value,
101+
Message* message);
102+
103+
// Checks whether the type specified in the type URL matches the given type.
104+
// A type is considered matching if its full name matches the full name after
105+
// the last "/" in the type URL.
106+
template <typename T>
107+
bool InternalIs(absl::string_view type_url) {
108+
return InternalIsLite(GetAnyMessageName<T>(), type_url);
109+
}
110+
111+
#undef URL_TYPE
112+
#undef VALUE_TYPE
108113

109114
// Get the proto type name from Any::type_url value. For example, passing
110115
// "type.googleapis.com/rpc.QueryOrigin" will return "rpc.QueryOrigin" in

src/google/protobuf/any_lite.cc

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <cstdlib>
99
#include <string>
1010

11+
#include "absl/strings/cord.h"
1112
#include "absl/strings/match.h"
1213
#include "absl/strings/str_cat.h"
1314
#include "absl/strings/string_view.h"
@@ -20,6 +21,13 @@ namespace google {
2021
namespace protobuf {
2122
namespace internal {
2223

24+
using UrlType = std::string;
25+
using ValueType = std::string;
26+
27+
const char kAnyFullTypeName[] = "google.protobuf.Any";
28+
const char kTypeGoogleApisComPrefix[] = "type.googleapis.com/";
29+
const char kTypeGoogleProdComPrefix[] = "type.googleprod.com/";
30+
2331
std::string GetTypeUrl(absl::string_view message_name,
2432
absl::string_view type_url_prefix) {
2533
if (!type_url_prefix.empty() &&
@@ -36,27 +44,25 @@ bool EndsWithTypeName(absl::string_view type_url, absl::string_view type_name) {
3644
absl::EndsWith(type_url, type_name);
3745
}
3846

39-
const char kAnyFullTypeName[] = "google.protobuf.Any";
40-
const char kTypeGoogleApisComPrefix[] = "type.googleapis.com/";
41-
const char kTypeGoogleProdComPrefix[] = "type.googleprod.com/";
42-
43-
bool AnyMetadata::InternalPackFrom(Arena* arena, const MessageLite& message,
44-
absl::string_view type_url_prefix,
45-
absl::string_view type_name) {
46-
type_url_->Set(GetTypeUrl(type_name, type_url_prefix), arena);
47-
return message.SerializeToString(value_->Mutable(arena));
47+
bool InternalPackFromLite(const MessageLite& message,
48+
absl::string_view type_url_prefix,
49+
absl::string_view type_name, UrlType* dst_url,
50+
ValueType* dst_value) {
51+
*dst_url = GetTypeUrl(type_name, type_url_prefix);
52+
return message.SerializeToString(dst_value);
4853
}
4954

50-
bool AnyMetadata::InternalUnpackTo(absl::string_view type_name,
51-
MessageLite* message) const {
52-
if (!InternalIs(type_name)) {
55+
bool InternalUnpackToLite(absl::string_view type_name,
56+
absl::string_view type_url, const ValueType& value,
57+
MessageLite* dst_message) {
58+
if (!InternalIsLite(type_name, type_url)) {
5359
return false;
5460
}
55-
return message->ParseFromString(value_->Get());
61+
return dst_message->ParseFromString(value);
5662
}
5763

58-
bool AnyMetadata::InternalIs(absl::string_view type_name) const {
59-
return EndsWithTypeName(type_url_->Get(), type_name);
64+
bool InternalIsLite(absl::string_view type_name, absl::string_view type_url) {
65+
return EndsWithTypeName(type_url, type_name);
6066
}
6167

6268
bool ParseAnyTypeUrl(absl::string_view type_url, std::string* url_prefix,

src/google/protobuf/any_test.cc

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,6 @@ namespace google {
2424
namespace protobuf {
2525
namespace {
2626

27-
TEST(AnyMetadataTest, ConstInit) {
28-
PROTOBUF_CONSTINIT static internal::AnyMetadata metadata(nullptr, nullptr);
29-
(void)metadata;
30-
}
31-
3227
TEST(AnyTest, TestPackAndUnpack) {
3328
protobuf_unittest::TestAny submessage;
3429
submessage.set_int32_value(12345);

src/google/protobuf/compiler/cpp/file.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,8 @@ void FileGenerator::GenerateSharedHeaderCode(io::Printer* p) {
236236
NamespaceOpener ns(ProtobufNamespace(options_), p);
237237
p->Emit(R"cc(
238238
namespace internal {
239-
class AnyMetadata;
239+
template <typename T>
240+
::absl::string_view GetAnyMessageName();
240241
} // namespace internal
241242
)cc");
242243
}},

0 commit comments

Comments
 (0)