#![warn(clippy::all)]
#![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
),
)
}
}
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 = [NUL; IN_PLACE_SIZE];
for i in 0..=len {
match iter.next() {
Some(NUL) => {
return Err(nul_error(buf[..i].to_vec(), iter));
}
Some(c) => {
buf[i] = c;
}
None => {
return Ok(CStrBuf {
data: CStrData::InPlace(buf),
});
}
}
}
let mut vec = Vec::<u8>::with_capacity(len + 2);
vec.extend_from_slice(&buf[..=len]);
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([NUL; IN_PLACE_SIZE])) {
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;
}