| 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<CppStdString>, |
| 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<Value> MapValue for Value |
| where |
| Value: Singular + CppMapTypeConversions, |
| { |
| fn map_new<Key: MapKey>(_private: Private) -> Map<Key, Self> { |
| unsafe { |
| Map::from_inner( |
| Private, |
| InnerMap::new(proto2_rust_map_new(Key::get_prototype(), Value::get_prototype())), |
| ) |
| } |
| } |
| |
| unsafe fn map_free<Key: MapKey>(_private: Private, map: &mut Map<Key, Self>) { |
| unsafe { |
| proto2_rust_map_free(map.as_raw(Private)); |
| } |
| } |
| |
| fn map_clear<Key: MapKey>(_private: Private, mut map: MapMut<Key, Self>) { |
| unsafe { |
| proto2_rust_map_clear(map.as_raw(Private)); |
| } |
| } |
| |
| fn map_len<Key: MapKey>(_private: Private, map: MapView<Key, Self>) -> usize { |
| unsafe { proto2_rust_map_size(map.as_raw(Private)) } |
| } |
| |
| fn map_insert<Key: MapKey>( |
| _private: Private, |
| mut map: MapMut<Key, Self>, |
| key: View<'_, Key>, |
| value: impl IntoProxied<Self>, |
| ) -> 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<View<'a, Self>> { |
| 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<Mut<'a, Self>> |
| 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<Key: MapKey>( |
| _private: Private, |
| mut map: MapMut<Key, Self>, |
| key: View<'_, Key>, |
| ) -> bool { |
| unsafe { Key::remove(map.as_raw(Private), key) } |
| } |
| |
| fn map_iter<Key: MapKey>(_private: Private, map: MapView<Key, Self>) -> MapIter<Key, 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, 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::<Key, Self, _, _>( |
| |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; |
| ); |