Implement map iteration

PiperOrigin-RevId: 604447996
pull/15691/head
Alyssa Haroldsen 1 year ago committed by Copybara-Service
parent 37826c1da6
commit 035d6ec2cb
  1. 173
      rust/cpp.rs
  2. 94
      rust/cpp_kernel/cpp_api.cc
  3. 145
      rust/map.rs
  4. 2
      rust/shared.rs
  5. 77
      rust/test/shared/accessors_map_test.rs
  6. 61
      rust/upb.rs
  7. 4
      rust/upb_kernel/upb_api.c
  8. 39
      src/google/protobuf/map.h
  9. 11
      src/google/protobuf/map_test.cc

@ -9,15 +9,15 @@
use crate::__internal::{Enum, Private, PtrAndLen, RawArena, RawMap, RawMessage, RawRepeatedField};
use crate::{
Map, Mut, ProtoStr, Proxied, ProxiedInMapValue, ProxiedInRepeated, Repeated, RepeatedMut,
RepeatedView, SettableValue, View,
Map, MapIter, Mut, ProtoStr, Proxied, ProxiedInMapValue, ProxiedInRepeated, Repeated,
RepeatedMut, RepeatedView, SettableValue, View,
};
use core::fmt::Debug;
use paste::paste;
use std::alloc::Layout;
use std::cell::UnsafeCell;
use std::convert::identity;
use std::ffi::c_int;
use std::ffi::{c_int, c_void};
use std::fmt;
use std::marker::PhantomData;
use std::mem::MaybeUninit;
@ -157,6 +157,7 @@ pub type BytesPresentMutData<'msg> = crate::vtable::RawVTableOptionalMutatorData
pub type BytesAbsentMutData<'msg> = crate::vtable::RawVTableOptionalMutatorData<'msg, [u8]>;
pub type InnerBytesMut<'msg> = crate::vtable::RawVTableMutator<'msg, [u8]>;
pub type InnerPrimitiveMut<'msg, T> = crate::vtable::RawVTableMutator<'msg, T>;
pub type RawMapIter = UntypedMapIterator;
#[derive(Debug)]
pub struct MessageVTable {
@ -407,8 +408,93 @@ impl<'msg> InnerMapMut<'msg> {
}
}
/// An untyped iterator in a map, produced via `.cbegin()` on a typed map.
///
/// This struct is ABI-compatible with `proto2::internal::UntypedMapIterator`.
/// It is trivially constructible and destructible.
#[repr(C)]
pub struct UntypedMapIterator {
node: *mut c_void,
map: *const c_void,
bucket_index: u32,
}
impl UntypedMapIterator {
/// Returns `true` if this iterator is at the end of the map.
fn at_end(&self) -> bool {
// This behavior is verified via test `IteratorNodeFieldIsNullPtrAtEnd`.
self.node.is_null()
}
/// Assumes that the map iterator is for the input types, gets the current
/// entry, and moves the iterator forward to the next entry.
///
/// Conversion to and from FFI types is provided by the user.
/// This is a helper function for implementing
/// `ProxiedInMapValue::iter_next`.
///
/// # Safety
/// - The backing map must be valid and not be mutated for `'a`.
/// - The thunk must be safe to call if the iterator is not at the end of
/// the map.
/// - The thunk must always write to the `key` and `value` fields, but not
/// read from them.
/// - The get thunk must not move the iterator forward or backward.
#[inline(always)]
pub unsafe fn next_unchecked<'a, K, V, FfiKey, FfiValue>(
&mut self,
_private: Private,
iter_get_thunk: unsafe extern "C" fn(
iter: &mut UntypedMapIterator,
key: *mut FfiKey,
value: *mut FfiValue,
),
from_ffi_key: impl FnOnce(FfiKey) -> View<'a, K>,
from_ffi_value: impl FnOnce(FfiValue) -> View<'a, V>,
) -> Option<(View<'a, K>, View<'a, V>)>
where
K: Proxied + ?Sized + 'a,
V: ProxiedInMapValue<K> + ?Sized + 'a,
{
if self.at_end() {
return None;
}
let mut ffi_key = MaybeUninit::uninit();
let mut ffi_value = MaybeUninit::uninit();
// SAFETY:
// - The backing map outlives `'a`.
// - The iterator is not at the end (node is non-null).
// - `ffi_key` and `ffi_value` are not read (as uninit) as promised by the
// caller.
unsafe { (iter_get_thunk)(self, ffi_key.as_mut_ptr(), ffi_value.as_mut_ptr()) }
// SAFETY:
// - The backing map is alive as promised by the caller.
// - `self.at_end()` is false and the `get` does not change that.
// - `UntypedMapIterator` has the same ABI as
// `proto2::internal::UntypedMapIterator`. It is statically checked to be:
// - Trivially copyable.
// - Trivially destructible.
// - Standard layout.
// - The size and alignment of the Rust type above.
// - With the `node_` field first.
unsafe { __rust_proto_thunk__UntypedMapIterator_increment(self) }
// SAFETY:
// - The `get` function always writes valid values to `ffi_key` and `ffi_value`
// as promised by the caller.
unsafe {
Some((from_ffi_key(ffi_key.assume_init()), from_ffi_value(ffi_value.assume_init())))
}
}
}
extern "C" {
fn __rust_proto_thunk__UntypedMapIterator_increment(iter: &mut UntypedMapIterator);
}
macro_rules! impl_ProxiedInMapValue_for_non_generated_value_types {
($key_t:ty, $ffi_key_t:ty, $to_ffi_key:expr, for $($t:ty, $ffi_t:ty, $to_ffi_value:expr, $from_ffi_value:expr, $zero_val:literal;)*) => {
($key_t:ty, $ffi_key_t:ty, $to_ffi_key:expr, $from_ffi_key:expr, for $($t:ty, $ffi_t:ty, $to_ffi_value:expr, $from_ffi_value:expr;)*) => {
paste! { $(
extern "C" {
fn [< __rust_proto_thunk__Map_ $key_t _ $t _new >]() -> RawMap;
@ -417,6 +503,8 @@ macro_rules! impl_ProxiedInMapValue_for_non_generated_value_types {
fn [< __rust_proto_thunk__Map_ $key_t _ $t _size >](m: RawMap) -> usize;
fn [< __rust_proto_thunk__Map_ $key_t _ $t _insert >](m: RawMap, key: $ffi_key_t, value: $ffi_t) -> bool;
fn [< __rust_proto_thunk__Map_ $key_t _ $t _get >](m: RawMap, key: $ffi_key_t, value: *mut $ffi_t) -> bool;
fn [< __rust_proto_thunk__Map_ $key_t _ $t _iter >](m: RawMap) -> UntypedMapIterator;
fn [< __rust_proto_thunk__MapIter_ $key_t _ $t _get >](iter: &mut UntypedMapIterator, key: *mut $ffi_key_t, value: *mut $ffi_t);
fn [< __rust_proto_thunk__Map_ $key_t _ $t _remove >](m: RawMap, key: $ffi_key_t, value: *mut $ffi_t) -> bool;
}
@ -457,18 +545,50 @@ macro_rules! impl_ProxiedInMapValue_for_non_generated_value_types {
fn map_get<'a>(map: View<'a, Map<$key_t, Self>>, key: View<'_, $key_t>) -> Option<View<'a, Self>> {
let ffi_key = $to_ffi_key(key);
let mut ffi_value = $to_ffi_value($zero_val);
let found = unsafe { [< __rust_proto_thunk__Map_ $key_t _ $t _get >](map.as_raw(Private), ffi_key, &mut ffi_value) };
let mut ffi_value = MaybeUninit::uninit();
let found = unsafe { [< __rust_proto_thunk__Map_ $key_t _ $t _get >](map.as_raw(Private), ffi_key, ffi_value.as_mut_ptr()) };
if !found {
return None;
}
Some($from_ffi_value(ffi_value))
// SAFETY: if `found` is true, then the `ffi_value` was written to by `get`.
Some($from_ffi_value(unsafe { ffi_value.assume_init() }))
}
fn map_remove(mut map: Mut<'_, Map<$key_t, Self>>, key: View<'_, $key_t>) -> bool {
let ffi_key = $to_ffi_key(key);
let mut ffi_value = $to_ffi_value($zero_val);
unsafe { [< __rust_proto_thunk__Map_ $key_t _ $t _remove >](map.as_raw(Private), ffi_key, &mut ffi_value) }
let mut ffi_value = MaybeUninit::uninit();
unsafe { [< __rust_proto_thunk__Map_ $key_t _ $t _remove >](map.as_raw(Private), ffi_key, ffi_value.as_mut_ptr()) }
}
fn map_iter(map: View<'_, Map<$key_t, Self>>) -> MapIter<'_, $key_t, Self> {
// SAFETY:
// - The backing map for `map.as_raw` is valid for at least '_.
// - A View that is live for '_ guarantees the backing map is unmodified for '_.
// - The `iter` function produces an iterator that is valid for the key
// and value types, and live for at least '_.
unsafe {
MapIter::from_raw(
Private,
[< __rust_proto_thunk__Map_ $key_t _ $t _iter >](map.as_raw(Private))
)
}
}
fn map_iter_next<'a>(iter: &mut MapIter<'a, $key_t, Self>) -> Option<(View<'a, $key_t>, View<'a, Self>)> {
// SAFETY:
// - The `MapIter` API forbids the backing map from being mutated for 'a,
// and guarantees that it's the correct key and value types.
// - The thunk is safe to call as long as the iterator isn't at the end.
// - The thunk always writes to key and value fields and does not read.
// - The thunk does not increment the iterator.
unsafe {
iter.as_raw_mut(Private).next_unchecked::<$key_t, Self, _, _>(
Private,
[< __rust_proto_thunk__MapIter_ $key_t _ $t _get >],
$from_ffi_key,
$from_ffi_value,
)
}
}
}
)* }
@ -496,19 +616,20 @@ fn ptrlen_to_bytes<'msg>(val: PtrAndLen) -> &'msg [u8] {
}
macro_rules! impl_ProxiedInMapValue_for_key_types {
($($t:ty, $ffi_t:ty, $to_ffi_key:expr;)*) => {
($($t:ty, $ffi_t:ty, $to_ffi_key:expr, $from_ffi_key:expr;)*) => {
paste! {
$(
impl_ProxiedInMapValue_for_non_generated_value_types!($t, $ffi_t, $to_ffi_key, for
f32, f32, identity, identity, 0f32;
f64, f64, identity, identity, 0f64;
i32, i32, identity, identity, 0i32;
u32, u32, identity, identity, 0u32;
i64, i64, identity, identity, 0i64;
u64, u64, identity, identity, 0u64;
bool, bool, identity, identity, false;
ProtoStr, PtrAndLen, str_to_ptrlen, ptrlen_to_str, "";
Bytes, PtrAndLen, bytes_to_ptrlen, ptrlen_to_bytes, b"";
impl_ProxiedInMapValue_for_non_generated_value_types!(
$t, $ffi_t, $to_ffi_key, $from_ffi_key, for
f32, f32, identity, identity;
f64, f64, identity, identity;
i32, i32, identity, identity;
u32, u32, identity, identity;
i64, i64, identity, identity;
u64, u64, identity, identity;
bool, bool, identity, identity;
ProtoStr, PtrAndLen, str_to_ptrlen, ptrlen_to_str;
Bytes, PtrAndLen, bytes_to_ptrlen, ptrlen_to_bytes;
);
)*
}
@ -516,12 +637,12 @@ macro_rules! impl_ProxiedInMapValue_for_key_types {
}
impl_ProxiedInMapValue_for_key_types!(
i32, i32, identity;
u32, u32, identity;
i64, i64, identity;
u64, u64, identity;
bool, bool, identity;
ProtoStr, PtrAndLen, str_to_ptrlen;
i32, i32, identity, identity;
u32, u32, identity, identity;
i64, i64, identity, identity;
u64, u64, identity, identity;
bool, bool, identity, identity;
ProtoStr, PtrAndLen, str_to_ptrlen, ptrlen_to_str;
);
#[cfg(test)]

@ -96,9 +96,14 @@ expose_repeated_ptr_field_methods(Bytes);
#undef expose_repeated_ptr_field_methods
void __rust_proto_thunk__UntypedMapIterator_increment(
google::protobuf::internal::UntypedMapIterator* iter) {
iter->PlusPlus();
}
#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) \
to_ffi_key, value_ty, rust_value_ty, \
ffi_value_ty, to_cpp_value, to_ffi_value) \
google::protobuf::Map<key_ty, value_ty>* \
__rust_proto_thunk__Map_##rust_key_ty##_##rust_value_ty##_new() { \
return new google::protobuf::Map<key_ty, value_ty>(); \
@ -132,6 +137,21 @@ expose_repeated_ptr_field_methods(Bytes);
*value = to_ffi_value; \
return true; \
} \
google::protobuf::internal::UntypedMapIterator \
__rust_proto_thunk__Map_##rust_key_ty##_##rust_value_ty##_iter( \
const google::protobuf::Map<key_ty, value_ty>* m) { \
return google::protobuf::internal::UntypedMapIterator::FromTyped(m->cbegin()); \
} \
void __rust_proto_thunk__MapIter_##rust_key_ty##_##rust_value_ty##_get( \
const google::protobuf::internal::UntypedMapIterator* iter, ffi_key_ty* key, \
ffi_value_ty* value) { \
auto typed_iter = \
iter->ToTyped<google::protobuf::Map<key_ty, value_ty>::const_iterator>(); \
const auto& cpp_key = typed_iter->first; \
const auto& cpp_value = typed_iter->second; \
*key = to_ffi_key; \
*value = to_ffi_value; \
} \
bool __rust_proto_thunk__Map_##rust_key_ty##_##rust_value_ty##_remove( \
google::protobuf::Map<key_ty, value_ty>* m, ffi_key_ty key, ffi_value_ty* value) { \
auto cpp_key = to_cpp_key; \
@ -139,39 +159,47 @@ expose_repeated_ptr_field_methods(Bytes);
return num_removed > 0; \
}
#define expose_scalar_map_methods_for_key_type(key_ty, rust_key_ty, \
ffi_key_ty, to_cpp_key) \
expose_scalar_map_methods(key_ty, rust_key_ty, ffi_key_ty, to_cpp_key, \
int32_t, i32, int32_t, value, cpp_value); \
expose_scalar_map_methods(key_ty, rust_key_ty, ffi_key_ty, to_cpp_key, \
uint32_t, u32, uint32_t, value, cpp_value); \
expose_scalar_map_methods(key_ty, rust_key_ty, ffi_key_ty, to_cpp_key, \
float, f32, float, value, cpp_value); \
expose_scalar_map_methods(key_ty, rust_key_ty, ffi_key_ty, to_cpp_key, \
double, f64, double, value, cpp_value); \
expose_scalar_map_methods(key_ty, rust_key_ty, ffi_key_ty, to_cpp_key, bool, \
bool, bool, value, cpp_value); \
expose_scalar_map_methods(key_ty, rust_key_ty, ffi_key_ty, to_cpp_key, \
uint64_t, u64, uint64_t, value, cpp_value); \
expose_scalar_map_methods(key_ty, rust_key_ty, ffi_key_ty, to_cpp_key, \
int64_t, i64, int64_t, value, cpp_value); \
expose_scalar_map_methods( \
key_ty, rust_key_ty, ffi_key_ty, to_cpp_key, std::string, Bytes, \
google::protobuf::rust_internal::PtrAndLen, std::string(value.ptr, value.len), \
google::protobuf::rust_internal::PtrAndLen(cpp_value.data(), cpp_value.size())); \
expose_scalar_map_methods( \
key_ty, rust_key_ty, ffi_key_ty, to_cpp_key, std::string, ProtoStr, \
google::protobuf::rust_internal::PtrAndLen, std::string(value.ptr, value.len), \
#define expose_scalar_map_methods_for_key_type( \
key_ty, rust_key_ty, ffi_key_ty, to_cpp_key, to_ffi_key) \
expose_scalar_map_methods(key_ty, rust_key_ty, ffi_key_ty, to_cpp_key, \
to_ffi_key, int32_t, i32, int32_t, value, \
cpp_value); \
expose_scalar_map_methods(key_ty, rust_key_ty, ffi_key_ty, to_cpp_key, \
to_ffi_key, uint32_t, u32, uint32_t, value, \
cpp_value); \
expose_scalar_map_methods(key_ty, rust_key_ty, ffi_key_ty, to_cpp_key, \
to_ffi_key, float, f32, float, value, cpp_value); \
expose_scalar_map_methods(key_ty, rust_key_ty, ffi_key_ty, to_cpp_key, \
to_ffi_key, double, f64, double, value, \
cpp_value); \
expose_scalar_map_methods(key_ty, rust_key_ty, ffi_key_ty, to_cpp_key, \
to_ffi_key, bool, bool, bool, value, cpp_value); \
expose_scalar_map_methods(key_ty, rust_key_ty, ffi_key_ty, to_cpp_key, \
to_ffi_key, uint64_t, u64, uint64_t, value, \
cpp_value); \
expose_scalar_map_methods(key_ty, rust_key_ty, ffi_key_ty, to_cpp_key, \
to_ffi_key, int64_t, i64, int64_t, value, \
cpp_value); \
expose_scalar_map_methods( \
key_ty, rust_key_ty, ffi_key_ty, to_cpp_key, to_ffi_key, std::string, \
Bytes, google::protobuf::rust_internal::PtrAndLen, \
std::string(value.ptr, value.len), \
google::protobuf::rust_internal::PtrAndLen(cpp_value.data(), cpp_value.size())); \
expose_scalar_map_methods( \
key_ty, rust_key_ty, ffi_key_ty, to_cpp_key, to_ffi_key, std::string, \
ProtoStr, google::protobuf::rust_internal::PtrAndLen, \
std::string(value.ptr, value.len), \
google::protobuf::rust_internal::PtrAndLen(cpp_value.data(), cpp_value.size()));
expose_scalar_map_methods_for_key_type(int32_t, i32, int32_t, key);
expose_scalar_map_methods_for_key_type(uint32_t, u32, uint32_t, key);
expose_scalar_map_methods_for_key_type(bool, bool, bool, key);
expose_scalar_map_methods_for_key_type(uint64_t, u64, uint64_t, key);
expose_scalar_map_methods_for_key_type(int64_t, i64, int64_t, key);
expose_scalar_map_methods_for_key_type(std::string, ProtoStr,
google::protobuf::rust_internal::PtrAndLen,
std::string(key.ptr, key.len));
expose_scalar_map_methods_for_key_type(int32_t, i32, int32_t, key, cpp_key);
expose_scalar_map_methods_for_key_type(uint32_t, u32, uint32_t, key, cpp_key);
expose_scalar_map_methods_for_key_type(bool, bool, bool, key, cpp_key);
expose_scalar_map_methods_for_key_type(uint64_t, u64, uint64_t, key, cpp_key);
expose_scalar_map_methods_for_key_type(int64_t, i64, int64_t, key, cpp_key);
expose_scalar_map_methods_for_key_type(
std::string, ProtoStr, google::protobuf::rust_internal::PtrAndLen,
std::string(key.ptr, key.len),
google::protobuf::rust_internal::PtrAndLen(cpp_key.data(), cpp_key.size()));
#undef expose_scalar_map_methods
#undef expose_map_methods

@ -8,7 +8,7 @@
use crate::{
Mut, MutProxy, Proxied, SettableValue, View, ViewProxy,
__internal::{Private, RawMap},
__runtime::InnerMapMut,
__runtime::{InnerMapMut, RawMapIter},
};
use std::marker::PhantomData;
@ -82,6 +82,9 @@ where
fn map_insert(map: Mut<'_, Map<K, Self>>, key: View<'_, K>, value: View<'_, Self>) -> bool;
fn map_get<'a>(map: View<'a, Map<K, Self>>, key: View<'_, K>) -> Option<View<'a, Self>>;
fn map_remove(map: Mut<'_, Map<K, Self>>, key: View<'_, K>) -> bool;
fn map_iter(map: View<'_, Map<K, Self>>) -> MapIter<'_, K, Self>;
fn map_iter_next<'a>(iter: &mut MapIter<'a, K, Self>) -> Option<(View<'a, K>, View<'a, Self>)>;
}
impl<K: Proxied + ?Sized, V: ProxiedInMapValue<K> + ?Sized> Proxied for Map<K, V> {
@ -211,6 +214,11 @@ where
pub fn is_empty(self) -> bool {
self.len() == 0
}
/// An alias for `<Self as IntoIterator>::into_iterator`.
pub fn iter(self) -> MapIter<'msg, K, V> {
self.into_iter()
}
}
#[doc(hidden)]
@ -277,6 +285,102 @@ where
pub fn copy_from(&mut self, _src: MapView<'_, K, V>) {
todo!("implement b/28530933");
}
pub fn iter(&self) -> MapIter<'_, K, V> {
self.into_iter()
}
}
/// An iterator visiting all key-value pairs in arbitrary order.
///
/// The iterator element type is `(View<Key>, View<Value>)`.
pub struct MapIter<'msg, K: ?Sized, V: ?Sized> {
raw: RawMapIter,
_phantom: PhantomData<(&'msg K, &'msg V)>,
}
impl<'msg, K: ?Sized, V: ?Sized> MapIter<'msg, K, V> {
/// # Safety
/// - `raw` must be a valid instance of the raw iterator for `'msg`.
/// - The untyped `raw` iterator must be for a map of `K,V`.
/// - The backing map must be live and unmodified for `'msg`.
#[doc(hidden)]
pub unsafe fn from_raw(_private: Private, raw: RawMapIter) -> Self {
Self { raw, _phantom: PhantomData }
}
#[doc(hidden)]
pub fn as_raw_mut(&mut self, _private: Private) -> &mut RawMapIter {
&mut self.raw
}
}
impl<'msg, K, V> Iterator for MapIter<'msg, K, V>
where
K: Proxied + ?Sized + 'msg,
V: ProxiedInMapValue<K> + ?Sized + 'msg,
{
type Item = (View<'msg, K>, View<'msg, V>);
fn next(&mut self) -> Option<Self::Item> {
V::map_iter_next(self)
}
}
impl<'msg, K, V> IntoIterator for MapView<'msg, K, V>
where
K: Proxied + ?Sized + 'msg,
V: ProxiedInMapValue<K> + ?Sized + 'msg,
{
type IntoIter = MapIter<'msg, K, V>;
type Item = (View<'msg, K>, View<'msg, V>);
fn into_iter(self) -> MapIter<'msg, K, V> {
V::map_iter(self)
}
}
impl<'msg, K, V> IntoIterator for &'msg Map<K, V>
where
K: Proxied + ?Sized + 'msg,
V: ProxiedInMapValue<K> + ?Sized + 'msg,
{
type IntoIter = MapIter<'msg, K, V>;
type Item = (View<'msg, K>, View<'msg, V>);
fn into_iter(self) -> MapIter<'msg, K, V> {
self.as_view().into_iter()
}
}
impl<'a, 'msg, K, V> IntoIterator for &'a MapView<'msg, K, V>
where
'msg: 'a,
K: Proxied + ?Sized + 'msg,
V: ProxiedInMapValue<K> + ?Sized + 'msg,
{
type IntoIter = MapIter<'msg, K, V>;
type Item = (View<'msg, K>, View<'msg, V>);
fn into_iter(self) -> MapIter<'msg, K, V> {
(*self).into_iter()
}
}
impl<'a, 'msg, K, V> IntoIterator for &'a MapMut<'msg, K, V>
where
'msg: 'a,
K: Proxied + ?Sized + 'msg,
V: ProxiedInMapValue<K> + ?Sized + 'msg,
{
type IntoIter = MapIter<'a, K, V>;
// The View's are valid for 'a instead of 'msg.
// This is because the mutator may mutate past 'a but before 'msg expires.
type Item = (View<'a, K>, View<'a, V>);
fn into_iter(self) -> MapIter<'a, K, V> {
self.as_view().into_iter()
}
}
#[cfg(test)]
@ -336,6 +440,45 @@ mod tests {
assert_that!(map_view_4.is_empty(), eq(false));
}
#[test]
fn test_proxied_iter() {
let mut map: Map<i32, ProtoStr> = Map::new();
let mut map_mut = map.as_mut();
map_mut.insert(15, "fizzbuzz");
map_mut.insert(5, "buzz");
map_mut.insert(3, "fizz");
// ProtoStr::from_str is necessary below because
// https://doc.rust-lang.org/std/primitive.tuple.html#impl-PartialEq-for-(T,)
// only compares where the types are the same, even when the tuple types can
// compare with each other.
// googletest-rust matchers also do not currently implement Clone.
assert_that!(
map.as_view().iter().collect::<Vec<_>>(),
unordered_elements_are![
eq((3, ProtoStr::from_str("fizz"))),
eq((5, ProtoStr::from_str("buzz"))),
eq((15, ProtoStr::from_str("fizzbuzz")))
]
);
assert_that!(
map.as_view().into_iter().collect::<Vec<_>>(),
unordered_elements_are![
eq((3, ProtoStr::from_str("fizz"))),
eq((5, ProtoStr::from_str("buzz"))),
eq((15, ProtoStr::from_str("fizzbuzz")))
]
);
assert_that!(
map.as_mut().iter().collect::<Vec<_>>(),
unordered_elements_are![
eq((3, ProtoStr::from_str("fizz"))),
eq((5, ProtoStr::from_str("buzz"))),
eq((15, ProtoStr::from_str("fizzbuzz")))
]
);
}
#[test]
fn test_all_maps_can_be_constructed() {
macro_rules! gen_proto_values {

@ -23,7 +23,7 @@ use std::fmt;
#[doc(hidden)]
pub mod __public {
pub use crate::r#enum::UnknownEnumValue;
pub use crate::map::{Map, MapMut, MapView, ProxiedInMapValue};
pub use crate::map::{Map, MapIter, MapMut, MapView, ProxiedInMapValue};
pub use crate::optional::{AbsentField, FieldEntry, Optional, PresentField};
pub use crate::primitive::PrimitiveMut;
pub use crate::proxied::{

@ -8,42 +8,85 @@
use googletest::prelude::*;
use map_unittest_proto::TestMap;
use paste::paste;
use std::collections::HashMap;
macro_rules! generate_map_primitives_tests {
(
$(($k_type:ty, $v_type:ty, $k_field:ident, $v_field:ident)),*
$(($k_type:ty, $v_type:ty, $k_field:ident, $v_field:ident,
$k_nonzero:expr, $v_nonzero:expr $(,)?)),*
$(,)?
) => {
paste! { $(
#[test]
fn [< test_map_ $k_field _ $v_field >]() {
let mut msg = TestMap::new();
let k: $k_type = Default::default();
let v: $v_type = Default::default();
assert_that!(msg.[< map_ $k_field _ $v_field >]().len(), eq(0));
assert_that!(
msg.[< map_ $k_field _ $v_field >]().iter().collect::<Vec<_>>(),
elements_are![]
);
let k = <$k_type>::default();
let v = <$v_type>::default();
assert_that!(msg.[< map_ $k_field _ $v_field _mut>]().insert(k, v), eq(true));
assert_that!(msg.[< map_ $k_field _ $v_field _mut>]().insert(k, v), eq(false));
assert_that!(msg.[< map_ $k_field _ $v_field >]().len(), eq(1));
assert_that!(
msg.[< map_ $k_field _ $v_field >]().iter().collect::<Vec<_>>(),
elements_are![eq((k, v))]
);
let k: $k_type = $k_nonzero;
let v: $v_type = $v_nonzero;
assert_that!(msg.[< map_ $k_field _ $v_field _mut>]().insert(k, v), eq(true));
assert_that!(msg.[< map_ $k_field _ $v_field >]().len(), eq(2));
assert_that!(
msg.[< map_ $k_field _ $v_field >]().iter().collect::<Vec<_>>(),
unordered_elements_are![
eq((k, v)),
eq((<$k_type>::default(), <$v_type>::default())),
]
);
}
)* }
};
}
generate_map_primitives_tests!(
(i32, i32, int32, int32),
(i64, i64, int64, int64),
(u32, u32, uint32, uint32),
(u64, u64, uint64, uint64),
(i32, i32, sint32, sint32),
(i64, i64, sint64, sint64),
(u32, u32, fixed32, fixed32),
(u64, u64, fixed64, fixed64),
(i32, i32, sfixed32, sfixed32),
(i64, i64, sfixed64, sfixed64),
(i32, f32, int32, float),
(i32, f64, int32, double),
(bool, bool, bool, bool),
(i32, &[u8], int32, bytes)
(i32, i32, int32, int32, 1, 1),
(i64, i64, int64, int64, 1, 1),
(u32, u32, uint32, uint32, 1, 1),
(u64, u64, uint64, uint64, 1, 1),
(i32, i32, sint32, sint32, 1, 1),
(i64, i64, sint64, sint64, 1, 1),
(u32, u32, fixed32, fixed32, 1, 1),
(u64, u64, fixed64, fixed64, 1, 1),
(i32, i32, sfixed32, sfixed32, 1, 1),
(i64, i64, sfixed64, sfixed64, 1, 1),
(i32, f32, int32, float, 1, 1.),
(i32, f64, int32, double, 1, 1.),
(bool, bool, bool, bool, true, true),
(i32, &[u8], int32, bytes, 1, b"foo"),
);
#[test]
fn collect_as_hashmap() {
// Highlights conversion from protobuf map to hashmap.
let mut msg = TestMap::new();
msg.map_string_string_mut().insert("hello", "world");
msg.map_string_string_mut().insert("fizz", "buzz");
msg.map_string_string_mut().insert("boo", "blah");
let hashmap: HashMap<String, String> =
msg.map_string_string().iter().map(|(k, v)| (k.to_string(), v.to_string())).collect();
assert_that!(
hashmap.into_iter().collect::<Vec<_>>(),
unordered_elements_are![
eq(("hello".to_owned(), "world".to_owned())),
eq(("fizz".to_owned(), "buzz".to_owned())),
eq(("boo".to_owned(), "blah".to_owned())),
]
);
}
#[test]
fn test_string_maps() {
let mut msg = TestMap::new();

@ -9,8 +9,8 @@
use crate::__internal::{Enum, Private, PtrAndLen, RawArena, RawMap, RawMessage, RawRepeatedField};
use crate::{
Map, MapMut, MapView, Mut, ProtoStr, Proxied, ProxiedInMapValue, ProxiedInRepeated, Repeated,
RepeatedMut, RepeatedView, SettableValue, View, ViewProxy,
Map, MapIter, MapMut, MapView, Mut, ProtoStr, Proxied, ProxiedInMapValue, ProxiedInRepeated,
Repeated, RepeatedMut, RepeatedView, SettableValue, View, ViewProxy,
};
use core::fmt::Debug;
use std::alloc;
@ -794,6 +794,34 @@ impl UpbTypeConversions for ProtoStr {
}
}
pub struct RawMapIter {
// TODO: Replace this `RawMap` with the const type.
map: RawMap,
iter: usize,
}
impl RawMapIter {
pub fn new(_private: Private, map: RawMap) -> Self {
// SAFETY: __rust_proto_kUpb_Map_Begin is never modified
RawMapIter { map, iter: unsafe { __rust_proto_kUpb_Map_Begin } }
}
/// # Safety
/// - `self.map` must be valid, and remain valid while the return value is
/// in use.
pub(crate) unsafe fn next_unchecked(
&mut self,
_private: Private,
) -> Option<(upb_MessageValue, upb_MessageValue)> {
let mut key = MaybeUninit::uninit();
let mut value = MaybeUninit::uninit();
// SAFETY: the `map` is valid as promised by the caller
unsafe { upb_Map_Next(self.map, key.as_mut_ptr(), value.as_mut_ptr(), &mut self.iter) }
// SAFETY: if upb_Map_Next returns true, then key and value have been populated.
.then(|| unsafe { (key.assume_init(), value.assume_init()) })
}
}
macro_rules! impl_ProxiedInMapValue_for_non_generated_value_types {
($key_t:ty ; $($t:ty),*) => {
$(
@ -870,6 +898,26 @@ macro_rules! impl_ProxiedInMapValue_for_non_generated_value_types {
&mut val)
}
}
fn map_iter(map: View<'_, Map<$key_t, Self>>) -> MapIter<'_, $key_t, Self> {
// SAFETY: View<Map<'_,..>> guarantees its RawMap outlives '_.
unsafe {
MapIter::from_raw(Private, RawMapIter::new(Private, map.as_raw(Private)))
}
}
fn map_iter_next<'a>(
iter: &mut MapIter<'a, $key_t, Self>
) -> Option<(View<'a, $key_t>, View<'a, Self>)> {
// SAFETY: MapIter<'a, ..> guarantees its RawMapIter outlives 'a.
unsafe { iter.as_raw_mut(Private).next_unchecked(Private) }
// SAFETY: MapIter<K, V> returns key and values message values
// with the variants for K and V active.
.map(|(k, v)| unsafe {(
<$key_t as UpbTypeConversions>::from_message_value(k),
<$t as UpbTypeConversions>::from_message_value(v),
)})
}
}
)*
}
@ -937,6 +985,15 @@ extern "C" {
removed_value: *mut upb_MessageValue,
) -> bool;
fn upb_Map_Clear(map: RawMap);
static __rust_proto_kUpb_Map_Begin: usize;
fn upb_Map_Next(
map: RawMap,
key: *mut upb_MessageValue,
value: *mut upb_MessageValue,
iter: &mut usize,
) -> bool;
}
#[cfg(test)]

@ -11,4 +11,6 @@
#include "upb/mem/arena.h" // IWYU pragma: keep
#include "upb/message/array.h" // IWYU pragma: keep
#include "upb/message/copy.h" // IWYU pragma: keep
#include "upb/message/map.h" // IWYU pragma: keep
#include "upb/message/map.h" // IWYU pragma: keep
const size_t __rust_proto_kUpb_Map_Begin = kUpb_Map_Begin;

@ -16,6 +16,7 @@
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <functional>
#include <initializer_list>
#include <iterator>
@ -32,6 +33,7 @@
#include "absl/base/attributes.h"
#include "absl/container/btree_map.h"
#include "absl/hash/hash.h"
#include "absl/log/absl_check.h"
#include "absl/meta/type_traits.h"
#include "absl/strings/string_view.h"
#include "google/protobuf/arena.h"
@ -495,11 +497,47 @@ class UntypedMapIterator {
}
}
// Conversion to and from a typed iterator child class is used by FFI.
template <class Iter>
static UntypedMapIterator FromTyped(Iter it) {
static_assert(
#if defined(__cpp_lib_is_layout_compatible) && \
__cpp_lib_is_layout_compatible >= 201907L
std::is_layout_compatible_v<Iter, UntypedMapIterator>,
#else
sizeof(it) == sizeof(UntypedMapIterator),
#endif
"Map iterator must not have extra state that the base class"
"does not define.");
return static_cast<UntypedMapIterator>(it);
}
template <class Iter>
Iter ToTyped() const {
return Iter(*this);
}
NodeBase* node_;
const UntypedMapBase* m_;
map_index_t bucket_index_;
};
// These properties are depended upon by Rust FFI.
static_assert(std::is_trivially_copyable<UntypedMapIterator>::value,
"UntypedMapIterator must be trivially copyable.");
static_assert(std::is_trivially_destructible<UntypedMapIterator>::value,
"UntypedMapIterator must be trivially destructible.");
static_assert(std::is_standard_layout<UntypedMapIterator>::value,
"UntypedMapIterator must be standard layout.");
static_assert(offsetof(UntypedMapIterator, node_) == 0,
"node_ must be the first field of UntypedMapIterator.");
static_assert(sizeof(UntypedMapIterator) ==
sizeof(void*) * 2 +
std::max(sizeof(uint32_t), alignof(void*)),
"UntypedMapIterator does not have the expected size for FFI");
static_assert(
alignof(UntypedMapIterator) == std::max(alignof(void*), alignof(uint32_t)),
"UntypedMapIterator does not have the expected alignment for FFI");
// Base class for all Map instantiations.
// This class holds all the data and provides the basic functionality shared
// among all instantiations.
@ -1280,6 +1318,7 @@ class Map : private internal::KeyMapBase<internal::KeyForBase<Key>> {
using BaseIt::BaseIt;
explicit const_iterator(const BaseIt& base) : BaseIt(base) {}
friend class Map;
friend class internal::UntypedMapIterator;
friend class internal::TypeDefinedMapFieldBase<Key, T>;
};

@ -271,6 +271,17 @@ TEST(MapTest, SizeTypeIsSizeT) {
(void)x;
}
TEST(MapTest, IteratorNodeFieldIsNullPtrAtEnd) {
Map<int, int> map;
EXPECT_EQ(internal::UntypedMapIterator::FromTyped(map.cbegin()).node_,
nullptr);
map.insert({1, 1});
// This behavior is depended on by Rust FFI.
EXPECT_NE(internal::UntypedMapIterator::FromTyped(map.cbegin()).node_,
nullptr);
EXPECT_EQ(internal::UntypedMapIterator::FromTyped(map.cend()).node_, nullptr);
}
template <typename Aligned, bool on_arena = false>
void MapTest_Aligned() {
Arena arena;

Loading…
Cancel
Save