pub(crate) mod int;
pub(crate) mod string;
pub(crate) mod transparent;
pub(crate) mod unknown;
#[allow(unused)]
#[cfg(test)]
use similar_asserts::assert_eq;
use std::{
ffi::{CStr, c_char},
fmt, ptr,
};
pub use self::{int::PositiveInt, unknown::UnknownVariant};
pub(crate) unsafe fn deref_ptr<T>(p: &*const T) -> Option<&T> {
if p.is_null() {
return None;
}
Some(unsafe { &**p })
}
pub(crate) unsafe fn deref_ptr_mut<T>(p: &*mut T) -> Option<&T> {
if p.is_null() {
return None;
}
Some(unsafe { &**p })
}
#[allow(unused)]
pub(crate) unsafe fn deref_mut_ptr<T>(p: &mut *mut T) -> Option<&mut T> {
if p.is_null() {
return None;
}
Some(unsafe { &mut **p })
}
pub(crate) unsafe fn deref_str(p: &*mut c_char) -> Option<&CStr> {
if p.is_null() {
return None;
}
Some(unsafe { CStr::from_ptr(*p) })
}
pub(crate) unsafe fn call_snprintf(
mut snprintf: impl FnMut(*mut c_char, usize) -> i32,
) -> Box<[c_char]> {
fn polymorphized(snprintf: &mut dyn FnMut(*mut c_char, usize) -> i32) -> Box<[c_char]> {
let len_i32 = snprintf(ptr::null_mut(), 0);
let len =
usize::try_from(len_i32).expect("Got invalid string length from an snprintf-like API");
let mut buf = vec![0; len + 1];
#[cfg(not(tarpaulin_include))]
assert_eq!(
snprintf(buf.as_mut_ptr(), buf.len()),
len_i32,
"Got inconsistent string length from an snprintf-like API"
);
#[cfg(not(tarpaulin_include))]
assert_eq!(
buf.last().copied(),
Some(0),
"Got non-NUL char at end of snprintf-like API output"
);
buf.into()
}
polymorphized(&mut snprintf)
}
pub(crate) unsafe fn write_snprintf(
f: &mut fmt::Formatter<'_>,
mut snprintf: impl FnMut(*mut c_char, usize) -> i32,
) -> fmt::Result {
fn polymorphized(
f: &mut fmt::Formatter<'_>,
snprintf: &mut dyn FnMut(*mut c_char, usize) -> i32,
) -> fmt::Result {
let buf = unsafe { call_snprintf(snprintf) };
let text = unsafe { CStr::from_ptr(buf.as_ptr()) }.to_string_lossy();
f.pad(&text)
}
polymorphized(f, &mut snprintf)
}
#[cfg(test)]
mod tests {
use super::*;
#[allow(unused)]
use similar_asserts::assert_eq;
use std::{ffi::CString, fmt::Debug};
#[test]
fn deref_ptr_like() {
assert_eq!(unsafe { deref_ptr(&ptr::null::<u16>()) }, None);
assert_eq!(unsafe { deref_ptr_mut(&ptr::null_mut::<u16>()) }, None);
let mut null_mut = ptr::null_mut::<u16>();
assert_eq!(unsafe { deref_mut_ptr(&mut null_mut) }, None);
let mut x = 42;
{
let p = &raw const x;
let r = unsafe { deref_ptr(&p).unwrap() };
assert!(ptr::eq(r, &raw const x));
}
{
let p_mut = &raw mut x;
let r = unsafe { deref_ptr_mut(&p_mut).unwrap() };
assert!(ptr::eq(r, &raw const x));
}
{
let mut p_mut = &raw mut x;
let r = unsafe { deref_mut_ptr(&mut p_mut).unwrap() };
assert!(ptr::eq(r, &raw const x));
}
}
#[test]
fn deref_str() {
assert_eq!(
unsafe { super::deref_str(&ptr::null_mut::<c_char>()) },
None
);
let s = CString::new("Hello world").unwrap();
let p = s.as_ptr().cast_mut();
let s2 = unsafe { super::deref_str(&p).unwrap() };
assert!(ptr::eq(s2.as_ptr(), p.cast_const()));
}
#[test]
fn snprintf_wrappers() {
const ORIGINAL: &str = "I'm sorry, Dave\0";
unsafe fn snprintf(buf: *mut c_char, len: usize) -> i32 {
assert!(!(buf.is_null() && len != 0));
isize::try_from(len).expect("Allocation is too large");
let original_bytes = ORIGINAL.as_bytes();
if !buf.is_null() {
let truncated_len = len.min(original_bytes.len());
let buf = unsafe { std::slice::from_raw_parts_mut(buf.cast::<u8>(), len) };
buf.copy_from_slice(&original_bytes[..truncated_len]);
}
i32::try_from(original_bytes.len() - 1).unwrap()
}
assert!(
unsafe { call_snprintf(|buf, len| snprintf(buf, len)) }
.iter()
.copied()
.eq(ORIGINAL.bytes().map(|b| c_char::try_from(b).unwrap()))
);
struct Harness;
impl Debug for Harness {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
unsafe { write_snprintf(f, |buf, len| snprintf(buf, len)) }
}
}
let x = format!("{Harness:?}");
assert_eq!(x, &ORIGINAL[..ORIGINAL.len() - 1]);
}
}