#[allow(unused_imports)]
use super::types::{ComponentType, SemanticVersion, StringView, UID};
#[repr(C)]
pub struct ICore {
_opaque: [u8; 0],
}
#[repr(C)]
pub struct IComponentList {
_opaque: [u8; 0],
}
#[repr(C)]
pub struct ILogger {
_opaque: [u8; 0],
}
#[repr(C)]
pub struct IEarlyConfig {
_opaque: [u8; 0],
}
#[cfg(not(target_env = "msvc"))]
#[repr(C)]
pub struct IComponentVTable {
pub get_extension: unsafe extern "C" fn(*mut OmpComponent, uid: UID) -> *mut (),
pub add_extension:
unsafe extern "C" fn(*mut OmpComponent, ext: *mut (), auto_delete: bool) -> bool,
pub remove_extension_ptr: unsafe extern "C" fn(*mut OmpComponent, ext: *mut ()) -> bool,
pub remove_extension_uid: unsafe extern "C" fn(*mut OmpComponent, uid: UID) -> bool,
pub destructor: unsafe extern "C" fn(*mut OmpComponent),
pub destructor_deleting: unsafe extern "C" fn(*mut OmpComponent),
pub supported_version: unsafe extern "C" fn(*const OmpComponent) -> i32,
pub component_name: unsafe extern "C" fn(*const OmpComponent) -> StringView,
pub component_type: unsafe extern "C" fn(*const OmpComponent) -> ComponentType,
pub component_version: unsafe extern "C" fn(*const OmpComponent) -> SemanticVersion,
pub on_load: unsafe extern "C" fn(*mut OmpComponent, *mut ICore),
pub on_init: unsafe extern "C" fn(*mut OmpComponent, *mut IComponentList),
pub on_ready: unsafe extern "C" fn(*mut OmpComponent),
pub on_free: unsafe extern "C" fn(*mut OmpComponent, *mut OmpComponent),
pub provide_configuration:
unsafe extern "C" fn(*mut OmpComponent, *mut ILogger, *mut IEarlyConfig, bool),
pub free: unsafe extern "C" fn(*mut OmpComponent),
pub reset: unsafe extern "C" fn(*mut OmpComponent),
}
#[cfg(target_env = "msvc")]
#[repr(C)]
pub struct IComponentVTable {
pub get_extension: unsafe extern "thiscall" fn(*mut OmpComponent, uid: UID) -> *mut (),
pub add_extension:
unsafe extern "thiscall" fn(*mut OmpComponent, ext: *mut (), auto_delete: bool) -> bool,
pub remove_extension_ptr: unsafe extern "thiscall" fn(*mut OmpComponent, ext: *mut ()) -> bool,
pub remove_extension_uid: unsafe extern "thiscall" fn(*mut OmpComponent, uid: UID) -> bool,
pub destructor: unsafe extern "thiscall" fn(),
pub supported_version: unsafe extern "thiscall" fn() -> i32,
pub component_name: unsafe extern "thiscall" fn(),
pub component_type: unsafe extern "thiscall" fn() -> i32,
pub component_version: unsafe extern "thiscall" fn(),
pub on_load: unsafe extern "thiscall" fn(*mut OmpComponent, *mut ICore),
pub on_init: unsafe extern "thiscall" fn(*mut OmpComponent, *mut IComponentList),
pub on_ready: unsafe extern "thiscall" fn(),
pub on_free: unsafe extern "thiscall" fn(*mut OmpComponent, *mut OmpComponent),
pub provide_configuration:
unsafe extern "thiscall" fn(*mut OmpComponent, *mut ILogger, *mut IEarlyConfig, bool),
pub free: unsafe extern "thiscall" fn(),
pub reset: unsafe extern "thiscall" fn(),
}
#[cfg(not(target_env = "msvc"))]
#[repr(C)]
pub struct IUIDProviderVTable {
pub destructor_complete: unsafe extern "C" fn(*mut u8),
pub destructor_deleting: unsafe extern "C" fn(*mut u8),
pub get_uid: unsafe extern "C" fn(*const u8) -> UID,
}
#[cfg(target_env = "msvc")]
#[repr(C)]
pub struct IUIDProviderVTable {
pub get_uid: unsafe extern "thiscall" fn(*const u8) -> UID,
}
#[cfg(target_env = "msvc")]
const MISC_EXT_SIZE: usize = 52;
#[cfg(not(target_env = "msvc"))]
const MISC_EXT_SIZE: usize = 36;
#[repr(C)]
pub struct OmpComponent {
vtable: *const IComponentVTable,
_misc_ext: [u8; MISC_EXT_SIZE],
uid_vtable: *const IUIDProviderVTable,
#[cfg(target_env = "msvc")]
_uid_pad: u32,
pub uid: UID,
pub plugin_ptr: *mut (),
}
unsafe impl Send for OmpComponent {}
unsafe impl Sync for OmpComponent {}
#[cfg(all(target_arch = "x86", target_os = "linux"))]
const _: () = {
assert!(
std::mem::offset_of!(OmpComponent, uid_vtable) == 40,
"OmpComponent: invalid offset. On GCC i686, uint64_t is aligned to 4 bytes — uid_vtable must be at offset 40."
);
assert!(
std::mem::size_of::<OmpComponent>() == 56,
"OmpComponent: invalid size for the Itanium ABI. Use --target i686-unknown-linux-gnu to compile with native Open Multiplayer support."
);
};
#[cfg(all(target_arch = "x86", target_env = "msvc"))]
const _: () = {
assert!(
std::mem::offset_of!(OmpComponent, uid_vtable) == 56,
"OmpComponent MSVC: uid_vtable must be at offset 56 (IUIDProvider after IExtensible=56 bytes)."
);
};
impl OmpComponent {
#[must_use]
pub fn new(
vtable: *const IComponentVTable,
uid_vtable: *const IUIDProviderVTable,
uid: UID,
) -> Self {
Self {
vtable,
_misc_ext: [0u8; MISC_EXT_SIZE],
uid_vtable,
#[cfg(target_env = "msvc")]
_uid_pad: 0,
uid,
plugin_ptr: std::ptr::null_mut(),
}
}
}
#[cfg(not(target_env = "msvc"))]
pub unsafe extern "C" fn ext_get_extension(_this: *mut OmpComponent, _uid: UID) -> *mut () {
std::ptr::null_mut()
}
#[cfg(not(target_env = "msvc"))]
pub unsafe extern "C" fn ext_add_extension(
_this: *mut OmpComponent,
_ext: *mut (),
_auto_delete: bool,
) -> bool {
false
}
#[cfg(not(target_env = "msvc"))]
pub unsafe extern "C" fn ext_remove_extension_ptr(_this: *mut OmpComponent, _ext: *mut ()) -> bool {
false
}
#[cfg(not(target_env = "msvc"))]
pub unsafe extern "C" fn ext_remove_extension_uid(_this: *mut OmpComponent, _uid: UID) -> bool {
false
}
#[cfg(not(target_env = "msvc"))]
pub unsafe extern "C" fn ext_destructor(_this: *mut OmpComponent) {}
#[cfg(not(target_env = "msvc"))]
pub unsafe extern "C" fn ext_destructor_deleting(_this: *mut OmpComponent) {}
#[cfg(not(target_env = "msvc"))]
#[must_use]
pub unsafe extern "C" fn comp_supported_version(_this: *const OmpComponent) -> i32 {
1
}
#[cfg(not(target_env = "msvc"))]
#[must_use]
pub unsafe extern "C" fn comp_component_type(_this: *const OmpComponent) -> ComponentType {
ComponentType::Other
}
#[cfg(not(target_env = "msvc"))]
pub unsafe extern "C" fn comp_on_init(_this: *mut OmpComponent, _components: *mut IComponentList) {}
#[cfg(not(target_env = "msvc"))]
pub unsafe extern "C" fn comp_on_ready(_this: *mut OmpComponent) {}
#[cfg(not(target_env = "msvc"))]
pub unsafe extern "C" fn comp_on_free(_this: *mut OmpComponent, _component: *mut OmpComponent) {}
#[cfg(not(target_env = "msvc"))]
pub unsafe extern "C" fn comp_provide_configuration(
_this: *mut OmpComponent,
_logger: *mut ILogger,
_config: *mut IEarlyConfig,
_defaults: bool,
) {
}
#[cfg(target_env = "msvc")]
pub unsafe extern "thiscall" fn ext_get_extension(_this: *mut OmpComponent, _uid: UID) -> *mut () {
std::ptr::null_mut()
}
#[cfg(target_env = "msvc")]
pub unsafe extern "thiscall" fn ext_add_extension(
_this: *mut OmpComponent,
_ext: *mut (),
_auto_delete: bool,
) -> bool {
false
}
#[cfg(target_env = "msvc")]
pub unsafe extern "thiscall" fn ext_remove_extension_ptr(
_this: *mut OmpComponent,
_ext: *mut (),
) -> bool {
false
}
#[cfg(target_env = "msvc")]
pub unsafe extern "thiscall" fn ext_remove_extension_uid(
_this: *mut OmpComponent,
_uid: UID,
) -> bool {
false
}
#[cfg(target_env = "msvc")]
pub unsafe extern "thiscall" fn ext_destructor() {}
#[cfg(target_env = "msvc")]
pub unsafe extern "thiscall" fn comp_supported_version() -> i32 {
1
}
#[cfg(target_env = "msvc")]
pub unsafe extern "thiscall" fn comp_component_type() -> i32 {
0
}
#[cfg(target_env = "msvc")]
pub unsafe extern "thiscall" fn comp_on_init(
_this: *mut OmpComponent,
_components: *mut IComponentList,
) {
}
#[cfg(target_env = "msvc")]
pub unsafe extern "thiscall" fn comp_on_ready() {}
#[cfg(target_env = "msvc")]
pub unsafe extern "thiscall" fn comp_on_free(
_this: *mut OmpComponent,
_component: *mut OmpComponent,
) {
}
#[cfg(target_env = "msvc")]
pub unsafe extern "thiscall" fn comp_provide_configuration(
_this: *mut OmpComponent,
_logger: *mut ILogger,
_config: *mut IEarlyConfig,
_defaults: bool,
) {
}
#[cfg(not(target_env = "msvc"))]
pub unsafe extern "C" fn uid_destructor_noop(_this: *mut u8) {}
#[cfg(not(target_env = "msvc"))]
#[must_use]
pub unsafe extern "C" fn uid_get_uid(this: *const u8) -> UID {
let offset = std::mem::offset_of!(OmpComponent, uid_vtable);
#[allow(clippy::cast_ptr_alignment)]
let comp_ptr = this.wrapping_sub(offset).cast::<OmpComponent>();
unsafe { (*comp_ptr).uid }
}
#[cfg(target_env = "msvc")]
pub unsafe extern "thiscall" fn uid_get_uid(this: *const u8) -> UID {
let offset = std::mem::offset_of!(OmpComponent, uid_vtable);
let comp_ptr = this.wrapping_sub(offset).cast::<OmpComponent>();
unsafe { (*comp_ptr).uid }
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(not(target_env = "msvc"))]
use crate::omp::types::SemanticVersion;
#[cfg(not(target_env = "msvc"))]
unsafe extern "C" fn test_name(_: *const OmpComponent) -> StringView {
StringView::from_static("test\0")
}
#[cfg(target_env = "msvc")]
unsafe extern "thiscall" fn test_name() {}
#[cfg(not(target_env = "msvc"))]
unsafe extern "C" fn test_version(_: *const OmpComponent) -> SemanticVersion {
SemanticVersion::new(1, 0, 0)
}
#[cfg(target_env = "msvc")]
unsafe extern "thiscall" fn test_version() {}
#[cfg(not(target_env = "msvc"))]
unsafe extern "C" fn test_on_load(_: *mut OmpComponent, _: *mut ICore) {}
#[cfg(target_env = "msvc")]
unsafe extern "thiscall" fn test_on_load(_: *mut OmpComponent, _: *mut ICore) {}
#[cfg(not(target_env = "msvc"))]
unsafe extern "C" fn test_on_init(_: *mut OmpComponent, _: *mut IComponentList) {}
#[cfg(target_env = "msvc")]
unsafe extern "thiscall" fn test_on_init(_: *mut OmpComponent, _: *mut IComponentList) {}
#[cfg(not(target_env = "msvc"))]
unsafe extern "C" fn test_on_ready(_: *mut OmpComponent) {}
#[cfg(target_env = "msvc")]
unsafe extern "thiscall" fn test_on_ready() {}
#[cfg(not(target_env = "msvc"))]
unsafe extern "C" fn test_on_free(_: *mut OmpComponent, _: *mut OmpComponent) {}
#[cfg(target_env = "msvc")]
unsafe extern "thiscall" fn test_on_free(_: *mut OmpComponent, _: *mut OmpComponent) {}
#[cfg(not(target_env = "msvc"))]
unsafe extern "C" fn test_provide_cfg(
_: *mut OmpComponent,
_: *mut ILogger,
_: *mut IEarlyConfig,
_: bool,
) {
}
#[cfg(target_env = "msvc")]
unsafe extern "thiscall" fn test_provide_cfg(
_: *mut OmpComponent,
_: *mut ILogger,
_: *mut IEarlyConfig,
_: bool,
) {
}
#[cfg(not(target_env = "msvc"))]
unsafe extern "C" fn test_free(_: *mut OmpComponent) {}
#[cfg(target_env = "msvc")]
unsafe extern "thiscall" fn test_free() {}
#[cfg(not(target_env = "msvc"))]
unsafe extern "C" fn test_reset(_: *mut OmpComponent) {}
#[cfg(target_env = "msvc")]
unsafe extern "thiscall" fn test_reset() {}
#[cfg(not(target_env = "msvc"))]
fn make_vtable() -> IComponentVTable {
IComponentVTable {
get_extension: ext_get_extension,
add_extension: ext_add_extension,
remove_extension_ptr: ext_remove_extension_ptr,
remove_extension_uid: ext_remove_extension_uid,
destructor: ext_destructor,
destructor_deleting: ext_destructor_deleting,
supported_version: comp_supported_version,
component_name: test_name,
component_type: comp_component_type,
component_version: test_version,
on_load: test_on_load,
on_init: test_on_init,
on_ready: test_on_ready,
on_free: test_on_free,
provide_configuration: test_provide_cfg,
free: test_free,
reset: test_reset,
}
}
#[cfg(target_env = "msvc")]
fn make_vtable() -> IComponentVTable {
IComponentVTable {
get_extension: ext_get_extension,
add_extension: ext_add_extension,
remove_extension_ptr: ext_remove_extension_ptr,
remove_extension_uid: ext_remove_extension_uid,
destructor: ext_destructor,
supported_version: comp_supported_version,
component_name: test_name,
component_type: comp_component_type,
component_version: test_version,
on_load: test_on_load,
on_init: test_on_init,
on_ready: test_on_ready,
on_free: test_on_free,
provide_configuration: test_provide_cfg,
free: test_free,
reset: test_reset,
}
}
#[cfg(not(target_env = "msvc"))]
fn make_uid_vtable() -> IUIDProviderVTable {
IUIDProviderVTable {
destructor_complete: uid_destructor_noop,
destructor_deleting: uid_destructor_noop,
get_uid: uid_get_uid,
}
}
#[cfg(target_env = "msvc")]
fn make_uid_vtable() -> IUIDProviderVTable {
IUIDProviderVTable {
get_uid: uid_get_uid,
}
}
#[test]
#[cfg(all(target_arch = "x86", target_os = "linux"))]
fn omp_component_layout_i686_linux() {
assert_eq!(std::mem::offset_of!(OmpComponent, uid_vtable), 40);
assert_eq!(std::mem::size_of::<OmpComponent>(), 56);
}
#[test]
#[cfg(all(target_arch = "x86", target_env = "msvc"))]
fn omp_component_layout_i686_msvc() {
assert_eq!(std::mem::offset_of!(OmpComponent, uid_vtable), 56);
}
#[test]
fn omp_component_new_stores_uid() {
let vt = make_vtable();
let uvt = make_uid_vtable();
let comp = OmpComponent::new(&raw const vt, &raw const uvt, 0xDEAD_BEEF_CAFE_BABE);
assert_eq!(comp.uid, 0xDEAD_BEEF_CAFE_BABE);
}
#[test]
fn omp_component_plugin_ptr_null_on_new() {
let vt = make_vtable();
let uvt = make_uid_vtable();
let comp = OmpComponent::new(&raw const vt, &raw const uvt, 0);
assert!(comp.plugin_ptr.is_null());
}
#[test]
fn uid_get_uid_recovers_from_subobject_pointer() {
let vt = make_vtable();
let uvt = make_uid_vtable();
let comp = OmpComponent::new(&raw const vt, &raw const uvt, 0xCAFE_BABE_u64);
let uid_ptr = (&raw const comp.uid_vtable).cast::<u8>();
let recovered = unsafe { uid_get_uid(uid_ptr) };
assert_eq!(recovered, 0xCAFE_BABE_u64);
}
#[test]
fn ext_get_extension_returns_null() {
let vt = make_vtable();
let uvt = make_uid_vtable();
let mut comp = OmpComponent::new(&raw const vt, &raw const uvt, 0);
let result = unsafe { ext_get_extension(&raw mut comp, 0) };
assert!(result.is_null());
}
#[test]
fn ext_add_extension_returns_false() {
let vt = make_vtable();
let uvt = make_uid_vtable();
let mut comp = OmpComponent::new(&raw const vt, &raw const uvt, 0);
let result = unsafe { ext_add_extension(&raw mut comp, std::ptr::null_mut(), false) };
assert!(!result);
}
#[test]
#[cfg(not(target_env = "msvc"))]
fn comp_supported_version_is_one() {
let vt = make_vtable();
let uvt = make_uid_vtable();
let comp = OmpComponent::new(&raw const vt, &raw const uvt, 0);
assert_eq!(unsafe { comp_supported_version(&raw const comp) }, 1);
}
#[test]
#[cfg(target_env = "msvc")]
fn comp_supported_version_is_one() {
assert_eq!(unsafe { comp_supported_version() }, 1);
}
#[test]
#[cfg(not(target_env = "msvc"))]
fn comp_component_type_is_other() {
let vt = make_vtable();
let uvt = make_uid_vtable();
let comp = OmpComponent::new(&raw const vt, &raw const uvt, 0);
assert_eq!(
unsafe { comp_component_type(&raw const comp) },
ComponentType::Other
);
}
#[test]
#[cfg(target_env = "msvc")]
fn comp_component_type_is_other() {
assert_eq!(
unsafe { comp_component_type() },
ComponentType::Other as i32
);
}
}