Implement rust repeated scalars for cpp and upb
PiperOrigin-RevId: 574261929
diff --git a/rust/BUILD b/rust/BUILD
index dd23b44..3e0ca71 100644
--- a/rust/BUILD
+++ b/rust/BUILD
@@ -52,6 +52,7 @@
"optional.rs",
"primitive.rs",
"proxied.rs",
+ "repeated.rs",
"shared.rs",
"string.rs",
"vtable.rs",
@@ -92,8 +93,14 @@
name = "protobuf_cpp",
srcs = PROTOBUF_SHARED + ["cpp.rs"],
crate_root = "shared.rs",
+ proc_macro_deps = [
+ "@crate_index//:paste",
+ ],
rustc_flags = ["--cfg=cpp_kernel"],
- deps = [":utf8"],
+ deps = [
+ ":utf8",
+ "//rust/cpp_kernel:cpp_api",
+ ],
)
rust_test(
diff --git a/rust/cpp.rs b/rust/cpp.rs
index c2af02d..6ef1240 100644
--- a/rust/cpp.rs
+++ b/rust/cpp.rs
@@ -7,7 +7,8 @@
// Rust Protobuf runtime using the C++ kernel.
-use crate::__internal::{Private, RawArena, RawMessage};
+use crate::__internal::{Private, RawArena, RawMessage, RawRepeatedField};
+use paste::paste;
use std::alloc::Layout;
use std::cell::UnsafeCell;
use std::fmt;
@@ -35,6 +36,7 @@
impl Arena {
/// Allocates a fresh arena.
#[inline]
+ #[allow(clippy::new_without_default)]
pub fn new() -> Self {
Self { ptr: NonNull::dangling(), _not_sync: PhantomData }
}
@@ -182,6 +184,116 @@
val
}
+/// RepeatedField impls delegate out to `extern "C"` functions exposed by
+/// `cpp_api.h` and store either a RepeatedField* or a RepeatedPtrField*
+/// depending on the type.
+///
+/// Note: even though this type is `Copy`, it should only be copied by
+/// protobuf internals that can maintain mutation invariants:
+///
+/// - No concurrent mutation for any two fields in a message: this means
+/// mutators cannot be `Send` but are `Sync`.
+/// - If there are multiple accessible `Mut` to a single message at a time, they
+/// must be different fields, and not be in the same oneof. As such, a `Mut`
+/// cannot be `Clone` but *can* reborrow itself with `.as_mut()`, which
+/// converts `&'b mut Mut<'a, T>` to `Mut<'b, T>`.
+#[derive(Clone, Copy)]
+pub struct RepeatedField<'msg, T: ?Sized> {
+ inner: RepeatedFieldInner<'msg>,
+ _phantom: PhantomData<&'msg mut T>,
+}
+
+/// CPP runtime-specific arguments for initializing a RepeatedField.
+/// See RepeatedField comment about mutation invariants for when this type can
+/// be copied.
+#[derive(Clone, Copy)]
+pub struct RepeatedFieldInner<'msg> {
+ pub raw: RawRepeatedField,
+ pub _phantom: PhantomData<&'msg ()>,
+}
+
+impl<'msg, T: ?Sized> RepeatedField<'msg, T> {
+ pub fn from_inner(_private: Private, inner: RepeatedFieldInner<'msg>) -> Self {
+ RepeatedField { inner, _phantom: PhantomData }
+ }
+}
+impl<'msg> RepeatedField<'msg, i32> {}
+
+pub trait RepeatedScalarOps {
+ fn new_repeated_field() -> RawRepeatedField;
+ fn push(f: RawRepeatedField, v: Self);
+ fn len(f: RawRepeatedField) -> usize;
+ fn get(f: RawRepeatedField, i: usize) -> Self;
+ fn set(f: RawRepeatedField, i: usize, v: Self);
+}
+
+macro_rules! impl_repeated_scalar_ops {
+ ($($t: ty),*) => {
+ paste! { $(
+ extern "C" {
+ fn [< __pb_rust_RepeatedField_ $t _new >]() -> RawRepeatedField;
+ fn [< __pb_rust_RepeatedField_ $t _add >](f: RawRepeatedField, v: $t);
+ fn [< __pb_rust_RepeatedField_ $t _size >](f: RawRepeatedField) -> usize;
+ fn [< __pb_rust_RepeatedField_ $t _get >](f: RawRepeatedField, i: usize) -> $t;
+ fn [< __pb_rust_RepeatedField_ $t _set >](f: RawRepeatedField, i: usize, v: $t);
+ }
+ impl RepeatedScalarOps for $t {
+ fn new_repeated_field() -> RawRepeatedField {
+ unsafe { [< __pb_rust_RepeatedField_ $t _new >]() }
+ }
+ fn push(f: RawRepeatedField, v: Self) {
+ unsafe { [< __pb_rust_RepeatedField_ $t _add >](f, v) }
+ }
+ fn len(f: RawRepeatedField) -> usize {
+ unsafe { [< __pb_rust_RepeatedField_ $t _size >](f) }
+ }
+ fn get(f: RawRepeatedField, i: usize) -> Self {
+ unsafe { [< __pb_rust_RepeatedField_ $t _get >](f, i) }
+ }
+ fn set(f: RawRepeatedField, i: usize, v: Self) {
+ unsafe { [< __pb_rust_RepeatedField_ $t _set >](f, i, v) }
+ }
+ }
+ )* }
+ };
+}
+
+impl_repeated_scalar_ops!(i32, u32, i64, u64, f32, f64, bool);
+
+impl<'msg, T: RepeatedScalarOps> RepeatedField<'msg, T> {
+ #[allow(clippy::new_without_default, dead_code)]
+ /// new() is not currently used in our normal pathways, it is only used
+ /// for testing. Existing `RepeatedField<>`s are owned by, and retrieved
+ /// from, the containing `Message`.
+ pub fn new() -> Self {
+ Self::from_inner(
+ Private,
+ RepeatedFieldInner::<'msg> { raw: T::new_repeated_field(), _phantom: PhantomData },
+ )
+ }
+ pub fn push(&mut self, val: T) {
+ T::push(self.inner.raw, val)
+ }
+ pub fn len(&self) -> usize {
+ T::len(self.inner.raw)
+ }
+ pub fn is_empty(&self) -> bool {
+ self.len() == 0
+ }
+ pub fn get(&self, index: usize) -> Option<T> {
+ if index >= self.len() {
+ return None;
+ }
+ Some(T::get(self.inner.raw, index))
+ }
+ pub fn set(&mut self, index: usize, val: T) {
+ if index >= self.len() {
+ return;
+ }
+ T::set(self.inner.raw, index, val)
+ }
+}
+
#[cfg(test)]
mod tests {
use super::*;
@@ -201,4 +313,27 @@
let serialized_data = SerializedData { data: NonNull::new(ptr).unwrap(), len: len };
assert_eq!(&*serialized_data, b"Hello world");
}
+
+ #[test]
+ fn repeated_field() {
+ let mut r = RepeatedField::<i32>::new();
+ assert_eq!(r.len(), 0);
+ r.push(32);
+ assert_eq!(r.get(0), Some(32));
+
+ let mut r = RepeatedField::<u32>::new();
+ assert_eq!(r.len(), 0);
+ r.push(32);
+ assert_eq!(r.get(0), Some(32));
+
+ let mut r = RepeatedField::<f64>::new();
+ assert_eq!(r.len(), 0);
+ r.push(0.1234f64);
+ assert_eq!(r.get(0), Some(0.1234));
+
+ let mut r = RepeatedField::<bool>::new();
+ assert_eq!(r.len(), 0);
+ r.push(true);
+ assert_eq!(r.get(0), Some(true));
+ }
}
diff --git a/rust/cpp_kernel/BUILD b/rust/cpp_kernel/BUILD
index 245772c..d10f9e7 100644
--- a/rust/cpp_kernel/BUILD
+++ b/rust/cpp_kernel/BUILD
@@ -4,13 +4,14 @@
cc_library(
name = "cpp_api",
+ srcs = ["cpp_api.cc"],
hdrs = ["cpp_api.h"],
visibility = [
"//src/google/protobuf:__subpackages__",
"//rust:__subpackages__",
],
deps = [
- ":rust_alloc_for_cpp_api",
+ ":rust_alloc_for_cpp_api", # buildcleaner: keep
"//:protobuf_nowkt",
],
)
diff --git a/rust/cpp_kernel/cpp_api.cc b/rust/cpp_kernel/cpp_api.cc
new file mode 100644
index 0000000..8ff79d8
--- /dev/null
+++ b/rust/cpp_kernel/cpp_api.cc
@@ -0,0 +1,35 @@
+#include "google/protobuf/repeated_field.h"
+
+extern "C" {
+
+#define expose_repeated_field_methods(ty, rust_ty) \
+ google::protobuf::RepeatedField<ty>* __pb_rust_RepeatedField_##rust_ty##_new() { \
+ return new google::protobuf::RepeatedField<ty>(); \
+ } \
+ void __pb_rust_RepeatedField_##rust_ty##_add(google::protobuf::RepeatedField<ty>* r, \
+ ty val) { \
+ r->Add(val); \
+ } \
+ size_t __pb_rust_RepeatedField_##rust_ty##_size( \
+ google::protobuf::RepeatedField<ty>* r) { \
+ return r->size(); \
+ } \
+ ty __pb_rust_RepeatedField_##rust_ty##_get(google::protobuf::RepeatedField<ty>* r, \
+ size_t index) { \
+ return r->Get(index); \
+ } \
+ void __pb_rust_RepeatedField_##rust_ty##_set(google::protobuf::RepeatedField<ty>* r, \
+ size_t index, ty val) { \
+ return r->Set(index, val); \
+ }
+
+expose_repeated_field_methods(int32_t, i32);
+expose_repeated_field_methods(uint32_t, u32);
+expose_repeated_field_methods(float, f32);
+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
+}
diff --git a/rust/internal.rs b/rust/internal.rs
index 1e0f536..e56c9dc 100644
--- a/rust/internal.rs
+++ b/rust/internal.rs
@@ -51,6 +51,17 @@
_data: [u8; 0],
_marker: std::marker::PhantomData<(*mut u8, ::std::marker::PhantomPinned)>,
}
+
+ /// Opaque pointee for [`RawRepeatedField`]
+ ///
+ /// This type is not meant to be dereferenced in Rust code.
+ /// It is only meant to provide type safety for raw pointers
+ /// which are manipulated behind FFI.
+ #[repr(C)]
+ pub struct RawRepeatedFieldData {
+ _data: [u8; 0],
+ _marker: std::marker::PhantomData<(*mut u8, ::std::marker::PhantomPinned)>,
+ }
}
/// A raw pointer to the underlying message for this runtime.
@@ -59,6 +70,9 @@
/// A raw pointer to the underlying arena for this runtime.
pub type RawArena = NonNull<_opaque_pointees::RawArenaData>;
+/// A raw pointer to the underlying repeated field container for this runtime.
+pub type RawRepeatedField = NonNull<_opaque_pointees::RawRepeatedFieldData>;
+
/// Represents an ABI-stable version of `NonNull<[u8]>`/`string_view` (a
/// borrowed slice of bytes) for FFI use only.
///
diff --git a/rust/repeated.rs b/rust/repeated.rs
new file mode 100644
index 0000000..b824de8
--- /dev/null
+++ b/rust/repeated.rs
@@ -0,0 +1,119 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2023 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
+
+/// Repeated scalar fields are implemented around the runtime-specific
+/// `RepeatedField` struct. `RepeatedField` stores an opaque pointer to the
+/// runtime-specific representation of a repeated scalar (`upb_Array*` on upb,
+/// and `RepeatedField<T>*` on cpp).
+use std::marker::PhantomData;
+
+use crate::{
+ __internal::{Private, RawRepeatedField},
+ __runtime::{RepeatedField, RepeatedFieldInner},
+};
+
+#[derive(Clone, Copy)]
+pub struct RepeatedFieldRef<'a> {
+ pub repeated_field: RawRepeatedField,
+ pub _phantom: PhantomData<&'a mut ()>,
+}
+
+unsafe impl<'a> Send for RepeatedFieldRef<'a> {}
+unsafe impl<'a> Sync for RepeatedFieldRef<'a> {}
+
+#[derive(Clone, Copy)]
+#[repr(transparent)]
+pub struct RepeatedView<'a, T: ?Sized> {
+ inner: RepeatedField<'a, T>,
+}
+
+impl<'msg, T: ?Sized> RepeatedView<'msg, T> {
+ pub fn from_inner(_private: Private, inner: RepeatedFieldInner<'msg>) -> Self {
+ Self { inner: RepeatedField::<'msg>::from_inner(_private, inner) }
+ }
+}
+
+pub struct RepeatedFieldIter<'a, T> {
+ inner: RepeatedField<'a, T>,
+ current_index: usize,
+}
+
+impl<'a, T> std::fmt::Debug for RepeatedView<'a, T> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_tuple("RepeatedView").finish()
+ }
+}
+
+#[repr(transparent)]
+pub struct RepeatedMut<'a, T: ?Sized> {
+ inner: RepeatedField<'a, T>,
+}
+
+impl<'msg, T: ?Sized> RepeatedMut<'msg, T> {
+ pub fn from_inner(_private: Private, inner: RepeatedFieldInner<'msg>) -> Self {
+ Self { inner: RepeatedField::from_inner(_private, inner) }
+ }
+}
+
+impl<'a, T> std::ops::Deref for RepeatedMut<'a, T> {
+ type Target = RepeatedView<'a, T>;
+ fn deref(&self) -> &Self::Target {
+ // SAFETY:
+ // - `Repeated{View,Mut}<'a, T>` are both `#[repr(transparent)]` over
+ // `RepeatedField<'a, T>`.
+ // - `RepeatedField` is a type alias for `NonNull`.
+ unsafe { &*(self as *const Self as *const RepeatedView<'a, T>) }
+ }
+}
+
+macro_rules! impl_repeated_primitives {
+ ($($t:ty),*) => {
+ $(
+ impl<'a> RepeatedView<'a, $t> {
+ pub fn len(&self) -> usize {
+ self.inner.len()
+ }
+ pub fn is_empty(&self) -> bool {
+ self.len() == 0
+ }
+ pub fn get(&self, index: usize) -> Option<$t> {
+ self.inner.get(index)
+ }
+ }
+
+ impl<'a> RepeatedMut<'a, $t> {
+ pub fn push(&mut self, val: $t) {
+ self.inner.push(val)
+ }
+ pub fn set(&mut self, index: usize, val: $t) {
+ self.inner.set(index, val)
+ }
+ }
+
+ impl<'a> std::iter::Iterator for RepeatedFieldIter<'a, $t> {
+ type Item = $t;
+ fn next(&mut self) -> Option<Self::Item> {
+ let val = self.inner.get(self.current_index);
+ if val.is_some() {
+ self.current_index += 1;
+ }
+ val
+ }
+ }
+
+ impl<'a> std::iter::IntoIterator for RepeatedView<'a, $t> {
+ type Item = $t;
+ type IntoIter = RepeatedFieldIter<'a, $t>;
+ fn into_iter(self) -> Self::IntoIter {
+ RepeatedFieldIter { inner: self.inner, current_index: 0 }
+ }
+ }
+ )*
+ }
+}
+
+impl_repeated_primitives!(i32, u32, bool, f32, f64, i64, u64);
diff --git a/rust/shared.rs b/rust/shared.rs
index 3c4408d..f8a9d11 100644
--- a/rust/shared.rs
+++ b/rust/shared.rs
@@ -22,6 +22,7 @@
pub use crate::proxied::{
Mut, MutProxy, Proxied, ProxiedWithPresence, SettableValue, View, ViewProxy,
};
+ pub use crate::repeated::{RepeatedFieldRef, RepeatedMut, RepeatedView};
pub use crate::string::{BytesMut, ProtoStr, ProtoStrMut};
}
pub use __public::*;
@@ -46,6 +47,7 @@
mod optional;
mod primitive;
mod proxied;
+mod repeated;
mod string;
mod vtable;
diff --git a/rust/test/cpp/interop/test_utils.cc b/rust/test/cpp/interop/test_utils.cc
index d5c3784..5c27a95 100644
--- a/rust/test/cpp/interop/test_utils.cc
+++ b/rust/test/cpp/interop/test_utils.cc
@@ -8,7 +8,7 @@
#include <cstddef>
#include "absl/strings/string_view.h"
-#include "google/protobuf/rust/cpp_kernel/cpp_api.h"
+#include "rust/cpp_kernel/cpp_api.h"
#include "google/protobuf/unittest.pb.h"
extern "C" void MutateTestAllTypes(protobuf_unittest::TestAllTypes* msg) {
diff --git a/rust/test/shared/BUILD b/rust/test/shared/BUILD
index e5be7b7..79d43dd 100644
--- a/rust/test/shared/BUILD
+++ b/rust/test/shared/BUILD
@@ -151,6 +151,9 @@
"//rust:protobuf_cpp": "protobuf",
"//rust/test/shared:matchers_cpp": "matchers",
},
+ proc_macro_deps = [
+ "@crate_index//:paste",
+ ],
tags = [
# TODO: Enable testing on arm once we support sanitizers for Rust on Arm.
"not_build:arm",
@@ -170,6 +173,9 @@
"//rust:protobuf_upb": "protobuf",
"//rust/test/shared:matchers_upb": "matchers",
},
+ proc_macro_deps = [
+ "@crate_index//:paste",
+ ],
tags = [
# TODO: Enable testing on arm once we support sanitizers for Rust on Arm.
"not_build:arm",
diff --git a/rust/test/shared/accessors_test.rs b/rust/test/shared/accessors_test.rs
index 40098b1..910f9f1 100644
--- a/rust/test/shared/accessors_test.rs
+++ b/rust/test/shared/accessors_test.rs
@@ -9,6 +9,7 @@
use googletest::prelude::*;
use matchers::{is_set, is_unset};
+use paste::paste;
use protobuf::Optional;
use unittest_proto::proto2_unittest::{TestAllTypes, TestAllTypes_};
@@ -398,3 +399,51 @@
// This should show it set to the OneofBytes but its not supported yet.
assert_that!(msg.oneof_field(), matches_pattern!(not_set(_)));
}
+
+macro_rules! generate_repeated_numeric_test {
+ ($(($t: ty, $field: ident)),*) => {
+ paste! { $(
+ #[test]
+ fn [< test_repeated_ $field _accessors >]() {
+ let mut msg = TestAllTypes::new();
+ assert_that!(msg.[< repeated_ $field >]().len(), eq(0));
+ assert_that!(msg.[<repeated_ $field >]().get(0), none());
+
+ let mut mutator = msg.[<repeated_ $field _mut >]();
+ mutator.push(1 as $t);
+ assert_that!(mutator.len(), eq(1));
+ assert_that!(mutator.get(0), some(eq(1 as $t)));
+ mutator.set(0, 2 as $t);
+ assert_that!(mutator.get(0), some(eq(2 as $t)));
+ mutator.push(1 as $t);
+
+ assert_that!(mutator.into_iter().collect::<Vec<_>>(), eq(vec![2 as $t, 1 as $t]));
+ }
+ )* }
+ };
+}
+
+generate_repeated_numeric_test!(
+ (i32, int32),
+ (u32, uint32),
+ (i64, int64),
+ (u64, uint64),
+ (f32, float),
+ (f64, double)
+);
+
+#[test]
+fn test_repeated_bool_accessors() {
+ let mut msg = TestAllTypes::new();
+ assert_that!(msg.repeated_bool().len(), eq(0));
+ assert_that!(msg.repeated_bool().get(0), none());
+
+ let mut mutator = msg.repeated_bool_mut();
+ mutator.push(true);
+ assert_that!(mutator.len(), eq(1));
+ assert_that!(mutator.get(0), some(eq(true)));
+ mutator.set(0, false);
+ assert_that!(mutator.get(0), some(eq(false)));
+ mutator.push(true);
+ assert_that!(mutator.into_iter().collect::<Vec<_>>(), eq(vec![false, true]));
+}
diff --git a/rust/upb.rs b/rust/upb.rs
index cd5cc77..57f9c55 100644
--- a/rust/upb.rs
+++ b/rust/upb.rs
@@ -7,7 +7,7 @@
//! UPB FFI wrapper code for use by Rust Protobuf.
-use crate::__internal::{Private, RawArena, RawMessage};
+use crate::__internal::{Private, PtrAndLen, RawArena, RawMessage, RawRepeatedField};
use std::alloc;
use std::alloc::Layout;
use std::cell::UnsafeCell;
@@ -284,6 +284,149 @@
}
}
+/// RepeatedFieldInner contains a `upb_Array*` as well as a reference to an
+/// `Arena`, most likely that of the containing `Message`. upb requires an Arena
+/// to perform mutations on a repeated field.
+#[derive(Clone, Copy, Debug)]
+pub struct RepeatedFieldInner<'msg> {
+ pub raw: RawRepeatedField,
+ pub arena: &'msg Arena,
+}
+
+#[derive(Clone, Copy, Debug)]
+pub struct RepeatedField<'msg, T: ?Sized> {
+ inner: RepeatedFieldInner<'msg>,
+ _phantom: PhantomData<&'msg mut T>,
+}
+
+impl<'msg, T: ?Sized> RepeatedField<'msg, T> {
+ pub fn len(&self) -> usize {
+ unsafe { upb_Array_Size(self.inner.raw) }
+ }
+ pub fn is_empty(&self) -> bool {
+ self.len() == 0
+ }
+ pub fn from_inner(_private: Private, inner: RepeatedFieldInner<'msg>) -> Self {
+ Self { inner, _phantom: PhantomData }
+ }
+}
+
+// Transcribed from google3/third_party/upb/upb/message/value.h
+#[repr(C)]
+#[derive(Clone, Copy)]
+union upb_MessageValue {
+ bool_val: bool,
+ float_val: std::ffi::c_float,
+ double_val: std::ffi::c_double,
+ uint32_val: u32,
+ int32_val: i32,
+ uint64_val: u64,
+ int64_val: i64,
+ array_val: *const std::ffi::c_void,
+ map_val: *const std::ffi::c_void,
+ msg_val: *const std::ffi::c_void,
+ str_val: PtrAndLen,
+}
+
+// Transcribed from google3/third_party/upb/upb/base/descriptor_constants.h
+#[repr(C)]
+#[allow(dead_code)]
+enum UpbCType {
+ Bool = 1,
+ Float = 2,
+ Int32 = 3,
+ UInt32 = 4,
+ Enum = 5,
+ Message = 6,
+ Double = 7,
+ Int64 = 8,
+ UInt64 = 9,
+ String = 10,
+ Bytes = 11,
+}
+
+extern "C" {
+ #[allow(dead_code)]
+ fn upb_Array_New(a: RawArena, r#type: std::ffi::c_int) -> RawRepeatedField;
+ fn upb_Array_Size(arr: RawRepeatedField) -> usize;
+ fn upb_Array_Set(arr: RawRepeatedField, i: usize, val: upb_MessageValue);
+ fn upb_Array_Get(arr: RawRepeatedField, i: usize) -> upb_MessageValue;
+ fn upb_Array_Append(arr: RawRepeatedField, val: upb_MessageValue, arena: RawArena);
+}
+
+macro_rules! impl_repeated_primitives {
+ ($(($rs_type:ty, $union_field:ident, $upb_tag:expr)),*) => {
+ $(
+ impl<'msg> RepeatedField<'msg, $rs_type> {
+ #[allow(dead_code)]
+ fn new(arena: &'msg Arena) -> Self {
+ Self {
+ inner: RepeatedFieldInner {
+ raw: unsafe { upb_Array_New(arena.raw, $upb_tag as std::ffi::c_int) },
+ arena,
+ },
+ _phantom: PhantomData,
+ }
+ }
+ pub fn push(&mut self, val: $rs_type) {
+ unsafe { upb_Array_Append(
+ self.inner.raw,
+ upb_MessageValue { $union_field: val },
+ self.inner.arena.raw(),
+ ) }
+ }
+ pub fn get(&self, i: usize) -> Option<$rs_type> {
+ if i >= self.len() {
+ None
+ } else {
+ unsafe { Some(upb_Array_Get(self.inner.raw, i).$union_field) }
+ }
+ }
+ pub fn set(&self, i: usize, val: $rs_type) {
+ if i >= self.len() {
+ return;
+ }
+ unsafe { upb_Array_Set(
+ self.inner.raw,
+ i,
+ upb_MessageValue { $union_field: val },
+ ) }
+ }
+ }
+ )*
+ }
+}
+
+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)
+);
+
+/// Returns a static thread-local empty RepeatedFieldInner for use in a
+/// RepeatedView.
+///
+/// # Safety
+/// TODO: Split RepeatedFieldInner into mut and const variants to
+/// enforce safety. The returned array must never be mutated.
+pub unsafe fn empty_array() -> RepeatedFieldInner<'static> {
+ // TODO: Consider creating empty array in C.
+ fn new_repeated_field_inner() -> RepeatedFieldInner<'static> {
+ let arena = Box::leak::<'static>(Box::new(Arena::new()));
+ // Provide `i32` as a placeholder type.
+ RepeatedField::<'static, i32>::new(arena).inner
+ }
+ thread_local! {
+ static REPEATED_FIELD: RepeatedFieldInner<'static> = new_repeated_field_inner();
+ }
+
+ REPEATED_FIELD.with(|inner| *inner)
+}
+
#[cfg(test)]
mod tests {
use super::*;
@@ -309,4 +452,35 @@
};
assert_eq!(&*serialized_data, b"Hello world");
}
+
+ #[test]
+ fn i32_array() {
+ let mut arena = Arena::new();
+ let mut arr = RepeatedField::<i32>::new(&arena);
+ assert_eq!(arr.len(), 0);
+ arr.push(1);
+ assert_eq!(arr.get(0), Some(1));
+ assert_eq!(arr.len(), 1);
+ arr.set(0, 3);
+ assert_eq!(arr.get(0), Some(3));
+ for i in 0..2048 {
+ arr.push(i);
+ assert_eq!(arr.get(arr.len() - 1), Some(i));
+ }
+ }
+ #[test]
+ fn u32_array() {
+ let mut arena = Arena::new();
+ let mut arr = RepeatedField::<u32>::new(&mut arena);
+ assert_eq!(arr.len(), 0);
+ arr.push(1);
+ assert_eq!(arr.get(0), Some(1));
+ assert_eq!(arr.len(), 1);
+ arr.set(0, 3);
+ assert_eq!(arr.get(0), Some(3));
+ for i in 0..2048 {
+ arr.push(i);
+ assert_eq!(arr.get(arr.len() - 1), Some(i));
+ }
+ }
}
diff --git a/rust/upb_kernel/BUILD b/rust/upb_kernel/BUILD
index dc65231..b06f182 100644
--- a/rust/upb_kernel/BUILD
+++ b/rust/upb_kernel/BUILD
@@ -8,6 +8,7 @@
"//rust:__subpackages__",
],
deps = [
+ "//upb:collections",
"//upb:mem",
],
)
diff --git a/rust/upb_kernel/upb_api.c b/rust/upb_kernel/upb_api.c
index 985749d..a30b4dd 100644
--- a/rust/upb_kernel/upb_api.c
+++ b/rust/upb_kernel/upb_api.c
@@ -8,4 +8,5 @@
#define UPB_BUILD_API
-#include "upb/mem/arena.h" // IWYU pragma: keep
+#include "upb/collections/array.h" // IWYU pragma: keep
+#include "upb/mem/arena.h" // IWYU pragma: keep