use cxx::{memory::UniquePtrTarget, UniquePtr};
use moveit::{AsMove, CopyNew, MoveNew, New};
use std::{marker::PhantomPinned, mem::MaybeUninit, ops::Deref, pin::Pin};
pub unsafe trait ValueParam<T> {
#[doc(hidden)]
type StackStorage;
#[doc(hidden)]
unsafe fn populate_stack_space(self, this: Pin<&mut Option<Self::StackStorage>>);
#[doc(hidden)]
fn get_ptr(stack: Pin<&mut Self::StackStorage>) -> *mut T;
#[doc(hidden)]
fn do_drop(_stack: Pin<&mut Self::StackStorage>) {}
}
unsafe impl<T> ValueParam<T> for &T
where
T: CopyNew,
{
type StackStorage = MaybeUninit<T>;
unsafe fn populate_stack_space(self, mut stack: Pin<&mut Option<Self::StackStorage>>) {
let slot = Pin::into_inner_unchecked(stack.as_mut());
*slot = Some(MaybeUninit::uninit());
crate::moveit::new::copy(self).new(Pin::new_unchecked(slot.as_mut().unwrap()))
}
fn get_ptr(stack: Pin<&mut Self::StackStorage>) -> *mut T {
unsafe { Pin::into_inner_unchecked(stack).assume_init_mut() as *mut T }
}
fn do_drop(stack: Pin<&mut Self::StackStorage>) {
unsafe { std::ptr::drop_in_place(Pin::into_inner_unchecked(stack).assume_init_mut()) };
}
}
unsafe impl<T> ValueParam<T> for UniquePtr<T>
where
T: UniquePtrTarget,
{
type StackStorage = UniquePtr<T>;
unsafe fn populate_stack_space(self, mut stack: Pin<&mut Option<Self::StackStorage>>) {
*Pin::into_inner_unchecked(stack.as_mut()) = Some(self)
}
fn get_ptr(stack: Pin<&mut Self::StackStorage>) -> *mut T {
unsafe {
(Pin::into_inner_unchecked(
(*Pin::into_inner_unchecked(stack))
.as_mut()
.expect("Passed a NULL UniquePtr as a C++ value parameter"),
)) as *mut T
}
}
}
unsafe impl<T> ValueParam<T> for Pin<Box<T>> {
type StackStorage = Pin<Box<T>>;
unsafe fn populate_stack_space(self, mut stack: Pin<&mut Option<Self::StackStorage>>) {
*Pin::into_inner_unchecked(stack.as_mut()) = Some(self)
}
fn get_ptr(stack: Pin<&mut Self::StackStorage>) -> *mut T {
unsafe {
(Pin::into_inner_unchecked((*Pin::into_inner_unchecked(stack)).as_mut())) as *mut T
}
}
}
unsafe impl<'a, T: 'a> ValueParam<T> for &'a UniquePtr<T>
where
T: UniquePtrTarget + CopyNew,
{
type StackStorage = <&'a T as ValueParam<T>>::StackStorage;
unsafe fn populate_stack_space(self, stack: Pin<&mut Option<Self::StackStorage>>) {
self.as_ref()
.expect("Passed a NULL &UniquePtr as a C++ value parameter")
.populate_stack_space(stack)
}
fn get_ptr(stack: Pin<&mut Self::StackStorage>) -> *mut T {
<&'a T as ValueParam<T>>::get_ptr(stack)
}
fn do_drop(stack: Pin<&mut Self::StackStorage>) {
<&'a T as ValueParam<T>>::do_drop(stack)
}
}
unsafe impl<'a, T: 'a> ValueParam<T> for &'a Pin<Box<T>>
where
T: CopyNew,
{
type StackStorage = <&'a T as ValueParam<T>>::StackStorage;
unsafe fn populate_stack_space(self, stack: Pin<&mut Option<Self::StackStorage>>) {
self.as_ref().get_ref().populate_stack_space(stack)
}
fn get_ptr(stack: Pin<&mut Self::StackStorage>) -> *mut T {
<&'a T as ValueParam<T>>::get_ptr(stack)
}
fn do_drop(stack: Pin<&mut Self::StackStorage>) {
<&'a T as ValueParam<T>>::do_drop(stack)
}
}
pub fn as_new<N: New>(constructor: N) -> impl ValueParam<N::Output> {
ByNew(constructor)
}
pub fn as_copy<P: Deref>(ptr: P) -> impl ValueParam<P::Target>
where
P::Target: CopyNew,
{
ByNew(crate::moveit::new::copy(ptr))
}
pub fn as_mov<P: AsMove>(ptr: P) -> impl ValueParam<P::Target>
where
P::Target: MoveNew,
{
ByNew(crate::moveit::new::mov(ptr))
}
#[doc(hidden)]
pub struct ByNew<N: New>(N);
unsafe impl<N: New> ValueParam<N::Output> for ByNew<N> {
type StackStorage = MaybeUninit<N::Output>;
unsafe fn populate_stack_space(self, mut stack: Pin<&mut Option<Self::StackStorage>>) {
let slot = Pin::into_inner_unchecked(stack.as_mut());
*slot = Some(MaybeUninit::uninit());
self.0.new(Pin::new_unchecked(slot.as_mut().unwrap()))
}
fn get_ptr(stack: Pin<&mut Self::StackStorage>) -> *mut N::Output {
unsafe { Pin::into_inner_unchecked(stack).assume_init_mut() as *mut N::Output }
}
fn do_drop(stack: Pin<&mut Self::StackStorage>) {
unsafe { std::ptr::drop_in_place(Pin::into_inner_unchecked(stack).assume_init_mut()) };
}
}
#[doc(hidden)]
pub struct ValueParamHandler<T, VP: ValueParam<T>> {
space: Option<VP::StackStorage>,
_pinned: PhantomPinned,
}
impl<T, VP: ValueParam<T>> ValueParamHandler<T, VP> {
pub unsafe fn populate(self: Pin<&mut Self>, param: VP) {
param.populate_stack_space(self.map_unchecked_mut(|s| &mut s.space))
}
pub fn get_ptr(self: Pin<&mut Self>) -> *mut T {
unsafe {
VP::get_ptr(Pin::new_unchecked(
self.get_unchecked_mut().space.as_mut().unwrap(),
))
}
}
}
impl<T, VP: ValueParam<T>> Default for ValueParamHandler<T, VP> {
fn default() -> Self {
Self {
space: None,
_pinned: PhantomPinned,
}
}
}
impl<T, VP: ValueParam<T>> Drop for ValueParamHandler<T, VP> {
fn drop(&mut self) {
if let Some(space) = self.space.as_mut() {
unsafe { VP::do_drop(Pin::new_unchecked(space)) }
}
}
}