use std::ffi::{CStr, CString, c_char};
use crate::GCAbiConversion;
pub type GCPluginFeatures = u64;
#[repr(transparent)]
pub struct GCPluginInfo(gc_abi_sys::sGCPluginInfo);
impl GCPluginInfo {
pub fn new(name: &str, version: &str, plugin_ifc_version: &str, features: GCPluginFeatures) -> Self {
GCPluginInfo(gc_abi_sys::sGCPluginInfo {
name: CString::new(name).unwrap().into_raw(),
version: CString::new(version).unwrap().into_raw(),
pluginIfcVersion: CString::new(plugin_ifc_version).unwrap().into_raw(),
features,
})
}
pub fn new_from_c(name: CString, version: CString, plugin_ifc_version: CString, features: GCPluginFeatures) -> Self {
GCPluginInfo(gc_abi_sys::sGCPluginInfo {
name: name.into_raw(),
version: version.into_raw(),
pluginIfcVersion: plugin_ifc_version.into_raw(),
features,
})
}
#[inline]
pub fn name(&self) -> String {
self.c_name().to_string_lossy().to_string()
}
#[inline]
pub fn version(&self) -> String {
self.c_version().to_string_lossy().to_string()
}
#[inline]
pub fn plugin_ifc_version(&self) -> String {
self.c_plugin_ifc_version().to_string_lossy().to_string()
}
#[inline]
pub fn c_name(&self) -> &CStr {
unsafe { std::ffi::CStr::from_ptr(self.0.name) }
}
#[inline]
pub fn c_version(&self) -> &CStr {
unsafe { std::ffi::CStr::from_ptr(self.0.version) }
}
#[inline]
pub fn c_plugin_ifc_version(&self) -> &CStr {
unsafe { std::ffi::CStr::from_ptr(self.0.pluginIfcVersion) }
}
#[inline]
pub fn features(&self) -> GCPluginFeatures {
self.0.features
}
}
impl Drop for GCPluginInfo {
fn drop(&mut self) {
unsafe {
drop(CString::from_raw(self.0.name as *mut c_char));
drop(CString::from_raw(self.0.version as *mut c_char));
drop(CString::from_raw(self.0.pluginIfcVersion as *mut c_char));
}
}
}
impl Clone for GCPluginInfo {
fn clone(&self) -> Self {
GCPluginInfo::new_from_c(
CString::clone(&self.c_name().to_owned()),
CString::clone(&self.c_version().to_owned()),
CString::clone(&self.c_plugin_ifc_version().to_owned()),
self.features(),
)
}
}
unsafe impl GCAbiConversion for GCPluginInfo {
type CType = gc_abi_sys::sGCPluginInfo;
fn from_c(c_type: Self::CType) -> Self {
GCPluginInfo(c_type)
}
fn into_c(self) -> Self::CType {
self.0
}
}
unsafe impl Send for GCPluginInfo {}
unsafe impl Sync for GCPluginInfo {}
#[cfg(test)]
mod tests {
use super::*;
use std::ptr;
#[test]
fn test_store_and_access() {
let info = allocation_counter::measure(|| {
let plugin_info = GCPluginInfo::new_from_c(
CString::new("test").unwrap(),
CString::new("test").unwrap(),
CString::new("test").unwrap(),
0,
);
assert_eq!(plugin_info.c_name().to_str().unwrap(), "test");
assert_eq!(plugin_info.c_version().to_str().unwrap(), "test");
assert_eq!(plugin_info.c_plugin_ifc_version().to_str().unwrap(), "test");
assert_eq!(plugin_info.features(), 0);
});
assert_eq!(info.bytes_current, 0, "Memory leak detected");
}
#[test]
fn test_conversion_to_c() {
let info = allocation_counter::measure(|| {
let plugin_info = GCPluginInfo::new("test", "test", "test", 0);
let c_info: gc_abi_sys::GCPluginInfo = unsafe { (plugin_info).as_ptr() };
assert!(
ptr::addr_eq(ptr::addr_of!(plugin_info), c_info),
"The rust abstraction should be zero cost"
);
unsafe {
assert_eq!(plugin_info.c_name().to_str().unwrap(), CStr::from_ptr((*c_info).name).to_str().unwrap());
assert_eq!(
plugin_info.c_version().to_str().unwrap(),
CStr::from_ptr((*c_info).version).to_str().unwrap()
);
assert_eq!(
plugin_info.c_plugin_ifc_version().to_str().unwrap(),
CStr::from_ptr((*c_info).pluginIfcVersion).to_str().unwrap()
);
assert_eq!(plugin_info.features(), (*c_info).features);
}
});
assert_eq!(info.bytes_current, 0, "Memory leak detected");
}
#[test]
fn test_plugin_info_clone() {
let info = allocation_counter::measure(|| {
let plugin_info = GCPluginInfo::new("test", "test", "test", 0);
let plugin_info_clone = plugin_info.clone();
drop(plugin_info);
assert_eq!(plugin_info_clone.c_name().to_str().unwrap(), "test");
assert_eq!(plugin_info_clone.c_version().to_str().unwrap(), "test");
assert_eq!(plugin_info_clone.c_plugin_ifc_version().to_str().unwrap(), "test");
assert_eq!(plugin_info_clone.features(), 0);
});
assert_eq!(info.bytes_current, 0, "Memory leak detected");
}
}