feat: fix cstr ffi again
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
//! When calling this dynamic library with outside programs,
|
//! When calling this dynamic library with outside programs,
|
||||||
//! outer programs may usually need to fetch string resource produced by Rust code.
|
//! outer programs may usually need to fetch string resource produced by Rust code.
|
||||||
//! However it is impossible pass Rust string directly to outer program.
|
//! However it is impossible pass Rust string directly to outer program.
|
||||||
//!
|
//!
|
||||||
//! This module provide **thread independent** string cache for resolving this issue.
|
//! This module provide **thread independent** string cache for resolving this issue.
|
||||||
//! When we need pass string to outer programs, we push that string into this string as C-like format,
|
//! When we need pass string to outer programs, we push that string into this string as C-like format,
|
||||||
//! then return its pointer to outer program.
|
//! then return its pointer to outer program.
|
||||||
@@ -9,21 +9,44 @@
|
|||||||
//! The only thing that outer programs should note is that this string is volatile,
|
//! The only thing that outer programs should note is that this string is volatile,
|
||||||
//! once they get it, they must dupliate it immediately before any futher calling to this dynamic library.
|
//! once they get it, they must dupliate it immediately before any futher calling to this dynamic library.
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::ffi::{CString, CStr, c_char};
|
use std::ffi::{CStr, CString, c_char};
|
||||||
|
use thiserror::Error as TeError;
|
||||||
|
|
||||||
struct StringCache {
|
// region: Error
|
||||||
|
|
||||||
|
/// Error occurs in this crate.
|
||||||
|
#[derive(Debug, TeError)]
|
||||||
|
pub enum Error {
|
||||||
|
#[error("unexpected NUL when parsing into C/C++ string")]
|
||||||
|
UnexpectedNul(#[from] std::ffi::NulError),
|
||||||
|
|
||||||
|
#[error("given string pointer is nullptr when parsing from C/C++ string")]
|
||||||
|
NullPtr,
|
||||||
|
#[error("invalid UTF8 sequence when parsing from C/C++ string")]
|
||||||
|
InvalidEncoding(#[from] std::str::Utf8Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Result type used in this crate.
|
||||||
|
type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region: FFI String
|
||||||
|
|
||||||
|
struct FfiString {
|
||||||
msg: CString,
|
msg: CString,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StringCache {
|
impl FfiString {
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
msg: CString::new("").expect("empty string must be valid for CString"),
|
msg: CString::new("").expect("empty string must be valid for CString"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_msg(&mut self, msg: &str) {
|
pub fn set_msg(&mut self, msg: &str) -> Result<()> {
|
||||||
self.msg = CString::new(msg).expect("unexpected blank in string push into string cache");
|
self.msg = CString::new(msg)?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_msg(&self) -> *const c_char {
|
pub fn get_msg(&self) -> *const c_char {
|
||||||
@@ -35,15 +58,19 @@ impl StringCache {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region: Exposed Functions
|
||||||
|
|
||||||
thread_local! {
|
thread_local! {
|
||||||
static STRING_CACHE: RefCell<StringCache> = RefCell::new(StringCache::new());
|
static STRING_CACHE: RefCell<FfiString> = RefCell::new(FfiString::new());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set thread local string exposed for C code.
|
/// Set thread local string exposed for C code.
|
||||||
pub fn set_ffi_string(msg: &str) {
|
pub fn set_ffi_string(msg: &str) -> Result<()> {
|
||||||
STRING_CACHE.with(|e| {
|
STRING_CACHE.with(|e| {
|
||||||
e.borrow_mut().set_msg(msg);
|
e.borrow_mut().set_msg(msg)
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get const pointer to thread local string exposed for C code.
|
/// Get const pointer to thread local string exposed for C code.
|
||||||
@@ -52,7 +79,7 @@ pub fn get_ffi_string() -> *const c_char {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Clear thread local string exposed for C code.
|
/// Clear thread local string exposed for C code.
|
||||||
///
|
///
|
||||||
/// This function usually should be called at the beginning of every exposed C functions.
|
/// This function usually should be called at the beginning of every exposed C functions.
|
||||||
pub fn clear_ffi_string() {
|
pub fn clear_ffi_string() {
|
||||||
STRING_CACHE.with(|e| {
|
STRING_CACHE.with(|e| {
|
||||||
@@ -61,6 +88,13 @@ pub fn clear_ffi_string() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Parse string given by C code into Rust string.
|
/// Parse string given by C code into Rust string.
|
||||||
pub fn parse_ffi_string<'a>(ptr: *const c_char) -> &'a CStr {
|
pub fn parse_ffi_string<'a>(ptr: *const c_char) -> Result<&'a str> {
|
||||||
unsafe { CStr::from_ptr(ptr) }
|
if ptr.is_null() {
|
||||||
|
Err(Error::NullPtr)
|
||||||
|
} else {
|
||||||
|
let c_str = unsafe { CStr::from_ptr(ptr) };
|
||||||
|
Ok(c_str.to_str()?)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|||||||
@@ -12,6 +12,9 @@ mod wrapper;
|
|||||||
/// Error occurs in this crate.
|
/// Error occurs in this crate.
|
||||||
#[derive(Debug, TeError)]
|
#[derive(Debug, TeError)]
|
||||||
enum Error {
|
enum Error {
|
||||||
|
/// Error when operating Schema.
|
||||||
|
#[error("{0}")]
|
||||||
|
Schema(#[from] wfassoc::highlevel::SchemaError),
|
||||||
/// Error when parsing Schema into Program.
|
/// Error when parsing Schema into Program.
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
ParseProgram(#[from] wfassoc::highlevel::ParseProgramError),
|
ParseProgram(#[from] wfassoc::highlevel::ParseProgramError),
|
||||||
@@ -19,10 +22,9 @@ enum Error {
|
|||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
Program(#[from] wfassoc::highlevel::ProgramError),
|
Program(#[from] wfassoc::highlevel::ProgramError),
|
||||||
|
|
||||||
#[error("error occurs when parsing into C/C++ string")]
|
/// Error when manipulating with C-style string.
|
||||||
CastIntoCStr(#[from] std::ffi::NulError),
|
#[error("{0}")]
|
||||||
#[error("error occurs when parsing from C/C++ string")]
|
CStrFfi(#[from] cstr_ffi::Error),
|
||||||
CastFromCStr(#[from] std::ffi::IntoStringError),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Result type used in this crate.
|
/// Result type used in this crate.
|
||||||
@@ -37,12 +39,12 @@ type Result<T> = std::result::Result<T, Error>;
|
|||||||
|
|
||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub extern "C" fn WFStartup() -> bool {
|
pub extern "C" fn WFStartup() -> bool {
|
||||||
false
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub extern "C" fn WFShutdown() -> bool {
|
pub extern "C" fn WFShutdown() -> bool {
|
||||||
false
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
|
|||||||
Reference in New Issue
Block a user