use core::{ffi::CStr, mem::MaybeUninit, ptr, slice};
#[cfg(feature = "std")]
use std::{
borrow::Cow,
ffi::{CString, OsStr, OsString},
os::unix::ffi::{OsStrExt, OsStringExt},
path::{Path, PathBuf},
};
pub trait AsCStr {
fn try_as_c_str<T, F>(self, f: F) -> crate::Result<T>
where
Self: Sized,
F: FnOnce(&CStr) -> crate::Result<T>;
}
impl AsCStr for &CStr {
#[inline]
fn try_as_c_str<T, F>(self, f: F) -> crate::Result<T>
where
F: FnOnce(&CStr) -> crate::Result<T>,
{
f(self)
}
}
#[cfg(feature = "std")]
impl AsCStr for &CString {
#[inline]
fn try_as_c_str<T, F>(self, f: F) -> crate::Result<T>
where
F: FnOnce(&CStr) -> crate::Result<T>,
{
f(self)
}
}
#[cfg(feature = "std")]
impl AsCStr for CString {
#[inline]
fn try_as_c_str<T, F>(self, f: F) -> crate::Result<T>
where
F: FnOnce(&CStr) -> crate::Result<T>,
{
f(&self)
}
}
impl AsCStr for &str {
#[inline]
fn try_as_c_str<T, F>(self, f: F) -> crate::Result<T>
where
F: FnOnce(&CStr) -> crate::Result<T>,
{
with_c_str(self.as_bytes(), f)
}
}
#[cfg(feature = "std")]
impl AsCStr for &String {
#[inline]
fn try_as_c_str<T, F>(self, f: F) -> crate::Result<T>
where
F: FnOnce(&CStr) -> crate::Result<T>,
{
with_c_str(self.as_bytes(), f)
}
}
#[cfg(feature = "std")]
impl AsCStr for String {
#[inline]
fn try_as_c_str<T, F>(self, f: F) -> crate::Result<T>
where
F: FnOnce(&CStr) -> crate::Result<T>,
{
f(&CString::new(self).map_err(|_| crate::Errno::INVAL)?)
}
}
#[cfg(feature = "std")]
impl AsCStr for &OsStr {
#[inline]
fn try_as_c_str<T, F>(self, f: F) -> crate::Result<T>
where
F: FnOnce(&CStr) -> crate::Result<T>,
{
with_c_str(self.as_bytes(), f)
}
}
#[cfg(feature = "std")]
impl AsCStr for &OsString {
#[inline]
fn try_as_c_str<T, F>(self, f: F) -> crate::Result<T>
where
F: FnOnce(&CStr) -> crate::Result<T>,
{
with_c_str(self.as_bytes(), f)
}
}
#[cfg(feature = "std")]
impl AsCStr for OsString {
#[inline]
fn try_as_c_str<T, F>(self, f: F) -> crate::Result<T>
where
F: FnOnce(&CStr) -> crate::Result<T>,
{
f(&CString::new(self.into_vec()).map_err(|_| crate::Errno::INVAL)?)
}
}
#[cfg(feature = "std")]
impl AsCStr for &Path {
#[inline]
fn try_as_c_str<T, F>(self, f: F) -> crate::Result<T>
where
F: FnOnce(&CStr) -> crate::Result<T>,
{
with_c_str(self.as_os_str().as_bytes(), f)
}
}
#[cfg(feature = "std")]
impl AsCStr for &PathBuf {
#[inline]
fn try_as_c_str<T, F>(self, f: F) -> crate::Result<T>
where
F: FnOnce(&CStr) -> crate::Result<T>,
{
with_c_str(self.as_os_str().as_bytes(), f)
}
}
#[cfg(feature = "std")]
impl AsCStr for PathBuf {
#[inline]
fn try_as_c_str<T, F>(self, f: F) -> crate::Result<T>
where
F: FnOnce(&CStr) -> crate::Result<T>,
{
f(&CString::new(self.into_os_string().into_vec()).map_err(|_| crate::Errno::INVAL)?)
}
}
impl AsCStr for &[u8] {
#[inline]
fn try_as_c_str<T, F>(self, f: F) -> crate::Result<T>
where
F: FnOnce(&CStr) -> crate::Result<T>,
{
with_c_str(self, f)
}
}
#[cfg(feature = "std")]
impl AsCStr for &Vec<u8> {
#[inline]
fn try_as_c_str<T, F>(self, f: F) -> crate::Result<T>
where
F: FnOnce(&CStr) -> crate::Result<T>,
{
with_c_str(self, f)
}
}
#[cfg(feature = "std")]
impl AsCStr for Vec<u8> {
#[inline]
fn try_as_c_str<T, F>(self, f: F) -> crate::Result<T>
where
F: FnOnce(&CStr) -> crate::Result<T>,
{
f(&CString::new(self).map_err(|_| crate::Errno::INVAL)?)
}
}
#[cfg(feature = "std")]
impl AsCStr for Cow<'_, CStr> {
#[inline]
fn try_as_c_str<T, F>(self, f: F) -> crate::Result<T>
where
F: FnOnce(&CStr) -> crate::Result<T>,
{
f(&self)
}
}
#[cfg(feature = "std")]
impl AsCStr for Cow<'_, str> {
#[inline]
fn try_as_c_str<T, F>(self, f: F) -> crate::Result<T>
where
F: FnOnce(&CStr) -> crate::Result<T>,
{
with_c_str(self.as_bytes(), f)
}
}
#[cfg(feature = "std")]
impl AsCStr for Cow<'_, OsStr> {
#[inline]
fn try_as_c_str<T, F>(self, f: F) -> crate::Result<T>
where
F: FnOnce(&CStr) -> crate::Result<T>,
{
with_c_str(self.as_bytes(), f)
}
}
#[inline]
fn with_c_str<T, F>(bytes: &[u8], f: F) -> crate::Result<T>
where
F: FnOnce(&CStr) -> crate::Result<T>,
{
const SMALL_PATH_BUFFER_SIZE: usize = 256;
if bytes.len() >= SMALL_PATH_BUFFER_SIZE {
return with_c_str_slow_path(bytes, f);
}
let mut buf = MaybeUninit::<[u8; SMALL_PATH_BUFFER_SIZE]>::uninit();
let buf_ptr = buf.as_mut_ptr().cast::<u8>();
debug_assert!(bytes.len() <= SMALL_PATH_BUFFER_SIZE);
unsafe {
ptr::copy_nonoverlapping(bytes.as_ptr(), buf_ptr, bytes.len());
buf_ptr.add(bytes.len()).write(b'\0');
}
match CStr::from_bytes_with_nul(unsafe { slice::from_raw_parts(buf_ptr, bytes.len() + 1) }) {
Ok(s) => f(s),
Err(_) => Err(crate::Errno::INVAL),
}
}
#[cold]
fn with_c_str_slow_path<T, F>(bytes: &[u8], f: F) -> crate::Result<T>
where
F: FnOnce(&CStr) -> crate::Result<T>,
{
#[cfg(feature = "std")]
{
f(&CString::new(bytes).map_err(|_cstr_err| crate::Errno::INVAL)?)
}
#[cfg(not(feature = "std"))]
{
const LARGE_PATH_BUFFER_SIZE: usize = linux_raw_sys::general::PATH_MAX as usize;
let mut buf = MaybeUninit::<[u8; LARGE_PATH_BUFFER_SIZE]>::uninit();
let buf_ptr = buf.as_mut_ptr().cast::<u8>();
if bytes.len() + 1 > LARGE_PATH_BUFFER_SIZE {
return Err(crate::Errno::NAMETOOLONG);
}
unsafe {
ptr::copy_nonoverlapping(bytes.as_ptr(), buf_ptr, bytes.len());
buf_ptr.add(bytes.len()).write(b'\0');
}
match CStr::from_bytes_with_nul(unsafe { slice::from_raw_parts(buf_ptr, bytes.len() + 1) })
{
Ok(s) => f(s),
Err(_) => Err(crate::Errno::INVAL),
}
}
}