PkmnLib_rs/src/ffi/ffi_handle.rs

346 lines
16 KiB
Rust

use crate::static_data::{
Ability, Form, GrowthRate, Item, LearnableMoves, MoveData, Nature, Parameter, SecondaryEffect, Species,
StaticStatisticSet, StatisticSet,
};
use anyhow::anyhow;
use hashbrown::HashMap;
use parking_lot::RwLock;
use std::hash::Hash;
use std::sync::atomic::AtomicUsize;
use std::sync::{Arc, LazyLock};
/// This function can be called from the FFI to release a handle when it is no longer needed. This
/// does not drop the object per se, but will reduce the reference count of the object. If the object
/// is then no longer referenced, it will be dropped.
#[no_mangle]
extern "C" fn ffi_release_handle(handle: usize) {
let mut write_lock = FFI_OBJECTS.write();
let mut write_lock_inverse = FFI_OBJECTS_INVERSE.write();
let obj = write_lock.remove(&handle);
if let Some(obj) = obj {
write_lock_inverse.remove(&obj);
}
}
/// A handle of an object that can be passed over FFI. We use this to avoid passing pointers over the
/// FFI boundary. This allows us to be able to move the data around in memory without having to worry
/// about the pointers being invalidated. It also allows us to have a type-safe interface.
#[repr(C)]
pub(super) struct FFIHandle<T> {
/// An incrementing handle that is unique for each object.
handle: usize,
/// The type of the object.
_marker: std::marker::PhantomData<T>,
}
impl<T> Clone for FFIHandle<T> {
fn clone(&self) -> Self {
Self {
handle: self.handle,
_marker: std::marker::PhantomData,
}
}
}
impl<T> Copy for FFIHandle<T> {}
impl<T> Default for FFIHandle<T> {
fn default() -> Self {
Self {
handle: 0,
_marker: std::marker::PhantomData,
}
}
}
/// An FFIObject we can fetch from the FFIHandle. We store this in a hashmap to be able to fetch
/// the object from the handle, and to be able to drop the object when the FFI is done with the handle
#[derive(Clone)]
#[allow(clippy::missing_docs_in_private_items)] // I'm not documenting these items.
pub(super) enum FFIObject {
Ability(Arc<dyn Ability>),
EffectParameter(Arc<Parameter>),
StatisticSetU8(Arc<StatisticSet<u8>>),
StatisticSetI8(Arc<StatisticSet<i8>>),
StatisticSetU32(Arc<StatisticSet<u32>>),
StaticStatisticSetU16(Arc<StaticStatisticSet<u16>>),
Form(Arc<dyn Form>),
LearnableMoves(Arc<dyn LearnableMoves>),
GrowthRate(Arc<dyn GrowthRate>),
Item(Arc<dyn Item>),
SecondaryEffect(Arc<dyn SecondaryEffect>),
MoveData(Arc<dyn MoveData>),
Nature(Arc<dyn Nature>),
Species(Arc<dyn Species>),
SpeciesLibrary(Arc<dyn crate::static_data::SpeciesLibrary>),
MoveLibrary(Arc<dyn crate::static_data::MoveLibrary>),
AbilityLibrary(Arc<dyn crate::static_data::AbilityLibrary>),
ItemLibrary(Arc<dyn crate::static_data::ItemLibrary>),
GrowthRateLibrary(Arc<dyn crate::static_data::GrowthRateLibrary>),
LibrarySettings(Arc<dyn crate::static_data::LibrarySettings>),
NatureLibrary(Arc<dyn crate::static_data::NatureLibrary>),
TypeLibrary(Arc<dyn crate::static_data::TypeLibrary>),
StaticData(Arc<dyn crate::static_data::StaticData>),
// DynamicData
TurnChoice(Arc<crate::dynamic_data::TurnChoice>),
Pokemon(crate::dynamic_data::Pokemon),
LearnedMove(Arc<crate::dynamic_data::LearnedMove>),
PokemonParty(Arc<crate::dynamic_data::PokemonParty>),
BattleParty(Arc<crate::dynamic_data::BattleParty>),
Battle(crate::dynamic_data::Battle),
BattleSide(crate::dynamic_data::BattleSide),
BattleRandom(Arc<crate::dynamic_data::BattleRandom>),
// DynamicLibrary
BattleStatCalculator(Arc<dyn crate::dynamic_data::BattleStatCalculator>),
DamageLibrary(Arc<dyn crate::dynamic_data::DamageLibrary>),
MiscLibrary(Arc<dyn crate::dynamic_data::MiscLibrary>),
ScriptResolver(Arc<dyn crate::dynamic_data::ScriptResolver>),
DynamicLibrary(Arc<dyn crate::dynamic_data::DynamicLibrary>),
// Events
Event(Arc<crate::dynamic_data::EventData>),
}
unsafe impl Send for FFIObject {}
unsafe impl Sync for FFIObject {}
/// The next handle to be used.
static NEXT_HANDLE: AtomicUsize = AtomicUsize::new(1);
/// A lookup to get an actual object from a handle.
static FFI_OBJECTS: LazyLock<RwLock<HashMap<usize, FFIObject>>> = LazyLock::new(|| RwLock::new(HashMap::new()));
/// A lookup to get a handle from an object.
static FFI_OBJECTS_INVERSE: LazyLock<RwLock<HashMap<FFIObject, usize>>> = LazyLock::new(|| RwLock::new(HashMap::new()));
impl<T> FFIHandle<T> {
/// Get a handle from an object.
pub fn get_handle(o: FFIObject) -> Self {
if let Some(handle) = FFI_OBJECTS_INVERSE.read().get(&o) {
return Self {
handle: *handle,
_marker: std::marker::PhantomData,
};
}
let handle = NEXT_HANDLE.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
FFI_OBJECTS.write().insert(handle, o.clone());
FFI_OBJECTS_INVERSE.write().insert(o, handle);
Self {
handle,
_marker: std::marker::PhantomData,
}
}
/// An empty handle. This is used when no value is returned. Represented as handle 0
pub fn none() -> Self {
Self {
handle: 0,
_marker: std::marker::PhantomData,
}
}
/// Get the object from the handle.
#[allow(clippy::unwrap_used)] // Unwrap used, as it is a potential security bug if this fails
pub fn resolve(&self) -> FFIObject {
FFI_OBJECTS
.read()
.get(&self.handle)
.ok_or(anyhow!("Unable to get handle {} from FFI_OBJECTS", self.handle))
.unwrap()
.clone()
}
/// Check if the handle is empty.
pub fn is_none(&self) -> bool {
self.handle == 0
}
}
#[allow(clippy::from_over_into)]
impl<T> Into<anyhow_ext::Result<FFIObject>> for FFIHandle<T> {
fn into(self) -> anyhow_ext::Result<FFIObject> {
Ok(FFI_OBJECTS
.read()
.get(&self.handle)
.ok_or(anyhow!("Unable to get handle"))?
.clone())
}
}
impl Eq for FFIObject {}
/// A trait to convert a handle into an object.
#[allow(clippy::wrong_self_convention)]
pub(super) trait FromFFIHandle<T> {
/// Convert a handle into an object.
fn from_ffi_handle(&self) -> T;
/// Convert a handle into an object, returning `None` if the handle is empty.
fn from_ffi_handle_opt(&self) -> Option<T>;
}
impl Hash for FFIObject {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
match self {
Self::Ability(a) => Arc::as_ptr(a).hash(state),
Self::EffectParameter(a) => Arc::as_ptr(a).hash(state),
Self::StatisticSetU8(a) => Arc::as_ptr(a).hash(state),
Self::StatisticSetI8(a) => Arc::as_ptr(a).hash(state),
Self::StatisticSetU32(a) => Arc::as_ptr(a).hash(state),
Self::StaticStatisticSetU16(a) => Arc::as_ptr(a).hash(state),
Self::Form(a) => Arc::as_ptr(a).hash(state),
Self::LearnableMoves(a) => Arc::as_ptr(a).hash(state),
Self::GrowthRate(a) => Arc::as_ptr(a).hash(state),
Self::Item(a) => Arc::as_ptr(a).hash(state),
Self::SecondaryEffect(a) => Arc::as_ptr(a).hash(state),
Self::MoveData(a) => Arc::as_ptr(a).hash(state),
Self::Nature(a) => Arc::as_ptr(a).hash(state),
Self::Species(a) => Arc::as_ptr(a).hash(state),
Self::SpeciesLibrary(a) => Arc::as_ptr(a).hash(state),
Self::MoveLibrary(a) => Arc::as_ptr(a).hash(state),
Self::AbilityLibrary(a) => Arc::as_ptr(a).hash(state),
Self::ItemLibrary(a) => Arc::as_ptr(a).hash(state),
Self::GrowthRateLibrary(a) => Arc::as_ptr(a).hash(state),
Self::LibrarySettings(a) => Arc::as_ptr(a).hash(state),
Self::NatureLibrary(a) => Arc::as_ptr(a).hash(state),
Self::TypeLibrary(a) => Arc::as_ptr(a).hash(state),
Self::StaticData(a) => Arc::as_ptr(a).hash(state),
Self::TurnChoice(a) => Arc::as_ptr(a).hash(state),
Self::Pokemon(a) => a.as_ptr().hash(state),
Self::LearnedMove(a) => Arc::as_ptr(a).hash(state),
Self::PokemonParty(a) => Arc::as_ptr(a).hash(state),
Self::BattleParty(a) => Arc::as_ptr(a).hash(state),
Self::Battle(a) => a.as_ptr().hash(state),
Self::BattleSide(a) => a.as_ptr().hash(state),
Self::BattleRandom(a) => Arc::as_ptr(a).hash(state),
Self::BattleStatCalculator(a) => Arc::as_ptr(a).hash(state),
Self::DamageLibrary(a) => Arc::as_ptr(a).hash(state),
Self::MiscLibrary(a) => Arc::as_ptr(a).hash(state),
Self::ScriptResolver(a) => Arc::as_ptr(a).hash(state),
Self::DynamicLibrary(a) => Arc::as_ptr(a).hash(state),
FFIObject::Event(a) => Arc::as_ptr(a).hash(state),
}
}
}
impl PartialEq for FFIObject {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Ability(a), Self::Ability(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::EffectParameter(a), Self::EffectParameter(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::StatisticSetU8(a), Self::StatisticSetU8(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::StatisticSetI8(a), Self::StatisticSetI8(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::StatisticSetU32(a), Self::StatisticSetU32(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::StaticStatisticSetU16(a), Self::StaticStatisticSetU16(b)) => {
Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr()
}
(Self::Form(a), Self::Form(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::LearnableMoves(a), Self::LearnableMoves(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::GrowthRate(a), Self::GrowthRate(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::Item(a), Self::Item(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::SecondaryEffect(a), Self::SecondaryEffect(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::MoveData(a), Self::MoveData(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::Nature(a), Self::Nature(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::Species(a), Self::Species(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::SpeciesLibrary(a), Self::SpeciesLibrary(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::MoveLibrary(a), Self::MoveLibrary(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::AbilityLibrary(a), Self::AbilityLibrary(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::ItemLibrary(a), Self::ItemLibrary(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::GrowthRateLibrary(a), Self::GrowthRateLibrary(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::LibrarySettings(a), Self::LibrarySettings(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::NatureLibrary(a), Self::NatureLibrary(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::TypeLibrary(a), Self::TypeLibrary(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::StaticData(a), Self::StaticData(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::TurnChoice(a), Self::TurnChoice(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::Pokemon(a), Self::Pokemon(b)) => a.eq(b),
(Self::LearnedMove(a), Self::LearnedMove(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::PokemonParty(a), Self::PokemonParty(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::BattleParty(a), Self::BattleParty(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::Battle(a), Self::Battle(b)) => a.eq(b),
(Self::BattleSide(a), Self::BattleSide(b)) => a.eq(b),
(Self::BattleRandom(a), Self::BattleRandom(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::BattleStatCalculator(a), Self::BattleStatCalculator(b)) => {
Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr()
}
(Self::DamageLibrary(a), Self::DamageLibrary(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::MiscLibrary(a), Self::MiscLibrary(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::ScriptResolver(a), Self::ScriptResolver(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::DynamicLibrary(a), Self::DynamicLibrary(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
(Self::Event(a), Self::Event(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(),
_ => false,
}
}
}
/// Helper macro to implement the `From` trait for a `FFIObject` enum variant, and the `FromFFIHandle`
/// trait for a `FFIHandle` enum variant.
macro_rules! ffi_obj_conversions {
($res_type:ty, $ffi_obj_name:ident) => {
impl From<$res_type> for FFIObject {
fn from(a: $res_type) -> Self {
Self::$ffi_obj_name(a)
}
}
impl FromFFIHandle<$res_type> for FFIHandle<$res_type> {
fn from_ffi_handle(&self) -> $res_type {
match self.resolve() {
FFIObject::$ffi_obj_name(a) => a.clone(),
_ => panic!("Invalid handle"),
}
}
fn from_ffi_handle_opt(&self) -> Option<$res_type> {
if (self.is_none()) {
return None;
}
match self.resolve() {
FFIObject::$ffi_obj_name(a) => Some(a.clone()),
_ => panic!("Invalid handle"),
}
}
}
};
}
ffi_obj_conversions!(Arc<dyn Ability>, Ability);
ffi_obj_conversions!(Arc<Parameter>, EffectParameter);
ffi_obj_conversions!(Arc<StatisticSet<i8>>, StatisticSetI8);
ffi_obj_conversions!(Arc<StatisticSet<u8>>, StatisticSetU8);
ffi_obj_conversions!(Arc<StatisticSet<u32>>, StatisticSetU32);
ffi_obj_conversions!(Arc<StaticStatisticSet<u16>>, StaticStatisticSetU16);
ffi_obj_conversions!(Arc<dyn Form>, Form);
ffi_obj_conversions!(Arc<dyn LearnableMoves>, LearnableMoves);
ffi_obj_conversions!(Arc<dyn GrowthRate>, GrowthRate);
ffi_obj_conversions!(Arc<dyn Item>, Item);
ffi_obj_conversions!(Arc<dyn SecondaryEffect>, SecondaryEffect);
ffi_obj_conversions!(Arc<dyn MoveData>, MoveData);
ffi_obj_conversions!(Arc<dyn Nature>, Nature);
ffi_obj_conversions!(Arc<dyn Species>, Species);
ffi_obj_conversions!(Arc<dyn crate::static_data::SpeciesLibrary>, SpeciesLibrary);
ffi_obj_conversions!(Arc<dyn crate::static_data::MoveLibrary>, MoveLibrary);
ffi_obj_conversions!(Arc<dyn crate::static_data::AbilityLibrary>, AbilityLibrary);
ffi_obj_conversions!(Arc<dyn crate::static_data::ItemLibrary>, ItemLibrary);
ffi_obj_conversions!(Arc<dyn crate::static_data::GrowthRateLibrary>, GrowthRateLibrary);
ffi_obj_conversions!(Arc<dyn crate::static_data::LibrarySettings>, LibrarySettings);
ffi_obj_conversions!(Arc<dyn crate::static_data::NatureLibrary>, NatureLibrary);
ffi_obj_conversions!(Arc<dyn crate::static_data::TypeLibrary>, TypeLibrary);
ffi_obj_conversions!(Arc<dyn crate::static_data::StaticData>, StaticData);
ffi_obj_conversions!(Arc<crate::dynamic_data::TurnChoice>, TurnChoice);
ffi_obj_conversions!(crate::dynamic_data::Pokemon, Pokemon);
ffi_obj_conversions!(Arc<crate::dynamic_data::LearnedMove>, LearnedMove);
ffi_obj_conversions!(Arc<crate::dynamic_data::PokemonParty>, PokemonParty);
ffi_obj_conversions!(Arc<crate::dynamic_data::BattleParty>, BattleParty);
ffi_obj_conversions!(crate::dynamic_data::Battle, Battle);
ffi_obj_conversions!(crate::dynamic_data::BattleSide, BattleSide);
ffi_obj_conversions!(Arc<crate::dynamic_data::BattleRandom>, BattleRandom);
ffi_obj_conversions!(Arc<dyn crate::dynamic_data::BattleStatCalculator>, BattleStatCalculator);
ffi_obj_conversions!(Arc<dyn crate::dynamic_data::DamageLibrary>, DamageLibrary);
ffi_obj_conversions!(Arc<dyn crate::dynamic_data::MiscLibrary>, MiscLibrary);
ffi_obj_conversions!(Arc<dyn crate::dynamic_data::ScriptResolver>, ScriptResolver);
ffi_obj_conversions!(Arc<dyn crate::dynamic_data::DynamicLibrary>, DynamicLibrary);
ffi_obj_conversions!(Arc<crate::dynamic_data::EventData>, Event);