godot_ffi/
toolbox.rs

1/*
2 * Copyright (c) godot-rust; Bromeon and contributors.
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
6 */
7
8//! Functions and macros that are not very specific to gdext, but come in handy.
9
10use std::fmt::{Display, Write};
11
12use crate as sys;
13
14// ----------------------------------------------------------------------------------------------------------------------------------------------
15// Macros
16
17/// Verifies a condition at compile time.
18// https://blog.rust-lang.org/2021/12/02/Rust-1.57.0.html#panic-in-const-contexts
19#[macro_export]
20macro_rules! static_assert {
21    ($cond:expr) => {
22        const _: () = assert!($cond);
23    };
24    ($cond:expr, $msg:literal) => {
25        const _: () = assert!($cond, $msg);
26    };
27}
28
29/// Verifies at compile time that two types `T` and `U` have the same size and alignment.
30#[macro_export]
31macro_rules! static_assert_eq_size_align {
32    ($T:ty, $U:ty) => {
33        godot_ffi::static_assert!(
34            std::mem::size_of::<$T>() == std::mem::size_of::<$U>()
35                && std::mem::align_of::<$T>() == std::mem::align_of::<$U>()
36        );
37    };
38    ($T:ty, $U:ty, $msg:literal) => {
39        godot_ffi::static_assert!(
40            std::mem::size_of::<$T>() == std::mem::size_of::<$U>()
41                && std::mem::align_of::<$T>() == std::mem::align_of::<$U>(),
42            $msg
43        );
44    };
45}
46
47/// Trace output.
48#[cfg(feature = "debug-log")] #[cfg_attr(published_docs, doc(cfg(feature = "debug-log")))]
49#[macro_export]
50macro_rules! out {
51    ()                          => (eprintln!());
52    ($fmt:literal)              => (eprintln!($fmt));
53    ($fmt:literal, $($arg:tt)*) => (eprintln!($fmt, $($arg)*));
54}
55
56/// Trace output.
57#[cfg(not(feature = "debug-log"))] #[cfg_attr(published_docs, doc(cfg(not(feature = "debug-log"))))]
58#[macro_export]
59macro_rules! out {
60    ()                          => ({});
61    ($fmt:literal)              => ({});
62    ($fmt:literal, $($arg:tt)*) => {{
63        // Discard; should not generate any code.
64        if false {
65            format_args!($fmt, $($arg)*);
66        }
67    }}
68}
69
70/// Extract a function pointer from its `Option` and convert it to the (dereferenced) target type.
71///
72/// ```ignore
73///  let get_godot_version = get_proc_address(sys::c_str(b"get_godot_version\0"));
74///  let get_godot_version = sys::cast_fn_ptr!(get_godot_version as sys::GDExtensionInterfaceGetGodotVersion);
75/// ```
76///
77/// # Safety
78///
79/// `$ToType` must be an option of an `unsafe extern "C"` function pointer.
80#[allow(unused)]
81#[macro_export]
82macro_rules! unsafe_cast_fn_ptr {
83    ($option:ident as $ToType:ty) => {{
84        // SAFETY: `$ToType` is an `unsafe extern "C"` function pointer and is thus compatible with `unsafe extern "C" fn()`.
85        // And `Option<T>` is compatible with `Option<U>` when both `T` and `U` are compatible function pointers.
86        #[allow(unused_unsafe)]
87        let ptr: Option<_> = unsafe { std::mem::transmute::<Option<unsafe extern "C" fn()>, $ToType>($option) };
88        ptr.expect("null function pointer")
89    }};
90}
91
92// ----------------------------------------------------------------------------------------------------------------------------------------------
93// Utility functions
94
95/// Extract value from box before `into_inner()` is stable
96#[allow(clippy::boxed_local)] // false positive
97pub fn unbox<T>(value: Box<T>) -> T {
98    // Deref-move is a Box magic feature; see https://stackoverflow.com/a/42264074
99    *value
100}
101
102/// Explicitly cast away `const` from a pointer, similar to C++ `const_cast`.
103///
104/// The `as` conversion simultaneously doing 10 other things, potentially causing unintended transmutations.
105pub fn force_mut_ptr<T>(ptr: *const T) -> *mut T {
106    ptr as *mut T
107}
108
109/// Add `const` to a mut ptr.
110pub fn to_const_ptr<T>(ptr: *mut T) -> *const T {
111    ptr as *const T
112}
113
114/// If `ptr` is not null, returns `Some(mapper(ptr))`; otherwise `None`.
115#[inline]
116pub fn ptr_then<T, R, F>(ptr: *mut T, mapper: F) -> Option<R>
117where
118    F: FnOnce(*mut T) -> R,
119{
120    // Could also use NonNull in signature, but for this project we always deal with FFI raw pointers
121    if ptr.is_null() {
122        None
123    } else {
124        Some(mapper(ptr))
125    }
126}
127
128/// Returns a C `const char*` for a null-terminated byte string.
129#[inline]
130pub fn c_str(s: &[u8]) -> *const std::ffi::c_char {
131    // Ensure null-terminated
132    debug_assert!(!s.is_empty() && s[s.len() - 1] == 0);
133
134    s.as_ptr() as *const std::ffi::c_char
135}
136
137/// Returns a C `const char*` for a null-terminated string slice. UTF-8 encoded.
138#[inline]
139pub fn c_str_from_str(s: &str) -> *const std::ffi::c_char {
140    c_str(s.as_bytes())
141}
142
143/// Returns an ad-hoc hash of any object.
144pub fn hash_value<T: std::hash::Hash>(t: &T) -> u64 {
145    use std::hash::Hasher;
146    let mut hasher = std::collections::hash_map::DefaultHasher::new();
147    t.hash(&mut hasher);
148    hasher.finish()
149}
150
151pub fn join<T, I>(iter: I) -> String
152where
153    T: std::fmt::Display,
154    I: Iterator<Item = T>,
155{
156    join_with(iter, ", ", |item| format!("{item}"))
157}
158
159pub fn join_debug<T, I>(iter: I) -> String
160where
161    T: std::fmt::Debug,
162    I: Iterator<Item = T>,
163{
164    join_with(iter, ", ", |item| format!("{item:?}"))
165}
166
167pub fn join_with<T, I, F, S>(mut iter: I, sep: &str, mut format_elem: F) -> String
168where
169    I: Iterator<Item = T>,
170    F: FnMut(&T) -> S,
171    S: Display,
172{
173    let mut result = String::new();
174
175    if let Some(first) = iter.next() {
176        // write! propagates error only if given formatter fails.
177        // String formatting by itself is an infallible operation.
178        // Read more at: https://doc.rust-lang.org/stable/std/fmt/index.html#formatting-traits
179        write!(&mut result, "{first}", first = format_elem(&first))
180            .expect("Formatter should not fail!");
181        for item in iter {
182            write!(&mut result, "{sep}{item}", item = format_elem(&item))
183                .expect("Formatter should not fail!");
184        }
185    }
186    result
187}
188
189pub fn i64_to_ordering(value: i64) -> std::cmp::Ordering {
190    match value {
191        -1 => std::cmp::Ordering::Less,
192        0 => std::cmp::Ordering::Equal,
193        1 => std::cmp::Ordering::Greater,
194        _ => panic!("cannot convert value {value} to cmp::Ordering"),
195    }
196}
197
198/*
199pub fn unqualified_type_name<T>() -> &'static str {
200    let type_name = std::any::type_name::<T>();
201    type_name.split("::").last().unwrap()
202}
203*/
204
205/// Like [`std::any::type_name`], but returns a short type name without module paths.
206pub fn short_type_name<T: ?Sized>() -> String {
207    let full_name = std::any::type_name::<T>();
208    strip_module_paths(full_name)
209}
210
211/// Like [`std::any::type_name_of_val`], but returns a short type name without module paths.
212pub fn short_type_name_of_val<T: ?Sized>(val: &T) -> String {
213    let full_name = std::any::type_name_of_val(val);
214    strip_module_paths(full_name)
215}
216
217/// Helper function to strip module paths from a fully qualified type name.
218fn strip_module_paths(full_name: &str) -> String {
219    let mut result = String::new();
220    let mut identifier = String::new();
221
222    let mut chars = full_name.chars().peekable();
223
224    while let Some(c) = chars.next() {
225        match c {
226            '<' | '>' | ',' | ' ' | '&' | '(' | ')' | '[' | ']' => {
227                // Process the current identifier.
228                if !identifier.is_empty() {
229                    let short_name = identifier.split("::").last().unwrap_or(&identifier);
230                    result.push_str(short_name);
231                    identifier.clear();
232                }
233                result.push(c);
234
235                // Handle spaces after commas for readability.
236                if c == ',' && chars.peek().is_some_and(|&next_c| next_c != ' ') {
237                    result.push(' ');
238                }
239            }
240            ':' => {
241                // Check for '::' indicating module path separator.
242                if chars.peek() == Some(&':') {
243                    // Skip the second ':'
244                    chars.next();
245                    identifier.push_str("::");
246                } else {
247                    identifier.push(c);
248                }
249            }
250            _ => {
251                // Part of an identifier.
252                identifier.push(c);
253            }
254        }
255    }
256
257    // Process any remaining identifier.
258    if !identifier.is_empty() {
259        let short_name = identifier.split("::").last().unwrap_or(&identifier);
260        result.push_str(short_name);
261    }
262
263    result
264}
265
266// ----------------------------------------------------------------------------------------------------------------------------------------------
267// Private helpers
268
269/// Metafunction to extract inner function pointer types from all the bindgen `Option<F>` type names.
270/// Needed for `unsafe_cast_fn_ptr` macro.
271pub trait Inner: Sized {
272    type FnPtr: Sized;
273}
274
275impl<T> Inner for Option<T> {
276    type FnPtr = T;
277}
278
279// ----------------------------------------------------------------------------------------------------------------------------------------------
280// Function types used for table loaders
281
282pub(crate) type GetClassMethod = unsafe extern "C" fn(
283    p_classname: sys::GDExtensionConstStringNamePtr,
284    p_methodname: sys::GDExtensionConstStringNamePtr,
285    p_hash: sys::GDExtensionInt,
286) -> sys::GDExtensionMethodBindPtr;
287
288/// Newtype around `GDExtensionMethodBindPtr` so we can implement `Sync` and `Send` for it manually.    
289#[derive(Copy, Clone)]
290pub struct ClassMethodBind(pub sys::GDExtensionMethodBindPtr);
291
292// SAFETY: `sys::GDExtensionMethodBindPtr` is effectively the same as a `unsafe extern "C" fn`. So sharing it between
293// threads is fine, as using it in any way requires `unsafe` and it is up to the caller to ensure it is thread safe
294// to do so.
295unsafe impl Sync for ClassMethodBind {}
296// SAFETY: See `Sync` impl safety doc.
297unsafe impl Send for ClassMethodBind {}
298
299pub(crate) type GetBuiltinMethod = unsafe extern "C" fn(
300    p_type: sys::GDExtensionVariantType,
301    p_method: sys::GDExtensionConstStringNamePtr,
302    p_hash: sys::GDExtensionInt,
303) -> sys::GDExtensionPtrBuiltInMethod;
304
305// GDExtensionPtrBuiltInMethod
306pub type BuiltinMethodBind = unsafe extern "C" fn(
307    p_base: sys::GDExtensionTypePtr,
308    p_args: *const sys::GDExtensionConstTypePtr,
309    r_return: sys::GDExtensionTypePtr,
310    p_argument_count: std::os::raw::c_int,
311);
312
313pub(crate) type GetUtilityFunction = unsafe extern "C" fn(
314    p_function: sys::GDExtensionConstStringNamePtr,
315    p_hash: sys::GDExtensionInt,
316) -> sys::GDExtensionPtrUtilityFunction;
317
318pub type UtilityFunctionBind = unsafe extern "C" fn(
319    r_return: sys::GDExtensionTypePtr,
320    p_args: *const sys::GDExtensionConstTypePtr,
321    p_argument_count: std::os::raw::c_int,
322);
323
324// ----------------------------------------------------------------------------------------------------------------------------------------------
325// Utility functions
326
327// TODO: Most of these should be `unsafe` since the caller passes an `unsafe extern "C"` function pointer which it must be legal to call.
328// But for now we can just rely on knowing that these aren't called in the wrong context.
329
330pub(crate) fn load_class_method(
331    get_method_bind: GetClassMethod,
332    string_names: &mut sys::StringCache,
333    class_sname_ptr: Option<sys::GDExtensionStringNamePtr>,
334    class_name: &'static str,
335    method_name: &'static str,
336    hash: i64,
337) -> ClassMethodBind {
338    /*crate::out!(
339        "Load class method {}::{} (hash {})...",
340        class_name,
341        method_name,
342        hash
343    );*/
344
345    let method_sname_ptr: sys::GDExtensionStringNamePtr = string_names.fetch(method_name);
346    let class_sname_ptr = class_sname_ptr.unwrap_or_else(|| string_names.fetch(class_name));
347
348    // SAFETY: function pointers provided by Godot. We have no way to validate them.
349    let method: sys::GDExtensionMethodBindPtr =
350        unsafe { get_method_bind(class_sname_ptr, method_sname_ptr, hash) };
351
352    if method.is_null() {
353        panic!("Failed to load class method {class_name}::{method_name} (hash {hash}).{INFO}")
354    }
355
356    ClassMethodBind(method)
357}
358
359pub(crate) fn load_builtin_method(
360    get_builtin_method: GetBuiltinMethod,
361    string_names: &mut sys::StringCache,
362    variant_type: sys::GDExtensionVariantType,
363    variant_type_str: &'static str,
364    method_name: &'static str,
365    hash: i64,
366) -> BuiltinMethodBind {
367    /*crate::out!(
368        "Load builtin method {}::{} (hash {})...",
369        variant_type,
370        method_name,
371        hash
372    );*/
373
374    let method_sname = string_names.fetch(method_name);
375    // SAFETY: function pointers provided by Godot. We have no way to validate them.
376    let method = unsafe { get_builtin_method(variant_type, method_sname, hash) };
377
378    method.unwrap_or_else(|| {
379        panic!(
380            "Failed to load builtin method {variant_type_str}::{method_name} (hash {hash}).{INFO}"
381        )
382    })
383}
384
385pub(crate) fn validate_builtin_lifecycle<T>(function: Option<T>, description: &str) -> T {
386    function.unwrap_or_else(|| {
387        panic!("Failed to load builtin lifecycle function {description}.{INFO}",)
388    })
389}
390
391pub(crate) fn load_utility_function(
392    get_utility_fn: GetUtilityFunction,
393    string_names: &mut sys::StringCache,
394    fn_name_str: &'static str,
395    hash: i64,
396) -> UtilityFunctionBind {
397    // SAFETY: function pointers provided by Godot. We have no way to validate them.
398    let utility_fn = unsafe { get_utility_fn(string_names.fetch(fn_name_str), hash) };
399
400    utility_fn.unwrap_or_else(|| {
401        panic!("Failed to load utility function {fn_name_str} (hash {hash}).{INFO}")
402    })
403}
404
405pub(crate) fn read_version_string(version_ptr: &sys::GDExtensionGodotVersion) -> String {
406    let char_ptr = version_ptr.string;
407
408    // SAFETY: GDExtensionGodotVersion has the (manually upheld) invariant of a valid string field.
409    let c_str = unsafe { std::ffi::CStr::from_ptr(char_ptr) };
410
411    let full_version = c_str.to_str().unwrap_or("(invalid UTF-8 in version)");
412
413    full_version
414        .strip_prefix("Godot Engine ")
415        .unwrap_or(full_version)
416        .to_string()
417}
418
419const INFO: &str = "\nMake sure gdext and Godot are compatible: https://godot-rust.github.io/book/toolchain/compatibility.html";
420
421// ----------------------------------------------------------------------------------------------------------------------------------------------
422// Private abstractions
423// Don't use abstractions made here outside this crate, if needed then we should discuss making it more of a first-class
424// abstraction like `godot-cell`.
425
426/// Module to encapsulate `ManualInitCell`.
427mod manual_init_cell {
428    use std::cell::UnsafeCell;
429    use std::hint::unreachable_unchecked;
430
431    /// A cell which can be initialized and uninitialized, with manual synchronization from the caller.
432    ///
433    /// Similar to a [`OnceLock`](std::sync::OnceLock), but without the overhead of locking for initialization. In most cases the compiler
434    /// seems able to optimize `OnceLock` to equivalent code. But this guaranteed does not have any overhead at runtime.
435    ///
436    /// This cell additionally allows to deinitialize the value. Access to uninitialized values is UB, but checked in Debug mode.
437    pub(crate) struct ManualInitCell<T> {
438        // Invariant: Is `None` until initialized, and then never modified after (except, possibly, through interior mutability).
439        cell: UnsafeCell<Option<T>>,
440    }
441
442    impl<T> ManualInitCell<T> {
443        /// Creates a new empty cell.
444        pub const fn new() -> Self {
445            Self {
446                cell: UnsafeCell::new(None),
447            }
448        }
449
450        /// Initialize the value stored in this cell.
451        ///
452        /// # Safety
453        ///
454        /// - Must only be called once, unless a [`clear()`][Self::clear] call has happened in between.
455        /// - Calls to this method must not happen concurrently with a call to any other method on this cell.
456        ///
457        /// Note that the other methods of this cell do not have a safety invariant that they are not called concurrently with `set`.
458        /// This is because doing so would violate the safety invariants of `set` and so they do not need to explicitly have that as a
459        /// safety invariant as well. This has the added benefit that `is_initialized` can be a safe method.
460        #[inline]
461        pub unsafe fn set(&self, value: T) {
462            // SAFETY: `set` has exclusive access to the cell, per the safety requirements.
463            let option = unsafe { &mut *self.cell.get() };
464
465            // Tell the compiler that the cell is `None`, even if it can't prove that on its own.
466            if option.is_some() {
467                // SAFETY: `set` cannot be called multiple times without `clear` in between, so the cell must be `None` at this point.
468                // This panics in Debug mode.
469                unsafe { unreachable_unchecked() }
470            }
471
472            *option = Some(value);
473        }
474
475        /// Clear the value stored in this cell.
476        ///
477        /// # Safety
478        ///
479        /// - Must only be called after [`set`](Self::set) has been called.
480        /// - Calls to this method must not happen concurrently with a call to any other method on this cell.
481        #[inline]
482        pub unsafe fn clear(&self) {
483            // SAFETY: `set` is only ever called once, and is not called concurrently with any other methods. Therefore, we can take
484            // a mutable reference to the contents of the cell.
485            let option = unsafe { &mut *self.cell.get() };
486
487            // Tell the compiler that the cell is `Some`.
488            if option.is_none() {
489                // SAFETY: `set` has been called before this, so the option is known to be a `Some`.
490                // This panics in Debug mode.
491                unsafe { unreachable_unchecked() }
492            }
493
494            *option = None;
495        }
496
497        /// Gets the value stored in the cell.
498        ///
499        /// # Safety
500        ///
501        /// - [`set`](ManualInitCell::set) must have been called before calling this method.
502        #[inline]
503        pub unsafe fn get_unchecked(&self) -> &T {
504            // SAFETY: There are no `&mut` references, since only `set` can create one and this method cannot be called concurrently with `set`.
505            let option = unsafe { &*self.cell.get() };
506
507            // SAFETY: `set` has been called before this, so the option is known to be a `Some`.
508            // This panics in Debug mode.
509            unsafe { option.as_ref().unwrap_unchecked() }
510        }
511
512        /// Checks whether the cell contains a value.
513        #[inline]
514        pub fn is_initialized(&self) -> bool {
515            // SAFETY: There are no `&mut` references, since only `set` can create one and this method cannot be called concurrently with `set`.
516            let option = unsafe { &*self.cell.get() };
517
518            option.is_some()
519        }
520    }
521
522    // SAFETY: The user is responsible for ensuring thread safe initialization of the cell.
523    // This also requires `Send` for the same reasons `OnceLock` does.
524    unsafe impl<T: Send + Sync> Sync for ManualInitCell<T> {}
525    // SAFETY: See `Sync` impl.
526    unsafe impl<T: Send> Send for ManualInitCell<T> {}
527}
528
529pub(crate) use manual_init_cell::ManualInitCell;
530
531// ----------------------------------------------------------------------------------------------------------------------------------------------
532// Unit tests
533
534#[cfg(test)] #[cfg_attr(published_docs, doc(cfg(test)))]
535mod tests {
536    use super::*;
537
538    #[test]
539    fn test_short_type_name() {
540        assert_eq!(short_type_name::<i32>(), "i32");
541        assert_eq!(short_type_name::<Option<i32>>(), "Option<i32>");
542        assert_eq!(
543            short_type_name::<Result<Option<i32>, String>>(),
544            "Result<Option<i32>, String>"
545        );
546        assert_eq!(
547            short_type_name::<Vec<Result<Option<i32>, String>>>(),
548            "Vec<Result<Option<i32>, String>>"
549        );
550        assert_eq!(
551            short_type_name::<std::collections::HashMap<String, Vec<i32>>>(),
552            "HashMap<String, Vec<i32>>"
553        );
554        assert_eq!(
555            short_type_name::<Result<Option<i32>, String>>(),
556            "Result<Option<i32>, String>"
557        );
558        assert_eq!(short_type_name::<i32>(), "i32");
559        assert_eq!(short_type_name::<Vec<String>>(), "Vec<String>");
560    }
561
562    #[test]
563    fn test_short_type_name_of_val() {
564        let value = Some(42);
565        assert_eq!(short_type_name_of_val(&value), "Option<i32>");
566
567        let result: Result<_, String> = Ok(Some(42));
568        assert_eq!(
569            short_type_name_of_val(&result),
570            "Result<Option<i32>, String>"
571        );
572
573        let vec = vec![result];
574        assert_eq!(
575            short_type_name_of_val(&vec),
576            "Vec<Result<Option<i32>, String>>"
577        );
578    }
579}