use std::fmt;
use std::fmt::Debug;
use std::marker::PhantomData;
use std::mem::{self, MaybeUninit};
use std::ptr;
#[cfg_attr(
has_diagnostic_namespace,
diagnostic::on_unimplemented(
message = "`{Self}` is not linked to a C representation",
label = "needs an implementation of `FreeForeign`",
note = "`FreeForeign` provides the corresponding C pointer type and a `free_foreign` function"
)
)]
pub trait FreeForeign {
type Foreign;
unsafe fn free_foreign(p: *mut Self::Foreign);
}
#[cfg_attr(
has_diagnostic_namespace,
diagnostic::on_unimplemented(
message = "`{Self}` cannot be copied into a C datum",
label = "needs an implementation of `CloneToForeign`",
note = "`clone_to_foreign` safely allocates a fresh C datum and clones `self` into it"
)
)]
pub trait CloneToForeign: FreeForeign {
fn clone_to_foreign(&self) -> OwnedPointer<Self>;
#[must_use]
fn clone_to_foreign_ptr(&self) -> *mut Self::Foreign {
self.clone_to_foreign().into_inner()
}
}
#[cfg_attr(
has_diagnostic_namespace,
diagnostic::on_unimplemented(
message = "a slice, array or `Vec` of `{Self}` cannot be converted to C",
label = "needs an implementation of `FixedAlloc`",
note = "a slice converts its elements in place, so `{Self}` must occupy a fixed amount of foreign memory",
)
)]
pub unsafe trait FixedAlloc: CloneToForeign + FreeForeign + Sized {
const FOREIGN_LEN: usize = 1;
unsafe fn clone_into_foreign(dest: *mut Self::Foreign, src: &Self);
unsafe fn clone_from_native_slice(mut dest: *mut Self::Foreign, src: &[Self]) {
unsafe {
for item in src {
Self::clone_into_foreign(dest, item);
dest = dest.add(Self::FOREIGN_LEN);
}
}
}
}
#[cfg_attr(
has_diagnostic_namespace,
diagnostic::on_unimplemented(
message = "`{Self}` cannot be read back out of a C array",
label = "cannot be read back out of C",
note = "this is not implemented directly: writing to C needs only `FixedAlloc`, and \
reading back additionally needs `FromForeign`, so implement that",
note = "a type that borrows cannot, since `cloned_from_foreign` has no lifetime to \
constrain its result with"
)
)]
pub trait FixedAllocFrom: FixedAlloc + FromForeign {
unsafe fn clone_array_from_foreign<const N: usize>(mut src: *const Self::Foreign) -> [Self; N] {
unsafe {
let mut uninit = MaybeUninit::<[Self; N]>::uninit();
let mut dest = uninit.as_mut_ptr().cast::<Self>();
for _ in 0..N {
ptr::write(dest, Self::cloned_from_foreign(src));
dest = dest.add(1);
src = src.add(Self::FOREIGN_LEN);
}
uninit.assume_init()
}
}
}
impl<T: FixedAlloc + FromForeign> FixedAllocFrom for T {}
pub trait IntoNative<U> {
unsafe fn into_native(self) -> U;
}
impl<T, U> IntoNative<U> for *mut T
where
U: FromForeign<Foreign = T>,
{
unsafe fn into_native(self) -> U {
U::from_foreign(self)
}
}
#[cfg_attr(
has_diagnostic_namespace,
diagnostic::on_unimplemented(
message = "`{Self}` cannot be built from a C datum",
label = "needs an implementation of `FromForeign`",
note = "a type that borrows cannot implement this at all, since nothing in the signature constrains the lifetime of `Self`"
)
)]
pub trait FromForeign: FreeForeign + Sized {
unsafe fn cloned_from_foreign(p: *const Self::Foreign) -> Self;
unsafe fn from_foreign(p: *mut Self::Foreign) -> Self {
let result = Self::cloned_from_foreign(p);
Self::free_foreign(p);
result
}
}
pub struct OwnedPointer<T: FreeForeign + ?Sized> {
ptr: *mut <T as FreeForeign>::Foreign,
}
impl<T: FreeForeign + ?Sized> OwnedPointer<T> {
pub fn null_mut() -> Self {
OwnedPointer {
ptr: ptr::null_mut(),
}
}
pub unsafe fn new(ptr: *mut <T as FreeForeign>::Foreign) -> Self {
OwnedPointer { ptr }
}
pub fn from<U>(x: OwnedPointer<U>) -> Self
where
U: FreeForeign<Foreign = <T as FreeForeign>::Foreign> + ?Sized,
{
unsafe { OwnedPointer::new(x.into_inner()) }
}
pub fn into<U>(self) -> OwnedPointer<U>
where
U: FreeForeign<Foreign = <T as FreeForeign>::Foreign> + ?Sized,
{
OwnedPointer::from(self)
}
pub fn as_ptr(&self) -> *const <T as FreeForeign>::Foreign {
self.ptr
}
pub fn as_mut_ptr(&self) -> *mut <T as FreeForeign>::Foreign {
self.ptr
}
#[must_use]
pub fn into_inner(mut self) -> *mut <T as FreeForeign>::Foreign {
let result = mem::replace(&mut self.ptr, ptr::null_mut());
mem::forget(self);
result
}
}
impl<T: FromForeign> OwnedPointer<T> {
pub fn into_native(self) -> T {
unsafe { T::from_foreign(self.into_inner()) }
}
}
impl<T: FreeForeign + ?Sized> Debug for OwnedPointer<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let name = std::any::type_name::<T>();
let name = format!("OwnedPointer<{name}>");
f.debug_tuple(&name).field(&self.as_ptr()).finish()
}
}
impl<T: CloneToForeign + Default> Default for OwnedPointer<T> {
fn default() -> Self {
<T as Default>::default().clone_to_foreign()
}
}
impl<T: FreeForeign + ?Sized> Drop for OwnedPointer<T> {
fn drop(&mut self) {
let p = mem::replace(&mut self.ptr, ptr::null_mut());
unsafe { T::free_foreign(p) }
}
}
pub struct BorrowedPointer<P: FreeForeign + ?Sized, T> {
ptr: *const <P as FreeForeign>::Foreign,
storage: T,
}
impl<P: FreeForeign + ?Sized, T> BorrowedPointer<P, T> {
pub unsafe fn new(ptr: *const <P as FreeForeign>::Foreign, storage: T) -> Self {
BorrowedPointer { ptr, storage }
}
pub fn as_ptr(&self) -> *const <P as FreeForeign>::Foreign {
self.ptr
}
pub(crate) fn into<Q>(self) -> BorrowedPointer<Q, T>
where
Q: FreeForeign<Foreign = <P as FreeForeign>::Foreign> + ?Sized,
{
BorrowedPointer {
ptr: self.ptr,
storage: self.storage,
}
}
pub(crate) fn map<Q: FreeForeign<Foreign = P::Foreign> + ?Sized, F: FnOnce(T) -> U, U>(
self,
f: F,
) -> BorrowedPointer<Q, U> {
BorrowedPointer {
ptr: self.ptr,
storage: f(self.storage),
}
}
}
impl<P: FreeForeign + ?Sized> FreeForeign for &P {
type Foreign = <P as FreeForeign>::Foreign;
unsafe fn free_foreign(p: *mut Self::Foreign) {
P::free_foreign(p)
}
}
impl<P: CloneToForeign + ?Sized> CloneToForeign for &P {
fn clone_to_foreign(&self) -> OwnedPointer<Self> {
OwnedPointer::from(CloneToForeign::clone_to_foreign(&**self))
}
}
impl<P: FreeForeign + ?Sized, T> FreeForeign for BorrowedPointer<P, T> {
type Foreign = <P as FreeForeign>::Foreign;
unsafe fn free_foreign(p: *mut Self::Foreign) {
P::free_foreign(p)
}
}
impl<P, T> CloneToForeign for BorrowedPointer<P, T>
where
P: FreeForeign + ?Sized,
T: CloneToForeign<Foreign = <P as FreeForeign>::Foreign>,
{
fn clone_to_foreign(&self) -> OwnedPointer<Self> {
OwnedPointer::from(self.storage.clone_to_foreign())
}
}
impl<'a, P: FreeForeign + ?Sized> BorrowedPointer<P, &'a P> {
pub unsafe fn new_borrowed<F>(value: &'a P, f: F) -> Self
where
F: FnOnce(*const P) -> *const <P as FreeForeign>::Foreign,
{
let raw: *const P = value;
let ptr = f(raw);
unsafe { BorrowedPointer::new(ptr, value) }
}
}
impl<P: FreeForeign + ?Sized, T> Debug for BorrowedPointer<P, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let ptr_name = std::any::type_name::<*mut <P as FreeForeign>::Foreign>();
let storage_name = std::any::type_name::<T>();
let name = format!("BorrowedPointer<{ptr_name}, {storage_name}>");
f.debug_tuple(&name).field(&self.as_ptr()).finish()
}
}
#[cfg_attr(
has_diagnostic_namespace,
diagnostic::on_unimplemented(
message = "`{Self}` cannot be borrowed as a C datum",
label = "needs an implementation of `BorrowForeign`",
note = "borrowing copies nothing, so the C representation has to be findable inside `{Self}`",
note = "use `clone_to_foreign` if a copy is acceptable"
)
)]
pub trait BorrowForeign: FreeForeign {
type Storage<'a>
where
Self: 'a;
fn borrow_foreign(&self) -> BorrowedPointer<Self, Self::Storage<'_>>;
}
#[cfg_attr(
has_diagnostic_namespace,
diagnostic::on_unimplemented(
message = "`{Self}` cannot be consumed into a C datum",
label = "needs an implementation of `IntoForeign`",
note = "`into_foreign` hands its allocation to the result instead of copying, so the C representation has to be findable inside `{Self}`",
note = "use `clone_to_foreign` if a copy is acceptable"
)
)]
pub trait IntoForeign: FreeForeign {
type Storage;
fn into_foreign(self) -> BorrowedMutPointer<Self, Self::Storage>;
}
pub struct BorrowedMutPointer<P: FreeForeign + ?Sized, T> {
ptr: *mut <P as FreeForeign>::Foreign,
storage: T,
}
impl<P: FreeForeign + ?Sized, T> BorrowedMutPointer<P, T> {
pub unsafe fn new(ptr: *mut <P as FreeForeign>::Foreign, storage: T) -> Self {
BorrowedMutPointer { ptr, storage }
}
pub fn as_ptr(&self) -> *const <P as FreeForeign>::Foreign {
self.ptr
}
pub fn as_mut_ptr(&mut self) -> *mut <P as FreeForeign>::Foreign {
self.ptr
}
pub(crate) fn into<Q>(self) -> BorrowedMutPointer<Q, T>
where
Q: FreeForeign<Foreign = <P as FreeForeign>::Foreign> + ?Sized,
{
BorrowedMutPointer {
ptr: self.ptr,
storage: self.storage,
}
}
pub(crate) fn map<Q: FreeForeign<Foreign = P::Foreign> + ?Sized, U, F: FnOnce(T) -> U>(
self,
f: F,
) -> BorrowedMutPointer<Q, U> {
BorrowedMutPointer {
ptr: self.ptr,
storage: f(self.storage),
}
}
}
impl<P: FreeForeign + ?Sized, T> FreeForeign for BorrowedMutPointer<P, T> {
type Foreign = <P as FreeForeign>::Foreign;
unsafe fn free_foreign(p: *mut Self::Foreign) {
P::free_foreign(p)
}
}
impl<P, T> CloneToForeign for BorrowedMutPointer<P, T>
where
P: FreeForeign + ?Sized,
T: CloneToForeign<Foreign = <P as FreeForeign>::Foreign>,
{
fn clone_to_foreign(&self) -> OwnedPointer<Self> {
OwnedPointer::from(self.storage.clone_to_foreign())
}
}
impl<'a, P: FreeForeign + ?Sized> BorrowedMutPointer<P, BorrowedStorage<'a, P>> {
pub unsafe fn new_borrowed<F>(value: &'a mut P, f: F) -> Self
where
F: FnOnce(*mut P) -> *mut <P as FreeForeign>::Foreign,
{
let raw: *mut P = value;
let ptr = f(raw);
unsafe { BorrowedMutPointer::new(ptr, BorrowedStorage::new(raw)) }
}
}
impl<P: FreeForeign + ?Sized, T> Debug for BorrowedMutPointer<P, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let ptr_name = std::any::type_name::<*mut <P as FreeForeign>::Foreign>();
let storage_name = std::any::type_name::<T>();
let name = format!("BorrowedMutPointer<{ptr_name}, {storage_name}>");
f.debug_tuple(&name).field(&self.as_ptr()).finish()
}
}
pub struct BorrowedStorage<'a, T: ?Sized> {
ptr: *mut T,
_marker: PhantomData<&'a mut T>,
}
impl<'a, T: ?Sized> BorrowedStorage<'a, T> {
pub(crate) unsafe fn new(ptr: *mut T) -> Self {
BorrowedStorage {
ptr,
_marker: PhantomData,
}
}
}
impl<T: FreeForeign + ?Sized> FreeForeign for BorrowedStorage<'_, T> {
type Foreign = <T as FreeForeign>::Foreign;
unsafe fn free_foreign(p: *mut Self::Foreign) {
T::free_foreign(p)
}
}
impl<T: CloneToForeign + ?Sized> CloneToForeign for BorrowedStorage<'_, T> {
fn clone_to_foreign(&self) -> OwnedPointer<Self> {
let p = unsafe { (*self.ptr).clone_to_foreign() };
OwnedPointer::from(p)
}
}
#[cfg_attr(
has_diagnostic_namespace,
diagnostic::on_unimplemented(
message = "`{Self}` cannot be borrowed mutably as a C datum",
label = "needs an implementation of `BorrowForeignMut`",
note = "borrowing copies nothing, so the C representation has to be findable inside `{Self}`",
note = "use `clone_to_foreign` if a copy is acceptable"
)
)]
pub trait BorrowForeignMut: FreeForeign {
type Storage<'a>
where
Self: 'a;
fn borrow_foreign_mut(&mut self) -> BorrowedMutPointer<Self, Self::Storage<'_>>;
}