Add support for repeated strings/bytes

PiperOrigin-RevId: 599822292
pull/15502/head
Marcel Hlopko 1 year ago committed by Copybara-Service
parent 457ed9b44f
commit 39e8ca7faf
  1. 72
      rust/cpp.rs
  2. 44
      rust/cpp_kernel/cpp_api.cc
  3. 8
      rust/internal.rs
  4. 4
      rust/repeated.rs
  5. 46
      rust/test/shared/accessors_repeated_test.rs
  6. 34
      rust/upb.rs
  7. 2
      src/google/protobuf/compiler/rust/accessors/accessors.cc
  8. 6
      src/google/protobuf/compiler/rust/accessors/repeated_field.cc

@ -7,11 +7,10 @@
// Rust Protobuf runtime using the C++ kernel.
use crate::ProtoStr;
use crate::__internal::{Enum, Private, PtrAndLen, RawArena, RawMap, RawMessage, RawRepeatedField};
use crate::{
Map, Mut, ProxiedInMapValue, ProxiedInRepeated, Repeated, RepeatedMut, RepeatedView,
SettableValue, View,
Map, Mut, ProtoStr, Proxied, ProxiedInMapValue, ProxiedInRepeated, Repeated, RepeatedMut,
RepeatedView, SettableValue, View,
};
use core::fmt::Debug;
use paste::paste;
@ -227,6 +226,48 @@ impl<'msg> InnerRepeatedMut<'msg> {
}
}
trait CppTypeConversions: Proxied {
type ElemType;
fn elem_to_view<'msg>(v: Self::ElemType) -> View<'msg, Self>;
}
macro_rules! impl_cpp_type_conversions_for_scalars {
($($t:ty),* $(,)?) => {
$(
impl CppTypeConversions for $t {
type ElemType = Self;
fn elem_to_view<'msg>(v: Self) -> View<'msg, Self> {
v
}
}
)*
}
}
impl_cpp_type_conversions_for_scalars!(i32, u32, i64, u64, f32, f64, bool);
impl CppTypeConversions for ProtoStr {
type ElemType = PtrAndLen;
fn elem_to_view<'msg>(v: PtrAndLen) -> View<'msg, ProtoStr> {
ptrlen_to_str(v)
}
}
impl CppTypeConversions for [u8] {
type ElemType = PtrAndLen;
fn elem_to_view<'msg>(v: Self::ElemType) -> View<'msg, Self> {
ptrlen_to_bytes(v)
}
}
// This type alias is used so macros can generate valid extern "C" symbol names
// for functions working with [u8] types.
type Bytes = [u8];
macro_rules! impl_repeated_primitives {
(@impl $($t:ty => [
$new_thunk:ident,
@ -242,10 +283,15 @@ macro_rules! impl_repeated_primitives {
extern "C" {
fn $new_thunk() -> RawRepeatedField;
fn $free_thunk(f: RawRepeatedField);
fn $add_thunk(f: RawRepeatedField, v: $t);
fn $add_thunk(f: RawRepeatedField, v: <$t as CppTypeConversions>::ElemType);
fn $size_thunk(f: RawRepeatedField) -> usize;
fn $get_thunk(f: RawRepeatedField, i: usize) -> $t;
fn $set_thunk(f: RawRepeatedField, i: usize, v: $t);
fn $get_thunk(
f: RawRepeatedField,
i: usize) -> <$t as CppTypeConversions>::ElemType;
fn $set_thunk(
f: RawRepeatedField,
i: usize,
v: <$t as CppTypeConversions>::ElemType);
fn $clear_thunk(f: RawRepeatedField);
fn $copy_from_thunk(src: RawRepeatedField, dst: RawRepeatedField);
}
@ -265,16 +311,17 @@ macro_rules! impl_repeated_primitives {
unsafe { $size_thunk(f.as_raw(Private)) }
}
fn repeated_push(mut f: Mut<Repeated<$t>>, v: View<$t>) {
unsafe { $add_thunk(f.as_raw(Private), v) }
unsafe { $add_thunk(f.as_raw(Private), v.into()) }
}
fn repeated_clear(mut f: Mut<Repeated<$t>>) {
unsafe { $clear_thunk(f.as_raw(Private)) }
}
unsafe fn repeated_get_unchecked(f: View<Repeated<$t>>, i: usize) -> View<$t> {
unsafe { $get_thunk(f.as_raw(Private), i) }
<$t as CppTypeConversions>::elem_to_view(
unsafe { $get_thunk(f.as_raw(Private), i) })
}
unsafe fn repeated_set_unchecked(mut f: Mut<Repeated<$t>>, i: usize, v: View<$t>) {
unsafe { $set_thunk(f.as_raw(Private), i, v) }
unsafe { $set_thunk(f.as_raw(Private), i, v.into()) }
}
fn repeated_copy_from(src: View<Repeated<$t>>, mut dest: Mut<Repeated<$t>>) {
unsafe { $copy_from_thunk(src.as_raw(Private), dest.as_raw(Private)) }
@ -300,7 +347,7 @@ macro_rules! impl_repeated_primitives {
};
}
impl_repeated_primitives!(i32, u32, i64, u64, f32, f64, bool);
impl_repeated_primitives!(i32, u32, i64, u64, f32, f64, bool, ProtoStr, Bytes);
/// Cast a `RepeatedView<SomeEnum>` to `RepeatedView<c_int>`.
pub fn cast_enum_repeated_view<E: Enum + ProxiedInRepeated>(
@ -431,11 +478,6 @@ fn ptrlen_to_bytes<'msg>(val: PtrAndLen) -> &'msg [u8] {
unsafe { val.as_ref() }
}
// This type alias is used so the macro
// `impl_ProxiedInMapValue_for_non_generated_value_types` can generate
// valid extern "C" symbol names for functions working with [u8] types.
type Bytes = [u8];
macro_rules! impl_ProxiedInMapValue_for_key_types {
($($t:ty, $ffi_t:ty, $to_ffi_key:expr;)*) => {
paste! {

@ -6,6 +6,7 @@
#include "google/protobuf/map.h"
#include "google/protobuf/repeated_field.h"
#include "google/protobuf/repeated_ptr_field.h"
extern "C" {
@ -49,9 +50,52 @@ expose_repeated_field_methods(double, f64);
expose_repeated_field_methods(bool, bool);
expose_repeated_field_methods(uint64_t, u64);
expose_repeated_field_methods(int64_t, i64);
#undef expose_repeated_field_methods
#define expose_repeated_ptr_field_methods(ty) \
google::protobuf::RepeatedPtrField<std::string>* \
__pb_rust_RepeatedField_##ty##_new() { \
return new google::protobuf::RepeatedPtrField<std::string>(); \
} \
void __pb_rust_RepeatedField_##ty##_free( \
google::protobuf::RepeatedPtrField<std::string>* r) { \
delete r; \
} \
void __pb_rust_RepeatedField_##ty##_add( \
google::protobuf::RepeatedPtrField<std::string>* r, \
google::protobuf::rust_internal::PtrAndLen val) { \
r->Add(std::string(val.ptr, val.len)); \
} \
size_t __pb_rust_RepeatedField_##ty##_size( \
google::protobuf::RepeatedPtrField<std::string>* r) { \
return r->size(); \
} \
google::protobuf::rust_internal::PtrAndLen __pb_rust_RepeatedField_##ty##_get( \
google::protobuf::RepeatedPtrField<std::string>* r, size_t index) { \
const std::string& s = r->Get(index); \
return google::protobuf::rust_internal::PtrAndLen(s.data(), s.size()); \
} \
void __pb_rust_RepeatedField_##ty##_set( \
google::protobuf::RepeatedPtrField<std::string>* r, size_t index, \
google::protobuf::rust_internal::PtrAndLen val) { \
*r->Mutable(index) = std::string(val.ptr, val.len); \
} \
void __pb_rust_RepeatedField_##ty##_copy_from( \
const google::protobuf::RepeatedPtrField<std::string>* src, \
google::protobuf::RepeatedPtrField<std::string>* dst) { \
dst->CopyFrom(*src); \
} \
void __pb_rust_RepeatedField_##ty##_clear( \
google::protobuf::RepeatedPtrField<std::string>* r) { \
r->Clear(); \
}
expose_repeated_ptr_field_methods(ProtoStr);
expose_repeated_ptr_field_methods(Bytes);
#undef expose_repeated_field_methods
#undef expose_repeated_ptr_field_methods
#define expose_scalar_map_methods(key_ty, rust_key_ty, ffi_key_ty, to_cpp_key, \
value_ty, rust_value_ty, ffi_value_ty, \
to_cpp_value, to_ffi_value) \

@ -16,6 +16,7 @@ pub use crate::vtable::{
ProxiedWithRawOptionalVTable, ProxiedWithRawVTable, RawVTableMutator,
RawVTableOptionalMutatorData,
};
pub use crate::ProtoStr;
use std::ptr::NonNull;
use std::slice;
@ -134,3 +135,10 @@ impl From<&[u8]> for PtrAndLen {
Self { ptr: slice.as_ptr(), len: slice.len() }
}
}
impl From<&ProtoStr> for PtrAndLen {
fn from(s: &ProtoStr) -> Self {
let bytes = s.as_bytes();
Self { ptr: bytes.as_ptr(), len: bytes.len() }
}
}

@ -13,7 +13,6 @@ use std::iter::FusedIterator;
/// runtime-specific representation of a repeated scalar (`upb_Array*` on upb,
/// and `RepeatedField<T>*` on cpp).
use std::marker::PhantomData;
use std::ops::Deref;
use crate::{
Mut, MutProxy, Proxied, SettableValue, View, ViewProxy,
@ -164,6 +163,7 @@ where
/// Appends `val` to the end of the repeated field.
pub fn push(&mut self, val: View<T>) {
// TODO: b/320936046 - Use SettableValue instead of View for added ergonomics.
T::repeated_push(self.as_mut(), val);
}
@ -176,6 +176,7 @@ where
if index >= len {
panic!("index {index} >= repeated len {len}");
}
// TODO: b/320936046 - Use SettableValue instead of View for added ergonomics.
// SAFETY: `index` has been checked to be in-bounds.
unsafe { self.set_unchecked(index, val) }
}
@ -185,6 +186,7 @@ where
/// # Safety
/// Undefined behavior if `index >= len`
pub unsafe fn set_unchecked(&mut self, index: usize, val: View<T>) {
// TODO: b/320936046 - Use SettableValue instead of View for added ergonomics.
// SAFETY: `index` is in-bounds as promised by the caller.
unsafe { T::repeated_set_unchecked(self.as_mut(), index, val) }
}

@ -192,3 +192,49 @@ fn test_repeated_message() {
eq(vec![2]),
);
}
#[test]
fn test_repeated_strings() {
let mut older_msg = TestAllTypes::new();
{
let mut msg = TestAllTypes::new();
assert_that!(msg.repeated_string(), empty());
{
// TODO: b/320936046 - Test SettableValue once available
msg.repeated_string_mut().push("set from Mut".into());
}
assert_that!(msg.repeated_string().len(), eq(1));
// TODO: b/87654321 - Use elements_are! when ready
assert_that!(msg.repeated_string().get(0).unwrap(), eq("set from Mut"));
older_msg.repeated_string_mut().copy_from(msg.repeated_string());
}
// TODO: b/87654321 - Use elements_are! when ready
assert_that!(older_msg.repeated_string().len(), eq(1));
older_msg.repeated_string_mut().clear();
assert_that!(older_msg.repeated_string(), empty());
}
#[test]
fn test_repeated_bytes() {
let mut older_msg = TestAllTypes::new();
{
let mut msg = TestAllTypes::new();
assert_that!(msg.repeated_bytes(), empty());
{
// TODO: b/320936046 - Test SettableValue once available
msg.repeated_bytes_mut().push(b"set from Mut");
}
assert_that!(msg.repeated_bytes().len(), eq(1));
// TODO: b/87654321 - Use elements_are! when ready
assert_that!(msg.repeated_bytes().get(0).unwrap(), eq(b"set from Mut"));
older_msg.repeated_bytes_mut().copy_from(msg.repeated_bytes());
}
// TODO: b/87654321 - Use elements_are! when ready
assert_that!(older_msg.repeated_bytes().len(), eq(1));
older_msg.repeated_bytes_mut().clear();
assert_that!(older_msg.repeated_bytes(), empty());
}

@ -434,7 +434,7 @@ extern "C" {
}
macro_rules! impl_repeated_primitives {
($(($t:ty, $ufield:ident, $upb_tag:expr)),* $(,)?) => {
($(($t:ty, $elem_t:ty, $ufield:ident, $upb_tag:expr)),* $(,)?) => {
$(
unsafe impl ProxiedInRepeated for $t {
#[allow(dead_code)]
@ -467,7 +467,7 @@ macro_rules! impl_repeated_primitives {
unsafe {
upb_Array_Append(
f.as_raw(Private),
upb_MessageValue { $ufield: v },
<$t as UpbTypeConversions>::to_message_value(v),
f.raw_arena(Private))
}
}
@ -475,10 +475,17 @@ macro_rules! impl_repeated_primitives {
unsafe { upb_Array_Resize(f.as_raw(Private), 0, f.raw_arena(Private)); }
}
unsafe fn repeated_get_unchecked(f: View<Repeated<$t>>, i: usize) -> View<$t> {
unsafe { upb_Array_Get(f.as_raw(Private), i).$ufield }
unsafe {
<$t as UpbTypeConversions>::from_message_value(
upb_Array_Get(f.as_raw(Private), i)) }
}
unsafe fn repeated_set_unchecked(mut f: Mut<Repeated<$t>>, i: usize, v: View<$t>) {
unsafe { upb_Array_Set(f.as_raw(Private), i, upb_MessageValue { $ufield: v.into() }) }
unsafe {
upb_Array_Set(
f.as_raw(Private),
i,
<$t as UpbTypeConversions>::to_message_value(v.into()))
}
}
fn repeated_copy_from(src: View<Repeated<$t>>, mut dest: Mut<Repeated<$t>>) {
// SAFETY:
@ -492,7 +499,7 @@ macro_rules! impl_repeated_primitives {
ptr::copy_nonoverlapping(
upb_Array_DataPtr(src.as_raw(Private)).cast::<u8>(),
upb_Array_MutableDataPtr(dest.as_raw(Private)).cast::<u8>(),
size_of::<$t>() * src.len());
size_of::<$elem_t>() * src.len());
}
}
}
@ -509,13 +516,16 @@ impl<'msg, T: ?Sized> RepeatedMut<'msg, T> {
}
impl_repeated_primitives!(
(bool, bool_val, UpbCType::Bool),
(f32, float_val, UpbCType::Float),
(f64, double_val, UpbCType::Double),
(i32, int32_val, UpbCType::Int32),
(u32, uint32_val, UpbCType::UInt32),
(i64, int64_val, UpbCType::Int64),
(u64, uint64_val, UpbCType::UInt64),
// proxied type, element type, upb_MessageValue field name, UpbCType variant
(bool, bool, bool_val, UpbCType::Bool),
(f32, f32, float_val, UpbCType::Float),
(f64, f64, double_val, UpbCType::Double),
(i32, i32, int32_val, UpbCType::Int32),
(u32, u32, uint32_val, UpbCType::UInt32),
(i64, i64, int64_val, UpbCType::Int64),
(u64, u64, uint64_val, UpbCType::UInt64),
(ProtoStr, PtrAndLen, str_val, UpbCType::String),
([u8], PtrAndLen, str_val, UpbCType::Bytes),
);
/// Copy the contents of `src` into `dest`.

@ -77,7 +77,7 @@ std::unique_ptr<AccessorGenerator> AccessorGeneratorFor(
case FieldDescriptor::TYPE_BYTES:
case FieldDescriptor::TYPE_STRING:
if (field.is_repeated()) {
return std::make_unique<UnsupportedField>("repeated str not supported");
return std::make_unique<RepeatedField>();
}
return std::make_unique<SingularString>();
case FieldDescriptor::TYPE_MESSAGE:

@ -150,8 +150,12 @@ bool IsRepeatedPrimitive(const FieldDescriptor& field) {
field.cpp_type() == FieldDescriptor::CPPTYPE_UINT64;
}
bool IsRepeatedPtrPrimitive(const FieldDescriptor& field) {
return field.cpp_type() == FieldDescriptor::CPPTYPE_STRING;
}
std::string CppElementType(const FieldDescriptor& field) {
if (IsRepeatedPrimitive(field)) {
if (IsRepeatedPrimitive(field) || IsRepeatedPtrPrimitive(field)) {
return cpp::PrimitiveTypeName(field.cpp_type());
} else {
return cpp::QualifiedClassName(field.message_type());

Loading…
Cancel
Save