#![cfg_attr(not(feature = "std"), no_std)]
#![warn(rust_2018_idioms)]
#![cfg_attr(feature = "nightly", feature(const_raw_ptr_deref))]
#![cfg_attr(feature = "nightly", feature(const_slice_from_raw_parts))]
#![cfg_attr(feature = "nightly", feature(const_str_from_utf8))]
#![cfg_attr(feature = "nightly", feature(const_eval_select))]
#[cfg(test)]
extern crate std;
#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(feature = "alloc")]
use alloc::borrow::{Borrow, Cow, ToOwned};
#[cfg(feature = "alloc")]
use alloc::boxed::Box;
#[cfg(feature = "alloc")]
use alloc::rc::Rc;
#[cfg(feature = "alloc")]
use alloc::string::String;
#[cfg(feature = "alloc")]
#[cfg(feature = "arc")]
use alloc::sync::Arc;
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
#[cfg(feature = "alloc")]
use core::{mem, ops, ptr};
use core::ascii;
use core::cmp::Ordering;
use core::fmt::{self, Write};
use core::slice;
use core::str::{self, Utf8Error};
pub use cty::c_char;
#[inline]
unsafe fn strlen(p: *const c_char) -> usize {
let mut n = 0;
while *p.offset(n as isize) != 0 {
n += 1;
}
n
}
#[cfg(feature = "nightly")]
const fn memchr_const(needle: u8, haystack: &[u8]) -> Option<usize> {
let mut i = 0;
loop {
i += 1;
if haystack.len() == i {
return None;
}
if haystack[i] == needle {
return Some(i);
}
}
}
#[inline]
#[cfg(feature = "nightly")]
const fn memchr(needle: u8, haystack: &[u8]) -> Option<usize> {
unsafe { core::intrinsics::const_eval_select((needle, haystack), memchr_const, memchr::memchr) }
}
#[cfg(not(feature = "nightly"))]
use memchr::memchr;
#[cfg(feature = "alloc")]
#[derive(PartialEq, PartialOrd, Eq, Ord, Hash, Clone)]
pub struct CString {
inner: Box<[u8]>,
}
#[derive(Hash)]
pub struct CStr {
inner: [c_char],
}
#[cfg(feature = "alloc")]
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct NulError(usize, Vec<u8>);
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct FromBytesWithNulError {
kind: FromBytesWithNulErrorKind,
}
#[derive(Clone, PartialEq, Eq, Debug)]
enum FromBytesWithNulErrorKind {
InteriorNul(usize),
NotNulTerminated,
}
impl FromBytesWithNulError {
const fn interior_nul(pos: usize) -> FromBytesWithNulError {
FromBytesWithNulError {
kind: FromBytesWithNulErrorKind::InteriorNul(pos),
}
}
const fn not_nul_terminated() -> FromBytesWithNulError {
FromBytesWithNulError {
kind: FromBytesWithNulErrorKind::NotNulTerminated,
}
}
}
#[cfg(feature = "alloc")]
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct IntoStringError {
inner: CString,
error: Utf8Error,
}
#[cfg(feature = "alloc")]
impl CString {
pub fn new<T: Into<Vec<u8>>>(t: T) -> Result<CString, NulError> {
Self::_new(t.into())
}
fn _new(bytes: Vec<u8>) -> Result<CString, NulError> {
match memchr::memchr(0, &bytes) {
Some(i) => Err(NulError(i, bytes)),
None => Ok(unsafe { CString::from_vec_unchecked(bytes) }),
}
}
pub unsafe fn from_vec_unchecked(mut v: Vec<u8>) -> CString {
v.reserve_exact(1);
v.push(0);
CString {
inner: v.into_boxed_slice(),
}
}
pub unsafe fn from_raw(ptr: *mut c_char) -> CString {
let len = strlen(ptr) + 1; let slice = slice::from_raw_parts_mut(ptr, len as usize);
CString {
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()) as *mut 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 { CString::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(0u8));
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) }
}
fn into_inner(self) -> Box<[u8]> {
let this = mem::ManuallyDrop::new(self);
unsafe { ptr::read(&this.inner) }
}
}
#[cfg(feature = "alloc")]
impl Drop for CString {
#[inline]
fn drop(&mut self) {
unsafe {
*self.inner.get_unchecked_mut(0) = 0;
}
}
}
#[cfg(feature = "alloc")]
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()) }
}
}
#[cfg(feature = "alloc")]
impl fmt::Debug for CString {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&**self, f)
}
}
#[cfg(feature = "alloc")]
impl From<CString> for Vec<u8> {
#[inline]
fn from(s: CString) -> Vec<u8> {
s.into_bytes()
}
}
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<'a> Default for &'a CStr {
fn default() -> &'a CStr {
const SLICE: &[c_char] = &[0];
unsafe { CStr::from_ptr(SLICE.as_ptr()) }
}
}
#[cfg(feature = "alloc")]
impl Default for CString {
fn default() -> CString {
let a: &CStr = Default::default();
a.to_owned()
}
}
#[cfg(feature = "alloc")]
impl Borrow<CStr> for CString {
#[inline]
fn borrow(&self) -> &CStr {
self
}
}
#[cfg(feature = "alloc")]
impl<'a> From<Cow<'a, CStr>> for CString {
#[inline]
fn from(s: Cow<'a, CStr>) -> Self {
s.into_owned()
}
}
#[cfg(feature = "alloc")]
impl From<&CStr> for Box<CStr> {
fn from(s: &CStr) -> Box<CStr> {
let boxed: Box<[u8]> = Box::from(s.to_bytes_with_nul());
unsafe { Box::from_raw(Box::into_raw(boxed) as *mut CStr) }
}
}
#[cfg(feature = "alloc")]
impl From<Box<CStr>> for CString {
#[inline]
fn from(s: Box<CStr>) -> CString {
s.into_c_string()
}
}
#[cfg(feature = "alloc")]
impl Clone for Box<CStr> {
#[inline]
fn clone(&self) -> Self {
(**self).into()
}
}
#[cfg(feature = "alloc")]
impl From<CString> for Box<CStr> {
#[inline]
fn from(s: CString) -> Box<CStr> {
s.into_boxed_c_str()
}
}
#[cfg(feature = "alloc")]
impl<'a> From<CString> for Cow<'a, CStr> {
#[inline]
fn from(s: CString) -> Cow<'a, CStr> {
Cow::Owned(s)
}
}
#[cfg(feature = "alloc")]
impl<'a> From<&'a CStr> for Cow<'a, CStr> {
#[inline]
fn from(s: &'a CStr) -> Cow<'a, CStr> {
Cow::Borrowed(s)
}
}
#[cfg(feature = "alloc")]
impl<'a> From<&'a CString> for Cow<'a, CStr> {
#[inline]
fn from(s: &'a CString) -> Cow<'a, CStr> {
Cow::Borrowed(s.as_c_str())
}
}
#[cfg(feature = "alloc")]
#[cfg(feature = "arc")]
impl From<CString> for Arc<CStr> {
#[inline]
fn from(s: CString) -> Arc<CStr> {
let arc: Arc<[u8]> = Arc::from(s.into_inner());
unsafe { Arc::from_raw(Arc::into_raw(arc) as *const CStr) }
}
}
#[cfg(feature = "alloc")]
#[cfg(feature = "arc")]
impl<'a> From<&'a CStr> for Arc<CStr> {
#[inline]
fn from(s: &CStr) -> Arc<CStr> {
let arc: Arc<[u8]> = Arc::from(s.to_bytes_with_nul());
unsafe { Arc::from_raw(Arc::into_raw(arc) as *const CStr) }
}
}
#[cfg(feature = "alloc")]
impl From<CString> for Rc<CStr> {
#[inline]
fn from(s: CString) -> Rc<CStr> {
let rc: Rc<[u8]> = Rc::from(s.into_inner());
unsafe { Rc::from_raw(Rc::into_raw(rc) as *const CStr) }
}
}
#[cfg(feature = "alloc")]
impl<'a> From<&'a CStr> for Rc<CStr> {
#[inline]
fn from(s: &CStr) -> Rc<CStr> {
let rc: Rc<[u8]> = Rc::from(s.to_bytes_with_nul());
unsafe { Rc::from_raw(Rc::into_raw(rc) as *const CStr) }
}
}
#[cfg(feature = "alloc")]
impl Default for Box<CStr> {
fn default() -> Box<CStr> {
let boxed: Box<[u8]> = Box::from([0]);
unsafe { Box::from_raw(Box::into_raw(boxed) as *mut CStr) }
}
}
#[cfg(feature = "std")]
use std::ffi::{CStr as StdCStr, CString as StdCString};
#[cfg(feature = "std")]
impl From<CString> for StdCString {
#[inline]
fn from(s: CString) -> StdCString {
unsafe { StdCString::from_vec_unchecked(s.into_bytes()) }
}
}
#[cfg(feature = "std")]
impl<'a> From<&'a CStr> for &'a StdCStr {
#[inline]
fn from(s: &'a CStr) -> &'a StdCStr {
s.as_ref()
}
}
#[cfg(feature = "std")]
impl From<StdCString> for CString {
#[inline]
fn from(s: StdCString) -> CString {
unsafe { CString::from_vec_unchecked(s.into_bytes()) }
}
}
#[cfg(feature = "std")]
impl<'a> From<&'a StdCStr> for &'a CStr {
#[inline]
fn from(s: &'a StdCStr) -> &'a CStr {
unsafe { CStr::from_bytes_with_nul_unchecked(s.to_bytes_with_nul()) }
}
}
#[cfg(feature = "std")]
impl AsRef<StdCStr> for CString {
#[inline]
fn as_ref(&self) -> &StdCStr {
AsRef::<CStr>::as_ref(self).as_ref()
}
}
#[cfg(feature = "std")]
impl Borrow<StdCStr> for CString {
#[inline]
fn borrow(&self) -> &StdCStr {
self.as_ref()
}
}
#[cfg(feature = "std")]
impl AsRef<StdCStr> for CStr {
#[inline]
fn as_ref(&self) -> &StdCStr {
unsafe { StdCStr::from_bytes_with_nul_unchecked(self.to_bytes_with_nul()) }
}
}
#[cfg(feature = "alloc")]
impl NulError {
pub fn nul_position(&self) -> usize {
self.0
}
pub fn into_vec(self) -> Vec<u8> {
self.1
}
}
#[cfg(feature = "alloc")]
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 FromBytesWithNulError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let description = match self.kind {
FromBytesWithNulErrorKind::InteriorNul(..) => {
"data provided contains an interior nul byte"
}
FromBytesWithNulErrorKind::NotNulTerminated => "data provided is not nul terminated",
};
f.write_str(description)?;
if let FromBytesWithNulErrorKind::InteriorNul(pos) = self.kind {
write!(f, " at byte pos {}", pos)?;
}
Ok(())
}
}
#[cfg(feature = "alloc")]
impl IntoStringError {
pub fn into_cstring(self) -> CString {
self.inner
}
pub fn utf8_error(&self) -> Utf8Error {
self.error
}
}
#[cfg(feature = "alloc")]
impl fmt::Display for IntoStringError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
"C string contained non-utf8 bytes".fmt(f)
}
}
impl CStr {
pub unsafe fn from_ptr<'a>(ptr: *const c_char) -> &'a CStr {
let len = strlen(ptr);
let ptr = ptr as *const u8;
CStr::from_bytes_with_nul_unchecked(slice::from_raw_parts(ptr, len as usize + 1))
}
#[cfg(feature = "nightly")]
pub const fn from_bytes_with_nul(bytes: &[u8]) -> Result<&CStr, FromBytesWithNulError> {
let nul_pos = 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 { CStr::from_bytes_with_nul_unchecked(bytes) })
} else {
Err(FromBytesWithNulError::not_nul_terminated())
}
}
#[cfg(not(feature = "nightly"))]
pub fn from_bytes_with_nul(bytes: &[u8]) -> Result<&CStr, FromBytesWithNulError> {
let nul_pos = 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 { CStr::from_bytes_with_nul_unchecked(bytes) })
} else {
Err(FromBytesWithNulError::not_nul_terminated())
}
}
#[cfg(feature = "nightly")]
#[inline]
pub const unsafe fn from_bytes_with_nul_unchecked(bytes: &[u8]) -> &CStr {
&*(bytes as *const [u8] as *const CStr)
}
#[cfg(not(feature = "nightly"))]
#[inline]
pub unsafe fn from_bytes_with_nul_unchecked(bytes: &[u8]) -> &CStr {
&*(bytes as *const [u8] as *const CStr)
}
#[inline]
pub const fn as_ptr(&self) -> *const c_char {
self.inner.as_ptr()
}
#[inline]
#[cfg(not(feature = "nightly"))]
pub fn to_bytes(&self) -> &[u8] {
let bytes = self.to_bytes_with_nul();
&bytes[..bytes.len() - 1]
}
#[inline]
#[cfg(feature = "nightly")]
pub const fn to_bytes(&self) -> &[u8] {
let bytes = self.to_bytes_with_nul();
unsafe {
slice::from_raw_parts(bytes.as_ptr(), bytes.len() - 1)
}
}
#[inline]
#[cfg(feature = "nightly")]
pub const fn to_bytes_with_nul(&self) -> &[u8] {
unsafe { &*(&self.inner as *const [c_char] as *const [u8]) }
}
#[inline]
#[cfg(not(feature = "nightly"))]
pub fn to_bytes_with_nul(&self) -> &[u8] {
unsafe { &*(&self.inner as *const [c_char] as *const [u8]) }
}
#[cfg(not(feature = "nightly"))]
pub fn to_str(&self) -> Result<&str, Utf8Error> {
str::from_utf8(self.to_bytes())
}
#[cfg(feature = "nightly")]
pub const fn to_str(&self) -> Result<&str, Utf8Error> {
str::from_utf8(self.to_bytes())
}
#[cfg(feature = "alloc")]
pub fn to_string_lossy(&self) -> Cow<'_, str> {
String::from_utf8_lossy(self.to_bytes())
}
#[cfg(feature = "alloc")]
pub fn into_c_string(self: Box<CStr>) -> CString {
let raw = Box::into_raw(self) as *mut [u8];
CString {
inner: unsafe { Box::from_raw(raw) },
}
}
}
impl PartialEq for CStr {
fn eq(&self, other: &CStr) -> bool {
self.to_bytes().eq(other.to_bytes())
}
}
impl Eq for CStr {}
impl PartialOrd for CStr {
fn partial_cmp(&self, other: &CStr) -> Option<Ordering> {
self.to_bytes().partial_cmp(&other.to_bytes())
}
}
impl Ord for CStr {
fn cmp(&self, other: &CStr) -> Ordering {
self.to_bytes().cmp(&other.to_bytes())
}
}
#[cfg(feature = "alloc")]
impl ToOwned for CStr {
type Owned = CString;
fn to_owned(&self) -> CString {
CString {
inner: self.to_bytes_with_nul().into(),
}
}
}
#[cfg(feature = "alloc")]
impl From<&CStr> for CString {
fn from(s: &CStr) -> CString {
s.to_owned()
}
}
#[cfg(feature = "alloc")]
impl ops::Index<ops::RangeFull> for CString {
type Output = CStr;
#[inline]
fn index(&self, _index: ops::RangeFull) -> &CStr {
self
}
}
impl AsRef<CStr> for CStr {
#[inline]
fn as_ref(&self) -> &CStr {
self
}
}
#[cfg(feature = "alloc")]
impl AsRef<CStr> for CString {
#[inline]
fn as_ref(&self) -> &CStr {
self
}
}
#[inline]
#[doc(hidden)]
pub const fn bytes_are_valid(bytes: &[u8]) -> bool {
if bytes.len() == 0 || bytes[bytes.len() - 1] != 0 {
return false;
}
let mut index = 0;
while index < bytes.len() - 1 {
if bytes[index] == 0 {
return false;
}
index += 1;
}
true
}
#[macro_export]
macro_rules! cstr {
($e:expr) => {{
const STR: &[u8] = concat!($e, "\0").as_bytes();
const STR_VALID: bool = $crate::bytes_are_valid(STR);
let _ = [(); 0 - (!(STR_VALID) as usize)];
unsafe {
$crate::CStr::from_bytes_with_nul_unchecked(STR)
}
}}
}
#[cfg(test)]
mod tests {
use super::*;
use std::borrow::Cow::{Borrowed, Owned};
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use std::{format, vec};
#[test]
fn c_to_rust() {
let data = b"123\0";
let ptr = data.as_ptr() as *const c_char;
unsafe {
assert_eq!(CStr::from_ptr(ptr).to_bytes(), b"123");
assert_eq!(CStr::from_ptr(ptr).to_bytes_with_nul(), b"123\0");
}
}
#[test]
fn simple() {
let s = CString::new("1234").unwrap();
assert_eq!(s.as_bytes(), b"1234");
assert_eq!(s.as_bytes_with_nul(), b"1234\0");
}
#[test]
fn build_with_zero1() {
assert!(CString::new(&b"\0"[..]).is_err());
}
#[test]
fn build_with_zero2() {
assert!(CString::new(vec![0]).is_err());
}
#[test]
fn build_with_zero3() {
unsafe {
let s = CString::from_vec_unchecked(vec![0]);
assert_eq!(s.as_bytes(), b"\0");
}
}
#[test]
fn formatted() {
let s = CString::new(&b"abc\x01\x02\n\xE2\x80\xA6\xFF"[..]).unwrap();
assert_eq!(format!("{:?}", s), r#""abc\x01\x02\n\xe2\x80\xa6\xff""#);
}
#[test]
fn borrowed() {
unsafe {
let s = CStr::from_ptr(b"12\0".as_ptr() as *const _);
assert_eq!(s.to_bytes(), b"12");
assert_eq!(s.to_bytes_with_nul(), b"12\0");
}
}
#[test]
fn to_str() {
let data = b"123\xE2\x80\xA6\0";
let ptr = data.as_ptr() as *const c_char;
unsafe {
assert_eq!(CStr::from_ptr(ptr).to_str(), Ok("123…"));
assert_eq!(CStr::from_ptr(ptr).to_string_lossy(), Borrowed("123…"));
}
let data = b"123\xE2\0";
let ptr = data.as_ptr() as *const c_char;
unsafe {
assert!(CStr::from_ptr(ptr).to_str().is_err());
assert_eq!(
CStr::from_ptr(ptr).to_string_lossy(),
Owned::<str>(format!("123\u{FFFD}"))
);
}
}
#[test]
fn to_owned() {
let data = b"123\0";
let ptr = data.as_ptr() as *const c_char;
let owned = unsafe { CStr::from_ptr(ptr).to_owned() };
assert_eq!(owned.as_bytes_with_nul(), data);
}
#[test]
fn equal_hash() {
let data = b"123\xE2\xFA\xA6\0";
let ptr = data.as_ptr() as *const c_char;
let cstr: &'static CStr = unsafe { CStr::from_ptr(ptr) };
let mut s = DefaultHasher::new();
cstr.hash(&mut s);
let cstr_hash = s.finish();
let mut s = DefaultHasher::new();
CString::new(&data[..data.len() - 1]).unwrap().hash(&mut s);
let cstring_hash = s.finish();
assert_eq!(cstr_hash, cstring_hash);
}
#[test]
fn from_bytes_with_nul() {
let data = b"123\0";
let cstr = CStr::from_bytes_with_nul(data);
assert_eq!(cstr.map(CStr::to_bytes), Ok(&b"123"[..]));
let cstr = CStr::from_bytes_with_nul(data);
assert_eq!(cstr.map(CStr::to_bytes_with_nul), Ok(&b"123\0"[..]));
unsafe {
let cstr = CStr::from_bytes_with_nul(data);
let cstr_unchecked = CStr::from_bytes_with_nul_unchecked(data);
assert_eq!(cstr, Ok(cstr_unchecked));
}
}
#[test]
fn from_bytes_with_nul_unterminated() {
let data = b"123";
let cstr = CStr::from_bytes_with_nul(data);
assert!(cstr.is_err());
}
#[test]
fn from_bytes_with_nul_interior() {
let data = b"1\023\0";
let cstr = CStr::from_bytes_with_nul(data);
assert!(cstr.is_err());
}
#[test]
fn into_boxed() {
let orig: &[u8] = b"Hello, world!\0";
let cstr = CStr::from_bytes_with_nul(orig).unwrap();
let boxed: Box<CStr> = Box::from(cstr);
let cstring = cstr.to_owned().into_boxed_c_str().into_c_string();
assert_eq!(cstr, &*boxed);
assert_eq!(&*boxed, &*cstring);
assert_eq!(&*cstring, cstr);
}
#[test]
fn boxed_default() {
let boxed = <Box<CStr>>::default();
assert_eq!(boxed.to_bytes_with_nul(), &[0]);
}
#[test]
#[cfg(feature = "alloc")]
#[cfg(feature = "arc")]
fn into_rc() {
let orig: &[u8] = b"Hello, world!\0";
let cstr = CStr::from_bytes_with_nul(orig).unwrap();
let rc: Rc<CStr> = Rc::from(cstr);
let arc: Arc<CStr> = Arc::from(cstr);
assert_eq!(&*rc, cstr);
assert_eq!(&*arc, cstr);
let rc2: Rc<CStr> = Rc::from(cstr.to_owned());
let arc2: Arc<CStr> = Arc::from(cstr.to_owned());
assert_eq!(&*rc2, cstr);
assert_eq!(&*arc2, cstr);
}
#[test]
#[cfg(feature = "nightly")]
fn const_cstr() {
const TESTING_CSTR: &CStr =
unsafe { CStr::from_bytes_with_nul_unchecked(b"Hello world!\0") };
let _ = TESTING_CSTR.as_ptr();
}
}