jlrs 0.23.0

jlrs provides bindings to the Julia C API that enable Julia code to be called from Rust and more.
Documentation
//! Raw, inactive GC frame.

use std::{
    ffi::c_void,
    pin::Pin,
    ptr::{NonNull, null_mut},
};

use jlrs_sys::{SplitGcFrame, pop_frame};

use super::context::stack::Stack;

/// A raw, inactive GC frame.
#[repr(C)]
pub struct StackFrame<const N: usize> {
    s: SplitGcFrame<1, N>,
}

impl StackFrame<0> {
    /// Returns a new `StackFrame`.
    #[inline]
    pub fn new() -> Self {
        Self::new_n()
    }
}

impl<const N: usize> StackFrame<N> {
    #[inline]
    pub(crate) fn new_n() -> Self {
        unsafe {
            StackFrame {
                s: SplitGcFrame::new(),
            }
        }
    }

    // Safety: Must only be called once, if a new frame is pushed it must be popped before
    // this one is.
    #[inline]
    #[cfg_attr(
        not(any(feature = "local-rt", feature = "async-rt", feature = "ccall")),
        allow(unused)
    )]
    pub(crate) unsafe fn pin<'scope>(&'scope mut self) -> PinnedFrame<'scope, N> {
        unsafe {
            self.s.get_head_root(0).set(null_mut());
            for i in 0..N {
                self.s.set_tail_root(i, null_mut());
            }
            PinnedFrame::new(self)
        }
    }
}

pub(crate) struct PinnedFrame<'scope, const N: usize> {
    raw: Pin<&'scope StackFrame<N>>,
}

impl<'scope, const N: usize> PinnedFrame<'scope, N> {
    #[inline]
    #[cfg_attr(
        not(any(feature = "local-rt", feature = "async-rt", feature = "ccall")),
        allow(unused)
    )]
    unsafe fn new(raw: &'scope mut StackFrame<N>) -> Self {
        unsafe {
            raw.s.push_frame();
            PinnedFrame {
                raw: Pin::new_unchecked(raw),
            }
        }
    }

    #[inline]
    #[cfg_attr(
        not(any(feature = "local-rt", feature = "async-rt", feature = "ccall")),
        allow(unused)
    )]
    pub(crate) unsafe fn stack_frame<'inner>(
        &'inner mut self,
    ) -> JlrsStackFrame<'scope, 'inner, N> {
        unsafe { JlrsStackFrame::new(self) }
    }

    #[inline]
    #[allow(unused)]
    pub(crate) unsafe fn set_sync_root(&self, root: *mut c_void) {
        unsafe {
            self.raw.s.set_head_root(0, root);
        }
    }
}

impl<'scope, const N: usize> Drop for PinnedFrame<'scope, N> {
    fn drop(&mut self) {
        unsafe { pop_frame() }
    }
}

#[cfg_attr(
    not(any(feature = "local-rt", feature = "async-rt", feature = "ccall")),
    allow(unused)
)]
pub(crate) struct JlrsStackFrame<'scope, 'inner, const N: usize> {
    pinned: &'inner mut PinnedFrame<'scope, N>,
}

impl<'scope, 'inner, const N: usize> JlrsStackFrame<'scope, 'inner, N> {
    #[inline]
    #[cfg_attr(
        not(any(feature = "local-rt", feature = "async-rt", feature = "ccall")),
        allow(unused)
    )]
    unsafe fn new(pinned: &'inner mut PinnedFrame<'scope, N>) -> Self {
        unsafe {
            if pinned.raw.s.get_head_root(0).get().is_null() {
                {
                    let ptr = Stack::alloc();
                    pinned.raw.s.set_head_root(0, ptr.cast());
                }

                for i in 0..N {
                    let ptr = Stack::alloc();
                    pinned.raw.s.set_tail_root(i, ptr.cast());
                }
            }

            JlrsStackFrame { pinned }
        }
    }

    #[inline]
    #[cfg_attr(
        not(any(feature = "local-rt", feature = "async-rt", feature = "ccall")),
        allow(unused)
    )]
    pub(crate) unsafe fn sync_stack(&self) -> &'scope Stack {
        unsafe {
            NonNull::new_unchecked(self.pinned.raw.s.get_head_root(0).get())
                .cast()
                .as_ref()
        }
    }

    #[cfg(feature = "async")]
    #[inline]
    pub(crate) unsafe fn nth_stack(&self, n: usize) -> &'scope Stack {
        unsafe {
            NonNull::new_unchecked(self.pinned.raw.s.get_tail_root(n).get())
                .cast()
                .as_ref()
        }
    }
}