use crate::core::Core;
use crate::sys;
use std::ffi::CStr;
use std::os::raw::c_int;
pub type CorePlugin = sys::RCorePlugin;
pub type PluginMeta = sys::RPluginMeta;
pub type PluginSession = sys::RCorePluginSession;
pub type LibraryPlugin = sys::RLibStruct;
pub const PLUGIN_STATUS_OK: c_int = 3;
pub const LIB_TYPE_CORE: c_int = 12;
pub static R2_VERSION: &CStr =
unsafe { CStr::from_bytes_with_nul_unchecked(concat!(env!("R2_VERSION"), "\0").as_bytes()) };
pub const R2_ABI_VERSION: u32 = parse_decimal(env!("R2_ABIVERSION"));
#[macro_export]
macro_rules! plugin_meta {
($name:literal, $description:literal, $author:literal, $version:literal, $license:literal) => {
$crate::PluginMeta {
name: concat!($name, "\0").as_ptr() as *mut ::std::os::raw::c_char,
desc: concat!($description, "\0").as_ptr() as *mut ::std::os::raw::c_char,
author: concat!($author, "\0").as_ptr() as *mut ::std::os::raw::c_char,
version: concat!($version, "\0").as_ptr() as *mut ::std::os::raw::c_char,
license: concat!($license, "\0").as_ptr() as *mut ::std::os::raw::c_char,
contact: ::std::ptr::null_mut(),
copyright: ::std::ptr::null_mut(),
status: $crate::plugin::PLUGIN_STATUS_OK,
}
};
}
#[macro_export]
macro_rules! export_core_plugin {
($plugin:expr, $package:literal) => {
#[unsafe(no_mangle)]
pub static radare_plugin: $crate::plugin::LibraryPlugin = $crate::plugin::LibraryPlugin {
type_: $crate::plugin::LIB_TYPE_CORE,
data: $plugin as *const $crate::CorePlugin as *mut ::std::ffi::c_void,
version: $crate::plugin::R2_VERSION.as_ptr(),
free: None,
pkgname: concat!($package, "\0").as_ptr() as *const ::std::os::raw::c_char,
abiversion: $crate::plugin::R2_ABI_VERSION,
};
};
}
pub unsafe fn session_core(session: *mut PluginSession) -> Option<Core> {
unsafe { Core::from_session(session) }
}
pub unsafe fn install_state<T>(session: *mut PluginSession, state: T) -> Result<(), T> {
let Some(session) = (unsafe { session.as_mut() }) else {
return Err(state);
};
if !session.data.is_null() {
return Err(state);
}
session.data = Box::into_raw(Box::new(state)).cast();
Ok(())
}
pub unsafe fn state<T>(session: *const PluginSession) -> Option<&'static T> {
let session = unsafe { session.as_ref()? };
unsafe { session.data.cast::<T>().as_ref() }
}
pub unsafe fn state_mut<T>(session: *mut PluginSession) -> Option<&'static mut T> {
let session = unsafe { session.as_mut()? };
unsafe { session.data.cast::<T>().as_mut() }
}
pub unsafe fn take_state<T>(session: *mut PluginSession) -> Option<T> {
let session = unsafe { session.as_mut()? };
if session.data.is_null() {
return None;
}
let state = unsafe { Box::from_raw(session.data.cast::<T>()) };
session.data = std::ptr::null_mut();
Some(*state)
}
const fn parse_decimal(value: &str) -> u32 {
let bytes = value.as_bytes();
let mut result = 0u32;
let mut index = 0;
while index < bytes.len() {
let digit = bytes[index];
assert!(digit >= b'0' && digit <= b'9', "invalid decimal value");
result = result * 10 + (digit - b'0') as u32;
index += 1;
}
result
}
#[cfg(test)]
mod tests {
use super::*;
use std::ffi::c_void;
fn session() -> PluginSession {
PluginSession {
core: std::ptr::null_mut(),
plugin: std::ptr::null_mut::<c_void>(),
data: std::ptr::null_mut(),
}
}
#[test]
fn owns_typed_session_state() {
let mut session = session();
unsafe {
install_state(&mut session, String::from("state")).unwrap();
state_mut::<String>(&mut session).unwrap().push('!');
assert_eq!(
state::<String>(&session).map(String::as_str),
Some("state!")
);
assert_eq!(take_state::<String>(&mut session), Some("state!".into()));
assert!(session.data.is_null());
}
}
#[test]
fn parses_abi_version() {
assert_eq!(parse_decimal("0"), 0);
assert_eq!(parse_decimal("42"), 42);
}
}