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