#![allow(clippy::missing_safety_doc)]
use crate::ffi::{c_char, libc::strlen, CStr};
use alloc::borrow::{Borrow, Cow};
use core::{
fmt, mem, ops, ptr,
slice::{self, memchr},
str::Utf8Error,
};
#[derive(PartialEq, PartialOrd, Eq, Ord, Hash, Clone)]
pub struct CString {
pub(super) inner: Box<[u8]>,
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct NulError(usize, Vec<u8>);
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct IntoStringError {
inner: CString,
error: Utf8Error,
}
impl CString {
pub fn new<T: Into<Vec<u8>>>(t: T) -> Result<Self, NulError> {
Self::_new(t.into())
}
fn _new(bytes: Vec<u8>) -> Result<Self, NulError> {
match memchr::memchr(0, &bytes) {
Some(i) => Err(NulError(i, bytes)),
None => Ok(unsafe { Self::from_vec_unchecked(bytes) }),
}
}
pub unsafe fn from_vec_unchecked(mut v: Vec<u8>) -> Self {
v.reserve_exact(1);
v.push(0);
Self { inner: v.into_boxed_slice() }
}
pub unsafe fn from_raw(ptr: *mut c_char) -> Self {
unsafe {
let len = strlen(ptr) + 1; let slice = slice::from_raw_parts_mut(ptr, len as usize);
Self { inner: Box::from_raw(slice as *mut [c_char] as *mut [u8]) }
}
}
#[inline]
pub fn into_raw(self) -> *mut c_char {
Box::into_raw(self.into_inner()).cast::<c_char>()
}
pub fn into_string(self) -> Result<String, IntoStringError> {
String::from_utf8(self.into_bytes()).map_err(|e| IntoStringError {
error: e.utf8_error(),
inner: unsafe { Self::from_vec_unchecked(e.into_bytes()) },
})
}
pub fn into_bytes(self) -> Vec<u8> {
let mut vec = self.into_inner().into_vec();
let nul = vec.pop();
debug_assert_eq!(nul, Some(0_u8));
vec
}
pub fn into_bytes_with_nul(self) -> Vec<u8> {
self.into_inner().into_vec()
}
#[inline]
pub fn as_bytes(&self) -> &[u8] {
&self.inner[..self.inner.len() - 1]
}
#[inline]
pub fn as_bytes_with_nul(&self) -> &[u8] {
&self.inner
}
#[inline]
pub fn as_c_str(&self) -> &CStr {
&*self
}
pub fn into_boxed_c_str(self) -> Box<CStr> {
unsafe { Box::from_raw(Box::into_raw(self.into_inner()) as *mut CStr) }
}
pub(super) fn into_inner(self) -> Box<[u8]> {
let this = mem::ManuallyDrop::new(self);
unsafe { ptr::read(&this.inner) }
}
}
impl NulError {
pub fn nul_position(&self) -> usize {
self.0
}
pub fn into_vec(self) -> Vec<u8> {
self.1
}
}
impl IntoStringError {
pub fn into_cstring(self) -> CString {
self.inner
}
pub fn utf8_error(&self) -> Utf8Error {
self.error
}
}
impl Drop for CString {
#[inline]
fn drop(&mut self) {
unsafe { *self.inner.get_unchecked_mut(0) = 0 };
}
}
impl ops::Deref for CString {
type Target = CStr;
#[inline]
fn deref(&self) -> &CStr {
unsafe { CStr::from_bytes_with_nul_unchecked(self.as_bytes_with_nul()) }
}
}
impl fmt::Debug for CString {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&**self, f)
}
}
impl From<CString> for Vec<u8> {
#[inline]
fn from(s: CString) -> Self {
s.into_bytes()
}
}
impl Default for CString {
fn default() -> Self {
let a: &CStr = Default::default();
a.to_owned()
}
}
impl Borrow<CStr> for CString {
#[inline]
fn borrow(&self) -> &CStr {
self
}
}
impl From<&CStr> for CString {
fn from(s: &CStr) -> Self {
s.to_owned()
}
}
impl<'a> From<Cow<'a, CStr>> for CString {
#[inline]
fn from(s: Cow<'a, CStr>) -> Self {
s.into_owned()
}
}
impl From<Box<CStr>> for CString {
#[inline]
fn from(s: Box<CStr>) -> Self {
s.into_c_string()
}
}
impl ops::Index<ops::RangeFull> for CString {
type Output = CStr;
#[inline]
fn index(&self, _index: ops::RangeFull) -> &CStr {
self
}
}
impl AsRef<CStr> for CString {
#[inline]
fn as_ref(&self) -> &CStr {
self
}
}
impl fmt::Display for NulError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "nul byte found in provided data at position: {}", self.0)
}
}
impl fmt::Display for IntoStringError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "C string contained non-utf8 bytes")
}
}