#![no_std]
use core::cmp::Ordering::{self,Less,Greater,Equal};
use core::marker::PhantomData;
use core::result::Result;
use core::slice;
#[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))]
#[allow(non_camel_case_types)]
pub type c_char = i8;
#[cfg(any(target_arch = "arm", target_arch = "aarch64", target_arch = "powerpc"))]
#[allow(non_camel_case_types)]
pub type c_char = u8;
#[macro_export]
macro_rules! cstr {
($arg:expr) => ($crate::CString::new(concat!($arg, "\0")).unwrap());
}
pub struct CString<'a> {
data: *const c_char,
len: usize,
_marker: PhantomData<&'a c_char>
}
#[derive(Debug)]
pub struct Error(&'static str);
fn strlen(ptr: *const c_char) -> isize {
let mut ctr = 0isize;
loop {
if unsafe { *ptr.offset(ctr) == 0 as c_char } {
break
}
ctr += 1;
}
ctr
}
fn ascii_guard(ptr: *const c_char, len: usize) -> bool {
let mut ctr = 0usize;
while ctr < len {
if unsafe { *ptr.offset(ctr as isize) < 0 as c_char } {
return false;
}
ctr += 1;
}
true
}
impl<'a> CString<'a> {
pub fn new(s: &'a str) -> Result<CString<'a>, Error> {
if s.len() == 0 {
return Err(Error("0-length cstring found"));
}
let ret = CString {
data: s.as_ptr() as *const c_char,
len: s.len(),
_marker: PhantomData
};
if ! ascii_guard(ret.data, ret.len) {
return Err(Error("Invalid character in string"));
}
if unsafe { *ret.into_raw().offset(ret.len as isize - 1) != 0 } {
Err(Error("No NULL terminator present"))
} else {
Ok(ret)
}
}
pub unsafe fn from_raw(ptr: *const c_char) -> Result<CString<'a>, Error> {
let siz = strlen(ptr) as usize + 1usize;
let ret = CString {
data: ptr,
len: siz,
_marker: PhantomData
};
if ! ascii_guard(ret.data, ret.len) {
Err(Error("Invalid character in string"))
} else {
Ok(ret)
}
}
pub unsafe fn into_raw(&self) -> *const c_char {
self.data
}
pub fn len(&self) -> usize {
self.len
}
}
impl<'a> Eq for CString<'a> { }
impl<'a> PartialEq for CString<'a> {
fn eq(&self, other: &CString) -> bool {
unsafe {
slice::from_raw_parts(self.data, other.len()) ==
slice::from_raw_parts(other.data, other.len())
}
}
fn ne(&self, other: &CString) -> bool {
! self.eq(other)
}
}
impl<'a> PartialOrd for CString<'a> {
fn partial_cmp(&self, other: &CString) -> Option<Ordering> {
unsafe {
slice::from_raw_parts(self.data, self.len())
.partial_cmp(slice::from_raw_parts(other.data, other.len()))
}
}
fn le(&self, other: &CString) -> bool {
match self.partial_cmp(other) {
Some(Less) => true,
Some(Equal) => true,
_ => false
}
}
fn ge(&self, other: &CString) -> bool {
match self.partial_cmp(other) {
Some(Greater) => true,
Some(Equal) => true,
_ => false
}
}
fn lt(&self, other: &CString) -> bool {
match self.partial_cmp(other) {
Some(Less) => true,
_ => false
}
}
fn gt(&self, other: &CString) -> bool {
match self.partial_cmp(other) {
Some(Greater) => true,
_ => false
}
}
}
impl<'a> Ord for CString<'a> {
fn cmp(&self, other: &CString) -> Ordering {
self.data.cmp(&other.data)
}
}
#[cfg(test)] #[macro_use] extern crate std;
#[cfg(test)]
mod test {
use super::{c_char,CString,Error};
use std::ffi::CString as OldString;
use std::slice;
use std::string::String;
#[test]
fn new_cstring() {
{
let cstr = cstr!("foo");
let buf = "foo\0".as_ptr() as *const c_char;
unsafe {
assert_eq!(slice::from_raw_parts(cstr.data, cstr.len()),
slice::from_raw_parts(buf, 4));
}
}
{
let raw = OldString::new("foo").unwrap().into_raw();
let cstr = unsafe { CString::from_raw(raw).unwrap() };
let buf = "foo\0".as_ptr() as *const c_char;
unsafe {
assert_eq!(slice::from_raw_parts(cstr.data, cstr.len()),
slice::from_raw_parts(buf, 4));
}
}
{
let cstr_res = CString::new("foo");
assert!(! cstr_res.is_ok());
}
{
let cstr_res = CString::new("");
assert!(! cstr_res.is_ok());
}
{
let cstr_res = CString::new("ñ\0");
assert!(! cstr_res.is_ok());
}
{
let cstr_res = CString::new("ай да, пирожки\0");
assert!(! cstr_res.is_ok());
}
{
{
let s = String::from("foo\0");
let cstr_res: Result<CString, Error> = CString::new(s.as_str());
assert!(cstr_res.is_ok());
}
}
}
#[test]
fn from_cstring() {
{
let cstr = cstr!("foo");
let raw = unsafe { cstr.into_raw() };
let other = OldString::new("foo").unwrap().into_raw();
unsafe {
assert_eq!(*raw, *other);
}
}
}
#[test]
fn cstring_len() {
{
let cstr = cstr!("foo");
assert_eq!(cstr.len(), 4);
}
{
let raw = OldString::new("foo").unwrap().into_raw();
let cstr = unsafe { CString::from_raw(raw).unwrap() };
assert_eq!(cstr.len(), 4);
}
}
#[test]
fn eq() {
{
let cstr = cstr!("foo");
let other = cstr!("bar");
assert!(cstr != other);
}
{
let cstr = cstr!("foo");
let other = cstr!("foo");
assert!(cstr == other);
}
}
#[test]
fn cmp() {
{
let cstr = cstr!("foo");
let other = cstr!("bar");
assert!(cstr > other);
}
{
let cstr = cstr!("foo");
let other = cstr!("bar");
assert!(other < cstr);
}
}
}