// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. use std::collections::HashMap; use uniffi::FfiConverter; use uniffi::RustBuffer; use super::types; macro_rules! forward_ffi_converter { ($ty:ty) => { unsafe impl FfiConverter for $ty { type FfiType = RustBuffer; fn lower(obj: Self) -> Self::FfiType { uniffi::Lower::::lower(obj) } fn try_lift(v: Self::FfiType) -> uniffi::Result { uniffi::Lift::::try_lift(v) } fn write(obj: Self, buf: &mut Vec) { uniffi::Lower::::write(obj, buf) } fn try_read(buf: &mut &[u8]) -> uniffi::Result { uniffi::Lift::::try_read(buf) } const TYPE_ID_META: uniffi::MetadataBuffer = uniffi::MetadataBuffer::from_code(0); } }; } unsafe impl FfiConverter for () { type FfiType = (); fn lower(_obj: Self) -> Self::FfiType {} fn try_lift(_v: Self::FfiType) -> uniffi::Result { Ok(()) } fn write(_obj: Self, _buf: &mut Vec) {} fn try_read(_buf: &mut &[u8]) -> uniffi::Result { Ok(()) } const TYPE_ID_META: uniffi::MetadataBuffer = uniffi::MetadataBuffer::from_code(0); } unsafe impl FfiConverter for types::CowString { type FfiType = >::FfiType; fn lower(s: types::CowString) -> Self::FfiType { >::lower(s.into_owned()) } fn try_lift(v: Self::FfiType) -> ::uniffi::Result { let s = >::try_lift(v)?; Ok(types::CowString::from(s)) } fn write(s: types::CowString, buf: &mut Vec) { >::write(s.into_owned(), buf); } fn try_read(buf: &mut &[u8]) -> ::uniffi::Result { let s = >::try_read(buf)?; Ok(types::CowString::from(s)) } const TYPE_ID_META: uniffi::MetadataBuffer = uniffi::MetadataBuffer::from_code(0); } forward_ffi_converter!(Option); forward_ffi_converter!(Vec); forward_ffi_converter!(Vec); forward_ffi_converter!(Vec); forward_ffi_converter!(Option); forward_ffi_converter!(Option); forward_ffi_converter!(Option>); forward_ffi_converter!(Option>); forward_ffi_converter!(Option); forward_ffi_converter!(Option>); forward_ffi_converter!(HashMap); forward_ffi_converter!(Option>>); forward_ffi_converter!(Option); forward_ffi_converter!(Option); forward_ffi_converter!(Option); forward_ffi_converter!(Option); uniffi::derive_ffi_traits!(local types::CowString); pub trait CloneFfiArg { fn clone_for_ffi(&self) -> T; } pub trait DestroyFfiArg { fn destroy(self); } /// Consume a remote type. /// /// This might clone the data and drop self using the FFI. trait ConsumeRemoteType { fn consume(self) -> Self; } /// Wrapper around `FfiConverter::try_lift` to add `ConsumeRemoteType` functionality. pub trait LocalTryLift: Sized { type FfiType; fn try_lift(v: Self::FfiType) -> Result; } macro_rules! impl_clone_ffi_arg_primitive { ($($ty:ty),+) => { $( impl CloneFfiArg<$ty> for $ty { fn clone_for_ffi(&self) -> $ty { *self } } impl DestroyFfiArg for $ty { fn destroy(self) { /* left empty */ } } impl ConsumeRemoteType for $ty { fn consume(self) -> Self { self } } )* } } impl_clone_ffi_arg_primitive!(i8, i32, i64); impl CloneFfiArg for RustBuffer { fn clone_for_ffi(&self) -> RustBuffer { // SAFETY: // * `ForeignBytes` is a UniFFI type and `repr(C)` of a `len: i32` and `data: *const u8`. // * `RustBuffer` is a UniFFI type, `repr(C)` and has a `capacity`, `len` and `data`. // * `data_pointer` gives us a valid pointer to the underlying allocated data (backed by a `Vec`) // * We assert that the length fits in `i32`. // * The data pointer of the `RustBuffer` is valid while we're in this call. We can pass it over. // * `RustBuffer`'s `from_bytes` copies the bytes from the passed data pointer into a new allocation // * and wraps that allocation into a new `RustBuffer`. debug_assert!(self.len() <= i32::MAX as usize); unsafe { let bytes = uniffi::ForeignBytes::from_raw_parts( self.data_pointer(), self.len().try_into().unwrap(), ); let mut call_status = uniffi::RustCallStatus::default(); (crate::GLEAN.ffi_glean_core_rustbuffer_from_bytes)(bytes, &mut call_status) } } } impl DestroyFfiArg for RustBuffer { fn destroy(self) { _ = self.destroy_into_vec(); } } impl ConsumeRemoteType for () { fn consume(self) -> Self { /* intentionally left empty */ } } /// A remote `RustBuffer` is consumed by /// /// 1) Cloning the data into a local `RustBuffer` /// 2) Freeing the remote `RustBuffer` using `ffi_glean_core_rustbuffer_free`. impl ConsumeRemoteType for RustBuffer { fn consume(self) -> Self { let src = self.data_pointer(); let reslen = self.len(); let mut newvec = Vec::with_capacity(reslen); // SAFETY: // * `src` is a newly created `Vec` with `reslen` capacity. // * `dst` is a `Vec` pointer with size `reslen` // * Both `src` (newly created `Vec`) and `dst` (a pointer obtained from a `Vec`) // are correctly aligned. // * `src` and `dst` are definitely separate `Vec`s, thus not overlapping. unsafe { let dst = newvec.as_mut_ptr(); std::ptr::copy_nonoverlapping(src, dst, reslen); newvec.set_len(reslen); } let mut call_status = uniffi::RustCallStatus::default(); // SAFETY: // * `call_status` is an empty status. // * `ffi_glean_core_rustbuffer_free` is generated by `UniFFI` // and accepts the `repr(C)` `RustBuffer` that we previously got via FFI. unsafe { (crate::GLEAN.ffi_glean_core_rustbuffer_free)(self, &mut call_status); } uniffi::RustBuffer::from_vec(newvec) } } /// Forwards `try_lift` to `FfiConverter::try_lift`, but consumes any remote type for local usage. impl LocalTryLift for T where T: FfiConverter, >::FfiType: ConsumeRemoteType, { type FfiType = >::FfiType; fn try_lift(v: Self::FfiType) -> Result { let v = v.consume(); uniffi::FfiConverter::::try_lift(v) } }