1use alloc::boxed::Box;
6use crate::term::Term;
7use core::ffi::c_void;
8
9#[repr(C)]
11pub struct Context {
12 _private: [u8; 0],
13}
14
15pub type GlobalContext = c_void;
17
18extern "C" {
20 pub fn create_port_context(global: *const GlobalContext) -> *mut Context;
22
23 pub fn destroy_port_context(ctx: *mut Context);
25
26 pub fn port_is_alive(ctx: *const Context) -> i32;
28
29 pub fn context_get_platform_data(ctx: *const Context) -> *mut c_void;
31
32 pub fn context_set_platform_data(ctx: *mut Context, data: *mut c_void);
34
35 pub fn context_get_user_data(ctx: *const Context) -> u64;
37
38 pub fn context_set_user_data(ctx: *mut Context, data: u64);
40
41 pub fn global_context_ptr() -> *mut GlobalContext;
43}
44
45pub trait ContextExt {
47 unsafe fn set_platform_data(&mut self, data: *mut c_void);
49
50 unsafe fn get_platform_data(&self) -> *mut c_void;
52
53 unsafe fn set_user_data(&mut self, data: u64);
55
56 unsafe fn get_user_data(&self) -> u64;
58
59 unsafe fn get_platform_data_as<T>(&self) -> *mut T {
61 self.get_platform_data() as *mut T
62 }
63
64 unsafe fn set_platform_data_box<T>(&mut self, data: Box<T>) {
66 self.set_platform_data(Box::into_raw(data) as *mut c_void);
67 }
68
69 unsafe fn take_platform_data_box<T>(&mut self) -> Option<Box<T>> {
71 let ptr = self.get_platform_data() as *mut T;
72 if ptr.is_null() {
73 None
74 } else {
75 self.set_platform_data(core::ptr::null_mut());
76 Some(Box::from_raw(ptr))
77 }
78 }
79
80 unsafe fn set_user_term(&mut self, term: Term) {
82 self.set_user_data(term.raw().try_into().unwrap());
83 }
84
85 unsafe fn get_user_term(&self) -> Term {
87 Term::from_raw(self.get_user_data().try_into().unwrap())
88 }
89
90 fn has_platform_data(&self) -> bool {
92 unsafe { !self.get_platform_data().is_null() }
93 }
94
95 fn has_user_data(&self) -> bool {
97 unsafe { self.get_user_data() != 0 }
98 }
99}
100
101impl ContextExt for Context {
102 unsafe fn set_platform_data(&mut self, data: *mut c_void) {
103 context_set_platform_data(self, data);
104 }
105
106 unsafe fn get_platform_data(&self) -> *mut c_void {
107 context_get_platform_data(self)
108 }
109
110 unsafe fn set_user_data(&mut self, data: u64) {
111 context_set_user_data(self, data);
112 }
113
114 unsafe fn get_user_data(&self) -> u64 {
115 context_get_user_data(self)
116 }
117}
118
119pub fn create_port_context_safe(global: &GlobalContext) -> *mut Context {
121 unsafe { create_port_context(global as *const GlobalContext) }
122}
123
124pub fn destroy_port_context_safe(ctx: *mut Context) {
126 if !ctx.is_null() {
127 unsafe { destroy_port_context(ctx) }
128 }
129}
130
131pub fn is_port_alive(ctx: &Context) -> bool {
133 unsafe { port_is_alive(ctx as *const Context) != 0 }
134}
135
136pub fn get_global_context() -> *mut GlobalContext {
138 unsafe { global_context_ptr() }
139}
140
141pub struct PortBuilder<T> {
143 data: T,
144}
145
146impl<T> PortBuilder<T> {
147 pub fn new(data: T) -> Self {
149 Self { data }
150 }
151
152 pub fn build(self, global: &GlobalContext) -> *mut Context {
154 let ctx = create_port_context_safe(global);
155 if !ctx.is_null() {
156 unsafe {
157 let boxed_data = Box::new(self.data);
158 (*ctx).set_platform_data_box(boxed_data);
159 }
160 }
161 ctx
162 }
163
164 pub fn build_with_user_data(self, global: &GlobalContext, user_data: u64) -> *mut Context {
166 let ctx = self.build(global);
167 if !ctx.is_null() {
168 unsafe {
169 (*ctx).set_user_data(user_data);
170 }
171 }
172 ctx
173 }
174
175 pub fn build_with_user_term(self, global: &GlobalContext, user_term: Term) -> *mut Context {
177 let ctx = self.build(global);
178 if !ctx.is_null() {
179 unsafe {
180 (*ctx).set_user_term(user_term);
181 }
182 }
183 ctx
184 }
185}
186
187pub struct ContextGuard {
189 ctx: *mut Context,
190}
191
192impl ContextGuard {
193 pub unsafe fn new(ctx: *mut Context) -> Self {
198 Self { ctx }
199 }
200
201 pub fn context(&self) -> &Context {
203 unsafe { &*self.ctx }
204 }
205
206 pub fn context_mut(&mut self) -> &mut Context {
208 unsafe { &mut *self.ctx }
209 }
210
211 pub fn release(mut self) -> *mut Context {
213 let ctx = self.ctx;
214 self.ctx = core::ptr::null_mut();
215 ctx
216 }
217
218 pub fn is_valid(&self) -> bool {
220 !self.ctx.is_null()
221 }
222}
223
224impl Drop for ContextGuard {
225 fn drop(&mut self) {
226 destroy_port_context_safe(self.ctx);
227 }
228}
229
230pub struct ContextManager {
232 contexts: alloc::vec::Vec<*mut Context>,
233}
234
235impl ContextManager {
236 pub fn new() -> Self {
238 Self {
239 contexts: alloc::vec::Vec::new(),
240 }
241 }
242
243 pub fn add_context(&mut self, ctx: *mut Context) {
245 if !ctx.is_null() {
246 self.contexts.push(ctx);
247 }
248 }
249
250 pub fn remove_context(&mut self, ctx: *mut Context) -> bool {
252 if let Some(pos) = self.contexts.iter().position(|&x| x == ctx) {
253 self.contexts.remove(pos);
254 true
255 } else {
256 false
257 }
258 }
259
260 pub fn count(&self) -> usize {
262 self.contexts.len()
263 }
264
265 pub fn contains(&self, ctx: *mut Context) -> bool {
267 self.contexts.contains(&ctx)
268 }
269
270 pub fn destroy_all(&mut self) {
272 for &ctx in &self.contexts {
273 destroy_port_context_safe(ctx);
274 }
275 self.contexts.clear();
276 }
277}
278
279impl Drop for ContextManager {
280 fn drop(&mut self) {
281 self.destroy_all();
282 }
283}
284
285impl Default for ContextManager {
286 fn default() -> Self {
287 Self::new()
288 }
289}
290
291pub trait PlatformData: Sized {
293 fn cleanup(&mut self) {}
295
296 unsafe fn store_in_context(self, ctx: &mut Context) {
298 ctx.set_platform_data_box(Box::new(self));
299 }
300
301 unsafe fn from_context(ctx: &Context) -> Option<&Self> {
303 let ptr = ctx.get_platform_data_as::<Self>();
304 if ptr.is_null() {
305 None
306 } else {
307 Some(&*ptr)
308 }
309 }
310
311 unsafe fn from_context_mut(ctx: &mut Context) -> Option<&mut Self> {
313 let ptr = ctx.get_platform_data_as::<Self>();
314 if ptr.is_null() {
315 None
316 } else {
317 Some(&mut *ptr)
318 }
319 }
320
321 unsafe fn take_from_context(ctx: &mut Context) -> Option<Self> {
323 ctx.take_platform_data_box::<Self>().map(|boxed| *boxed)
324 }
325}
326
327#[macro_export]
329macro_rules! impl_platform_data {
330 ($type:ty) => {
331 impl $crate::context::PlatformData for $type {}
332 };
333 ($type:ty, cleanup = $cleanup:expr) => {
334 impl $crate::context::PlatformData for $type {
335 fn cleanup(&mut self) {
336 $cleanup(self)
337 }
338 }
339 };
340}
341
342pub fn with_platform_data<T, R, F>(ctx: &Context, f: F) -> Option<R>
346where
347 T: PlatformData,
348 F: FnOnce(&T) -> R,
349{
350 unsafe {
351 T::from_context(ctx).map(f)
352 }
353}
354
355pub fn with_platform_data_mut<T, R, F>(ctx: &mut Context, f: F) -> Option<R>
357where
358 T: PlatformData,
359 F: FnOnce(&mut T) -> R,
360{
361 unsafe {
362 T::from_context_mut(ctx).map(f)
363 }
364}
365
366pub fn init_platform_data<T: PlatformData>(ctx: &mut Context, data: T) {
368 unsafe {
369 data.store_in_context(ctx);
370 }
371}
372
373pub fn cleanup_platform_data<T: PlatformData>(ctx: &mut Context) -> Option<T> {
375 unsafe {
376 T::take_from_context(ctx)
377 }
378}