use std::ffi::NulError;
use std::mem::MaybeUninit;
use std::str::Utf8Error;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum CReprOfError {
#[error("A string contains a nul bit")]
StringContainsNullBit(#[from] NulError),
#[error("An error occurred during conversion to C repr; {}", .0)]
Other(#[from] Box<dyn std::error::Error + Send + Sync>),
}
pub trait CReprOf<T>: Sized + CDrop {
fn c_repr_of(input: T) -> Result<Self, CReprOfError>;
}
#[derive(Error, Debug)]
pub enum CDropError {
#[error("unexpected null pointer")]
NullPointer(#[from] UnexpectedNullPointerError),
#[error("An error occurred while dropping C struct: {}", .0)]
Other(#[from] Box<dyn std::error::Error + Send + Sync>),
}
pub trait CDrop {
fn do_drop(&mut self) -> Result<(), CDropError>;
}
#[derive(Error, Debug)]
pub enum AsRustError {
#[error("unexpected null pointer")]
NullPointer(#[from] UnexpectedNullPointerError),
#[error("could not convert string as it is not UTF-8: {}", .0)]
Utf8Error(#[from] Utf8Error),
#[error("An error occurred during conversion to Rust: {}", .0)]
Other(#[from] Box<dyn std::error::Error + Send + Sync>),
}
pub trait AsRust<T> {
fn as_rust(&self) -> Result<T, AsRustError>;
}
#[derive(Error, Debug)]
#[error("Could not use raw pointer: unexpected null pointer")]
pub struct UnexpectedNullPointerError;
pub trait RawPointerConverter<T>: Sized {
fn into_raw_pointer(self) -> *const T;
fn into_raw_pointer_mut(self) -> *mut T;
unsafe fn from_raw_pointer(input: *const T) -> Result<Self, UnexpectedNullPointerError>;
unsafe fn from_raw_pointer_mut(input: *mut T) -> Result<Self, UnexpectedNullPointerError>;
unsafe fn drop_raw_pointer(input: *const T) -> Result<(), UnexpectedNullPointerError> {
unsafe { Self::from_raw_pointer(input) }.map(|_| ())
}
unsafe fn drop_raw_pointer_mut(input: *mut T) -> Result<(), UnexpectedNullPointerError> {
unsafe { Self::from_raw_pointer_mut(input) }.map(|_| ())
}
}
#[doc(hidden)]
pub fn convert_into_raw_pointer<T>(pointee: T) -> *const T {
Box::into_raw(Box::new(pointee)) as _
}
#[doc(hidden)]
pub fn convert_into_raw_pointer_mut<T>(pointee: T) -> *mut T {
Box::into_raw(Box::new(pointee))
}
#[doc(hidden)]
pub unsafe fn take_back_from_raw_pointer<T>(
input: *const T,
) -> Result<T, UnexpectedNullPointerError> {
unsafe { take_back_from_raw_pointer_mut(input as _) }
}
#[doc(hidden)]
pub unsafe fn take_back_from_raw_pointer_mut<T>(
input: *mut T,
) -> Result<T, UnexpectedNullPointerError> {
if input.is_null() {
Err(UnexpectedNullPointerError)
} else {
Ok(*unsafe { Box::from_raw(input) })
}
}
pub trait RawBorrow<T> {
unsafe fn raw_borrow<'a>(input: *const T) -> Result<&'a Self, UnexpectedNullPointerError>;
}
pub trait RawBorrowMut<T> {
unsafe fn raw_borrow_mut<'a>(input: *mut T)
-> Result<&'a mut Self, UnexpectedNullPointerError>;
}
impl<T> RawBorrow<T> for T {
unsafe fn raw_borrow<'a>(input: *const T) -> Result<&'a Self, UnexpectedNullPointerError> {
unsafe { input.as_ref() }.ok_or(UnexpectedNullPointerError)
}
}
impl<T> RawBorrowMut<T> for T {
unsafe fn raw_borrow_mut<'a>(
input: *mut T,
) -> Result<&'a mut Self, UnexpectedNullPointerError> {
unsafe { input.as_mut() }.ok_or(UnexpectedNullPointerError)
}
}
impl RawPointerConverter<std::ffi::c_void> for std::ffi::CString {
fn into_raw_pointer(self) -> *const std::ffi::c_void {
self.into_raw() as _
}
fn into_raw_pointer_mut(self) -> *mut std::ffi::c_void {
self.into_raw() as _
}
unsafe fn from_raw_pointer(
input: *const std::ffi::c_void,
) -> Result<Self, UnexpectedNullPointerError> {
unsafe { Self::from_raw_pointer_mut(input as *mut std::ffi::c_void) }
}
unsafe fn from_raw_pointer_mut(
input: *mut std::ffi::c_void,
) -> Result<Self, UnexpectedNullPointerError> {
if input.is_null() {
Err(UnexpectedNullPointerError)
} else {
Ok(unsafe { std::ffi::CString::from_raw(input as *mut std::ffi::c_char) })
}
}
}
impl RawPointerConverter<std::ffi::c_char> for std::ffi::CString {
fn into_raw_pointer(self) -> *const std::ffi::c_char {
self.into_raw() as _
}
fn into_raw_pointer_mut(self) -> *mut std::ffi::c_char {
self.into_raw()
}
unsafe fn from_raw_pointer(
input: *const std::ffi::c_char,
) -> Result<Self, UnexpectedNullPointerError> {
unsafe { Self::from_raw_pointer_mut(input as *mut std::ffi::c_char) }
}
unsafe fn from_raw_pointer_mut(
input: *mut std::ffi::c_char,
) -> Result<Self, UnexpectedNullPointerError> {
if input.is_null() {
Err(UnexpectedNullPointerError)
} else {
Ok(unsafe { std::ffi::CString::from_raw(input as *mut std::ffi::c_char) })
}
}
}
impl RawBorrow<std::ffi::c_char> for std::ffi::CStr {
unsafe fn raw_borrow<'a>(
input: *const std::ffi::c_char,
) -> Result<&'a Self, UnexpectedNullPointerError> {
if input.is_null() {
Err(UnexpectedNullPointerError)
} else {
Ok(unsafe { Self::from_ptr(input) })
}
}
}
macro_rules! impl_noop_c_drop_for {
($typ:ty) => {
impl CDrop for $typ {
fn do_drop(&mut self) -> Result<(), CDropError> {
Ok(())
}
}
};
}
impl_noop_c_drop_for!(usize);
impl_noop_c_drop_for!(i8);
impl_noop_c_drop_for!(u8);
impl_noop_c_drop_for!(i16);
impl_noop_c_drop_for!(u16);
impl_noop_c_drop_for!(i32);
impl_noop_c_drop_for!(u32);
impl_noop_c_drop_for!(i64);
impl_noop_c_drop_for!(u64);
impl_noop_c_drop_for!(f32);
impl_noop_c_drop_for!(f64);
impl_noop_c_drop_for!(bool);
impl_noop_c_drop_for!(std::ffi::CString);
macro_rules! impl_c_repr_of_for {
($typ:ty) => {
impl CReprOf<$typ> for $typ {
fn c_repr_of(input: $typ) -> Result<$typ, CReprOfError> {
Ok(input)
}
}
};
($from_typ:ty, $to_typ:ty) => {
impl CReprOf<$from_typ> for $to_typ {
fn c_repr_of(input: $from_typ) -> Result<$to_typ, CReprOfError> {
Ok(input as $to_typ)
}
}
};
}
impl_c_repr_of_for!(usize);
impl_c_repr_of_for!(i8);
impl_c_repr_of_for!(u8);
impl_c_repr_of_for!(i16);
impl_c_repr_of_for!(u16);
impl_c_repr_of_for!(i32);
impl_c_repr_of_for!(u32);
impl_c_repr_of_for!(i64);
impl_c_repr_of_for!(u64);
impl_c_repr_of_for!(f32);
impl_c_repr_of_for!(f64);
impl_c_repr_of_for!(bool);
impl_c_repr_of_for!(usize, i32);
impl CReprOf<String> for std::ffi::CString {
fn c_repr_of(input: String) -> Result<Self, CReprOfError> {
Ok(std::ffi::CString::new(input)?)
}
}
macro_rules! impl_as_rust_for {
($typ:ty) => {
impl AsRust<$typ> for $typ {
fn as_rust(&self) -> Result<$typ, AsRustError> {
Ok(*self)
}
}
};
($from_typ:ty, $to_typ:ty) => {
impl AsRust<$to_typ> for $from_typ {
fn as_rust(&self) -> Result<$to_typ, AsRustError> {
Ok(*self as $to_typ)
}
}
};
}
impl_as_rust_for!(usize);
impl_as_rust_for!(i8);
impl_as_rust_for!(u8);
impl_as_rust_for!(i16);
impl_as_rust_for!(u16);
impl_as_rust_for!(i32);
impl_as_rust_for!(u32);
impl_as_rust_for!(i64);
impl_as_rust_for!(u64);
impl_as_rust_for!(f32);
impl_as_rust_for!(f64);
impl_as_rust_for!(bool);
impl_as_rust_for!(i32, usize);
impl AsRust<String> for std::ffi::CStr {
fn as_rust(&self) -> Result<String, AsRustError> {
self.to_str().map(|s| s.to_owned()).map_err(|e| e.into())
}
}
macro_rules! impl_rawpointerconverter_for {
($typ:ty) => {
impl RawPointerConverter<$typ> for $typ {
fn into_raw_pointer(self) -> *const $typ {
convert_into_raw_pointer(self)
}
fn into_raw_pointer_mut(self) -> *mut $typ {
convert_into_raw_pointer_mut(self)
}
unsafe fn from_raw_pointer(
input: *const $typ,
) -> Result<Self, UnexpectedNullPointerError> {
unsafe { take_back_from_raw_pointer(input) }
}
unsafe fn from_raw_pointer_mut(
input: *mut $typ,
) -> Result<Self, UnexpectedNullPointerError> {
unsafe { take_back_from_raw_pointer_mut(input) }
}
}
};
}
impl_rawpointerconverter_for!(usize);
impl_rawpointerconverter_for!(i16);
impl_rawpointerconverter_for!(u16);
impl_rawpointerconverter_for!(i32);
impl_rawpointerconverter_for!(u32);
impl_rawpointerconverter_for!(i64);
impl_rawpointerconverter_for!(u64);
impl_rawpointerconverter_for!(f32);
impl_rawpointerconverter_for!(f64);
impl_rawpointerconverter_for!(bool);
impl<U, T: CReprOf<U>, const N: usize> CReprOf<[U; N]> for [T; N]
where
[T; N]: CDrop,
{
fn c_repr_of(values: [U; N]) -> Result<[T; N], CReprOfError> {
let mut array: [MaybeUninit<T>; N] = [const { MaybeUninit::uninit() }; N];
for (n, value) in values.into_iter().enumerate() {
let item = &mut array[n];
match T::c_repr_of(value) {
Ok(value) => {
item.write(value);
}
Err(err) => {
for item in &mut array[0..n] {
unsafe {
let _ = item.assume_init_mut().do_drop();
}
}
return Err(err);
}
}
}
let array = unsafe {
(&raw const array).cast::<[T; N]>().read()
};
Ok(array)
}
}
impl<T: CDrop, const N: usize> CDrop for [T; N] {
fn do_drop(&mut self) -> Result<(), CDropError> {
let mut result = Ok(());
for value in self {
if let Err(err) = value.do_drop()
&& result.is_ok()
{
result = Err(err);
}
}
result
}
}
impl<U: AsRust<T>, T, const N: usize> AsRust<[T; N]> for [U; N] {
fn as_rust(&self) -> Result<[T; N], AsRustError> {
let mut array: [MaybeUninit<T>; N] = [const { MaybeUninit::uninit() }; N];
for (n, value) in self.iter().enumerate() {
let item = &mut array[n];
match value.as_rust() {
Ok(value) => {
item.write(value);
}
Err(err) => {
for item in &mut array[0..n] {
unsafe {
item.assume_init_drop();
}
}
return Err(err);
}
}
}
let array = unsafe {
(&raw const array).cast::<[T; N]>().read()
};
Ok(array)
}
}