use std::{
collections::HashMap,
os::raw::c_void,
ptr,
sync::{Arc, Mutex, OnceLock},
};
use crate::{pxs_debug, shared::{PtrMagic, module::ModuleCallback}};
pub type FreeMethod = unsafe extern "C" fn(ptr: *mut c_void);
pub struct ObjectCallback {
pub cbk: ModuleCallback,
pub is_id: bool
}
#[allow(non_camel_case_types)]
pub struct pxs_PixelObject {
pub type_name: String,
pub ptr: *mut c_void,
pub lang_ptr: Mutex<*mut c_void>,
pub free_lang_ptr: Mutex<bool>,
pub free_method: FreeMethod,
pub callbacks: Vec<ObjectCallback>,
pub ref_count: Mutex<u16>
}
impl pxs_PixelObject {
pub fn new(ptr: *mut c_void, free_method: FreeMethod, type_name: &str) -> Self {
Self {
ptr,
free_method,
callbacks: vec![],
lang_ptr: Mutex::new(ptr::null_mut()),
type_name: type_name.to_string(),
free_lang_ptr: Mutex::new(true),
ref_count: Mutex::new(1)
}
}
pub fn add_callback(&mut self, name: &str, full_name: &str, idx: i32, is_id: bool) {
self.callbacks.push(
ObjectCallback {
cbk: ModuleCallback {
name: name.to_string(),
full_name: full_name.to_string(),
idx,
}, is_id});
}
pub fn update_lang_ptr(&self, n_ptr: *mut c_void) {
let mut guard = self.lang_ptr.lock().unwrap();
if !guard.is_null() {
eprintln!("Can not mutate if ptr is already set.");
return;
}
*guard = n_ptr;
}
pub fn update_free_lang_ptr(&self, val: bool) {
let mut guard = self.free_lang_ptr.lock().unwrap();
*guard = val;
}
pub fn add_reference(&self) {
let mut guard = self.ref_count.lock().unwrap();
*guard += 1;
}
pub fn sub_reference(&self) {
let mut guard = self.ref_count.lock().unwrap();
*guard -= 1;
}
pub fn current_ref_count(&self) -> u16 {
let guard = self.ref_count.lock().unwrap();
return *guard;
}
}
impl PtrMagic for pxs_PixelObject {}
unsafe impl Send for pxs_PixelObject {}
unsafe impl Sync for pxs_PixelObject {}
impl Drop for pxs_PixelObject {
fn drop(&mut self) {
let mut lang_ptr = self.lang_ptr.lock().unwrap();
if *self.free_lang_ptr.lock().unwrap() {
if !lang_ptr.is_null() {
let _ = unsafe { Box::from_raw(*lang_ptr) };
}
}
*lang_ptr = ptr::null_mut();
if self.ptr.is_null() {
return;
}
unsafe {
(self.free_method)(self.ptr);
}
}
}
pub struct ObjectLookup {
pub object_hash: HashMap<i32, Arc<pxs_PixelObject>>,
pub next_idx: i32
}
static OBJECT_LOOKUP: OnceLock<Mutex<ObjectLookup>> = OnceLock::new();
fn get_object_lookup() -> std::sync::MutexGuard<'static, ObjectLookup> {
OBJECT_LOOKUP
.get_or_init(|| {
Mutex::new(ObjectLookup {
object_hash: HashMap::new(),
next_idx: 0
})
})
.lock()
.unwrap()
}
pub(crate) fn apply_ref_count_delete(idx: i32) {
let mut lookup = get_object_lookup();
let object = lookup.object_hash.get(&idx);
if let Some(object) = object {
object.sub_reference();
if object.current_ref_count() == 0 {
lookup.object_hash.remove(&idx);
}
}
}
pub(crate) fn apply_ref_count_alloc(idx: i32) {
let lookup = get_object_lookup();
let object = lookup.object_hash.get(&idx);
if let Some(object) = object {
object.add_reference();
}
}
pub(crate) fn clear_object_lookup() {
let mut lookup = get_object_lookup();
pxs_debug!("Clearing object lookup size: {}", lookup.object_hash.len());
lookup.object_hash.clear();
}
pub(crate) fn lookup_add_object(pixel_obj: Arc<pxs_PixelObject>) -> i32 {
let mut lookup = get_object_lookup();
let idx = lookup.next_idx;
lookup
.object_hash
.insert(idx as i32, Arc::clone(&pixel_obj));
lookup.next_idx += 1;
idx as i32
}
pub(crate) fn get_object(idx: i32) -> Option<Arc<pxs_PixelObject>> {
let lookup = get_object_lookup();
if let Some(res) = lookup.object_hash.get(&idx) {
Some(res.to_owned())
} else {
None
}
}