#![crate_name = "c_string"]
#![crate_type = "lib"]
#![allow(trivial_numeric_casts)]
extern crate libc;
use std::cmp::Ordering;
use std::error::Error;
use std::ffi::CStr;
use std::fmt;
use std::fmt::Debug;
use std::hash;
use std::io::Error as IoError;
use std::io::ErrorKind::InvalidInput;
use std::iter::IntoIterator;
use std::marker;
use std::mem;
use std::ops::Deref;
pub use libc::c_char;
const NUL: u8 = 0;
pub struct OwnedCString {
ptr: *const c_char,
dtor: DestroyFn
}
impl Drop for OwnedCString {
fn drop(&mut self) {
let dtor = self.dtor;
unsafe { dtor(self.ptr) };
}
}
impl Deref for OwnedCString {
type Target = CStr;
fn deref(&self) -> &CStr {
unsafe { CStr::from_ptr(self.ptr) }
}
}
impl PartialEq for OwnedCString {
fn eq(&self, other: &OwnedCString) -> bool {
unsafe { libc::strcmp(self.ptr, other.ptr) == 0 }
}
}
impl Eq for OwnedCString {
}
impl PartialOrd for OwnedCString {
#[inline]
fn partial_cmp(&self, other: &OwnedCString) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for OwnedCString {
fn cmp(&self, other: &OwnedCString) -> Ordering {
let res = unsafe { libc::strcmp(self.ptr, other.ptr) };
res.cmp(&(0 as libc::c_int))
}
}
impl hash::Hash for OwnedCString {
fn hash<H>(&self, state: &mut H) where H: hash::Hasher {
self.to_bytes().hash(state)
}
}
pub type DestroyFn = unsafe fn(*const c_char);
pub unsafe fn libc_free(ptr: *const c_char) {
libc::free(ptr as *mut libc::c_void);
}
impl OwnedCString {
pub unsafe fn new(ptr: *const c_char, dtor: DestroyFn) -> OwnedCString {
assert!(!ptr.is_null());
OwnedCString { ptr: ptr, dtor: dtor }
}
}
impl Debug for OwnedCString {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "\"{}\"", escape_bytestring(self.to_bytes()))
}
}
pub struct NulError {
position: usize,
bytes: Vec<u8>
}
impl NulError {
pub fn nul_position(&self) -> usize { self.position }
pub fn into_bytes(self) -> Vec<u8> {
self.bytes
}
}
impl Error for NulError {
fn description(&self) -> &str {
"invalid data for C string: contains a NUL byte"
}
}
impl fmt::Display for NulError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "invalid data for C string: NUL at position {}", self.position)
}
}
impl fmt::Debug for NulError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "NulError {{ position: {}, bytes: \"{}\" }}",
self.position, escape_bytestring(&self.bytes))
}
}
impl From<NulError> for IoError {
fn from(err: NulError) -> IoError {
IoError::new(InvalidInput,
format!("invalid data for C string: NUL at position {}", err.position).as_ref())
}
}
const IN_PLACE_SIZE: usize = 31;
#[derive(Clone)]
enum CStrData {
Owned(Vec<u8>),
InPlace([u8; IN_PLACE_SIZE])
}
#[derive(Clone)]
pub struct CStrBuf {
data: CStrData
}
fn find_nul(bytes: &[u8]) -> Option<usize> {
bytes.iter().position(|b| *b == NUL)
}
fn vec_into_c_str(mut v: Vec<u8>) -> CStrBuf {
v.push(NUL);
CStrBuf { data: CStrData::Owned(v) }
}
fn escape_bytestring(s: &[u8]) -> String {
use std::ascii;
let mut acc = Vec::with_capacity(s.len());
acc.extend(s.iter().cloned().flat_map(ascii::escape_default));
unsafe { String::from_utf8_unchecked(acc) }
}
#[macro_export]
macro_rules! c_str {
($lit:expr) => {
unsafe {
std::ffi::CStr::from_ptr(concat!($lit, "\0").as_ptr()
as *const $crate::c_char)
}
}
}
impl CStrBuf {
pub fn from_iter<I>(iterable: I) -> Result<CStrBuf, NulError>
where I: IntoIterator<Item=u8>
{
fn nul_error<I>(mut collected: Vec<u8>, remaining: I) -> NulError
where I: Iterator<Item=u8>
{
let pos = collected.len();
collected.push(NUL);
collected.extend(remaining);
NulError { position: pos, bytes: collected }
}
let mut iter = iterable.into_iter();
let mut vec: Vec<u8> = match iter.size_hint() {
(_, Some(len)) if len < IN_PLACE_SIZE => {
let mut buf: [u8; IN_PLACE_SIZE]
= unsafe { mem::uninitialized() };
for i in 0 .. len + 1 {
match iter.next() {
Some(NUL) => {
return Err(nul_error(buf[.. i].to_vec(), iter));
}
Some(c) => {
buf[i] = c;
}
None => {
buf[i] = NUL;
return Ok(CStrBuf {
data: CStrData::InPlace(buf)
});
}
}
}
let mut vec = Vec::<u8>::with_capacity(len + 2);
vec.extend(buf[.. len + 1].iter().cloned());
vec
}
(lower, _) => {
Vec::with_capacity(lower + 1)
}
};
loop {
match iter.next() {
None => {
break;
}
Some(NUL) => {
return Err(nul_error(vec, iter));
}
Some(c) => {
let len = vec.len();
unsafe {
*vec.get_unchecked_mut(len) = c;
vec.set_len(len + 1);
}
if vec.len() == vec.capacity() {
let (lower, _) = iter.size_hint();
vec.reserve(lower + 1);
}
}
}
}
{
let len = vec.len();
unsafe {
*vec.get_unchecked_mut(len) = NUL;
vec.set_len(len + 1);
}
}
Ok(CStrBuf { data: CStrData::Owned(vec) })
}
#[inline]
pub fn from_str(s: &str) -> Result<CStrBuf, NulError> {
CStrBuf::from_iter(s.as_bytes().iter().cloned())
}
pub fn from_vec(vec: Vec<u8>) -> Result<CStrBuf, NulError> {
if let Some(pos) = find_nul(&vec[..]) {
return Err(NulError {position: pos, bytes: vec});
}
Ok(vec_into_c_str(vec))
}
pub unsafe fn from_vec_unchecked(vec: Vec<u8>) -> CStrBuf {
vec_into_c_str(vec)
}
pub fn into_vec(mut self) -> Vec<u8> {
match mem::replace(&mut self.data,
CStrData::InPlace(unsafe { mem::uninitialized() }))
{
CStrData::Owned(mut v) => {
let len = v.len();
v.truncate(len - 1);
v
}
CStrData::InPlace(ref a) => {
a[.. find_nul(a).unwrap()].to_vec()
}
}
}
fn as_bytes(&self) -> &[u8] {
match self.data {
CStrData::Owned(ref v) => &v[.. v.len() - 1],
CStrData::InPlace(ref a) => &a[.. find_nul(a).unwrap()]
}
}
}
impl fmt::Debug for CStrBuf {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "\"{}\"", escape_bytestring(self.as_bytes()))
}
}
impl Deref for CStrBuf {
type Target = CStr;
fn deref(&self) -> &CStr {
let p = match self.data {
CStrData::Owned(ref v) => (*v).as_ptr(),
CStrData::InPlace(ref a) => a.as_ptr()
} as *const c_char;
unsafe { CStr::from_ptr(p) }
}
}
pub struct CChars<'a> {
ptr: *const c_char,
lifetime: marker::PhantomData<&'a [c_char]>,
}
impl<'a> CChars<'a> {
#[inline]
pub fn from_c_str(s: &'a CStr) -> CChars<'a> {
CChars { ptr: s.as_ptr(), lifetime: marker::PhantomData }
}
}
impl<'a> Clone for CChars<'a> {
#[inline]
fn clone(&self) -> CChars<'a> {
CChars { ptr: self.ptr, lifetime: marker::PhantomData }
}
}
impl<'a> Copy for CChars<'a> { }
impl<'a> Iterator for CChars<'a> {
type Item = c_char;
fn next(&mut self) -> Option<c_char> {
let ch = unsafe { *self.ptr };
if ch == 0 {
None
} else {
self.ptr = unsafe { self.ptr.offset(1) };
Some(ch)
}
}
}
pub unsafe fn parse_c_multistring<F>(buf: *const c_char,
limit: Option<usize>,
mut f: F) -> usize
where F: FnMut(&[u8])
{
let mut curr_ptr = buf;
let mut ctr: usize = 0;
let (limited_count, limit) = match limit {
Some(limit) => (true, limit),
None => (false, 0)
};
while (!limited_count || ctr < limit) && *curr_ptr != 0 {
let bytes = CStr::from_ptr(curr_ptr).to_bytes();
f(bytes);
curr_ptr = curr_ptr.offset(bytes.len() as isize + 1);
ctr += 1;
}
return ctr;
}