use super::*; unsafe extern "C" { fn proto2_rust_thunk_UntypedMapIterator_increment(iter: &mut UntypedMapIterator); pub fn proto2_rust_map_new(key_prototype: FfiMapValue, value_prototype: FfiMapValue) -> RawMap; pub fn proto2_rust_map_free(m: RawMap); pub fn proto2_rust_map_clear(m: RawMap); pub fn proto2_rust_map_size(m: RawMap) -> usize; pub fn proto2_rust_map_iter(m: RawMap) -> UntypedMapIterator; } /// A trait implemented by types which are allowed as keys in maps. /// This is all types for fields except for repeated, maps, bytes, messages, enums and floating point types. /// This trait is defined separately in cpp.rs and upb.rs to be able to set better subtrait bounds. #[doc(hidden)] pub trait MapKey: Proxied + FfiMapKey + CppMapTypeConversions + SealedInternal {} #[derive(Debug)] #[doc(hidden)] pub struct InnerMap { pub(crate) raw: RawMap, } impl InnerMap { pub fn new(raw: RawMap) -> Self { Self { raw } } pub fn as_mut(&mut self) -> InnerMapMut<'_> { InnerMapMut { raw: self.raw, _phantom: PhantomData } } } #[derive(Clone, Copy, Debug)] #[doc(hidden)] pub struct InnerMapMut<'msg> { pub(crate) raw: RawMap, _phantom: PhantomData<&'msg ()>, } #[doc(hidden)] impl<'msg> InnerMapMut<'msg> { pub fn new(raw: RawMap) -> Self { InnerMapMut { raw, _phantom: PhantomData } } pub fn as_raw(&self) -> RawMap { self.raw } } /// 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)] #[doc(hidden)] 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 /// `MapValue::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, iter_get_thunk: unsafe 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: MapKey + 'a, V: MapValue + '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 { proto2_rust_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()))) } } } // LINT.IfChange(map_ffi) #[doc(hidden)] #[repr(u8)] #[derive(Debug, PartialEq)] // Copy of UntypedMapBase::TypeKind pub enum FfiMapValueTag { Bool, U32, U64, F32, F64, String, Message, } // For the purposes of FFI, we treat all integral types of a given size the same // way. For example, u32 and i32 values are all represented as a u32. // Likewise, u64 and i64 values are all stored in a u64. #[doc(hidden)] #[repr(C)] pub union FfiMapValueUnion { pub b: bool, pub u: u32, pub uu: u64, pub f: f32, pub ff: f64, // Generally speaking, if s is set then it should not be None. However, we // do set it to None in the special case where the FfiMapValue is just a // "prototype" (see below). In that scenario, we just want to indicate the // value type without having to allocate a real C++ std::string. pub s: Option, pub m: RawMessage, } // We use this tagged union to represent map values for the purposes of FFI. #[doc(hidden)] #[repr(C)] pub struct FfiMapValue { pub tag: FfiMapValueTag, pub val: FfiMapValueUnion, } // LINT.ThenChange(//depot/google3/third_party/protobuf/rust/cpp_kernel/map.cc: // map_ffi) impl FfiMapValue { fn make_bool(b: bool) -> Self { FfiMapValue { tag: FfiMapValueTag::Bool, val: FfiMapValueUnion { b } } } pub fn make_u32(u: u32) -> Self { FfiMapValue { tag: FfiMapValueTag::U32, val: FfiMapValueUnion { u } } } fn make_u64(uu: u64) -> Self { FfiMapValue { tag: FfiMapValueTag::U64, val: FfiMapValueUnion { uu } } } pub fn make_f32(f: f32) -> Self { FfiMapValue { tag: FfiMapValueTag::F32, val: FfiMapValueUnion { f } } } fn make_f64(ff: f64) -> Self { FfiMapValue { tag: FfiMapValueTag::F64, val: FfiMapValueUnion { ff } } } fn make_string(s: CppStdString) -> Self { FfiMapValue { tag: FfiMapValueTag::String, val: FfiMapValueUnion { s: Some(s) } } } pub fn make_message(m: RawMessage) -> Self { FfiMapValue { tag: FfiMapValueTag::Message, val: FfiMapValueUnion { m } } } } pub trait CppMapTypeConversions: Proxied { // We have a notion of a map value "prototype", which is a FfiMapValue that // contains just enough information to indicate the value type of the map. // We need this on the C++ side to be able to determine size and offset // information about the map entry. For messages, the prototype is // the message default instance. For all other types, it is just a FfiMapValue // with the appropriate tag. fn get_prototype() -> FfiMapValue; fn to_map_value(self) -> FfiMapValue; /// # Safety /// - `value` must store the correct type for `Self`. If it is a string or /// bytes, then it must not be None. If `Self` is a closed enum, then /// `value` must store a valid value for that enum. If `Self` is a /// message, then `value` must store a message of the same type. /// - The value must be valid for `'a` lifetime. unsafe fn from_map_value<'a>(value: FfiMapValue) -> View<'a, Self>; /// # Safety /// - `value` must store a message of the same type as `Self`. /// - `value` must be valid and have exclusive mutable access for `'a` lifetime. #[allow(unused_variables)] unsafe fn mut_from_map_value<'a>(value: FfiMapValue) -> Mut<'a, Self> where Self: Message, { panic!("mut_from_map_value is only implemented for messages") } } impl CppMapTypeConversions for u32 { fn get_prototype() -> FfiMapValue { FfiMapValue::make_u32(0) } fn to_map_value(self) -> FfiMapValue { FfiMapValue::make_u32(self) } unsafe fn from_map_value<'a>(value: FfiMapValue) -> View<'a, Self> { debug_assert_eq!(value.tag, FfiMapValueTag::U32); unsafe { value.val.u } } } impl CppMapTypeConversions for i32 { fn get_prototype() -> FfiMapValue { FfiMapValue::make_u32(0) } fn to_map_value(self) -> FfiMapValue { FfiMapValue::make_u32(self as u32) } unsafe fn from_map_value<'a>(value: FfiMapValue) -> View<'a, Self> { debug_assert_eq!(value.tag, FfiMapValueTag::U32); unsafe { value.val.u as i32 } } } impl CppMapTypeConversions for u64 { fn get_prototype() -> FfiMapValue { FfiMapValue::make_u64(0) } fn to_map_value(self) -> FfiMapValue { FfiMapValue::make_u64(self) } unsafe fn from_map_value<'a>(value: FfiMapValue) -> View<'a, Self> { debug_assert_eq!(value.tag, FfiMapValueTag::U64); unsafe { value.val.uu } } } impl CppMapTypeConversions for i64 { fn get_prototype() -> FfiMapValue { FfiMapValue::make_u64(0) } fn to_map_value(self) -> FfiMapValue { FfiMapValue::make_u64(self as u64) } unsafe fn from_map_value<'a>(value: FfiMapValue) -> View<'a, Self> { debug_assert_eq!(value.tag, FfiMapValueTag::U64); unsafe { value.val.uu as i64 } } } impl CppMapTypeConversions for f32 { fn get_prototype() -> FfiMapValue { FfiMapValue::make_f32(0f32) } fn to_map_value(self) -> FfiMapValue { FfiMapValue::make_f32(self) } unsafe fn from_map_value<'a>(value: FfiMapValue) -> View<'a, Self> { debug_assert_eq!(value.tag, FfiMapValueTag::F32); unsafe { value.val.f } } } impl CppMapTypeConversions for f64 { fn get_prototype() -> FfiMapValue { FfiMapValue::make_f64(0.0) } fn to_map_value(self) -> FfiMapValue { FfiMapValue::make_f64(self) } unsafe fn from_map_value<'a>(value: FfiMapValue) -> View<'a, Self> { debug_assert_eq!(value.tag, FfiMapValueTag::F64); unsafe { value.val.ff } } } impl CppMapTypeConversions for bool { fn get_prototype() -> FfiMapValue { FfiMapValue::make_bool(false) } fn to_map_value(self) -> FfiMapValue { FfiMapValue::make_bool(self) } unsafe fn from_map_value<'a>(value: FfiMapValue) -> View<'a, Self> { debug_assert_eq!(value.tag, FfiMapValueTag::Bool); unsafe { value.val.b } } } impl CppMapTypeConversions for ProtoString { fn get_prototype() -> FfiMapValue { FfiMapValue { tag: FfiMapValueTag::String, val: FfiMapValueUnion { s: None } } } fn to_map_value(self) -> FfiMapValue { FfiMapValue::make_string(protostr_into_cppstdstring(self)) } unsafe fn from_map_value<'a>(value: FfiMapValue) -> &'a ProtoStr { debug_assert_eq!(value.tag, FfiMapValueTag::String); unsafe { ptrlen_to_str(proto2_rust_cpp_string_to_view(value.val.s.unwrap())) } } } impl CppMapTypeConversions for ProtoBytes { fn get_prototype() -> FfiMapValue { FfiMapValue { tag: FfiMapValueTag::String, val: FfiMapValueUnion { s: None } } } fn to_map_value(self) -> FfiMapValue { FfiMapValue::make_string(protobytes_into_cppstdstring(self)) } unsafe fn from_map_value<'a>(value: FfiMapValue) -> &'a [u8] { debug_assert_eq!(value.tag, FfiMapValueTag::String); unsafe { proto2_rust_cpp_string_to_view(value.val.s.unwrap()).as_ref() } } } // This trait encapsulates functionality that is specific to each map key type. // We need this primarily so that we can call the appropriate FFI function for // the key type. #[doc(hidden)] pub trait FfiMapKey where Self: Proxied, { type FfiKey; fn to_view<'a>(key: Self::FfiKey) -> View<'a, Self>; unsafe fn insert(m: RawMap, key: View<'_, Self>, value: FfiMapValue) -> bool; unsafe fn get(m: RawMap, key: View<'_, Self>, value: *mut FfiMapValue) -> bool; unsafe fn iter_get( iter: &mut UntypedMapIterator, key: *mut Self::FfiKey, value: *mut FfiMapValue, ); unsafe fn remove(m: RawMap, key: View<'_, Self>) -> bool; } macro_rules! generate_map_key_impl { ( $($key:ty, $mutable_ffi_key:ty, $to_ffi:expr, $from_ffi:expr;)* ) => { paste! { $( impl FfiMapKey for $key { type FfiKey = $mutable_ffi_key; #[inline] fn to_view<'a>(key: Self::FfiKey) -> View<'a, Self> { $from_ffi(key) } #[inline] unsafe fn insert( m: RawMap, key: View<'_, Self>, value: FfiMapValue, ) -> bool { unsafe { [< proto2_rust_map_insert_ $key >](m, $to_ffi(key), value) } } #[inline] unsafe fn get( m: RawMap, key: View<'_, Self>, value: *mut FfiMapValue, ) -> bool { unsafe { [< proto2_rust_map_get_ $key >](m, $to_ffi(key), value) } } #[inline] unsafe fn iter_get( iter: &mut UntypedMapIterator, key: *mut Self::FfiKey, value: *mut FfiMapValue, ) { unsafe { [< proto2_rust_map_iter_get_ $key >](iter, key, value) } } #[inline] unsafe fn remove(m: RawMap, key: View<'_, Self>) -> bool { unsafe { [< proto2_rust_map_remove_ $key >](m, $to_ffi(key)) } } } )* } } } generate_map_key_impl!( bool, bool, identity, identity; i32, i32, identity, identity; u32, u32, identity, identity; i64, i64, identity, identity; u64, u64, identity, identity; ProtoString, PtrAndLen, str_to_ptrlen, ptrlen_to_str; ); impl MapValue for Value where Value: Singular + CppMapTypeConversions, { fn map_new(_private: Private) -> Map { unsafe { Map::from_inner( Private, InnerMap::new(proto2_rust_map_new(Key::get_prototype(), Value::get_prototype())), ) } } unsafe fn map_free(_private: Private, map: &mut Map) { unsafe { proto2_rust_map_free(map.as_raw(Private)); } } fn map_clear(_private: Private, mut map: MapMut) { unsafe { proto2_rust_map_clear(map.as_raw(Private)); } } fn map_len(_private: Private, map: MapView) -> usize { unsafe { proto2_rust_map_size(map.as_raw(Private)) } } fn map_insert( _private: Private, mut map: MapMut, key: View<'_, Key>, value: impl IntoProxied, ) -> bool { unsafe { Key::insert(map.as_raw(Private), key, value.into_proxied(Private).to_map_value()) } } fn map_get<'a, Key: MapKey>( _private: Private, map: MapView<'a, Key, Self>, key: View<'_, Key>, ) -> Option> { let mut value = std::mem::MaybeUninit::uninit(); let found = unsafe { Key::get(map.as_raw(Private), key, value.as_mut_ptr()) }; if !found { return None; } unsafe { Some(Self::from_map_value(value.assume_init())) } } fn map_get_mut<'a, Key: MapKey>( _private: Private, mut map: MapMut<'a, Key, Self>, key: View<'_, Key>, ) -> Option> where Value: Message, { let mut value = std::mem::MaybeUninit::uninit(); let found = unsafe { Key::get(map.as_raw(Private), key, value.as_mut_ptr()) }; if !found { return None; } // SAFETY: `value` has been initialized because it was found. // - `value` is a message as required by the trait. // - `value` is valid for the `'a` lifetime of the `MapMut`. unsafe { Some(Self::mut_from_map_value(value.assume_init())) } } fn map_remove( _private: Private, mut map: MapMut, key: View<'_, Key>, ) -> bool { unsafe { Key::remove(map.as_raw(Private), key) } } fn map_iter(_private: Private, map: MapView) -> MapIter { // 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, proto2_rust_map_iter(map.as_raw(Private))) } } fn map_iter_next<'a, Key: MapKey>( _private: Private, iter: &mut MapIter<'a, Key, Self>, ) -> Option<(View<'a, Key>, 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::( |iter, key, value| Key::iter_get(iter, key, value), |ffi_key| Key::to_view(ffi_key), |value| Self::from_map_value(value), ) } } } macro_rules! impl_map_primitives { (@impl $(($rust_type:ty, $cpp_type:ty) => [ $insert_thunk:ident, $get_thunk:ident, $iter_get_thunk:ident, $remove_thunk:ident, ]),* $(,)?) => { $( unsafe extern "C" { pub fn $insert_thunk( m: RawMap, key: $cpp_type, value: FfiMapValue, ) -> bool; pub fn $get_thunk( m: RawMap, key: $cpp_type, value: *mut FfiMapValue, ) -> bool; pub fn $iter_get_thunk( iter: &mut UntypedMapIterator, key: *mut $cpp_type, value: *mut FfiMapValue, ); pub fn $remove_thunk(m: RawMap, key: $cpp_type) -> bool; } )* }; ($($rust_type:ty, $cpp_type:ty;)* $(,)?) => { paste!{ impl_map_primitives!(@impl $( ($rust_type, $cpp_type) => [ [< proto2_rust_map_insert_ $rust_type >], [< proto2_rust_map_get_ $rust_type >], [< proto2_rust_map_iter_get_ $rust_type >], [< proto2_rust_map_remove_ $rust_type >], ], )*); } }; } impl_map_primitives!( i32, i32; u32, u32; i64, i64; u64, u64; bool, bool; ProtoString, PtrAndLen; );