#![deny(clippy::unwrap_used)]
use anyhow::{bail, Result};
use std::{
cell::RefCell,
collections::HashMap,
ffi::{CStr, CString},
};
struct RawCStrs(RefCell<HashMap<String, *mut i8>>);
impl Drop for RawCStrs {
fn drop(&mut self) {
self.0.borrow_mut().iter_mut().for_each(|(_, c)| unsafe {
#[cfg(target_arch = "aarch64")]
drop(CString::from_raw((*c) as *mut u8));
#[cfg(not(target_arch = "aarch64"))]
drop(CString::from_raw(*c));
});
self.0.borrow_mut().clear();
}
}
thread_local! {
static RAW_CSTRS: RawCStrs = RawCStrs(RefCell::new(HashMap::new()));
}
pub fn raw_cstr<S>(str: S) -> Result<*mut i8>
where
S: AsRef<str>,
{
RAW_CSTRS.with(|rc| {
let mut raw_cstrs_map = rc.0.borrow_mut();
let saved = raw_cstrs_map.get(str.as_ref());
if let Some(saved) = saved {
Ok(*saved)
} else {
#[cfg(target_arch = "aarch64")]
let raw = CString::new(str.as_ref())?.into_raw() as *mut i8;
#[cfg(not(target_arch = "aarch64"))]
let raw = CString::new(str.as_ref())?.into_raw();
raw_cstrs_map.insert(str.as_ref().to_string(), raw);
Ok(raw)
}
})
}
pub trait AsRawCstr {
fn as_raw_cstr(&self) -> Result<*mut i8>;
}
impl AsRawCstr for &'static [u8] {
fn as_raw_cstr(&self) -> Result<*mut i8> {
if self.last().is_some_and(|l| *l == 0) {
Ok(self.as_ptr() as *const i8 as *mut i8)
} else {
bail!("Empty slice or last element is nonzero: {:?}", self);
}
}
}
impl AsRawCstr for *mut i8 {
fn as_raw_cstr(&self) -> Result<*mut i8> {
Ok(*self)
}
}
impl AsRawCstr for &str {
fn as_raw_cstr(&self) -> Result<*mut i8> {
raw_cstr(self)
}
}
impl AsRawCstr for String {
fn as_raw_cstr(&self) -> Result<*mut i8> {
raw_cstr(self)
}
}
impl AsRawCstr for CString {
fn as_raw_cstr(&self) -> Result<*mut i8> {
raw_cstr(self.to_str()?)
}
}
impl AsRawCstr for CStr {
fn as_raw_cstr(&self) -> Result<*mut i8> {
raw_cstr(self.to_str()?)
}
}
impl AsRawCstr for &'static CStr {
fn as_raw_cstr(&self) -> Result<*mut i8> {
return Ok(self.as_ptr() as *mut _);
}
}