use alloc::boxed::Box;
use crate::term::Term;
use core::ffi::c_void;
#[repr(C)]
pub struct Context {
_private: [u8; 0],
}
pub type GlobalContext = c_void;
extern "C" {
pub fn create_port_context(global: *const GlobalContext) -> *mut Context;
pub fn destroy_port_context(ctx: *mut Context);
pub fn port_is_alive(ctx: *const Context) -> i32;
pub fn context_get_platform_data(ctx: *const Context) -> *mut c_void;
pub fn context_set_platform_data(ctx: *mut Context, data: *mut c_void);
pub fn context_get_user_data(ctx: *const Context) -> u64;
pub fn context_set_user_data(ctx: *mut Context, data: u64);
pub fn global_context_ptr() -> *mut GlobalContext;
}
pub trait ContextExt {
unsafe fn set_platform_data(&mut self, data: *mut c_void);
unsafe fn get_platform_data(&self) -> *mut c_void;
unsafe fn set_user_data(&mut self, data: u64);
unsafe fn get_user_data(&self) -> u64;
unsafe fn get_platform_data_as<T>(&self) -> *mut T {
self.get_platform_data() as *mut T
}
unsafe fn set_platform_data_box<T>(&mut self, data: Box<T>) {
self.set_platform_data(Box::into_raw(data) as *mut c_void);
}
unsafe fn take_platform_data_box<T>(&mut self) -> Option<Box<T>> {
let ptr = self.get_platform_data() as *mut T;
if ptr.is_null() {
None
} else {
self.set_platform_data(core::ptr::null_mut());
Some(Box::from_raw(ptr))
}
}
unsafe fn set_user_term(&mut self, term: Term) {
self.set_user_data(term.raw().try_into().unwrap());
}
unsafe fn get_user_term(&self) -> Term {
Term::from_raw(self.get_user_data().try_into().unwrap())
}
fn has_platform_data(&self) -> bool {
unsafe { !self.get_platform_data().is_null() }
}
fn has_user_data(&self) -> bool {
unsafe { self.get_user_data() != 0 }
}
}
impl ContextExt for Context {
unsafe fn set_platform_data(&mut self, data: *mut c_void) {
context_set_platform_data(self, data);
}
unsafe fn get_platform_data(&self) -> *mut c_void {
context_get_platform_data(self)
}
unsafe fn set_user_data(&mut self, data: u64) {
context_set_user_data(self, data);
}
unsafe fn get_user_data(&self) -> u64 {
context_get_user_data(self)
}
}
pub fn create_port_context_safe(global: &GlobalContext) -> *mut Context {
unsafe { create_port_context(global as *const GlobalContext) }
}
pub fn destroy_port_context_safe(ctx: *mut Context) {
if !ctx.is_null() {
unsafe { destroy_port_context(ctx) }
}
}
pub fn is_port_alive(ctx: &Context) -> bool {
unsafe { port_is_alive(ctx as *const Context) != 0 }
}
pub fn get_global_context() -> *mut GlobalContext {
unsafe { global_context_ptr() }
}
pub struct PortBuilder<T> {
data: T,
}
impl<T> PortBuilder<T> {
pub fn new(data: T) -> Self {
Self { data }
}
pub fn build(self, global: &GlobalContext) -> *mut Context {
let ctx = create_port_context_safe(global);
if !ctx.is_null() {
unsafe {
let boxed_data = Box::new(self.data);
(*ctx).set_platform_data_box(boxed_data);
}
}
ctx
}
pub fn build_with_user_data(self, global: &GlobalContext, user_data: u64) -> *mut Context {
let ctx = self.build(global);
if !ctx.is_null() {
unsafe {
(*ctx).set_user_data(user_data);
}
}
ctx
}
pub fn build_with_user_term(self, global: &GlobalContext, user_term: Term) -> *mut Context {
let ctx = self.build(global);
if !ctx.is_null() {
unsafe {
(*ctx).set_user_term(user_term);
}
}
ctx
}
}
pub struct ContextGuard {
ctx: *mut Context,
}
impl ContextGuard {
pub unsafe fn new(ctx: *mut Context) -> Self {
Self { ctx }
}
pub fn context(&self) -> &Context {
unsafe { &*self.ctx }
}
pub fn context_mut(&mut self) -> &mut Context {
unsafe { &mut *self.ctx }
}
pub fn release(mut self) -> *mut Context {
let ctx = self.ctx;
self.ctx = core::ptr::null_mut();
ctx
}
pub fn is_valid(&self) -> bool {
!self.ctx.is_null()
}
}
impl Drop for ContextGuard {
fn drop(&mut self) {
destroy_port_context_safe(self.ctx);
}
}
pub struct ContextManager {
contexts: alloc::vec::Vec<*mut Context>,
}
impl ContextManager {
pub fn new() -> Self {
Self {
contexts: alloc::vec::Vec::new(),
}
}
pub fn add_context(&mut self, ctx: *mut Context) {
if !ctx.is_null() {
self.contexts.push(ctx);
}
}
pub fn remove_context(&mut self, ctx: *mut Context) -> bool {
if let Some(pos) = self.contexts.iter().position(|&x| x == ctx) {
self.contexts.remove(pos);
true
} else {
false
}
}
pub fn count(&self) -> usize {
self.contexts.len()
}
pub fn contains(&self, ctx: *mut Context) -> bool {
self.contexts.contains(&ctx)
}
pub fn destroy_all(&mut self) {
for &ctx in &self.contexts {
destroy_port_context_safe(ctx);
}
self.contexts.clear();
}
}
impl Drop for ContextManager {
fn drop(&mut self) {
self.destroy_all();
}
}
impl Default for ContextManager {
fn default() -> Self {
Self::new()
}
}
pub trait PlatformData: Sized {
fn cleanup(&mut self) {}
unsafe fn store_in_context(self, ctx: &mut Context) {
ctx.set_platform_data_box(Box::new(self));
}
unsafe fn from_context(ctx: &Context) -> Option<&Self> {
let ptr = ctx.get_platform_data_as::<Self>();
if ptr.is_null() {
None
} else {
Some(&*ptr)
}
}
unsafe fn from_context_mut(ctx: &mut Context) -> Option<&mut Self> {
let ptr = ctx.get_platform_data_as::<Self>();
if ptr.is_null() {
None
} else {
Some(&mut *ptr)
}
}
unsafe fn take_from_context(ctx: &mut Context) -> Option<Self> {
ctx.take_platform_data_box::<Self>().map(|boxed| *boxed)
}
}
#[macro_export]
macro_rules! impl_platform_data {
($type:ty) => {
impl $crate::context::PlatformData for $type {}
};
($type:ty, cleanup = $cleanup:expr) => {
impl $crate::context::PlatformData for $type {
fn cleanup(&mut self) {
$cleanup(self)
}
}
};
}
pub fn with_platform_data<T, R, F>(ctx: &Context, f: F) -> Option<R>
where
T: PlatformData,
F: FnOnce(&T) -> R,
{
unsafe {
T::from_context(ctx).map(f)
}
}
pub fn with_platform_data_mut<T, R, F>(ctx: &mut Context, f: F) -> Option<R>
where
T: PlatformData,
F: FnOnce(&mut T) -> R,
{
unsafe {
T::from_context_mut(ctx).map(f)
}
}
pub fn init_platform_data<T: PlatformData>(ctx: &mut Context, data: T) {
unsafe {
data.store_in_context(ctx);
}
}
pub fn cleanup_platform_data<T: PlatformData>(ctx: &mut Context) -> Option<T> {
unsafe {
T::take_from_context(ctx)
}
}