Skip to main content

msrt_ffi/
endpoint.rs

1//! Endpoint storage and lifecycle internals.
2
3use core::mem::{align_of, size_of};
4use core::ptr;
5
6#[cfg(feature = "std")]
7use std::boxed::Box;
8
9use msrt::endpoint::{ClientEndpoint, EngineConfig, PassiveEndpoint};
10
11use crate::config::MsrtConfig;
12use crate::constants::{
13    ENDPOINT_STORAGE_BYTES, MAX_EVENT_BYTES, MSRT_ROLE_CLIENT, MSRT_ROLE_PASSIVE,
14    MSRT_STATUS_INVALID_STORAGE, MSRT_STATUS_NULL, MSRT_STATUS_OK, MSRT_STATUS_UNSUPPORTED,
15};
16
17/// Caller-owned endpoint storage for heap-free C integrations.
18///
19/// C users should treat this as opaque storage and only pass it to
20/// `msrt_endpoint_init` and `msrt_endpoint_deinit`.
21#[repr(C, align(8))]
22#[derive(Clone, Copy)]
23pub struct MsrtEndpointStorage {
24    pub(crate) bytes: [u8; ENDPOINT_STORAGE_BYTES],
25}
26
27/// Opaque MSRT endpoint handle.
28pub struct MsrtEndpoint {
29    pub(crate) inner: EndpointInner,
30    pub(crate) tx_buf: [u8; MAX_EVENT_BYTES],
31}
32
33pub(crate) enum EndpointInner {
34    Client(ClientEndpoint),
35    Passive(PassiveEndpoint),
36}
37
38impl EndpointInner {
39    pub(crate) fn new(role: u32, config: EngineConfig) -> Result<Self, ()> {
40        match role {
41            MSRT_ROLE_CLIENT => Ok(Self::Client(ClientEndpoint::new(config))),
42            MSRT_ROLE_PASSIVE => Ok(Self::Passive(PassiveEndpoint::new(config))),
43            _ => Err(()),
44        }
45    }
46}
47
48/// Returns the endpoint bytes required by this build.
49pub const fn endpoint_storage_size() -> usize {
50    size_of::<MsrtEndpoint>()
51}
52
53/// Returns the endpoint alignment required by this build.
54pub const fn endpoint_storage_align() -> usize {
55    align_of::<MsrtEndpoint>()
56}
57
58/// Initializes an endpoint in caller-owned storage.
59pub(crate) unsafe fn init_endpoint(
60    storage: *mut MsrtEndpointStorage,
61    config: *const MsrtConfig,
62    out: *mut *mut MsrtEndpoint,
63) -> i32 {
64    let Some(storage) = (unsafe { storage.as_mut() }) else {
65        return MSRT_STATUS_NULL;
66    };
67    let Some(out) = (unsafe { out.as_mut() }) else {
68        return MSRT_STATUS_NULL;
69    };
70
71    *out = ptr::null_mut();
72    if endpoint_storage_size() > ENDPOINT_STORAGE_BYTES
73        || endpoint_storage_align() > align_of::<MsrtEndpointStorage>()
74    {
75        return MSRT_STATUS_INVALID_STORAGE;
76    }
77
78    let config = if config.is_null() {
79        MsrtConfig::default()
80    } else {
81        unsafe { *config }
82    };
83
84    let Ok(engine_config) = config.engine_config() else {
85        return MSRT_STATUS_UNSUPPORTED;
86    };
87    let Ok(inner) = EndpointInner::new(config.role, engine_config) else {
88        return MSRT_STATUS_UNSUPPORTED;
89    };
90
91    let endpoint = ptr::from_mut(storage).cast::<MsrtEndpoint>();
92    unsafe {
93        endpoint.write(MsrtEndpoint {
94            inner,
95            tx_buf: [0; MAX_EVENT_BYTES],
96        });
97    }
98    *out = endpoint;
99    MSRT_STATUS_OK
100}
101
102/// Drops an endpoint initialized in caller-owned storage.
103pub(crate) unsafe fn deinit_endpoint(endpoint: *mut MsrtEndpoint) -> i32 {
104    if endpoint.is_null() {
105        return MSRT_STATUS_NULL;
106    }
107
108    unsafe {
109        ptr::drop_in_place(endpoint);
110    }
111    MSRT_STATUS_OK
112}
113
114/// Creates a heap-allocated endpoint handle.
115#[cfg(feature = "std")]
116pub(crate) unsafe fn new_endpoint(config: *const MsrtConfig) -> *mut MsrtEndpoint {
117    let mut storage = MsrtEndpointStorage {
118        bytes: [0; ENDPOINT_STORAGE_BYTES],
119    };
120    let mut endpoint = ptr::null_mut();
121    let status = unsafe { init_endpoint(&mut storage, config, &mut endpoint) };
122    if status != MSRT_STATUS_OK {
123        return ptr::null_mut();
124    }
125
126    Box::into_raw(Box::new(unsafe { ptr::read(endpoint) }))
127}
128
129/// Frees a heap-allocated endpoint handle.
130#[cfg(feature = "std")]
131pub(crate) unsafe fn free_endpoint(endpoint: *mut MsrtEndpoint) {
132    if endpoint.is_null() {
133        return;
134    }
135
136    drop(unsafe { Box::from_raw(endpoint) });
137}
138
139#[cfg(test)]
140pub(crate) fn endpoint_from_storage(
141    config: *const MsrtConfig,
142) -> (*mut MsrtEndpointStorage, *mut MsrtEndpoint) {
143    let storage = Box::into_raw(Box::new(MsrtEndpointStorage {
144        bytes: [0; ENDPOINT_STORAGE_BYTES],
145    }));
146    let mut endpoint = ptr::null_mut();
147    assert_eq!(
148        unsafe { init_endpoint(storage, config, &mut endpoint) },
149        MSRT_STATUS_OK
150    );
151    (storage, endpoint)
152}
153
154#[cfg(test)]
155pub(crate) unsafe fn free_storage_endpoint(
156    storage: *mut MsrtEndpointStorage,
157    endpoint: *mut MsrtEndpoint,
158) {
159    assert_eq!(unsafe { deinit_endpoint(endpoint) }, MSRT_STATUS_OK);
160    drop(unsafe { Box::from_raw(storage) });
161}