mu_lib 0.2.2

XCENA mu Library
Documentation
#![doc(hidden)]
use crate::ndarray;
use core::any::type_name;

#[allow(non_snake_case)]
pub trait FunctionTraits<F, Args> {
    fn invoke(fp: F, mode: u32, ndarray_value_bit: u32, buffer: *mut u8);
}

fn is_pointer<T>() -> bool {
    type_name::<T>().starts_with("*mut ") || type_name::<T>().starts_with("*const ")
}

fn is_ndarray<T>() -> bool {
    type_name::<T>().contains("::NDArray<")
}

unsafe fn read_arg<T1>(
    buffer: *const u8,
    offset: usize,
    mode: u32,
    ndarray_value_bit: u32,
) -> (T1, usize)
where
    T1: Copy,
{
    let ptr_array = buffer as *const usize;
    let raw_addr = *ptr_array.add(offset);
    let is_ptr = is_pointer::<T1>();
    let is_ndarray_bit = ((ndarray_value_bit >> offset) & 1) != 0;

    let read_ndarray = |raw: usize| -> *const T1 {
        let arr = raw as *const ndarray::NDArray<u8>;
        let data_ptr = unsafe { (&*arr).data() };
        if !is_ptr && !mode_is_normal(mode) && !is_ndarray::<T1>() {
            unsafe { *(data_ptr as *const *const T1) }
        } else if is_ptr && !is_ndarray::<T1>() {
            data_ptr as *const T1
        } else {
            unsafe { *(raw_addr as *const *const T1) }
        }
    };

    let arg_ptr: *const T1 = if is_ndarray_bit {
        read_ndarray(raw_addr)
    } else if !is_ptr && !mode_is_normal(mode) {
        unsafe { *(raw_addr as *const *const T1) }
    } else {
        raw_addr as *const T1
    };

    let value = *arg_ptr;
    (value, offset + 1)
}

pub struct CallHelper;

#[inline(always)]
fn mode_is_normal(mode: u32) -> bool {
    mode == 0
}

macro_rules! impl_function_traits {
    () => {
        impl FunctionTraits<extern "C" fn(), ()> for CallHelper {
            fn invoke(fp: extern "C" fn(), _mode: u32, _ndarray_value_bit: u32, _buffer: *mut u8) {
                fp();
            }
        }
    };
    ($($T:ident),*) => {
        #[allow(unused_parens)]
        impl<$($T: Copy + 'static),*> FunctionTraits<extern "C" fn($($T),*), ($($T),*)> for CallHelper {
            fn invoke(fp: extern "C" fn($($T),*), mode: u32, ndarray_value_bit: u32, buffer: *mut u8) {
                unsafe {
                    let buf = buffer as *const u8;
                    let mut _offset = 0;
                    $(
                        let (arg, new_offset) = read_arg::<$T>(buf, _offset, mode, ndarray_value_bit);
                        _offset = new_offset;
                        let $T = arg;
                    )*
                    fp($($T),*);
                }
            }
        }
    };
}

#[allow(non_snake_case)]
mod impls {
    use super::*;

    impl_function_traits!();
    impl_function_traits!(T1);
    impl_function_traits!(T1, T2);
    impl_function_traits!(T1, T2, T3);
    impl_function_traits!(T1, T2, T3, T4);
    impl_function_traits!(T1, T2, T3, T4, T5);
    impl_function_traits!(T1, T2, T3, T4, T5, T6);
    impl_function_traits!(T1, T2, T3, T4, T5, T6, T7);
    impl_function_traits!(T1, T2, T3, T4, T5, T6, T7, T8);
    impl_function_traits!(T1, T2, T3, T4, T5, T6, T7, T8, T9);
}

pub fn call_main<FP, Args>(func: FP, mode: u32, ndarray_value_bit: u32, buffer: *mut u8)
where
    CallHelper: FunctionTraits<FP, Args>,
{
    CallHelper::invoke(func, mode, ndarray_value_bit, buffer);
}