Skip to content

Commit e1bb7d6

Browse files
Implement rust repeated scalars for cpp and upb
PiperOrigin-RevId: 574261929
1 parent 8489d8d commit e1bb7d6

21 files changed

+831
-18
lines changed

Cargo.bazel.lock

+63-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"checksum": "8bc2d235f612e77f4dca1b6886cc8bd14df348168fea27a687805ed9518a8f1a",
2+
"checksum": "641f887b045ff0fc19f64df79b53d96d77d1c03c96069036d84bd1104ddc0000",
33
"crates": {
44
"aho-corasick 1.1.2": {
55
"name": "aho-corasick",
@@ -108,6 +108,15 @@
108108
"selects": {}
109109
},
110110
"edition": "2018",
111+
"proc_macro_deps": {
112+
"common": [
113+
{
114+
"id": "paste 1.0.14",
115+
"target": "paste"
116+
}
117+
],
118+
"selects": {}
119+
},
111120
"version": "0.0.1"
112121
},
113122
"license": null
@@ -318,6 +327,59 @@
318327
},
319328
"license": "MIT OR Apache-2.0"
320329
},
330+
"paste 1.0.14": {
331+
"name": "paste",
332+
"version": "1.0.14",
333+
"repository": {
334+
"Http": {
335+
"url": "https://crates.io/api/v1/crates/paste/1.0.14/download",
336+
"sha256": "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
337+
}
338+
},
339+
"targets": [
340+
{
341+
"ProcMacro": {
342+
"crate_name": "paste",
343+
"crate_root": "src/lib.rs",
344+
"srcs": [
345+
"**/*.rs"
346+
]
347+
}
348+
},
349+
{
350+
"BuildScript": {
351+
"crate_name": "build_script_build",
352+
"crate_root": "build.rs",
353+
"srcs": [
354+
"**/*.rs"
355+
]
356+
}
357+
}
358+
],
359+
"library_target_name": "paste",
360+
"common_attrs": {
361+
"compile_data_glob": [
362+
"**"
363+
],
364+
"deps": {
365+
"common": [
366+
{
367+
"id": "paste 1.0.14",
368+
"target": "build_script_build"
369+
}
370+
],
371+
"selects": {}
372+
},
373+
"edition": "2018",
374+
"version": "1.0.14"
375+
},
376+
"build_script_attrs": {
377+
"data_glob": [
378+
"**"
379+
]
380+
},
381+
"license": "MIT OR Apache-2.0"
382+
},
321383
"proc-macro2 1.0.69": {
322384
"name": "proc-macro2",
323385
"version": "1.0.69",
@@ -769,4 +831,3 @@
769831
},
770832
"conditions": {}
771833
}
772-

Cargo.lock

+7-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

WORKSPACE

+3
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,9 @@ crates_repository(
193193
"googletest": crate.spec(
194194
version = ">0.0.0",
195195
),
196+
"paste": crate.spec(
197+
version = ">=1",
198+
),
196199
},
197200
)
198201

rust/BUILD

+8-1
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ PROTOBUF_SHARED = [
5252
"optional.rs",
5353
"primitive.rs",
5454
"proxied.rs",
55+
"repeated.rs",
5556
"shared.rs",
5657
"string.rs",
5758
"vtable.rs",
@@ -92,8 +93,14 @@ rust_library(
9293
name = "protobuf_cpp",
9394
srcs = PROTOBUF_SHARED + ["cpp.rs"],
9495
crate_root = "shared.rs",
96+
proc_macro_deps = [
97+
"@crate_index//:paste",
98+
],
9599
rustc_flags = ["--cfg=cpp_kernel"],
96-
deps = [":utf8"],
100+
deps = [
101+
":utf8",
102+
"//rust/cpp_kernel:cpp_api",
103+
],
97104
)
98105

99106
rust_test(

rust/cpp.rs

+136-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77

88
// Rust Protobuf runtime using the C++ kernel.
99

10-
use crate::__internal::{Private, RawArena, RawMessage};
10+
use crate::__internal::{Private, RawArena, RawMessage, RawRepeatedField};
11+
use paste::paste;
1112
use std::alloc::Layout;
1213
use std::cell::UnsafeCell;
1314
use std::fmt;
@@ -35,6 +36,7 @@ pub struct Arena {
3536
impl Arena {
3637
/// Allocates a fresh arena.
3738
#[inline]
39+
#[allow(clippy::new_without_default)]
3840
pub fn new() -> Self {
3941
Self { ptr: NonNull::dangling(), _not_sync: PhantomData }
4042
}
@@ -182,6 +184,116 @@ pub fn copy_bytes_in_arena_if_needed_by_runtime<'a>(
182184
val
183185
}
184186

187+
/// RepeatedField impls delegate out to `extern "C"` functions exposed by
188+
/// `cpp_api.h` and store either a RepeatedField* or a RepeatedPtrField*
189+
/// depending on the type.
190+
///
191+
/// Note: even though this type is `Copy`, it should only be copied by
192+
/// protobuf internals that can maintain mutation invariants:
193+
///
194+
/// - No concurrent mutation for any two fields in a message: this means
195+
/// mutators cannot be `Send` but are `Sync`.
196+
/// - If there are multiple accessible `Mut` to a single message at a time, they
197+
/// must be different fields, and not be in the same oneof. As such, a `Mut`
198+
/// cannot be `Clone` but *can* reborrow itself with `.as_mut()`, which
199+
/// converts `&'b mut Mut<'a, T>` to `Mut<'b, T>`.
200+
#[derive(Clone, Copy)]
201+
pub struct RepeatedField<'msg, T: ?Sized> {
202+
inner: RepeatedFieldInner<'msg>,
203+
_phantom: PhantomData<&'msg mut T>,
204+
}
205+
206+
/// CPP runtime-specific arguments for initializing a RepeatedField.
207+
/// See RepeatedField comment about mutation invariants for when this type can
208+
/// be copied.
209+
#[derive(Clone, Copy)]
210+
pub struct RepeatedFieldInner<'msg> {
211+
pub raw: RawRepeatedField,
212+
pub _phantom: PhantomData<&'msg ()>,
213+
}
214+
215+
impl<'msg, T: ?Sized> RepeatedField<'msg, T> {
216+
pub fn from_inner(_private: Private, inner: RepeatedFieldInner<'msg>) -> Self {
217+
RepeatedField { inner, _phantom: PhantomData }
218+
}
219+
}
220+
impl<'msg> RepeatedField<'msg, i32> {}
221+
222+
pub trait RepeatedScalarOps {
223+
fn new_repeated_field() -> RawRepeatedField;
224+
fn push(f: RawRepeatedField, v: Self);
225+
fn len(f: RawRepeatedField) -> usize;
226+
fn get(f: RawRepeatedField, i: usize) -> Self;
227+
fn set(f: RawRepeatedField, i: usize, v: Self);
228+
}
229+
230+
macro_rules! impl_repeated_scalar_ops {
231+
($($t: ty),*) => {
232+
paste! { $(
233+
extern "C" {
234+
fn [< __pb_rust_RepeatedField_ $t _new >]() -> RawRepeatedField;
235+
fn [< __pb_rust_RepeatedField_ $t _add >](f: RawRepeatedField, v: $t);
236+
fn [< __pb_rust_RepeatedField_ $t _size >](f: RawRepeatedField) -> usize;
237+
fn [< __pb_rust_RepeatedField_ $t _get >](f: RawRepeatedField, i: usize) -> $t;
238+
fn [< __pb_rust_RepeatedField_ $t _set >](f: RawRepeatedField, i: usize, v: $t);
239+
}
240+
impl RepeatedScalarOps for $t {
241+
fn new_repeated_field() -> RawRepeatedField {
242+
unsafe { [< __pb_rust_RepeatedField_ $t _new >]() }
243+
}
244+
fn push(f: RawRepeatedField, v: Self) {
245+
unsafe { [< __pb_rust_RepeatedField_ $t _add >](f, v) }
246+
}
247+
fn len(f: RawRepeatedField) -> usize {
248+
unsafe { [< __pb_rust_RepeatedField_ $t _size >](f) }
249+
}
250+
fn get(f: RawRepeatedField, i: usize) -> Self {
251+
unsafe { [< __pb_rust_RepeatedField_ $t _get >](f, i) }
252+
}
253+
fn set(f: RawRepeatedField, i: usize, v: Self) {
254+
unsafe { [< __pb_rust_RepeatedField_ $t _set >](f, i, v) }
255+
}
256+
}
257+
)* }
258+
};
259+
}
260+
261+
impl_repeated_scalar_ops!(i32, u32, i64, u64, f32, f64, bool);
262+
263+
impl<'msg, T: RepeatedScalarOps> RepeatedField<'msg, T> {
264+
#[allow(clippy::new_without_default, dead_code)]
265+
/// new() is not currently used in our normal pathways, it is only used
266+
/// for testing. Existing `RepeatedField<>`s are owned by, and retrieved
267+
/// from, the containing `Message`.
268+
pub fn new() -> Self {
269+
Self::from_inner(
270+
Private,
271+
RepeatedFieldInner::<'msg> { raw: T::new_repeated_field(), _phantom: PhantomData },
272+
)
273+
}
274+
pub fn push(&mut self, val: T) {
275+
T::push(self.inner.raw, val)
276+
}
277+
pub fn len(&self) -> usize {
278+
T::len(self.inner.raw)
279+
}
280+
pub fn is_empty(&self) -> bool {
281+
self.len() == 0
282+
}
283+
pub fn get(&self, index: usize) -> Option<T> {
284+
if index >= self.len() {
285+
return None;
286+
}
287+
Some(T::get(self.inner.raw, index))
288+
}
289+
pub fn set(&mut self, index: usize, val: T) {
290+
if index >= self.len() {
291+
return;
292+
}
293+
T::set(self.inner.raw, index, val)
294+
}
295+
}
296+
185297
#[cfg(test)]
186298
mod tests {
187299
use super::*;
@@ -201,4 +313,27 @@ mod tests {
201313
let serialized_data = SerializedData { data: NonNull::new(ptr).unwrap(), len: len };
202314
assert_eq!(&*serialized_data, b"Hello world");
203315
}
316+
317+
#[test]
318+
fn repeated_field() {
319+
let mut r = RepeatedField::<i32>::new();
320+
assert_eq!(r.len(), 0);
321+
r.push(32);
322+
assert_eq!(r.get(0), Some(32));
323+
324+
let mut r = RepeatedField::<u32>::new();
325+
assert_eq!(r.len(), 0);
326+
r.push(32);
327+
assert_eq!(r.get(0), Some(32));
328+
329+
let mut r = RepeatedField::<f64>::new();
330+
assert_eq!(r.len(), 0);
331+
r.push(0.1234f64);
332+
assert_eq!(r.get(0), Some(0.1234));
333+
334+
let mut r = RepeatedField::<bool>::new();
335+
assert_eq!(r.len(), 0);
336+
r.push(true);
337+
assert_eq!(r.get(0), Some(true));
338+
}
204339
}

rust/cpp_kernel/BUILD

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@ load("@rules_rust//rust:defs.bzl", "rust_library")
44

55
cc_library(
66
name = "cpp_api",
7+
srcs = ["cpp_api.cc"],
78
hdrs = ["cpp_api.h"],
89
visibility = [
910
"//src/google/protobuf:__subpackages__",
1011
"//rust:__subpackages__",
1112
],
1213
deps = [
13-
":rust_alloc_for_cpp_api",
14+
":rust_alloc_for_cpp_api", # buildcleaner: keep
1415
"//:protobuf_nowkt",
1516
],
1617
)

rust/cpp_kernel/cpp_api.cc

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#include "google/protobuf/repeated_field.h"
2+
3+
extern "C" {
4+
5+
#define expose_repeated_field_methods(ty, rust_ty) \
6+
google::protobuf::RepeatedField<ty>* __pb_rust_RepeatedField_##rust_ty##_new() { \
7+
return new google::protobuf::RepeatedField<ty>(); \
8+
} \
9+
void __pb_rust_RepeatedField_##rust_ty##_add(google::protobuf::RepeatedField<ty>* r, \
10+
ty val) { \
11+
r->Add(val); \
12+
} \
13+
size_t __pb_rust_RepeatedField_##rust_ty##_size( \
14+
google::protobuf::RepeatedField<ty>* r) { \
15+
return r->size(); \
16+
} \
17+
ty __pb_rust_RepeatedField_##rust_ty##_get(google::protobuf::RepeatedField<ty>* r, \
18+
size_t index) { \
19+
return r->Get(index); \
20+
} \
21+
void __pb_rust_RepeatedField_##rust_ty##_set(google::protobuf::RepeatedField<ty>* r, \
22+
size_t index, ty val) { \
23+
return r->Set(index, val); \
24+
}
25+
26+
expose_repeated_field_methods(int32_t, i32);
27+
expose_repeated_field_methods(uint32_t, u32);
28+
expose_repeated_field_methods(float, f32);
29+
expose_repeated_field_methods(double, f64);
30+
expose_repeated_field_methods(bool, bool);
31+
expose_repeated_field_methods(uint64_t, u64);
32+
expose_repeated_field_methods(int64_t, i64);
33+
34+
#undef expose_repeated_field_methods
35+
}

rust/internal.rs

+14
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,17 @@ mod _opaque_pointees {
5151
_data: [u8; 0],
5252
_marker: std::marker::PhantomData<(*mut u8, ::std::marker::PhantomPinned)>,
5353
}
54+
55+
/// Opaque pointee for [`RawRepeatedField`]
56+
///
57+
/// This type is not meant to be dereferenced in Rust code.
58+
/// It is only meant to provide type safety for raw pointers
59+
/// which are manipulated behind FFI.
60+
#[repr(C)]
61+
pub struct RawRepeatedFieldData {
62+
_data: [u8; 0],
63+
_marker: std::marker::PhantomData<(*mut u8, ::std::marker::PhantomPinned)>,
64+
}
5465
}
5566

5667
/// A raw pointer to the underlying message for this runtime.
@@ -59,6 +70,9 @@ pub type RawMessage = NonNull<_opaque_pointees::RawMessageData>;
5970
/// A raw pointer to the underlying arena for this runtime.
6071
pub type RawArena = NonNull<_opaque_pointees::RawArenaData>;
6172

73+
/// A raw pointer to the underlying repeated field container for this runtime.
74+
pub type RawRepeatedField = NonNull<_opaque_pointees::RawRepeatedFieldData>;
75+
6276
/// Represents an ABI-stable version of `NonNull<[u8]>`/`string_view` (a
6377
/// borrowed slice of bytes) for FFI use only.
6478
///

0 commit comments

Comments
 (0)