blob: c1c2050d49b0146d6aa0de45f6904b3d345d8503 [file] [log] [blame] [edit]
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;
);