use core::ptr::copy_nonoverlapping;
use core::mem::transmute;
use core::mem::size_of;
use core::str;
use core::result;
#[cfg(feature = "std")]
use std::ffi::{CStr, CString};
use error;
use endian::Endian;
pub trait MeasureWith<Ctx> {
type Units;
#[inline]
fn measure_with(&self, ctx: &Ctx) -> Self::Units;
}
impl<Ctx> MeasureWith<Ctx> for [u8] {
type Units = usize;
#[inline]
fn measure_with(&self, _ctx: &Ctx) -> Self::Units {
self.len()
}
}
impl<Ctx, T: AsRef<[u8]>> MeasureWith<Ctx> for T {
type Units = usize;
#[inline]
fn measure_with(&self, _ctx: &Ctx) -> Self::Units {
self.as_ref().len()
}
}
#[derive(Debug, Copy, Clone)]
pub enum StrCtx {
Delimiter(u8),
DelimiterUntil(u8, usize),
Length(usize),
}
pub const NULL: u8 = 0;
pub const SPACE: u8 = 0x20;
pub const RET: u8 = 0x0a;
pub const TAB: u8 = 0x09;
impl Default for StrCtx {
#[inline]
fn default() -> Self {
StrCtx::Delimiter(NULL)
}
}
impl StrCtx {
pub fn len(&self) -> usize {
match *self {
StrCtx::Delimiter(_) => 1,
StrCtx::DelimiterUntil(_, _) => 1,
StrCtx::Length(_) => 0,
}
}
}
pub trait FromCtx<Ctx: Copy = (), This: ?Sized = [u8]> {
#[inline]
fn from_ctx(this: &This, ctx: Ctx) -> Self;
}
pub trait TryFromCtx<'a, Ctx: Copy = (), This: ?Sized = [u8]> where Self: 'a + Sized {
type Error;
type Size;
#[inline]
fn try_from_ctx(from: &'a This, ctx: Ctx) -> Result<(Self, Self::Size), Self::Error>;
}
pub trait IntoCtx<Ctx: Copy = (), This: ?Sized = [u8]>: Sized {
fn into_ctx(self, &mut This, ctx: Ctx);
}
pub trait TryIntoCtx<Ctx: Copy = (), This: ?Sized = [u8]>: Sized {
type Error;
type Size;
fn try_into_ctx(self, &mut This, ctx: Ctx) -> Result<Self::Size, Self::Error>;
}
pub trait SizeWith<Ctx = ()> {
type Units;
#[inline]
fn size_with(ctx: &Ctx) -> Self::Units;
}
macro_rules! signed_to_unsigned {
(i8) => {u8 };
(u8) => {u8 };
(i16) => {u16};
(u16) => {u16};
(i32) => {u32};
(u32) => {u32};
(i64) => {u64};
(u64) => {u64};
(i128) => {u128};
(u128) => {u128};
(f32) => {u32};
(f64) => {u64};
}
macro_rules! write_into {
($typ:ty, $size:expr, $n:expr, $dst:expr, $endian:expr) => ({
unsafe {
assert!($dst.len() >= $size);
let bytes = transmute::<$typ, [u8; $size]>(if $endian.is_little() { $n.to_le() } else { $n.to_be() });
copy_nonoverlapping((&bytes).as_ptr(), $dst.as_mut_ptr(), $size);
}
});
}
macro_rules! into_ctx_impl {
($typ:tt, $size:expr) => {
impl IntoCtx<Endian> for $typ {
#[inline]
fn into_ctx(self, dst: &mut [u8], le: Endian) {
assert!(dst.len() >= $size);
write_into!($typ, $size, self, dst, le);
}
}
impl<'a> IntoCtx<Endian> for &'a $typ {
#[inline]
fn into_ctx(self, dst: &mut [u8], le: Endian) {
(*self).into_ctx(dst, le)
}
}
impl TryIntoCtx<Endian> for $typ where $typ: IntoCtx<Endian> {
type Error = error::Error;
type Size = usize;
#[inline]
fn try_into_ctx(self, dst: &mut [u8], le: Endian) -> error::Result<Self::Size> {
if $size > dst.len () {
Err(error::Error::TooBig{size: $size, len: dst.len()})
} else {
<$typ as IntoCtx<Endian>>::into_ctx(self, dst, le);
Ok($size)
}
}
}
impl<'a> TryIntoCtx<Endian> for &'a $typ {
type Error = error::Error;
type Size = usize;
#[inline]
fn try_into_ctx(self, dst: &mut [u8], le: Endian) -> error::Result<Self::Size> {
(*self).try_into_ctx(dst, le)
}
}
}
}
macro_rules! from_ctx_impl {
($typ:tt, $size:expr) => {
impl<'a> FromCtx<Endian> for $typ {
#[inline]
fn from_ctx(src: &[u8], le: Endian) -> Self {
assert!(src.len() >= $size);
let mut data: signed_to_unsigned!($typ) = 0;
unsafe {
copy_nonoverlapping(
src.as_ptr(),
&mut data as *mut signed_to_unsigned!($typ) as *mut u8,
$size);
}
(if le.is_little() { data.to_le() } else { data.to_be() }) as $typ
}
}
impl<'a> TryFromCtx<'a, Endian> for $typ where $typ: FromCtx<Endian> {
type Error = error::Error<usize>;
type Size = usize;
#[inline]
fn try_from_ctx(src: &'a [u8], le: Endian) -> result::Result<(Self, Self::Size), Self::Error> {
if $size > src.len () {
Err(error::Error::TooBig{size: $size, len: src.len()})
} else {
Ok((FromCtx::from_ctx(&src, le), $size))
}
}
}
impl<'a, T> FromCtx<Endian, T> for $typ where T: AsRef<[u8]> {
#[inline]
fn from_ctx(src: &T, le: Endian) -> Self {
let src = src.as_ref();
assert!(src.len() >= $size);
let mut data: signed_to_unsigned!($typ) = 0;
unsafe {
copy_nonoverlapping(
src.as_ptr(),
&mut data as *mut signed_to_unsigned!($typ) as *mut u8,
$size);
}
(if le.is_little() { data.to_le() } else { data.to_be() }) as $typ
}
}
impl<'a, T> TryFromCtx<'a, Endian, T> for $typ where $typ: FromCtx<Endian, T>, T: AsRef<[u8]> {
type Error = error::Error;
type Size = usize;
#[inline]
fn try_from_ctx(src: &'a T, le: Endian) -> result::Result<(Self, Self::Size), Self::Error> {
let src = src.as_ref();
Self::try_from_ctx(src, le)
}
}
};
}
macro_rules! ctx_impl {
($typ:tt, $size:expr) => {
from_ctx_impl!($typ, $size);
};
}
ctx_impl!(u8, 1);
ctx_impl!(i8, 1);
ctx_impl!(u16, 2);
ctx_impl!(i16, 2);
ctx_impl!(u32, 4);
ctx_impl!(i32, 4);
ctx_impl!(u64, 8);
ctx_impl!(i64, 8);
#[cfg(rust_1_26)]
ctx_impl!(u128, 16);
#[cfg(rust_1_26)]
ctx_impl!(i128, 16);
macro_rules! from_ctx_float_impl {
($typ:tt, $size:expr) => {
impl<'a> FromCtx<Endian> for $typ {
#[inline]
fn from_ctx(src: &[u8], le: Endian) -> Self {
assert!(src.len() >= ::core::mem::size_of::<Self>());
let mut data: signed_to_unsigned!($typ) = 0;
unsafe {
copy_nonoverlapping(
src.as_ptr(),
&mut data as *mut signed_to_unsigned!($typ) as *mut u8,
$size);
transmute(if le.is_little() { data.to_le() } else { data.to_be() })
}
}
}
impl<'a> TryFromCtx<'a, Endian> for $typ where $typ: FromCtx<Endian> {
type Error = error::Error;
type Size = usize;
#[inline]
fn try_from_ctx(src: &'a [u8], le: Endian) -> result::Result<(Self, Self::Size), Self::Error> {
if $size > src.len () {
Err(error::Error::TooBig{size: $size, len: src.len()})
} else {
Ok((FromCtx::from_ctx(src, le), $size))
}
}
}
}
}
from_ctx_float_impl!(f32, 4);
from_ctx_float_impl!(f64, 8);
into_ctx_impl!(u8, 1);
into_ctx_impl!(i8, 1);
into_ctx_impl!(u16, 2);
into_ctx_impl!(i16, 2);
into_ctx_impl!(u32, 4);
into_ctx_impl!(i32, 4);
into_ctx_impl!(u64, 8);
into_ctx_impl!(i64, 8);
#[cfg(rust_1_26)]
into_ctx_impl!(u128, 16);
#[cfg(rust_1_26)]
into_ctx_impl!(i128, 16);
macro_rules! into_ctx_float_impl {
($typ:tt, $size:expr) => {
impl IntoCtx<Endian> for $typ {
#[inline]
fn into_ctx(self, dst: &mut [u8], le: Endian) {
assert!(dst.len() >= $size);
write_into!(signed_to_unsigned!($typ), $size, transmute::<$typ, signed_to_unsigned!($typ)>(self), dst, le);
}
}
impl<'a> IntoCtx<Endian> for &'a $typ {
#[inline]
fn into_ctx(self, dst: &mut [u8], le: Endian) {
(*self).into_ctx(dst, le)
}
}
impl TryIntoCtx<Endian> for $typ where $typ: IntoCtx<Endian> {
type Error = error::Error;
type Size = usize;
#[inline]
fn try_into_ctx(self, dst: &mut [u8], le: Endian) -> error::Result<Self::Size> {
if $size > dst.len () {
Err(error::Error::TooBig{size: $size, len: dst.len()})
} else {
<$typ as IntoCtx<Endian>>::into_ctx(self, dst, le);
Ok($size)
}
}
}
impl<'a> TryIntoCtx<Endian> for &'a $typ {
type Error = error::Error;
type Size = usize;
#[inline]
fn try_into_ctx(self, dst: &mut [u8], le: Endian) -> error::Result<Self::Size> {
(*self).try_into_ctx(dst, le)
}
}
}
}
into_ctx_float_impl!(f32, 4);
into_ctx_float_impl!(f64, 8);
impl<'a> TryFromCtx<'a, StrCtx> for &'a str {
type Error = error::Error;
type Size = usize;
#[inline]
fn try_from_ctx(src: &'a [u8], ctx: StrCtx) -> Result<(Self, Self::Size), Self::Error> {
let len = match ctx {
StrCtx::Length(len) => len,
StrCtx::Delimiter(delimiter) => src.iter().take_while(|c| **c != delimiter).count(),
StrCtx::DelimiterUntil(delimiter, len) => {
if len > src.len() {
return Err(error::Error::TooBig{size: len, len: src.len()});
};
src
.iter()
.take_while(|c| **c != delimiter)
.take(len)
.count()
}
};
if len > src.len() {
return Err(error::Error::TooBig{size: len, len: src.len()});
};
match str::from_utf8(&src[..len]) {
Ok(res) => Ok((res, len + ctx.len())),
Err(_) => Err(error::Error::BadInput{size: src.len(), msg: "invalid utf8"})
}
}
}
impl<'a, T> TryFromCtx<'a, StrCtx, T> for &'a str where T: AsRef<[u8]> {
type Error = error::Error;
type Size = usize;
#[inline]
fn try_from_ctx(src: &'a T, ctx: StrCtx) -> result::Result<(Self, Self::Size), Self::Error> {
let src = src.as_ref();
TryFromCtx::try_from_ctx(src, ctx)
}
}
impl<'a> TryIntoCtx for &'a [u8] {
type Error = error::Error;
type Size = usize;
#[inline]
fn try_into_ctx(self, dst: &mut [u8], _ctx: ()) -> error::Result<Self::Size> {
let src_len = self.len() as isize;
let dst_len = dst.len() as isize;
if src_len > dst_len {
Err(error::Error::TooBig{ size: self.len(), len: dst.len()})
} else {
unsafe { copy_nonoverlapping(self.as_ptr(), dst.as_mut_ptr(), src_len as usize) };
Ok(self.len())
}
}
}
impl<'a> TryIntoCtx for &'a str {
type Error = error::Error;
type Size = usize;
#[inline]
fn try_into_ctx(self, dst: &mut [u8], ctx: ()) -> error::Result<Self::Size> {
let bytes = self.as_bytes();
TryIntoCtx::try_into_ctx(bytes, dst, ctx)
}
}
macro_rules! sizeof_impl {
($ty:ty) => {
impl SizeWith<Endian> for $ty {
type Units = usize;
#[inline]
fn size_with(_ctx: &Endian) -> usize {
size_of::<$ty>()
}
}
}
}
sizeof_impl!(u8);
sizeof_impl!(i8);
sizeof_impl!(u16);
sizeof_impl!(i16);
sizeof_impl!(u32);
sizeof_impl!(i32);
sizeof_impl!(u64);
sizeof_impl!(i64);
#[cfg(rust_1_26)]
sizeof_impl!(u128);
#[cfg(rust_1_26)]
sizeof_impl!(i128);
sizeof_impl!(f32);
sizeof_impl!(f64);
sizeof_impl!(usize);
sizeof_impl!(isize);
impl FromCtx<Endian> for usize {
#[inline]
fn from_ctx(src: &[u8], le: Endian) -> Self {
let size = ::core::mem::size_of::<Self>();
assert!(src.len() >= size);
let mut data: usize = 0;
unsafe {
copy_nonoverlapping(
src.as_ptr(),
&mut data as *mut usize as *mut u8,
size);
transmute(if le.is_little() { data.to_le() } else { data.to_be() })
}
}
}
impl<'a> TryFromCtx<'a, Endian> for usize where usize: FromCtx<Endian> {
type Error = error::Error;
type Size = usize;
#[inline]
fn try_from_ctx(src: &'a [u8], le: Endian) -> result::Result<(Self, Self::Size), Self::Error> {
let size = ::core::mem::size_of::<usize>();
if size > src.len () {
Err(error::Error::TooBig{size: size, len: src.len()})
} else {
Ok((FromCtx::from_ctx(src, le), size))
}
}
}
impl<'a> TryFromCtx<'a, usize> for &'a[u8] {
type Error = error::Error;
type Size = usize;
#[inline]
fn try_from_ctx(src: &'a [u8], size: usize) -> result::Result<(Self, Self::Size), Self::Error> {
if size > src.len () {
Err(error::Error::TooBig{size: size, len: src.len()})
} else {
Ok((&src[..size], size))
}
}
}
impl IntoCtx<Endian> for usize {
#[inline]
fn into_ctx(self, dst: &mut [u8], le: Endian) {
let size = ::core::mem::size_of::<Self>();
assert!(dst.len() >= size);
let mut data = if le.is_little() { self.to_le() } else { self.to_be() };
let data = &mut data as *mut usize as *mut u8;
unsafe {
copy_nonoverlapping(data, dst.as_mut_ptr(), size);
}
}
}
impl TryIntoCtx<Endian> for usize where usize: IntoCtx<Endian> {
type Error = error::Error;
type Size = usize;
#[inline]
fn try_into_ctx(self, dst: &mut [u8], le: Endian) -> error::Result<Self::Size> {
let size = ::core::mem::size_of::<usize>();
if size > dst.len() {
Err(error::Error::TooBig{size: size, len: dst.len()})
} else {
<usize as IntoCtx<Endian>>::into_ctx(self, dst, le);
Ok(size)
}
}
}
#[cfg(feature = "std")]
impl<'a> TryFromCtx<'a> for &'a CStr {
type Error = error::Error;
type Size = usize;
#[inline]
fn try_from_ctx(src: &'a [u8], _ctx: ()) -> result::Result<(Self, Self::Size), Self::Error> {
let null_byte = match src.iter().position(|b| *b == 0) {
Some(ix) => ix,
None => return Err(error::Error::BadInput {
size: 0,
msg: "The input doesn't contain a null byte",
})
};
let cstr = unsafe { CStr::from_bytes_with_nul_unchecked(&src[..null_byte+1]) };
Ok((cstr, null_byte+1))
}
}
#[cfg(feature = "std")]
impl<'a> TryFromCtx<'a> for CString {
type Error = error::Error;
type Size = usize;
#[inline]
fn try_from_ctx(src: &'a [u8], _ctx: ()) -> result::Result<(Self, Self::Size), Self::Error> {
let (raw, bytes_read) = <&CStr as TryFromCtx>::try_from_ctx(src, _ctx)?;
Ok((raw.to_owned(), bytes_read))
}
}
#[cfg(feature = "std")]
impl<'a> TryIntoCtx for &'a CStr {
type Error = error::Error;
type Size = usize;
#[inline]
fn try_into_ctx(self, dst: &mut [u8], _ctx: ()) -> error::Result<Self::Size> {
let data = self.to_bytes_with_nul();
if dst.len() < data.len() {
Err(error::Error::TooBig {
size: dst.len(),
len: data.len(),
})
} else {
unsafe {
copy_nonoverlapping(data.as_ptr(), dst.as_mut_ptr(), data.len());
}
Ok(data.len())
}
}
}
#[cfg(feature = "std")]
impl TryIntoCtx for CString {
type Error = error::Error;
type Size = usize;
#[inline]
fn try_into_ctx(self, dst: &mut [u8], _ctx: ()) -> error::Result<Self::Size> {
self.as_c_str().try_into_ctx(dst, _ctx)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[cfg(feature = "std")]
fn parse_a_cstr() {
let src = CString::new("Hello World").unwrap();
let as_bytes = src.as_bytes_with_nul();
let (got, bytes_read) = <&CStr as TryFromCtx>::try_from_ctx(as_bytes, ()).unwrap();
assert_eq!(bytes_read, as_bytes.len());
assert_eq!(got, src.as_c_str());
}
#[test]
#[cfg(feature = "std")]
fn round_trip_a_c_str() {
let src = CString::new("Hello World").unwrap();
let src = src.as_c_str();
let as_bytes = src.to_bytes_with_nul();
let mut buffer = vec![0; as_bytes.len()];
let bytes_written = src.try_into_ctx(&mut buffer, ()).unwrap();
assert_eq!(bytes_written, as_bytes.len());
let (got, bytes_read) = <&CStr as TryFromCtx>::try_from_ctx(&buffer, ()).unwrap();
assert_eq!(bytes_read, as_bytes.len());
assert_eq!(got, src);
}
}