#![allow(clippy::macro_metavars_in_unsafe)]
use std::ffi::{CStr, CString};
use std::os::raw::c_char;
#[inline]
pub fn cstr_to_str(ptr: *const c_char) -> &'static str {
if ptr.is_null() {
return "";
}
unsafe { CStr::from_ptr(ptr) }.to_str().unwrap_or("")
}
#[inline]
pub fn cstr_to_string(ptr: *const c_char) -> String {
if ptr.is_null() {
return String::new();
}
unsafe { CStr::from_ptr(ptr) }
.to_str()
.map(|s| s.to_string())
.unwrap_or_default()
}
#[inline]
pub fn cstr_to_option_str(ptr: *const c_char) -> Option<&'static str> {
if ptr.is_null() {
return None;
}
Some(unsafe { CStr::from_ptr(ptr) }.to_str().unwrap_or(""))
}
#[inline]
pub fn str_to_cstring(s: &str) -> Option<CString> {
CString::new(s).ok()
}
#[inline]
pub fn raw_to_slice<'a>(ptr: *const u8, len: usize) -> &'a [u8] {
if ptr.is_null() || len == 0 {
return &[];
}
unsafe { std::slice::from_raw_parts(ptr, len) }
}
#[inline]
pub fn raw_to_slice_mut<'a>(ptr: *mut u8, len: usize) -> &'a mut [u8] {
if ptr.is_null() || len == 0 {
return &mut [];
}
unsafe { std::slice::from_raw_parts_mut(ptr, len) }
}
#[inline]
pub fn raw_to_f32_slice<'a>(ptr: *const f32, count: usize) -> &'a [f32] {
if ptr.is_null() || count == 0 {
return &[];
}
unsafe { std::slice::from_raw_parts(ptr, count) }
}
#[inline]
pub fn raw_to_vec(ptr: *const u8, len: usize) -> Vec<u8> {
if ptr.is_null() || len == 0 {
return Vec::new();
}
unsafe { std::slice::from_raw_parts(ptr, len) }.to_vec()
}
#[inline]
pub fn raw_to_f32_vec(ptr: *const f32, count: usize) -> Vec<f32> {
if ptr.is_null() || count == 0 {
return Vec::new();
}
unsafe { std::slice::from_raw_parts(ptr, count) }.to_vec()
}
#[inline]
pub fn write_out<T>(ptr: *mut T, value: T) {
if ptr.is_null() {
return;
}
unsafe { *ptr = value };
}
#[inline]
pub fn try_write_out<T>(ptr: *mut T, value: T) -> bool {
if ptr.is_null() {
return false;
}
unsafe { *ptr = value };
true
}
#[inline]
pub fn write_bytes(dst: *mut u8, src: &[u8]) {
if dst.is_null() || src.is_empty() {
return;
}
unsafe {
std::ptr::copy_nonoverlapping(src.as_ptr(), dst, src.len());
}
}
#[inline]
pub const fn is_valid_handle(handle: u64) -> bool {
handle != 0
}
#[inline]
pub fn handle_to_index(handle: u64) -> Option<usize> {
if handle == 0 {
None
} else {
Some(handle as usize)
}
}
#[macro_export]
macro_rules! cstr_safe {
($ptr:expr) => {{
if $ptr.is_null() {
""
} else {
unsafe { std::ffi::CStr::from_ptr($ptr) }
.to_str()
.unwrap_or("")
}
}};
}
#[macro_export]
macro_rules! slice_safe {
($ptr:expr, $len:expr) => {{
if $ptr.is_null() || $len == 0 {
&[]
} else {
unsafe { std::slice::from_raw_parts($ptr, $len as usize) }
}
}};
}
#[macro_export]
macro_rules! write_safe {
($ptr:expr, $value:expr) => {{
if !$ptr.is_null() {
unsafe { *$ptr = $value };
}
}};
}
#[cfg(test)]
mod tests {
use super::*;
use std::ffi::CString;
#[test]
fn test_cstr_to_str_null() {
assert_eq!(cstr_to_str(std::ptr::null()), "");
}
#[test]
fn test_cstr_to_str_valid() {
let s = CString::new("hello").unwrap();
assert_eq!(cstr_to_str(s.as_ptr()), "hello");
}
#[test]
fn test_cstr_to_string_null() {
assert_eq!(cstr_to_string(std::ptr::null()), "");
}
#[test]
fn test_raw_to_slice_null() {
let slice: &[u8] = raw_to_slice(std::ptr::null(), 10);
assert!(slice.is_empty());
}
#[test]
fn test_raw_to_slice_valid() {
let data = [1u8, 2, 3, 4, 5];
let slice = raw_to_slice(data.as_ptr(), data.len());
assert_eq!(slice, &data);
}
#[test]
fn test_raw_to_vec_null() {
let vec = raw_to_vec(std::ptr::null(), 10);
assert!(vec.is_empty());
}
#[test]
fn test_write_out_null() {
write_out(std::ptr::null_mut::<i32>(), 42);
}
#[test]
fn test_write_out_valid() {
let mut value = 0i32;
write_out(&mut value as *mut i32, 42);
assert_eq!(value, 42);
}
#[test]
fn test_is_valid_handle() {
assert!(!is_valid_handle(0));
assert!(is_valid_handle(1));
assert!(is_valid_handle(u64::MAX));
}
#[test]
fn test_cstr_safe_macro_via_function() {
assert_eq!(cstr_to_str(std::ptr::null::<i8>()), "");
let s = CString::new("test").unwrap();
assert_eq!(cstr_to_str(s.as_ptr()), "test");
}
#[test]
fn test_slice_safe_macro_via_function() {
let slice: &[u8] = raw_to_slice(std::ptr::null(), 3);
assert!(slice.is_empty());
let data = [1u8, 2, 3];
let slice = raw_to_slice(data.as_ptr(), 3);
assert_eq!(slice, &data);
let empty_slice: &[u8] = raw_to_slice(data.as_ptr(), 0);
assert!(empty_slice.is_empty());
}
#[test]
fn test_write_safe_macro_via_function() {
write_out(std::ptr::null_mut::<i32>(), 99);
let mut value = 0i32;
write_out(&mut value as *mut i32, 99);
assert_eq!(value, 99);
}
}