use byteorder::{BigEndian, ByteOrder, LittleEndian, NativeEndian, ReadBytesExt, WriteBytesExt};
use std::default::Default;
use std::io::{Read, Result as IoResult, Write};
use std::marker::PhantomData;
pub trait HasOpposite: private::Sealed {
type Opposite;
}
impl HasOpposite for LittleEndian {
type Opposite = BigEndian;
}
impl HasOpposite for BigEndian {
type Opposite = LittleEndian;
}
pub trait StaticNative: private::Sealed {
fn is_native() -> bool;
}
impl StaticNative for NativeEndian {
fn is_native() -> bool {
true
}
}
#[cfg(target_endian = "little")]
impl StaticNative for BigEndian {
fn is_native() -> bool {
false
}
}
#[cfg(target_endian = "big")]
impl StaticNative for LittleEndian {
fn is_native() -> bool {
false
}
}
pub trait Endian: private::Sealed {
type Opposite;
fn is_native(&self) -> bool;
fn into_opposite(self) -> Self::Opposite;
fn read_i16<R>(&self, reader: R) -> IoResult<i16>
where
R: Read;
fn read_u16<R>(&self, reader: R) -> IoResult<u16>
where
R: Read;
fn read_i32<R>(&self, reader: R) -> IoResult<i32>
where
R: Read;
fn read_u32<R>(&self, reader: R) -> IoResult<u32>
where
R: Read;
fn read_i64<R>(&self, reader: R) -> IoResult<i64>
where
R: Read;
fn read_u64<R>(&self, reader: R) -> IoResult<u64>
where
R: Read;
#[cfg(feature = "i128")]
fn read_i128<R>(&self, reader: R) -> IoResult<i128>
where
R: Read;
#[cfg(feature = "i128")]
fn read_u128<R>(&self, reader: R) -> IoResult<u128>
where
R: Read;
fn read_f32<R>(&self, reader: R) -> IoResult<f32>
where
R: Read;
fn read_f64<R>(&self, reader: R) -> IoResult<f64>
where
R: Read;
fn write_i16<W>(&self, writer: W, v: i16) -> IoResult<()>
where
W: Write;
fn write_u16<W>(&self, writer: W, v: u16) -> IoResult<()>
where
W: Write;
fn write_i32<W>(&self, writer: W, v: i32) -> IoResult<()>
where
W: Write;
fn write_u32<W>(&self, writer: W, v: u32) -> IoResult<()>
where
W: Write;
fn write_i64<W>(&self, writer: W, v: i64) -> IoResult<()>
where
W: Write;
fn write_u64<W>(&self, writer: W, v: u64) -> IoResult<()>
where
W: Write;
#[cfg(feature = "i128")]
fn write_i128<W>(&self, writer: W, v: i128) -> IoResult<()>
where
W: Write;
#[cfg(feature = "i128")]
fn write_u128<W>(&self, writer: W, v: u128) -> IoResult<()>
where
W: Write;
fn write_f32<W>(&self, writer: W, v: f32) -> IoResult<()>
where
W: Write;
fn write_f64<W>(&self, writer: W, v: f64) -> IoResult<()>
where
W: Write;
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct StaticEndianness<E>(PhantomData<E>);
impl<E> Default for StaticEndianness<E> {
fn default() -> Self {
StaticEndianness::new()
}
}
impl<E> StaticEndianness<E> {
pub fn new() -> Self {
StaticEndianness(PhantomData)
}
}
impl StaticEndianness<NativeEndian> {
pub fn native() -> Self {
StaticEndianness::new()
}
}
impl PartialEq<StaticEndianness<LittleEndian>> for StaticEndianness<BigEndian> {
fn eq(&self, _: &StaticEndianness<LittleEndian>) -> bool {
false
}
}
impl PartialEq<StaticEndianness<BigEndian>> for StaticEndianness<LittleEndian> {
fn eq(&self, _: &StaticEndianness<BigEndian>) -> bool {
false
}
}
impl PartialEq<Endianness> for StaticEndianness<BigEndian> {
fn eq(&self, e: &Endianness) -> bool {
*e == Endianness::Big
}
}
impl PartialEq<Endianness> for StaticEndianness<LittleEndian> {
fn eq(&self, e: &Endianness) -> bool {
*e == Endianness::Little
}
}
impl<E> HasOpposite for StaticEndianness<E>
where
E: HasOpposite,
{
type Opposite = StaticEndianness<E::Opposite>;
}
macro_rules! fn_static_endianness_read {
($method:ident, $e:ty, $out:ty) => {
fn $method<S>(&self, mut src: S) -> IoResult<$out>
where
S: Read,
{
src.$method::< $e >()
}
};
}
macro_rules! fn_static_endianness_write {
($method:ident, $e:ty, $out:ty) => {
fn $method<W>(&self, mut src: W, x: $out) -> IoResult<()>
where
W: Write,
{
src.$method::< $e >(x)
}
};
}
impl<E> Endian for StaticEndianness<E>
where
E: HasOpposite,
E: StaticNative,
E: ByteOrder,
{
type Opposite = StaticEndianness<E::Opposite>;
fn into_opposite(self) -> Self::Opposite {
StaticEndianness(PhantomData)
}
fn is_native(&self) -> bool {
E::is_native()
}
fn_static_endianness_read!(read_i16, E, i16);
fn_static_endianness_read!(read_u16, E, u16);
fn_static_endianness_read!(read_i32, E, i32);
fn_static_endianness_read!(read_u32, E, u32);
fn_static_endianness_read!(read_i64, E, i64);
fn_static_endianness_read!(read_u64, E, u64);
#[cfg(feature = "i128")]
fn_static_endianness_read!(read_i128, E, i128);
#[cfg(feature = "i128")]
fn_static_endianness_read!(read_u128, E, u128);
fn_static_endianness_read!(read_f32, E, f32);
fn_static_endianness_read!(read_f64, E, f64);
fn_static_endianness_write!(write_i16, E, i16);
fn_static_endianness_write!(write_u16, E, u16);
fn_static_endianness_write!(write_i32, E, i32);
fn_static_endianness_write!(write_u32, E, u32);
fn_static_endianness_write!(write_i64, E, i64);
fn_static_endianness_write!(write_u64, E, u64);
#[cfg(feature = "i128")]
fn_static_endianness_write!(write_i128, E, i128);
#[cfg(feature = "i128")]
fn_static_endianness_write!(write_u128, E, u128);
fn_static_endianness_write!(write_f32, E, f32);
fn_static_endianness_write!(write_f64, E, f64);
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, PartialOrd, Ord)]
pub enum Endianness {
Little,
Big,
}
impl From<StaticEndianness<LittleEndian>> for Endianness {
fn from(_: StaticEndianness<LittleEndian>) -> Self {
Endianness::Little
}
}
impl From<StaticEndianness<BigEndian>> for Endianness {
fn from(_: StaticEndianness<BigEndian>) -> Self {
Endianness::Big
}
}
impl PartialEq<StaticEndianness<BigEndian>> for Endianness {
fn eq(&self, _: &StaticEndianness<BigEndian>) -> bool {
*self == Endianness::Big
}
}
impl PartialEq<StaticEndianness<LittleEndian>> for Endianness {
fn eq(&self, _: &StaticEndianness<LittleEndian>) -> bool {
*self == Endianness::Little
}
}
macro_rules! fn_runtime_endianness_read {
($method:ident, $out:ty) => {
fn $method<S>(&self, mut src: S) -> IoResult<$out>
where
S: Read,
{
match *self {
Endianness::Little => src.$method::<LittleEndian>(),
Endianness::Big => src.$method::<BigEndian>(),
}
}
};
}
macro_rules! fn_runtime_endianness_write {
($method:ident, $i:ty) => {
fn $method<S>(&self, mut src: S, v: $i) -> IoResult<()>
where
S: Write,
{
match *self {
Endianness::Little => src.$method::<LittleEndian>(v),
Endianness::Big => src.$method::<BigEndian>(v),
}
}
};
}
impl HasOpposite for Endianness {
type Opposite = Self;
}
impl Endian for Endianness {
type Opposite = Self;
fn into_opposite(self) -> Self::Opposite {
self.to_opposite()
}
fn is_native(&self) -> bool {
*self == Endianness::native()
}
fn_runtime_endianness_read!(read_i16, i16);
fn_runtime_endianness_read!(read_u16, u16);
fn_runtime_endianness_read!(read_i32, i32);
fn_runtime_endianness_read!(read_u32, u32);
fn_runtime_endianness_read!(read_i64, i64);
fn_runtime_endianness_read!(read_u64, u64);
fn_runtime_endianness_read!(read_f32, f32);
fn_runtime_endianness_read!(read_f64, f64);
#[cfg(feature = "i128")]
fn_runtime_endianness_read!(read_i128, i128);
#[cfg(feature = "i128")]
fn_runtime_endianness_read!(read_u128, u128);
fn_runtime_endianness_write!(write_i16, i16);
fn_runtime_endianness_write!(write_u16, u16);
fn_runtime_endianness_write!(write_i32, i32);
fn_runtime_endianness_write!(write_u32, u32);
fn_runtime_endianness_write!(write_i64, i64);
fn_runtime_endianness_write!(write_u64, u64);
fn_runtime_endianness_write!(write_f32, f32);
fn_runtime_endianness_write!(write_f64, f64);
#[cfg(feature = "i128")]
fn_runtime_endianness_write!(write_i128, i128);
#[cfg(feature = "i128")]
fn_runtime_endianness_write!(write_u128, u128);
}
impl Endianness {
#[cfg(target_endian = "little")]
#[inline]
pub fn native() -> Self {
Endianness::Little
}
#[cfg(target_endian = "big")]
#[inline]
pub fn native() -> Self {
Endianness::Big
}
#[inline]
pub fn le_iff(e: bool) -> Self {
if e {
Endianness::Little
} else {
Endianness::Big
}
}
#[inline]
pub fn be_iff(e: bool) -> Self {
if e {
Endianness::Big
} else {
Endianness::Little
}
}
#[inline]
pub fn to_opposite(self) -> Self {
if self == Endianness::Little {
Endianness::Big
} else {
Endianness::Little
}
}
}
mod private {
use super::{Endianness, StaticEndianness};
use byteorder::{BigEndian, LittleEndian};
pub trait Sealed {}
impl Sealed for LittleEndian {}
impl Sealed for BigEndian {}
impl<T> Sealed for StaticEndianness<T> {}
impl Sealed for Endianness {}
}
#[cfg(test)]
mod tests {
use super::*;
static TEST_BYTES: &'static [u8] = &[0x12, 0x34, 0x56, 0x78, 0x21, 0x43, 0x65, 0x87];
static TEST_U64DATA_LE: &'static [u64] = &[0x87654321_78563412];
static TEST_U64DATA_BE: &'static [u64] = &[0x12345678_21436587];
#[test]
fn test_read_u64() {
let mut data = TEST_BYTES;
let e = Endianness::Little;
let words = [e.read_u64(&mut data).unwrap()];
assert_eq!(words, TEST_U64DATA_LE);
let mut data = TEST_BYTES;
let e = Endianness::Big;
let words = [e.read_u64(&mut data).unwrap()];
assert_eq!(words, TEST_U64DATA_BE);
}
static TEST_U32DATA_LE: &'static [u32] = &[0x7856_3412, 0x8765_4321];
static TEST_U32DATA_BE: &'static [u32] = &[0x1234_5678, 0x2143_6587];
#[test]
fn test_read_u32() {
let mut data = TEST_BYTES;
let e = Endianness::Little;
let words = [
e.read_u32(&mut data).unwrap(),
e.read_u32(&mut data).unwrap(),
];
assert_eq!(words, TEST_U32DATA_LE);
let mut data = TEST_BYTES;
let e = Endianness::Big;
let words = [
e.read_u32(&mut data).unwrap(),
e.read_u32(&mut data).unwrap(),
];
assert_eq!(words, TEST_U32DATA_BE);
}
static TEST_U16DATA_LE: &'static [u16] = &[0x3412, 0x7856, 0x4321, 0x8765];
static TEST_U16DATA_BE: &'static [u16] = &[0x1234, 0x5678, 0x2143, 0x6587];
#[test]
fn test_read_u16() {
let mut data = TEST_BYTES;
let e = Endianness::Little;
let words = [
e.read_u16(&mut data).unwrap(),
e.read_u16(&mut data).unwrap(),
e.read_u16(&mut data).unwrap(),
e.read_u16(&mut data).unwrap(),
];
assert_eq!(words, TEST_U16DATA_LE);
let mut data = TEST_BYTES;
let e = Endianness::Big;
let words = [
e.read_u16(&mut data).unwrap(),
e.read_u16(&mut data).unwrap(),
e.read_u16(&mut data).unwrap(),
e.read_u16(&mut data).unwrap(),
];
assert_eq!(words, TEST_U16DATA_BE);
}
#[test]
fn test_native_is_le() {
if cfg!(target_endian = "little") {
assert_eq!(Endianness::native(), Endianness::Little);
} else if cfg!(target_endian = "big") {
assert_eq!(Endianness::native(), Endianness::Big);
} else {
unreachable!();
}
}
}