Rust C++: remove per-message functions for repeated fields

This CL deletes the per-message C++ functions for operating on repeated fields
and replaces them with functions in the runtime that can work with arbitrary
messages.

Similar to what we did with maps, this required refactoring the code to make it
work with `RepeatedPtrFieldBase`, the untyped base class of
`RepeatedPtrField<T>`. I added a `RustRepeatedMessageHelper` class to allow us
access to the protected methods we need.

This should save a bit of linker input code size, but I think more importantly
we are going to need this eventually to enable tree shaking to work well.

PiperOrigin-RevId: 693394959
pull/19103/head
Adam Cozzette 2 months ago committed by Copybara-Service
parent 31c5f73ec1
commit 9bdafdbb0c
  1. 17
      csharp/src/Google.Protobuf/Reflection/FeatureSetDescriptor.g.cs
  2. 24
      rust/cpp.rs
  3. 47
      rust/cpp_kernel/repeated.cc
  4. 14
      rust/test/shared/accessors_repeated_test.rs
  5. 99
      src/google/protobuf/compiler/rust/message.cc
  6. 33
      src/google/protobuf/repeated_ptr_field.h

@ -1,17 +0,0 @@
#region Copyright notice and license
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. 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
#endregion
namespace Google.Protobuf.Reflection;
internal sealed partial class FeatureSetDescriptor
{
// Canonical serialized form of the edition defaults, generated by embed_edition_defaults.
private const string DefaultsBase64 =
"ChMYhAciACoMCAEQAhgCIAMoATACChMY5wciACoMCAIQARgBIAIoATABChMY6AciDAgBEAEYASACKAEwASoAIOYHKOgH";
}

@ -589,6 +589,30 @@ macro_rules! impl_repeated_primitives {
impl_repeated_primitives!(i32, u32, i64, u64, f32, f64, bool, ProtoString, ProtoBytes);
extern "C" {
pub fn proto2_rust_RepeatedField_Message_new() -> RawRepeatedField;
pub fn proto2_rust_RepeatedField_Message_free(field: RawRepeatedField);
pub fn proto2_rust_RepeatedField_Message_size(field: RawRepeatedField) -> usize;
pub fn proto2_rust_RepeatedField_Message_get(
field: RawRepeatedField,
index: usize,
) -> RawMessage;
pub fn proto2_rust_RepeatedField_Message_get_mut(
field: RawRepeatedField,
index: usize,
) -> RawMessage;
pub fn proto2_rust_RepeatedField_Message_add(
field: RawRepeatedField,
prototype: RawMessage,
) -> RawMessage;
pub fn proto2_rust_RepeatedField_Message_clear(field: RawRepeatedField);
pub fn proto2_rust_RepeatedField_Message_copy_from(
dst: RawRepeatedField,
src: RawRepeatedField,
);
pub fn proto2_rust_RepeatedField_Message_reserve(field: RawRepeatedField, additional: usize);
}
/// Cast a `RepeatedView<SomeEnum>` to `RepeatedView<c_int>`.
pub fn cast_enum_repeated_view<E: Enum + ProxiedInRepeated>(
repeated: RepeatedView<E>,

@ -105,4 +105,51 @@ expose_repeated_ptr_field_methods(ProtoBytes);
#undef expose_repeated_ptr_field_methods
using google::protobuf::internal::GenericTypeHandler;
using google::protobuf::internal::RepeatedPtrFieldBase;
using google::protobuf::internal::RustRepeatedMessageHelper;
void* proto2_rust_RepeatedField_Message_new() {
return RustRepeatedMessageHelper::New();
}
void proto2_rust_RepeatedField_Message_free(RepeatedPtrFieldBase* field) {
RustRepeatedMessageHelper::Delete(field);
}
size_t proto2_rust_RepeatedField_Message_size(
const RepeatedPtrFieldBase* field) {
return RustRepeatedMessageHelper::Size(*field);
}
const google::protobuf::MessageLite* proto2_rust_RepeatedField_Message_get(
const RepeatedPtrFieldBase* field, size_t index) {
return &RustRepeatedMessageHelper::At(*field, index);
}
google::protobuf::MessageLite* proto2_rust_RepeatedField_Message_get_mut(
RepeatedPtrFieldBase* field, size_t index) {
return &RustRepeatedMessageHelper::At(*field, index);
}
google::protobuf::MessageLite* proto2_rust_RepeatedField_Message_add(
RepeatedPtrFieldBase* field, const google::protobuf::MessageLite* prototype) {
return field->AddMessage(prototype);
}
void proto2_rust_RepeatedField_Message_clear(RepeatedPtrFieldBase* field) {
field->Clear<GenericTypeHandler<google::protobuf::MessageLite>>();
}
void proto2_rust_RepeatedField_Message_copy_from(
RepeatedPtrFieldBase* dst, const RepeatedPtrFieldBase* src) {
dst->Clear<GenericTypeHandler<google::protobuf::MessageLite>>();
dst->MergeFrom<google::protobuf::MessageLite>(*src);
}
void proto2_rust_RepeatedField_Message_reserve(RepeatedPtrFieldBase* field,
size_t additional) {
RustRepeatedMessageHelper::Reserve(*field, additional);
}
} // extern "C"

@ -7,7 +7,7 @@
use googletest::prelude::*;
use paste::paste;
use protobuf::AsView;
use protobuf::{AsMut, AsView, Repeated};
use unittest_rust_proto::{test_all_types, test_all_types::NestedMessage, TestAllTypes};
macro_rules! generate_repeated_numeric_test {
@ -209,7 +209,13 @@ fn test_repeated_message() {
assert_that!(msg.repeated_nested_message().get(0).unwrap().bb(), eq(1));
let mut msg2 = TestAllTypes::new();
for _i in 0..2 {
msg2.repeated_nested_message_mut().push(NestedMessage::new());
}
assert_that!(msg2.repeated_nested_message().len(), eq(2));
msg2.repeated_nested_message_mut().copy_from(msg.repeated_nested_message());
assert_that!(msg2.repeated_nested_message().len(), eq(1));
assert_that!(msg2.repeated_nested_message().get(0).unwrap().bb(), eq(1));
let mut nested2 = NestedMessage::new();
@ -239,6 +245,12 @@ fn test_repeated_message_setter() {
assert_that!(msg.repeated_nested_message().get(0).unwrap().bb(), eq(1));
}
#[gtest]
fn test_repeated_message_drop() {
let mut repeated = Repeated::<TestAllTypes>::new();
repeated.as_mut().push(TestAllTypes::new());
}
#[gtest]
fn test_repeated_strings() {
let mut older_msg = TestAllTypes::new();

@ -195,28 +195,10 @@ void CppMessageExterns(Context& ctx, const Descriptor& msg) {
ABSL_CHECK(ctx.is_cpp());
ctx.Emit(
{{"new_thunk", ThunkName(ctx, msg, "new")},
{"default_instance_thunk", ThunkName(ctx, msg, "default_instance")},
{"repeated_new_thunk", ThunkName(ctx, msg, "repeated_new")},
{"repeated_free_thunk", ThunkName(ctx, msg, "repeated_free")},
{"repeated_len_thunk", ThunkName(ctx, msg, "repeated_len")},
{"repeated_get_thunk", ThunkName(ctx, msg, "repeated_get")},
{"repeated_get_mut_thunk", ThunkName(ctx, msg, "repeated_get_mut")},
{"repeated_add_thunk", ThunkName(ctx, msg, "repeated_add")},
{"repeated_clear_thunk", ThunkName(ctx, msg, "repeated_clear")},
{"repeated_copy_from_thunk", ThunkName(ctx, msg, "repeated_copy_from")},
{"repeated_reserve_thunk", ThunkName(ctx, msg, "repeated_reserve")}},
{"default_instance_thunk", ThunkName(ctx, msg, "default_instance")}},
R"rs(
fn $new_thunk$() -> $pbr$::RawMessage;
fn $default_instance_thunk$() -> $pbr$::RawMessage;
fn $repeated_new_thunk$() -> $pbr$::RawRepeatedField;
fn $repeated_free_thunk$(raw: $pbr$::RawRepeatedField);
fn $repeated_len_thunk$(raw: $pbr$::RawRepeatedField) -> usize;
fn $repeated_add_thunk$(raw: $pbr$::RawRepeatedField) -> $pbr$::RawMessage;
fn $repeated_get_thunk$(raw: $pbr$::RawRepeatedField, index: usize) -> $pbr$::RawMessage;
fn $repeated_get_mut_thunk$(raw: $pbr$::RawRepeatedField, index: usize) -> $pbr$::RawMessage;
fn $repeated_clear_thunk$(raw: $pbr$::RawRepeatedField);
fn $repeated_copy_from_thunk$(dst: $pbr$::RawRepeatedField, src: $pbr$::RawRepeatedField);
fn $repeated_reserve_thunk$(raw: $pbr$::RawRepeatedField, additional: usize);
)rs");
}
@ -365,18 +347,6 @@ void MessageProxiedInRepeated(Context& ctx, const Descriptor& msg) {
ctx.Emit(
{
{"Msg", RsSafeName(msg.name())},
{"repeated_len_thunk", ThunkName(ctx, msg, "repeated_len")},
{"repeated_get_thunk", ThunkName(ctx, msg, "repeated_get")},
{"repeated_get_mut_thunk",
ThunkName(ctx, msg, "repeated_get_mut")},
{"repeated_add_thunk", ThunkName(ctx, msg, "repeated_add")},
{"repeated_new_thunk", ThunkName(ctx, msg, "repeated_new")},
{"repeated_free_thunk", ThunkName(ctx, msg, "repeated_free")},
{"repeated_clear_thunk", ThunkName(ctx, msg, "repeated_clear")},
{"repeated_copy_from_thunk",
ThunkName(ctx, msg, "repeated_copy_from")},
{"repeated_reserve_thunk",
ThunkName(ctx, msg, "repeated_reserve")},
},
R"rs(
unsafe impl $pb$::ProxiedInRepeated for $Msg$ {
@ -385,8 +355,7 @@ void MessageProxiedInRepeated(Context& ctx, const Descriptor& msg) {
// - The thunk returns an unaliased and valid `RepeatedPtrField*`
unsafe {
$pb$::Repeated::from_inner($pbi$::Private,
$pbr$::InnerRepeated::from_raw($repeated_new_thunk$()
)
$pbr$::InnerRepeated::from_raw($pbr$::proto2_rust_RepeatedField_Message_new())
)
}
}
@ -394,12 +363,12 @@ void MessageProxiedInRepeated(Context& ctx, const Descriptor& msg) {
unsafe fn repeated_free(_private: $pbi$::Private, f: &mut $pb$::Repeated<Self>) {
// SAFETY
// - `f.raw()` is a valid `RepeatedPtrField*`.
unsafe { $repeated_free_thunk$(f.as_view().as_raw($pbi$::Private)) }
unsafe { $pbr$::proto2_rust_RepeatedField_Message_free(f.as_view().as_raw($pbi$::Private)) }
}
fn repeated_len(f: $pb$::View<$pb$::Repeated<Self>>) -> usize {
// SAFETY: `f.as_raw()` is a valid `RepeatedPtrField*`.
unsafe { $repeated_len_thunk$(f.as_raw($pbi$::Private)) }
unsafe { $pbr$::proto2_rust_RepeatedField_Message_size(f.as_raw($pbi$::Private)) }
}
unsafe fn repeated_set_unchecked(
@ -413,7 +382,7 @@ void MessageProxiedInRepeated(Context& ctx, const Descriptor& msg) {
// - `v.raw_msg()` is a valid `const Message&`.
unsafe {
$pbr$::proto2_rust_Message_copy_from(
$repeated_get_mut_thunk$(f.as_raw($pbi$::Private), i),
$pbr$::proto2_rust_RepeatedField_Message_get_mut(f.as_raw($pbi$::Private), i),
v.into_proxied($pbi$::Private).raw_msg(),
);
}
@ -426,13 +395,13 @@ void MessageProxiedInRepeated(Context& ctx, const Descriptor& msg) {
// SAFETY:
// - `f.as_raw()` is a valid `const RepeatedPtrField&`.
// - `i < len(f)` is promised by caller.
let msg = unsafe { $repeated_get_thunk$(f.as_raw($pbi$::Private), i) };
let msg = unsafe { $pbr$::proto2_rust_RepeatedField_Message_get(f.as_raw($pbi$::Private), i) };
$pb$::View::<Self>::new($pbi$::Private, msg)
}
fn repeated_clear(mut f: $pb$::Mut<$pb$::Repeated<Self>>) {
// SAFETY:
// - `f.as_raw()` is a valid `RepeatedPtrField*`.
unsafe { $repeated_clear_thunk$(f.as_raw($pbi$::Private)) };
unsafe { $pbr$::proto2_rust_RepeatedField_Message_clear(f.as_raw($pbi$::Private)) };
}
fn repeated_push(mut f: $pb$::Mut<$pb$::Repeated<Self>>, v: impl $pb$::IntoProxied<Self>) {
@ -440,7 +409,8 @@ void MessageProxiedInRepeated(Context& ctx, const Descriptor& msg) {
// - `f.as_raw()` is a valid `RepeatedPtrField*`.
// - `v.raw_msg()` is a valid `const Message&`.
unsafe {
let new_elem = $repeated_add_thunk$(f.as_raw($pbi$::Private));
let prototype = <$Msg$View as $std$::default::Default>::default().raw_msg();
let new_elem = $pbr$::proto2_rust_RepeatedField_Message_add(f.as_raw($pbi$::Private), prototype);
$pbr$::proto2_rust_Message_copy_from(new_elem, v.into_proxied($pbi$::Private).raw_msg());
}
}
@ -453,7 +423,7 @@ void MessageProxiedInRepeated(Context& ctx, const Descriptor& msg) {
// - `dest.as_raw()` is a valid `RepeatedPtrField*`.
// - `src.as_raw()` is a valid `const RepeatedPtrField&`.
unsafe {
$repeated_copy_from_thunk$(dest.as_raw($pbi$::Private), src.as_raw($pbi$::Private));
$pbr$::proto2_rust_RepeatedField_Message_copy_from(dest.as_raw($pbi$::Private), src.as_raw($pbi$::Private));
}
}
@ -463,7 +433,7 @@ void MessageProxiedInRepeated(Context& ctx, const Descriptor& msg) {
) {
// SAFETY:
// - `f.as_raw()` is a valid `RepeatedPtrField*`.
unsafe { $repeated_reserve_thunk$(f.as_raw($pbi$::Private), additional) }
unsafe { $pbr$::proto2_rust_RepeatedField_Message_reserve(f.as_raw($pbi$::Private), additional) }
}
}
)rs");
@ -1286,15 +1256,6 @@ void GenerateThunksCc(Context& ctx, const Descriptor& msg) {
{"QualifiedMsg", cpp::QualifiedClassName(&msg)},
{"new_thunk", ThunkName(ctx, msg, "new")},
{"default_instance_thunk", ThunkName(ctx, msg, "default_instance")},
{"repeated_new_thunk", ThunkName(ctx, msg, "repeated_new")},
{"repeated_free_thunk", ThunkName(ctx, msg, "repeated_free")},
{"repeated_len_thunk", ThunkName(ctx, msg, "repeated_len")},
{"repeated_get_thunk", ThunkName(ctx, msg, "repeated_get")},
{"repeated_get_mut_thunk", ThunkName(ctx, msg, "repeated_get_mut")},
{"repeated_add_thunk", ThunkName(ctx, msg, "repeated_add")},
{"repeated_clear_thunk", ThunkName(ctx, msg, "repeated_clear")},
{"repeated_copy_from_thunk", ThunkName(ctx, msg, "repeated_copy_from")},
{"repeated_reserve_thunk", ThunkName(ctx, msg, "repeated_reserve")},
{"nested_msg_thunks",
[&] {
for (int i = 0; i < msg.nested_type_count(); ++i) {
@ -1325,44 +1286,6 @@ void GenerateThunksCc(Context& ctx, const Descriptor& msg) {
return &$QualifiedMsg$::default_instance();
}
void* $repeated_new_thunk$() {
return new google::protobuf::RepeatedPtrField<$QualifiedMsg$>();
}
void $repeated_free_thunk$(void* ptr) {
delete static_cast<google::protobuf::RepeatedPtrField<$QualifiedMsg$>*>(ptr);
}
size_t $repeated_len_thunk$(google::protobuf::RepeatedPtrField<$QualifiedMsg$>* field) {
return field->size();
}
const $QualifiedMsg$* $repeated_get_thunk$(
google::protobuf::RepeatedPtrField<$QualifiedMsg$>* field,
size_t index) {
return &field->Get(index);
}
$QualifiedMsg$* $repeated_get_mut_thunk$(
google::protobuf::RepeatedPtrField<$QualifiedMsg$>* field,
size_t index) {
return field->Mutable(index);
}
$QualifiedMsg$* $repeated_add_thunk$(google::protobuf::RepeatedPtrField<$QualifiedMsg$>* field) {
return field->Add();
}
void $repeated_clear_thunk$(google::protobuf::RepeatedPtrField<$QualifiedMsg$>* field) {
field->Clear();
}
void $repeated_copy_from_thunk$(
google::protobuf::RepeatedPtrField<$QualifiedMsg$>& dst,
const google::protobuf::RepeatedPtrField<$QualifiedMsg$>& src) {
dst = src;
}
void $repeated_reserve_thunk$(
google::protobuf::RepeatedPtrField<$QualifiedMsg$>* field,
size_t additional) {
field->Reserve(field->size() + additional);
}
$accessor_thunks$
$oneof_thunks$

@ -572,6 +572,8 @@ class PROTOBUF_EXPORT RepeatedPtrFieldBase {
friend class internal::SwapFieldHelper;
friend class LazyRepeatedPtrField;
friend class RustRepeatedMessageHelper;
// Concrete Arena enabled copy function used to copy messages instances.
// This follows the `Arena::CopyConstruct` signature so that the compiler
// can have the inlined call into the out of line copy function(s) simply pass
@ -1537,6 +1539,37 @@ inline int RepeatedPtrField<Element>::Capacity() const {
namespace internal {
// This class gives the Rust implementation access to some protected methods on
// RepeatedPtrFieldBase. These methods allow us to operate solely on the
// MessageLite interface so that we do not need to generate code for each
// concrete message type.
class RustRepeatedMessageHelper {
public:
static RepeatedPtrFieldBase* New() { return new RepeatedPtrFieldBase; }
static void Delete(RepeatedPtrFieldBase* field) {
field->DestroyProtos();
delete field;
}
static size_t Size(const RepeatedPtrFieldBase& field) {
return static_cast<size_t>(field.size());
}
static void Reserve(RepeatedPtrFieldBase& field, size_t additional) {
field.Reserve(field.size() + additional);
}
static const MessageLite& At(const RepeatedPtrFieldBase& field,
size_t index) {
return field.at<GenericTypeHandler<MessageLite>>(index);
}
static MessageLite& At(RepeatedPtrFieldBase& field, size_t index) {
return field.at<GenericTypeHandler<MessageLite>>(index);
}
};
// STL-like iterator implementation for RepeatedPtrField. You should not
// refer to this class directly; use RepeatedPtrField<T>::iterator instead.
//

Loading…
Cancel
Save