use core::ptr::copy_nonoverlapping;
use core::mem::transmute;
use core::mem::size_of;
use core::str;
use error;
use endian;
pub type DefaultCtx = endian::Endian;
pub const CTX: DefaultCtx = endian::NATIVE;
#[derive(Debug, Copy, Clone)]
pub struct StrCtx {
pub delimiter: u8
}
pub const NULL: StrCtx = StrCtx { delimiter: 0 };
pub const SPACE: StrCtx = StrCtx { delimiter: 0x20 };
pub const RET: StrCtx = StrCtx { delimiter: 0x0a };
pub const TAB: StrCtx = StrCtx { delimiter: 0x09 };
impl Default for StrCtx {
#[inline]
fn default() -> Self {
NULL
}
}
impl From<u8> for StrCtx {
fn from(delimiter: u8) -> Self {
StrCtx { delimiter: delimiter }
}
}
pub trait FromCtx<Ctx: Copy = DefaultCtx, This: ?Sized = [u8]> {
#[inline]
fn from_ctx(this: &This, ctx: Ctx) -> Self;
}
pub trait TryFromCtx<'a, Ctx: Copy = (usize, DefaultCtx), This: ?Sized = [u8]> where Self: 'a + Sized {
type Error;
#[inline]
fn try_from_ctx(from: &'a This, ctx: Ctx) -> Result<Self, Self::Error>;
}
pub trait IntoCtx<Ctx: Copy = DefaultCtx, This: ?Sized = [u8]>: Sized {
fn into_ctx(self, &mut This, ctx: Ctx);
}
pub trait TryIntoCtx<Ctx: Copy = (usize, DefaultCtx), This: ?Sized = [u8]>: Sized {
type Error;
fn try_into_ctx(self, &mut This, ctx: Ctx) -> Result<(), Self::Error>;
}
pub trait RefFrom<This: ?Sized = [u8], I = usize> {
type Error;
#[inline]
fn ref_from(from: &This, offset: I, count: I) -> Result<&Self, Self::Error>;
}
pub trait TryRefFromCtx<Ctx: Copy = (usize, usize, DefaultCtx), This: ?Sized = [u8]> {
type Error;
#[inline]
fn try_ref_from_ctx(from: &This, ctx: Ctx) -> Result<&Self, Self::Error>;
}
pub trait TryRefIntoCtx<Ctx: Copy = (usize, usize, DefaultCtx), This: ?Sized = [u8]>: Sized {
type Error;
fn try_ref_into_ctx(self, &mut This, ctx: Ctx) -> Result<(), Self::Error>;
}
pub trait SizeWith<Ctx = DefaultCtx> {
type Units;
#[inline]
fn size_with(ctx: &Ctx) -> Self::Units;
}
impl<T> TryRefFromCtx<(usize, usize, super::Endian), T> for [u8] where T: AsRef<[u8]> {
type Error = error::Error;
#[inline]
fn try_ref_from_ctx(b: &T, (offset, count, _): (usize, usize, super::Endian)) -> error::Result<&[u8]> {
let b = b.as_ref();
if offset + count > b.len () {
Err(error::Error::BadRange{range: (offset..offset+count), size: b.len()})
} else {
Ok(&b[offset..(offset+count)])
}
}
}
impl TryRefFromCtx for [u8] {
type Error = error::Error;
#[inline]
fn try_ref_from_ctx(b: &[u8], (offset, count, _): (usize, usize, super::Endian)) -> error::Result<&[u8]> {
if offset + count > b.len () {
Err(error::Error::BadRange{range: (offset..offset+count), size: b.len()})
} else {
Ok(&b[offset..(offset+count)])
}
}
}
impl TryRefFromCtx for str {
type Error = error::Error;
#[inline]
fn try_ref_from_ctx(b: &[u8], (offset, count, _): (usize, usize, super::Endian)) -> error::Result<&str> {
if offset + count > b.len () {
Err(error::Error::BadRange{range: (offset..offset+count), size: b.len()})
} else {
let bytes = &b[offset..(offset+count)];
str::from_utf8(bytes).map_err(| _err | {
error::Error::BadInput{ range: offset..offset+count, size: bytes.len(), msg: "invalid utf8" }
})
}
}
}
impl<T> TryRefFromCtx<(usize, usize, super::Endian), T> for str where T: AsRef<[u8]> {
type Error = error::Error;
#[inline]
fn try_ref_from_ctx(b: &T, (offset, count, _): (usize, usize, super::Endian)) -> error::Result<&str> {
let b = b.as_ref();
if offset + count > b.len () {
Err(error::Error::BadRange{range: (offset..offset+count), size: b.len()})
} else {
let bytes = &b[offset..(offset+count)];
str::from_utf8(bytes).map_err(| _err | {
error::Error::BadInput{ range: offset..offset+count, size: bytes.len(), msg: "invalid utf8" }
})
}
}
}
macro_rules! signed_to_unsigned {
(i8) => {u8 };
(u8) => {u8 };
(i16) => {u16};
(u16) => {u16};
(i32) => {u32};
(u32) => {u32};
(i64) => {u64};
(u64) => {u64};
(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, $ctx:ty) => {
impl IntoCtx for $typ {
#[inline]
fn into_ctx(self, dst: &mut [u8], le: super::Endian) {
assert!(dst.len() >= $size);
write_into!($typ, $size, self, dst, le);
}
}
impl TryIntoCtx<(usize, $ctx)> for $typ where $typ: IntoCtx<$ctx> {
type Error = error::Error;
#[inline]
fn try_into_ctx(self, dst: &mut [u8], (offset, le): (usize, super::Endian)) -> error::Result<()> {
if offset + $size > dst.len () {
Err(error::Error::BadRange{range: offset..offset+$size, size: dst.len()})
} else {
<$typ as IntoCtx<$ctx>>::into_ctx(self, &mut dst[offset..(offset+$size)], le);
Ok(())
}
}
}
}
}
macro_rules! from_ctx_impl {
($typ:tt, $size:expr, $ctx:ty) => {
impl<'a> FromCtx<$ctx> for $typ {
#[inline]
fn from_ctx(src: &[u8], le: $ctx) -> 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, (usize, $ctx)> for $typ where $typ: FromCtx<$ctx> {
type Error = error::Error;
#[inline]
fn try_from_ctx(src: &'a [u8], (offset, le): (usize, $ctx)) -> error::Result<Self> {
if offset + $size > src.len () {
Err(error::Error::BadRange{range: (offset..offset+$size), size: src.len()})
} else {
Ok(FromCtx::from_ctx(&src[offset..(offset + $size)], le))
}
}
}
impl<'a, T> FromCtx<$ctx, T> for $typ where T: AsRef<[u8]> {
#[inline]
fn from_ctx(src: &T, le: $ctx) -> 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, (usize, $ctx), T> for $typ where $typ: FromCtx<$ctx, T>, T: AsRef<[u8]> {
type Error = error::Error;
#[inline]
fn try_from_ctx(src: &'a T, (offset, le): (usize, $ctx)) -> error::Result<Self> {
let src = src.as_ref();
if offset + $size > src.len () {
Err(error::Error::BadRange{range: (offset..offset+$size), size: src.len()})
} else {
Ok(FromCtx::from_ctx(&src[offset..(offset + $size)], le))
}
}
}
};
}
macro_rules! ctx_impl {
($typ:tt, $size:expr) => {
from_ctx_impl!($typ, $size, super::Endian);
};
}
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);
macro_rules! from_ctx_float_impl {
($typ:tt, $size:expr, $ctx:ty) => {
impl<'a> FromCtx<$ctx> for $typ {
#[inline]
fn from_ctx(src: &[u8], le: $ctx) -> 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, (usize, $ctx)> for $typ where $typ: FromCtx<$ctx> {
type Error = error::Error;
#[inline]
fn try_from_ctx(src: &'a [u8], (offset, le): (usize, $ctx)) -> error::Result<Self> {
if offset + $size > src.len () {
Err(error::Error::BadRange{range: (offset..offset+$size), size: src.len()})
} else {
Ok(FromCtx::from_ctx(&src[offset..(offset + $size)], le))
}
}
}
}
}
from_ctx_float_impl!(f32, 4, super::Endian);
from_ctx_float_impl!(f64, 8, super::Endian);
into_ctx_impl!(u8, 1, super::Endian);
into_ctx_impl!(i8, 1, super::Endian);
into_ctx_impl!(u16, 2, super::Endian);
into_ctx_impl!(i16, 2, super::Endian);
into_ctx_impl!(u32, 4, super::Endian);
into_ctx_impl!(i32, 4, super::Endian);
into_ctx_impl!(u64, 8, super::Endian);
into_ctx_impl!(i64, 8, super::Endian);
macro_rules! into_ctx_float_impl {
($typ:tt, $size:expr, $ctx:ty) => {
impl IntoCtx for $typ {
#[inline]
fn into_ctx(self, dst: &mut [u8], le: super::Endian) {
assert!(dst.len() >= $size);
write_into!(signed_to_unsigned!($typ), $size, transmute::<$typ, signed_to_unsigned!($typ)>(self), dst, le);
}
}
impl TryIntoCtx<(usize, $ctx)> for $typ where $typ: IntoCtx<$ctx> {
type Error = error::Error;
#[inline]
fn try_into_ctx(self, dst: &mut [u8], (offset, le): (usize, super::Endian)) -> error::Result<()> {
if offset + $size > dst.len () {
Err(error::Error::BadRange{range: offset..offset+$size, size: dst.len()})
} else {
<$typ as IntoCtx<$ctx>>::into_ctx(self, &mut dst[offset..(offset+$size)], le);
Ok(())
}
}
}
}
}
into_ctx_float_impl!(f32, 4, super::Endian);
into_ctx_float_impl!(f64, 8, super::Endian);
#[inline(always)]
fn get_str_delimiter_offset(bytes: &[u8], idx: usize, delimiter: u8) -> usize {
let len = bytes.len();
let mut i = idx;
let mut byte = bytes[i];
if byte == delimiter {
return i;
}
while byte != delimiter && i < len {
byte = bytes[i];
i += 1;
}
if i < len || bytes[i - 1] == delimiter {
i -= 1;
}
i
}
impl<'a> TryFromCtx<'a, (usize, StrCtx)> for &'a str {
type Error = error::Error;
#[inline]
fn try_from_ctx(src: &'a [u8], (offset, StrCtx {delimiter}): (usize, StrCtx)) -> error::Result<Self> {
let len = src.len();
if offset >= len {
return Err(error::Error::BadOffset(offset))
}
let delimiter_offset = get_str_delimiter_offset(src, offset, delimiter);
let count = delimiter_offset - offset;
if count == 0 { return Ok("") }
let bytes = &src[offset..(offset+count)];
str::from_utf8(bytes).map_err(| _err | {
error::Error::BadInput{ range: offset..offset+count, size: bytes.len(), msg: "invalid utf8" }
})
}
}
impl<'a, T> TryFromCtx<'a, (usize, StrCtx), T> for &'a str where T: AsRef<[u8]> {
type Error = error::Error;
#[inline]
fn try_from_ctx(src: &'a T, ctx: (usize, StrCtx)) -> error::Result<Self> {
let src = src.as_ref();
TryFromCtx::try_from_ctx(src, ctx)
}
}
impl<'a> TryIntoCtx<(usize, DefaultCtx)> for &'a [u8] {
type Error = error::Error;
#[inline]
fn try_into_ctx(self, dst: &mut [u8], (uoffset, _): (usize, DefaultCtx)) -> error::Result<()> {
let src_len = self.len() as isize;
let dst_len = dst.len() as isize;
let offset = uoffset as isize;
if offset + src_len > dst_len {
Err(error::Error::BadRange{ range: uoffset..uoffset+self.len(), size: dst.len()})
} else {
unsafe { copy_nonoverlapping(self.as_ptr(), dst.as_mut_ptr().offset(offset as isize), src_len as usize) };
Ok(())
}
}
}
impl<'a> TryIntoCtx<(usize, StrCtx)> for &'a str {
type Error = error::Error;
#[inline]
fn try_into_ctx(self, dst: &mut [u8], (offset, _): (usize, StrCtx)) -> error::Result<()> {
let bytes = self.as_bytes();
TryIntoCtx::try_into_ctx(bytes, dst, (offset, CTX))
}
}
macro_rules! sizeof_impl {
($ty:ty) => {
impl SizeWith for $ty {
type Units = usize;
#[inline]
fn size_with(_ctx: &DefaultCtx) -> 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);
sizeof_impl!(f32);
sizeof_impl!(f64);
sizeof_impl!(usize);
sizeof_impl!(isize);
impl FromCtx for usize {
#[inline]
fn from_ctx(src: &[u8], le: super::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, (usize, super::Endian)> for usize where usize: FromCtx<super::Endian> {
type Error = error::Error;
#[inline]
fn try_from_ctx(src: &'a [u8], (offset, le): (usize, super::Endian)) -> error::Result<Self> {
let size = ::core::mem::size_of::<usize>();
if offset + size > src.len () {
Err(error::Error::BadRange{range: offset..offset+size, size: src.len()})
} else {
Ok(FromCtx::from_ctx(&src[offset..(offset + size)], le))
}
}
}
impl IntoCtx for usize {
#[inline]
fn into_ctx(self, dst: &mut [u8], le: super::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<(usize, super::Endian)> for usize where usize: IntoCtx<super::Endian> {
type Error = error::Error;
#[inline]
fn try_into_ctx(self, dst: &mut [u8], (offset, le): (usize, super::Endian)) -> error::Result<()> {
let size = ::core::mem::size_of::<usize>();
if offset + size > dst.len() {
Err(error::Error::BadRange{range: offset..offset+size, size: dst.len()})
} else {
<usize as IntoCtx<super::Endian>>::into_ctx(self, &mut dst[offset..(offset+size)], le);
Ok(())
}
}
}