#[cfg(test)]
#[macro_use]
extern crate std;
#[cfg(feature = "alloc")]
extern crate alloc;
extern crate cty;
extern crate memchr;
#[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")]
use alloc::sync::Arc;
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
#[cfg(feature = "alloc")]
use core::{mem, ops, ptr};
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
}
mod ascii {
use core::ops::Range;
pub struct EscapeDefault {
range: Range<usize>,
data: [u8; 4],
}
pub fn escape_default(c: u8) -> EscapeDefault {
let (data, len) = match c {
b'\t' => ([b'\\', b't', 0, 0], 2),
b'\r' => ([b'\\', b'r', 0, 0], 2),
b'\n' => ([b'\\', b'n', 0, 0], 2),
b'\\' => ([b'\\', b'\\', 0, 0], 2),
b'\'' => ([b'\\', b'\'', 0, 0], 2),
b'"' => ([b'\\', b'"', 0, 0], 2),
b'\x20'..=b'\x7e' => ([c, 0, 0, 0], 1),
_ => ([b'\\', b'x', hexify(c >> 4), hexify(c & 0xf)], 4),
};
return EscapeDefault {
range: (0..len),
data: data,
};
fn hexify(b: u8) -> u8 {
match b {
0..=9 => b'0' + b,
_ => b'a' + b - 10,
}
}
}
impl Iterator for EscapeDefault {
type Item = u8;
fn next(&mut self) -> Option<u8> {
self.range.next().map(|i| self.data[i])
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.range.size_hint()
}
}
impl DoubleEndedIterator for EscapeDefault {
fn next_back(&mut self) -> Option<u8> {
self.range.next_back().map(|i| self.data[i])
}
}
impl ExactSizeIterator for EscapeDefault {}
}
#[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 {
fn interior_nul(pos: usize) -> FromBytesWithNulError {
FromBytesWithNulError {
kind: FromBytesWithNulErrorKind::InteriorNul(pos),
}
}
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]> {
unsafe {
let result = ptr::read(&self.inner);
mem::forget(self);
result
}
}
}
#[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: &'static [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<&'a CStr> for Box<CStr> {
fn from(s: &'a 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 From<CString> for Box<CStr> {
#[inline]
fn from(s: CString) -> Box<CStr> {
s.into_boxed_c_str()
}
}
#[cfg(feature = "alloc")]
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")]
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 = "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 {
match self.kind {
FromBytesWithNulErrorKind::InteriorNul(..) => {
f.write_str("data provided contains an interior nul byte")?
}
FromBytesWithNulErrorKind::NotNulTerminated => {
f.write_str("data provided is not nul terminated")?
}
}
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 {
write!(f, "C string contained non-utf8 bytes")
}
}
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))
}
pub fn from_bytes_with_nul(bytes: &[u8]) -> Result<&CStr, 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 { CStr::from_bytes_with_nul_unchecked(bytes) })
} else {
Err(FromBytesWithNulError::not_nul_terminated())
}
}
#[inline]
pub unsafe fn from_bytes_with_nul_unchecked(bytes: &[u8]) -> &CStr {
&*(bytes as *const [u8] as *const CStr)
}
#[inline]
pub 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, 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<'a> From<&'a CStr> for CString {
fn from(s: &'a 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
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::borrow::Cow::{Borrowed, Owned};
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
#[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]
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);
}
}