Skip to main content

uika_runtime/
api.rs

1// Global API table storage. Initialized once during DLL load, then read-only.
2
3use std::sync::OnceLock;
4
5use uika_ffi::UikaApiTable;
6
7/// Wrapper so a raw pointer can live inside OnceLock (which requires Send+Sync).
8/// SAFETY: The API table is created by C++ before uika_init and lives for the
9/// entire DLL lifetime. Access is read-only after init.
10struct ApiRef(*const UikaApiTable);
11unsafe impl Send for ApiRef {}
12unsafe impl Sync for ApiRef {}
13
14static API: OnceLock<ApiRef> = OnceLock::new();
15
16/// Store the API table pointer. Called once by `uika_init`.
17/// Panics if called more than once.
18pub fn init_api(table: *const UikaApiTable) {
19    assert!(!table.is_null(), "init_api called with null pointer");
20    if API.set(ApiRef(table)).is_err() {
21        panic!("init_api called more than once");
22    }
23}
24
25/// Access the global API table. Panics if called before `init_api`.
26#[inline(always)]
27pub fn api() -> &'static UikaApiTable {
28    // SAFETY: The pointer was validated non-null in init_api, and the C++ side
29    // guarantees the table outlives the DLL.
30    unsafe { &*API.get().expect("uika API not initialized").0 }
31}
32
33/// Returns true if the API table has been initialized.
34#[inline]
35pub fn is_api_initialized() -> bool {
36    API.get().is_some()
37}