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(|| {
payload
.downcast_ref::<&str>()
.map_or_else(|| b"panic occurred".to_vec(), |s| s.as_bytes().to_vec())
}));
result.map_or_else(
|_| LastError::NoMemory,
|bytes| LastError::Message(CString::new(bytes).unwrap()),
)
}
#[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();
}