use std::any::Any;
use std::cell::RefCell;
use std::ffi::{c_char, CString};
use std::panic::AssertUnwindSafe;
enum LastError {
None,
Message(CString),
NoMemory,
}
impl LastError {
const NO_MEMORY_MSG: [c_char; 10] = [
b'n' as c_char,
b'o' as c_char,
b' ' as c_char,
b'm' as c_char,
b'e' as c_char,
b'm' as c_char,
b'o' as c_char,
b'r' as c_char,
b'y' as c_char,
b'\0' as c_char,
];
const NO_ERROR_MSG: [c_char; 9] = [
b'n' as c_char,
b'o' as c_char,
b' ' as c_char,
b'e' as c_char,
b'r' as c_char,
b'r' as c_char,
b'o' as c_char,
b'r' as c_char,
b'\0' as c_char,
];
fn as_ptr(&self) -> *const c_char {
match self {
Self::None => Self::NO_ERROR_MSG.as_ptr(),
Self::Message(cstring) => cstring.as_ptr(),
Self::NoMemory => Self::NO_MEMORY_MSG.as_ptr(),
}
}
fn len(&self) -> usize {
match self {
Self::None => Self::NO_ERROR_MSG.len() - 1,
Self::Message(cstring) => cstring.as_bytes().len(),
Self::NoMemory => Self::NO_MEMORY_MSG.len() - 1,
}
}
}
std::thread_local! {
pub(in crate::c_api) static LAST_LOCAL_ERROR: RefCell<LastError> = const { RefCell::new(LastError::None) };
}
pub(in crate::c_api) fn replace_last_error_with_panic_payload(payload: &Box<dyn Any + Send>) {
LAST_LOCAL_ERROR.with(|local_error| {
let _previous_error = local_error.replace(panic_payload_to_error(payload));
});
}
fn panic_payload_to_error(payload: &Box<dyn Any + Send>) -> LastError {
let result = std::panic::catch_unwind(AssertUnwindSafe(|| {
let bytes = payload
.downcast_ref::<&'static str>()
.map(|s| s.as_bytes().to_vec())
.or_else(|| {
payload
.downcast_ref::<String>()
.map(|s| s.as_bytes().to_vec())
})
.unwrap_or_else(|| b"panic occurred".to_vec());
CString::new(bytes)
.unwrap_or_else(|_| CString::new("panic message contained nul byte").unwrap())
}));
result.map_or(LastError::NoMemory, LastError::Message)
}
#[no_mangle]
pub unsafe extern "C" fn tfhe_error_get_last() -> *const c_char {
LAST_LOCAL_ERROR.with_borrow(|last_error| last_error.as_ptr())
}
#[no_mangle]
pub unsafe extern "C" fn tfhe_error_get_size() -> usize {
LAST_LOCAL_ERROR.with_borrow(|last_error| last_error.len())
}
#[no_mangle]
pub unsafe extern "C" fn tfhe_error_clear() {
LAST_LOCAL_ERROR.replace(LastError::None);
}
#[no_mangle]
pub unsafe extern "C" fn tfhe_error_disable_automatic_prints() {
std::panic::set_hook(Box::new(|_panic_info| {}));
}
#[no_mangle]
pub unsafe extern "C" fn tfhe_error_enable_automatic_prints() {
let _ = std::panic::take_hook();
}