miniextendr_api/
mx_abi.rs1use crate::abi::{SIDECAR_INIT_TAG, SidecarInitVTable, mx_erased, mx_tag};
9use crate::gc_protect::OwnedProtect;
10use crate::sys::{
11 R_ClearExternalPtr, R_ExternalPtrAddr, R_ExternalPtrTag, R_MakeExternalPtr, R_PreserveObject,
12 R_RegisterCCallable, R_RegisterCFinalizerEx, Rf_install,
13};
14use crate::{Rboolean, SEXP, SEXPTYPE, SexpExt};
15use std::ffi::CStr;
16use std::sync::OnceLock;
17
18const MX_TAG_NAME: &CStr = c"miniextendr::mx_erased";
20
21static MX_TAG: OnceLock<SEXP> = OnceLock::new();
24
25#[inline]
30pub(crate) unsafe fn get_tag() -> SEXP {
31 *MX_TAG.get_or_init(|| unsafe {
32 let tag = Rf_install(MX_TAG_NAME.as_ptr());
33 R_PreserveObject(tag);
34 tag
35 })
36}
37
38unsafe extern "C-unwind" fn mx_externalptr_finalizer(ptr: SEXP) {
43 unsafe {
44 if true {
{
match (&ptr.type_of(), &crate::SEXPTYPE::EXTPTRSXP) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val, ::core::option::Option::None);
}
}
}
};
};debug_assert_eq!(ptr.type_of(), crate::SEXPTYPE::EXTPTRSXP,);
45 let erased = R_ExternalPtrAddr(ptr) as *mut mx_erased;
46 if !erased.is_null() {
47 let base = (*erased).base;
48 if !base.is_null() {
49 ((*base).drop)(erased);
50 }
51 }
52 R_ClearExternalPtr(ptr);
53 }
54}
55
56#[unsafe(no_mangle)]
67pub unsafe extern "C-unwind" fn mx_wrap(ptr: *mut mx_erased) -> SEXP {
68 unsafe {
69 let tag = get_tag();
70 let guard = OwnedProtect::new(R_MakeExternalPtr(ptr.cast(), tag, SEXP::nil()));
71 R_RegisterCFinalizerEx(guard.get(), Some(mx_externalptr_finalizer), Rboolean::TRUE);
72 let init = ((*(*ptr).base).query)(ptr, SIDECAR_INIT_TAG).cast::<SidecarInitVTable>();
75 if !init.is_null() {
76 ((*init).init)(ptr, guard.get());
77 }
78 guard.get()
79 }
80}
81
82#[unsafe(no_mangle)]
93pub unsafe extern "C-unwind" fn mx_get(sexp: SEXP) -> *mut mx_erased {
94 unsafe {
95 if sexp.type_of() != SEXPTYPE::EXTPTRSXP {
96 return std::ptr::null_mut();
97 }
98 if R_ExternalPtrTag(sexp) != get_tag() {
99 return std::ptr::null_mut();
100 }
101 R_ExternalPtrAddr(sexp) as *mut mx_erased
102 }
103}
104
105#[unsafe(no_mangle)]
116pub unsafe extern "C-unwind" fn mx_query(sexp: SEXP, tag: mx_tag) -> *const std::ffi::c_void {
117 unsafe {
118 let erased = mx_get(sexp);
119 if erased.is_null() {
120 return std::ptr::null();
121 }
122 let base = (*erased).base;
123 if base.is_null() {
124 return std::ptr::null();
125 }
126 ((*base).query)(erased, tag)
127 }
128}
129
130pub unsafe fn mx_abi_register(pkg_name: &CStr) {
140 unsafe {
141 get_tag();
143
144 R_RegisterCCallable(
147 pkg_name.as_ptr(),
148 c"mx_wrap".as_ptr(),
149 Some(std::mem::transmute::<
150 unsafe extern "C-unwind" fn(*mut mx_erased) -> SEXP,
151 unsafe extern "C-unwind" fn() -> *mut std::os::raw::c_void,
152 >(mx_wrap)),
153 );
154 R_RegisterCCallable(
155 pkg_name.as_ptr(),
156 c"mx_get".as_ptr(),
157 Some(std::mem::transmute::<
158 unsafe extern "C-unwind" fn(SEXP) -> *mut mx_erased,
159 unsafe extern "C-unwind" fn() -> *mut std::os::raw::c_void,
160 >(mx_get)),
161 );
162 R_RegisterCCallable(
163 pkg_name.as_ptr(),
164 c"mx_query".as_ptr(),
165 Some(std::mem::transmute::<
166 unsafe extern "C-unwind" fn(SEXP, mx_tag) -> *const std::ffi::c_void,
167 unsafe extern "C-unwind" fn() -> *mut std::os::raw::c_void,
168 >(mx_query)),
169 );
170 }
171}