#![allow(clippy::missing_safety_doc)]
use crate::ffi::{c_char, libc::strlen, CString};
use alloc::{borrow::Cow, rc::Rc, sync::Arc};
use core::{
ascii,
cmp::Ordering,
fmt::{self, Write},
slice::{self, memchr},
str,
};
#[allow(clippy::derive_hash_xor_eq)]
#[derive(Hash)]
pub struct CStr {
inner: [c_char],
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct FromBytesWithNulError {
kind: FromBytesWithNulErrorKind,
}
#[derive(Clone, PartialEq, Eq, Debug)]
enum FromBytesWithNulErrorKind {
InteriorNul(usize),
NotNulTerminated,
}
impl CStr {
pub unsafe fn from_ptr<'a>(ptr: *const c_char) -> &'a Self {
unsafe {
let len = strlen(ptr);
let ptr = ptr.cast::<u8>();
Self::from_bytes_with_nul_unchecked(slice::from_raw_parts(ptr, len as usize + 1))
}
}
pub fn from_bytes_with_nul(bytes: &[u8]) -> Result<&Self, FromBytesWithNulError> {
let nul_pos = memchr::memchr(0, bytes);
if let Some(nul_pos) = nul_pos {
if nul_pos + 1 != bytes.len() {
return Err(FromBytesWithNulError::interior_nul(nul_pos));
}
Ok(unsafe { Self::from_bytes_with_nul_unchecked(bytes) })
} else {
Err(FromBytesWithNulError::not_nul_terminated())
}
}
#[allow(unsafe_op_in_unsafe_fn)] #[inline]
pub const unsafe fn from_bytes_with_nul_unchecked(bytes: &[u8]) -> &Self {
&*(bytes as *const [u8] as *const Self)
}
#[inline]
pub const fn as_ptr(&self) -> *const c_char {
self.inner.as_ptr()
}
#[inline]
pub fn to_bytes(&self) -> &[u8] {
let bytes = self.to_bytes_with_nul();
&bytes[..bytes.len() - 1]
}
#[inline]
pub fn to_bytes_with_nul(&self) -> &[u8] {
unsafe { &*(&self.inner as *const [c_char] as *const [u8]) }
}
pub fn to_str(&self) -> Result<&str, str::Utf8Error> {
str::from_utf8(self.to_bytes())
}
pub fn to_string_lossy(&self) -> Cow<'_, str> {
String::from_utf8_lossy(self.to_bytes())
}
#[allow(clippy::wrong_self_convention)]
pub fn into_c_string(self: Box<Self>) -> CString {
let raw = Box::into_raw(self) as *mut [u8];
CString { inner: unsafe { Box::from_raw(raw) } }
}
}
impl FromBytesWithNulError {
fn interior_nul(pos: usize) -> Self {
Self { kind: FromBytesWithNulErrorKind::InteriorNul(pos) }
}
fn not_nul_terminated() -> Self {
Self { kind: FromBytesWithNulErrorKind::NotNulTerminated }
}
}
impl fmt::Debug for CStr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "\"")?;
for byte in self.to_bytes().iter().flat_map(|&b| ascii::escape_default(b)) {
f.write_char(byte as char)?;
}
write!(f, "\"")
}
}
impl Default for &CStr {
fn default() -> Self {
const SLICE: &[c_char] = &[0];
unsafe { CStr::from_ptr(SLICE.as_ptr()) }
}
}
impl PartialEq for CStr {
fn eq(&self, other: &Self) -> bool {
self.to_bytes().eq(other.to_bytes())
}
}
impl Eq for CStr {}
impl PartialOrd for CStr {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.to_bytes().partial_cmp(&other.to_bytes())
}
}
impl Ord for CStr {
fn cmp(&self, other: &Self) -> Ordering {
self.to_bytes().cmp(&other.to_bytes())
}
}
impl ToOwned for CStr {
type Owned = CString;
fn to_owned(&self) -> CString {
CString { inner: self.to_bytes_with_nul().into() }
}
}
impl AsRef<CStr> for CStr {
#[inline]
fn as_ref(&self) -> &Self {
self
}
}
impl Default for Box<CStr> {
fn default() -> Self {
let boxed: Box<[u8]> = Box::from([0]);
unsafe { Box::from_raw(Box::into_raw(boxed) as *mut CStr) }
}
}
impl Clone for Box<CStr> {
#[inline]
fn clone(&self) -> Self {
(**self).into()
}
}
impl From<&CStr> for Box<CStr> {
fn from(s: &CStr) -> Self {
let boxed: Box<[u8]> = Box::from(s.to_bytes_with_nul());
unsafe { Box::from_raw(Box::into_raw(boxed) as *mut CStr) }
}
}
impl From<CString> for Box<CStr> {
#[inline]
fn from(s: CString) -> Self {
s.into_boxed_c_str()
}
}
impl From<CString> for Arc<CStr> {
#[inline]
fn from(s: CString) -> Self {
let arc: Arc<[u8]> = Arc::from(s.into_inner());
unsafe { Arc::from_raw(Arc::into_raw(arc) as *const CStr) }
}
}
impl From<&CStr> for Arc<CStr> {
#[inline]
fn from(s: &CStr) -> Self {
let arc: Arc<[u8]> = Arc::from(s.to_bytes_with_nul());
unsafe { Arc::from_raw(Arc::into_raw(arc) as *const CStr) }
}
}
impl From<CString> for Rc<CStr> {
#[inline]
fn from(s: CString) -> Self {
let rc: Rc<[u8]> = Rc::from(s.into_inner());
unsafe { Rc::from_raw(Rc::into_raw(rc) as *const CStr) }
}
}
impl From<&CStr> for Rc<CStr> {
#[inline]
fn from(s: &CStr) -> Self {
let rc: Rc<[u8]> = Rc::from(s.to_bytes_with_nul());
unsafe { Rc::from_raw(Rc::into_raw(rc) as *const CStr) }
}
}
impl<'a> From<CString> for Cow<'a, CStr> {
#[inline]
fn from(s: CString) -> Self {
Cow::Owned(s)
}
}
impl<'a> From<&'a CStr> for Cow<'a, CStr> {
#[inline]
fn from(s: &'a CStr) -> Self {
Cow::Borrowed(s)
}
}
impl<'a> From<&'a CString> for Cow<'a, CStr> {
#[inline]
fn from(s: &'a CString) -> Self {
Cow::Borrowed(s.as_c_str())
}
}
impl fmt::Display for FromBytesWithNulError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.kind)
}
}
impl fmt::Display for FromBytesWithNulErrorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
FromBytesWithNulErrorKind::InteriorNul(pos) => {
write!(f, "data provided contains an interior nul byte at byte pos {}", pos)
}
FromBytesWithNulErrorKind::NotNulTerminated => {
write!(f, "data provided is not nul terminated")
}
}
}
}