use core::ptr::NonNull;
pub trait PointerUpgrade<T>: Sized {
fn upgrade(self) -> Option<NonNull<T>>;
}
impl<T> PointerUpgrade<T> for *const T {
#[inline]
fn upgrade(self) -> Option<NonNull<T>> {
NonNull::new(self.cast_mut())
}
}
impl<T> PointerUpgrade<T> for *mut T {
#[inline]
fn upgrade(self) -> Option<NonNull<T>> {
NonNull::new(self)
}
}
pub trait RetUpgrade {
fn upgrade(self) -> Result<core::ffi::c_int, core::ffi::c_int>;
}
impl RetUpgrade for core::ffi::c_int {
#[inline]
fn upgrade(self) -> Result<core::ffi::c_int, core::ffi::c_int> {
if self < 0 { Err(self) } else { Ok(self) }
}
}
#[cfg(target_os = "windows")]
pub enum WindowsResult<T, E> {
Ok(T),
Err(E),
}
#[cfg(target_os = "windows")]
#[macro_export]
macro_rules! impl_from_integer_for_windows_result {
( $( $integer_type:ty ),+ ) => {
$(
impl From<$integer_type> for $crate::ffi_bedrock::WindowsResult<$integer_type, &'static str> {
fn from(return_value: $integer_type) -> Self {
match return_value {
0 => Self::Err("Windows API error: 0"),
_ => Self::Ok(return_value),
}
}
}
)+
};
}
#[cfg(target_os = "windows")]
pub trait ProcessWindowsResult<T, E> {
fn process(self) -> Result<T, E>;
}
#[cfg(target_os = "windows")]
impl<T, E> ProcessWindowsResult<T, E> for WindowsResult<T, E> {
#[inline]
fn process(self) -> Result<T, E> {
match self {
Self::Ok(v) => Ok(v),
Self::Err(e) => Err(e),
}
}
}
#[macro_export]
macro_rules! wrap_pure {
(
$(#[$meta:meta])*
($wrapped_type: ident): $ffi_type: ty
) => {
$(#[$meta])*
pub struct $wrapped_type {
inner: core::ptr::NonNull<$ffi_type>,
}
impl $wrapped_type {
#[inline]
pub fn as_ptr(&self) -> *const $ffi_type {
self.inner.as_ptr().cast_const()
}
#[inline]
pub fn as_mut_ptr(&mut self) -> *mut $ffi_type {
self.inner.as_ptr()
}
#[inline]
pub unsafe fn from_raw(raw: core::ptr::NonNull<$ffi_type>) -> Self {
Self { inner: raw }
}
#[inline]
pub fn into_raw(self) -> core::ptr::NonNull<$ffi_type> {
let raw = self.inner;
core::mem::forget(self);
raw
}
}
impl core::ops::Deref for $wrapped_type {
type Target = $ffi_type;
#[inline]
fn deref(&self) -> &Self::Target {
unsafe { self.inner.as_ref() }
}
}
unsafe impl Send for $wrapped_type where $ffi_type: Send {}
unsafe impl Sync for $wrapped_type where $ffi_type: Sync {}
};
}
#[macro_export]
macro_rules! wrap_ref {
($wrapped_type: ident, $wrapped_ref: ident, $ffi_type: ty) => {
#[repr(transparent)]
pub struct $wrapped_ref<'a> {
inner: core::mem::ManuallyDrop<$wrapped_type>,
_marker: core::marker::PhantomData<&'a $ffi_type>,
}
impl<'a> core::ops::Deref for $wrapped_ref<'a> {
type Target = $wrapped_type;
#[inline]
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<'a> $wrapped_ref<'a> {
pub unsafe fn from_raw(raw: core::ptr::NonNull<$ffi_type>) -> Self {
Self {
inner: core::mem::ManuallyDrop::new(unsafe { $wrapped_type::from_raw(raw) }),
_marker: core::marker::PhantomData,
}
}
}
};
}
#[allow(clippy::needless_pass_by_value)]
#[inline]
pub unsafe fn probe_len<T: PartialEq>(mut ptr: *const T, tail: T) -> usize {
for len in 0.. {
if unsafe { *ptr == tail } {
return len;
}
unsafe {
ptr = ptr.add(1);
}
}
usize::MAX
}
#[inline]
pub unsafe fn build_array<'a, T: PartialEq>(ptr: *const T, tail: T) -> Option<&'a [T]> {
if ptr.is_null() {
None
} else {
let len = unsafe { probe_len(ptr, tail) };
Some(unsafe { core::slice::from_raw_parts(ptr, len) })
}
}
#[cfg(test)]
#[allow(dead_code)]
mod tests {
use super::*;
#[repr(C)]
pub struct MockCStruct {
pub value: i32,
}
wrap_pure!((SafeHandle): MockCStruct);
wrap_ref!(SafeHandle, SafeHandleRef, MockCStruct);
impl Drop for SafeHandle {
fn drop(&mut self) {
}
}
#[test]
fn test_pointer_upgrade() {
let x = 42i32;
let ptr: *const i32 = &raw const x;
assert!(ptr.upgrade().is_some());
let null_ptr: *const i32 = core::ptr::null();
assert!(null_ptr.upgrade().is_none());
}
#[test]
fn test_ret_upgrade() {
let success: core::ffi::c_int = 0;
let failure: core::ffi::c_int = -1;
assert!(success.upgrade().is_ok());
assert!(failure.upgrade().is_err());
}
#[test]
fn test_wrapper_deref() {
let mut x = MockCStruct { value: 100 };
let ptr = NonNull::new(&raw mut x).expect("mutable reference is always non-null");
let handle = unsafe { SafeHandle::from_raw(ptr) };
assert_eq!(handle.value, 100);
let _ = handle.into_raw();
}
#[cfg(target_os = "windows")]
#[test]
fn test_windows_result_triage() {
let res: WindowsResult<i32, &'static str> = WindowsResult::Ok(42);
assert_eq!(res.process(), Ok(42));
let err: WindowsResult<i32, &'static str> = WindowsResult::Err("Windows API error: 0");
assert_eq!(err.process(), Err("Windows API error: 0"));
}
#[cfg(target_os = "windows")]
impl_from_integer_for_windows_result!(i32);
#[cfg(target_os = "windows")]
#[test]
fn test_windows_result_macro() {
let success: WindowsResult<i32, &'static str> = 42.into();
assert!(matches!(success, WindowsResult::Ok(42)));
let failure: WindowsResult<i32, &'static str> = 0.into();
assert!(matches!(failure, WindowsResult::Err(_)));
}
}