1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
//! Helper functionality for passing [`String`]s through FFI boundaries.
use std::{
ffi::{CStr, CString},
os::raw::c_char,
ptr,
};
use crate::api::propagate_panic;
/// Pointer to an extern function that frees the provided Dart native string.
type FreeDartNativeStringFunction = extern "C" fn(ptr::NonNull<c_char>);
/// Stores a pointer to the [`FreeDartNativeStringFunction`] extern function.
///
/// Must be initialized by Dart during FFI initialization phase.
static mut FREE_DART_NATIVE_STRING: Option<FreeDartNativeStringFunction> = None;
/// Constructs a Rust [`String`] from the provided raw C string.
///
/// # Panics
///
/// If the provided C string UTF-8 validation fails.
///
/// # Safety
///
/// Same as for [`CStr::from_ptr()`].
#[must_use]
pub unsafe fn c_str_into_string(string: ptr::NonNull<c_char>) -> String {
CStr::from_ptr(string.as_ptr()).to_str().unwrap().to_owned()
}
/// Leaks the given [`String`] returning a raw C string that can be passed
/// through FFI boundaries.
///
/// The pointer (returned by this function) must be returned to Rust and
/// reconstituted via [`CString::from_raw()`] for proper deallocating.
///
/// # Panics
///
/// If the provided [`String`] contains an internal `0x0` byte.
#[must_use]
pub fn string_into_c_str(string: String) -> ptr::NonNull<c_char> {
ptr::NonNull::new(CString::new(string).unwrap().into_raw()).unwrap()
}
/// Converts the provided C-string received from Dart into a Rust [`String`].
///
/// # Safety
///
/// The provided value must represent a valid C-string (`Pointer<Utf8>`
/// allocated on Dart-side).
#[must_use]
pub unsafe fn dart_string_into_rust(
dart_string: ptr::NonNull<c_char>,
) -> String {
let rust_string = c_str_into_string(dart_string);
free_dart_native_string(dart_string);
rust_string
}
/// Retakes ownership over a [`CString`] previously transferred to Dart via
/// [`CString::into_raw()`].
///
/// # Safety
///
/// Same as for [`CString::from_raw()`].
#[no_mangle]
pub unsafe extern "C" fn String_free(s: ptr::NonNull<c_char>) {
propagate_panic(move || {
drop(CString::from_raw(s.as_ptr()));
});
}
/// Registers the provided [`FreeDartNativeStringFunction`] as
/// [`FREE_DART_NATIVE_STRING`].
///
/// # Safety
///
/// Must ONLY be called by Dart during FFI initialization.
#[no_mangle]
pub unsafe extern "C" fn register_free_dart_native_string(
f: FreeDartNativeStringFunction,
) {
FREE_DART_NATIVE_STRING = Some(f);
}
/// Calls Dart to release memory allocated for the provided native string.
///
/// Should be used when Dart cannot release memory in place, e.g when Rust calls
/// a Dart function returning a native string.
///
/// # Safety
///
/// `FREE_DART_NATIVE_STRING` function must be registered and the provided
/// pointer must be a valid native string.
pub unsafe fn free_dart_native_string(s: ptr::NonNull<c_char>) {
FREE_DART_NATIVE_STRING.unwrap()(s);
}