extern crate memchr;
use std::os::raw::c_char;
use std::{slice, str, fmt, ascii, ops, ptr, mem};
use std::borrow::{Cow,Borrow};
use std::hash::{Hash, Hasher};
use std::ffi::{CStr, CString};
use std::cmp::Ordering;
use std::error::Error;
use memchr::memchr;
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct IntoStringError {
inner: CFixedString,
error: str::Utf8Error,
}
impl IntoStringError {
pub fn into_c_fixed_string(self) -> CFixedString {
self.inner
}
pub fn utf8_error(&self) -> str::Utf8Error {
self.error
}
}
impl Error for IntoStringError {
fn description(&self) -> &str {
"C fixed string contained non-utf8 bytes"
}
fn cause(&self) -> Option<&Error> {
Some(&self.error)
}
}
impl fmt::Display for IntoStringError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.description().fmt(f)
}
}
pub struct CFixedString {
inner: Box<[u8]>
}
impl CFixedString {
pub fn new<T: Into<Box<[u8]>>>(t: T) -> CFixedString {
CFixedString { inner: t.into() }
}
pub fn with_limit(limit: usize) -> CFixedString {
CFixedString { inner: vec![0; limit].into_boxed_slice() }
}
pub unsafe fn from_raw_parts(ptr: *mut c_char, limit: usize) -> CFixedString {
CFixedString {
inner: Box::from_raw(slice::from_raw_parts_mut(ptr as *mut u8, limit))
}
}
pub fn into_string(self) -> Result<String, IntoStringError> {
String::from_utf8(self.into_bytes())
.map_err(|e| IntoStringError {
error: e.utf8_error(),
inner: CFixedString::new(e.into_bytes()),
})
}
pub fn into_c_string(self) -> CString {
unsafe { CString::from_vec_unchecked(self.into_bytes()) }
}
pub fn into_bytes(self) -> Vec<u8> {
let l = self.len();
let mut v = self.into_inner().into_vec();
v.truncate(l);
v
}
pub fn into_bytes_full(self) -> Vec<u8> {
self.into_inner().into_vec()
}
pub fn as_c_fixed_str(&self) -> &CFixedStr {
&*self
}
pub fn as_c_fixed_str_mut(&mut self) -> &mut CFixedStr {
&mut *self
}
pub fn into_boxed_c_fixed_str(self) -> Box<CFixedStr> {
unsafe { Box::from_raw(Box::into_raw(self.into_inner()) as *mut CFixedStr) }
}
fn into_inner(self) -> Box<[u8]> {
unsafe {
let result = ptr::read(&self.inner);
mem::forget(self);
result
}
}
}
impl Drop for CFixedString {
#[inline]
fn drop(&mut self) {
if let Some(c) = self.inner.first_mut() {
*c = 0
}
}
}
impl ops::Deref for CFixedString {
type Target = CFixedStr;
#[inline]
fn deref(&self) -> &CFixedStr {
CFixedStr::from_bytes(&self.inner)
}
}
impl ops::DerefMut for CFixedString {
#[inline]
fn deref_mut(&mut self) -> &mut CFixedStr {
CFixedStr::from_bytes_mut(&mut self.inner)
}
}
impl fmt::Debug for CFixedString {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(&**self, f)
}
}
impl Clone for CFixedString {
fn clone(&self) -> Self {
CFixedString::new(self.as_bytes_full())
}
}
impl Hash for CFixedString {
fn hash<H: Hasher>(&self, state: &mut H) {
(**self).hash(state);
}
}
impl PartialEq for CFixedString {
fn eq(&self, other: &CFixedString) -> bool {
(**self).eq(other)
}
}
impl Eq for CFixedString {}
impl PartialOrd for CFixedString {
fn partial_cmp(&self, other: &CFixedString) -> Option<Ordering> {
(**self).partial_cmp(other)
}
}
impl Ord for CFixedString {
fn cmp(&self, other: &CFixedString) -> Ordering {
(**self).cmp(other)
}
}
impl From<CFixedString> for Vec<u8> {
#[inline]
fn from(s: CFixedString) -> Vec<u8> {
s.into_bytes()
}
}
pub struct CFixedStr {
inner: [u8]
}
impl CFixedStr {
pub unsafe fn from_ptr<'a>(ptr: *const c_char, limit: usize) -> &'a CFixedStr {
Self::from_bytes(slice::from_raw_parts(ptr as *const u8, limit))
}
pub unsafe fn from_mut_ptr<'a>(ptr: *mut c_char, limit: usize) -> &'a mut CFixedStr {
Self::from_bytes_mut(slice::from_raw_parts_mut(ptr as *mut u8, limit))
}
pub fn from_str(s: &str) -> &CFixedStr {
Self::from_bytes(s.as_bytes())
}
pub fn from_bytes(bytes: &[u8]) -> &CFixedStr {
unsafe { &*(bytes as *const [u8] as *const CFixedStr) }
}
pub fn from_bytes_mut(bytes: &mut [u8]) -> &mut CFixedStr {
unsafe { &mut *(bytes as *mut [u8] as *mut CFixedStr) }
}
pub fn from_c_str(c_str: &CStr) -> &CFixedStr {
Self::from_bytes(c_str.to_bytes_with_nul())
}
pub fn as_ptr(&self) -> *const c_char {
self.inner.as_ptr() as *const c_char
}
pub fn as_mut_ptr(&mut self) -> *mut c_char {
self.inner.as_mut_ptr() as *mut c_char
}
pub fn limit(&self) -> usize {
self.inner.len()
}
pub fn len(&self) -> usize {
memchr(0, &self.inner).unwrap_or(self.limit())
}
pub fn to_bytes(&self) -> &[u8] {
&self.inner[0..self.len()]
}
pub fn to_bytes_mut(&mut self) -> &mut [u8] {
let l = self.len();
&mut self.inner[0..l]
}
pub fn to_bytes_extended(&self) -> &[u8] {
if let Some(l) = memchr(0, &self.inner) {
&self.inner[0 .. l+1]
} else {
&self.inner
}
}
pub fn to_bytes_mut_extended(&mut self) -> &mut [u8] {
if let Some(l) = memchr(0, &self.inner) {
&mut self.inner[0 .. l+1]
} else {
&mut self.inner
}
}
pub fn as_bytes_full(&self) -> &[u8] {
&self.inner
}
pub fn as_bytes_mut_full(&mut self) -> &mut [u8] {
&mut self.inner
}
pub fn to_str(&self) -> Result<&str, str::Utf8Error> {
str::from_utf8(self.to_bytes())
}
pub fn to_string_lossy(&self) -> Cow<str> {
String::from_utf8_lossy(self.to_bytes())
}
pub fn to_c_str(&self) -> Cow<CStr> {
if let Some(l) = memchr(0, &self.inner) {
Cow::Borrowed(unsafe { CStr::from_bytes_with_nul_unchecked(&self.inner[0 .. l+1]) })
} else {
let mut v = Vec::with_capacity(self.inner.len() + 1);
v.extend(&self.inner);
Cow::Owned(unsafe { CString::from_vec_unchecked(v) })
}
}
pub fn into_c_fixed_string(self: Box<CFixedStr>) -> CFixedString {
let raw = Box::into_raw(self) as *mut [u8];
CFixedString { inner: unsafe { Box::from_raw(raw) } }
}
}
impl Hash for CFixedStr {
fn hash<H: Hasher>(&self, state: &mut H) {
self.to_bytes().hash(state);
}
}
impl fmt::Debug for CFixedStr {
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)) {
fmt::Write::write_char(f, byte as char)?;
}
write!(f, "\"")
}
}
impl<'a> Default for &'a CFixedStr {
fn default() -> &'a CFixedStr {
const SLICE: &'static [u8] = &[0];
CFixedStr::from_bytes(SLICE)
}
}
impl Default for CFixedString {
fn default() -> CFixedString {
CFixedString { inner: Default::default() }
}
}
impl Borrow<CFixedStr> for CFixedString {
#[inline]
fn borrow(&self) -> &CFixedStr { self }
}
impl PartialEq for CFixedStr {
fn eq(&self, other: &CFixedStr) -> bool {
self.to_bytes().eq(other.to_bytes())
}
}
impl Eq for CFixedStr {}
impl PartialOrd for CFixedStr {
fn partial_cmp(&self, other: &CFixedStr) -> Option<Ordering> {
self.to_bytes().partial_cmp(&other.to_bytes())
}
}
impl Ord for CFixedStr {
fn cmp(&self, other: &CFixedStr) -> Ordering {
self.to_bytes().cmp(&other.to_bytes())
}
}
impl ToOwned for CFixedStr {
type Owned = CFixedString;
fn to_owned(&self) -> CFixedString {
CFixedString { inner: self.to_bytes_extended().into() }
}
}
impl<'a> From<&'a CFixedStr> for CFixedString {
fn from(s: &'a CFixedStr) -> CFixedString {
s.to_owned()
}
}
impl ops::Index<ops::RangeFull> for CFixedString {
type Output = CFixedStr;
#[inline]
fn index(&self, _index: ops::RangeFull) -> &CFixedStr {
self
}
}
impl ops::IndexMut<ops::RangeFull> for CFixedString {
#[inline]
fn index_mut(&mut self, _index: ops::RangeFull) -> &mut CFixedStr {
self
}
}
impl AsRef<CFixedStr> for CFixedStr {
#[inline]
fn as_ref(&self) -> &CFixedStr {
self
}
}
impl AsRef<CFixedStr> for CFixedString {
#[inline]
fn as_ref(&self) -> &CFixedStr {
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
assert_eq!(&CFixedString::new(&b"hello,\0world!\0"[..])[..], CFixedStr::from_str("hello,"));
}
}