use crate::error::{BoxError, TarantoolErrorCode};
use std::ffi::CStr;
use std::os::raw::c_char;
use std::ptr::NonNull;
use libloading::os::unix::Library;
#[macro_export]
macro_rules! c_str {
($s:expr) => {{
#[allow(unused_unsafe)]
const RESULT: &'static ::std::ffi::CStr = unsafe {
::std::ffi::CStr::from_bytes_with_nul_unchecked(::std::concat!($s, "\0").as_bytes())
};
RESULT
}};
}
#[macro_export]
macro_rules! c_ptr {
($s:expr) => {
$crate::c_str!($s).as_ptr()
};
}
#[macro_export]
macro_rules! offset_of {
($type:ty, $field:tt) => {{
const RESULT: usize = unsafe {
let dummy = ::core::mem::MaybeUninit::<$type>::uninit();
let dummy_ptr = dummy.as_ptr();
let field_ptr = ::std::ptr::addr_of!((*dummy_ptr).$field);
let field_ptr = field_ptr.cast::<u8>();
let dummy_ptr = dummy_ptr.cast::<u8>();
field_ptr.offset_from(dummy_ptr) as usize
};
RESULT
}};
}
#[macro_export]
macro_rules! static_assert {
($e:expr $(,)?) => {
const _: () = assert!($e);
};
($e:expr, $msg:expr $(,)?) => {
const _: () = assert!($e, $msg);
};
}
#[macro_export]
macro_rules! define_dlsym_reloc {
(
$(
$(#[$meta:meta])*
pub $(($where:tt))? fn $sym:ident ( $( $args:ident: $types:ty ),* $(,)? ) $( -> $ret:ty )?;
)+
) => {
$(
$(#[$meta])*
#[inline(always)]
pub $(($where))? unsafe fn $sym($($args: $types),*) $(-> $ret)? {
return RELOC_FN($($args),*);
type SymType = unsafe fn($($args: $types),*) $(-> $ret)?;
static mut RELOC_FN: SymType = init;
unsafe fn init($($args: $types),*) $(-> $ret)? {
let sym_name = $crate::c_str!(::std::stringify!($sym));
let impl_fn: SymType = $crate::ffi::helper::get_any_symbol(sym_name)
.unwrap();
RELOC_FN = impl_fn;
RELOC_FN($($args),*)
}
}
)+
};
}
#[cfg(feature = "static_linking")]
#[macro_export]
macro_rules! define_extern_or_dlsym_reloc {
($($anything:tt)+) => {
extern "C" {
$($anything)+
}
}
}
#[cfg(not(feature = "static_linking"))]
#[macro_export]
macro_rules! define_extern_or_dlsym_reloc {
($($anything:tt)+) => {
$crate::define_dlsym_reloc! {
$($anything)+
}
}
}
#[inline]
pub unsafe fn tnt_internal_symbol<T>(name: &CStr) -> Option<T> {
if std::mem::size_of::<T>() != std::mem::size_of::<*mut ()>() {
return None;
}
let ptr = (RELOC_FN?)(name.as_ptr())?;
return Some(std::mem::transmute_copy(&ptr));
type SymType = unsafe fn(*const c_char) -> Option<NonNull<()>>;
static mut RELOC_FN: Option<SymType> = Some(init);
unsafe fn init(name: *const c_char) -> Option<NonNull<()>> {
let current_library = Library::this();
let internal_symbol = c_str!("tnt_internal_symbol").to_bytes();
match current_library.get(internal_symbol) {
Ok(sym) => {
RELOC_FN = Some(*sym);
(RELOC_FN.unwrap())(name)
}
Err(_) => {
RELOC_FN = None;
None
}
}
}
}
#[inline]
pub unsafe fn has_dyn_symbol(name: &CStr) -> bool {
get_dyn_symbol::<*const ()>(name).is_ok()
}
#[inline]
pub unsafe fn get_dyn_symbol<T: Copy>(name: &CStr) -> Result<T, BoxError> {
let current_library = Library::this();
let symbol_name = name.to_bytes_with_nul();
let symbol_pointer = current_library.get::<T>(symbol_name).map_err(|e| {
let code = TarantoolErrorCode::NoSuchProc;
let message = format!("symbol '{name:?}' not found: {e}");
BoxError::new(code, message)
})?;
Ok(*symbol_pointer)
}
#[inline]
pub unsafe fn get_any_symbol<T: Copy>(name: &CStr) -> Result<T, BoxError> {
if let Some(sym) = tnt_internal_symbol(name) {
return Ok(sym);
}
let current_library = Library::this();
let symbol_name = name.to_bytes_with_nul();
let symbol_pointer = current_library.get::<T>(symbol_name).map_err(|e| {
let code = TarantoolErrorCode::NoSuchProc;
let message = format!("symbol '{name:?}' not found: {e}");
BoxError::new(code, message)
})?;
Ok(*symbol_pointer)
}