use std::ffi;
use std::ops::Deref;
const SIZE: usize = 38;
#[derive(Clone)]
pub enum SmallCStr {
OnStack {
data: [u8; SIZE],
len_with_nul: u8,
},
OnHeap {
data: ffi::CString,
}
}
impl SmallCStr {
#[inline]
pub fn new(s: &str) -> SmallCStr {
if s.len() < SIZE {
let mut data = [0; SIZE];
data[.. s.len()].copy_from_slice(s.as_bytes());
let len_with_nul = s.len() + 1;
if let Err(e) = ffi::CStr::from_bytes_with_nul(&data[.. len_with_nul]) {
panic!("The string \"{}\" cannot be converted into a CStr: {}", s, e);
}
SmallCStr::OnStack {
data,
len_with_nul: len_with_nul as u8,
}
} else {
SmallCStr::OnHeap {
data: ffi::CString::new(s).unwrap()
}
}
}
#[inline]
pub fn as_c_str(&self) -> &ffi::CStr {
match *self {
SmallCStr::OnStack { ref data, len_with_nul } => {
unsafe {
let slice = &data[.. len_with_nul as usize];
ffi::CStr::from_bytes_with_nul_unchecked(slice)
}
}
SmallCStr::OnHeap { ref data } => {
data.as_c_str()
}
}
}
#[inline]
pub fn len_with_nul(&self) -> usize {
match *self {
SmallCStr::OnStack { len_with_nul, .. } => {
len_with_nul as usize
}
SmallCStr::OnHeap { ref data } => {
data.as_bytes_with_nul().len()
}
}
}
}
impl Deref for SmallCStr {
type Target = ffi::CStr;
fn deref(&self) -> &ffi::CStr {
self.as_c_str()
}
}
#[test]
fn short() {
const TEXT: &str = "abcd";
let reference = ffi::CString::new(TEXT.to_string()).unwrap();
let scs = SmallCStr::new(TEXT);
assert_eq!(scs.len_with_nul(), TEXT.len() + 1);
assert_eq!(scs.as_c_str(), reference.as_c_str());
assert!(if let SmallCStr::OnStack { .. } = scs { true } else { false });
}
#[test]
fn empty() {
const TEXT: &str = "";
let reference = ffi::CString::new(TEXT.to_string()).unwrap();
let scs = SmallCStr::new(TEXT);
assert_eq!(scs.len_with_nul(), TEXT.len() + 1);
assert_eq!(scs.as_c_str(), reference.as_c_str());
assert!(if let SmallCStr::OnStack { .. } = scs { true } else { false });
}
#[test]
fn long() {
const TEXT: &str = "01234567890123456789012345678901234567890123456789\
01234567890123456789012345678901234567890123456789\
01234567890123456789012345678901234567890123456789";
let reference = ffi::CString::new(TEXT.to_string()).unwrap();
let scs = SmallCStr::new(TEXT);
assert_eq!(scs.len_with_nul(), TEXT.len() + 1);
assert_eq!(scs.as_c_str(), reference.as_c_str());
assert!(if let SmallCStr::OnHeap { .. } = scs { true } else { false });
}
#[test]
#[should_panic]
fn internal_nul() {
let _ = SmallCStr::new("abcd\0def");
}