1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
/*! Endian conversion trait

This trait declares methods that perform endian conversions on data types. For
the primitives, which are essentially atomic in structure, this conversion is
simple: flip all their bytes around. This conversion is also defined as inherent
methods on the integral primitives, so `Endian::from_be(n: i32)` is equivalent
to `::std::i32::from_be(n: i32)`
!*/

#![deny(missing_docs)]

//  Re-export the custom-derive so that users don't need two crates explicitly.
#[allow(unused_imports)]
#[macro_use]
extern crate endian_trait_derive;
pub use endian_trait_derive::*;

/// Convert a type from one endian order to another.
///
/// The standard implementation of this trait is simply to call the methods on
/// the component members of a data type which are themselves `Endian`, until the
/// call stack bottoms out at one of Rust's primitives.
pub trait Endian {
	/// Converts from host endian to big-endian order.
	///
	/// On big-endian platforms, this is a no-op and should be compiled out.
	fn to_be(self) -> Self;

	/// Converts from host endian to little-endian order.
	///
	/// On little-endian platforms, this is a no-op and should be compiled out.
	fn to_le(self) -> Self;

	/// Converts from big-endian order to host endian.
	///
	/// On big-endian platforms, this is a no-op and should be compiled out.
	fn from_be(self) -> Self;

	/// Converts from little-endian order to host endian.
	///
	/// On little-endian platforms, this is a no-op and should be compiled out.
	fn from_le(self) -> Self;
}

/// Implementing Endian on the integer primitives just means delegating to their
/// inherent methods. As there are many integer primitives, this macro kills the
/// needless code duplication.
macro_rules! implendian {
	( $( $t:tt ),* ) => { $(
		impl Endian for $t {
			#[inline(always)]
			fn from_be(self) -> Self {
				$t::from_be(self)
			}
			#[inline(always)]
			fn from_le(self) -> Self {
				$t::from_le(self)
			}
			#[inline(always)]
			fn to_be(self) -> Self {
				$t::to_be(self)
			}
			#[inline(always)]
			fn to_le(self) -> Self {
				$t::to_le(self)
			}
		}
	)* };
}

/// Implement Endian on the floats by flipping their byte repr.
///
/// The to_ conversions use bare transmute, as the result may wind up looking
/// invalid on the host architecture when used in floating-point contexts. The
/// from_ conversions use Rust's from_bits functions, as the final result must
/// be a valid local floating-point number.
///
/// The to/from _bits() APIs on f32/64 were stabilized in Rust 1.20, and thus
/// this code cannot be run on Rust version below that.
//  TODO: Figure out how to isolate this so that older Rust versions can use the
//  crate?
macro_rules! implendian_f {
	( $( $t:tt ),* ) => { $(
		impl Endian for $t {
			fn from_be(self) -> Self {
				Self::from_bits(self.to_bits().from_be())
			}
			fn from_le(self) -> Self {
				Self::from_bits(self.to_bits().from_le())
			}
			fn to_be(self) -> Self {
				Self::from_bits(self.to_bits().to_be())
			}
			fn to_le(self) -> Self {
				Self::from_bits(self.to_bits().to_le())
			}
		}
	)* };
}

/// Implement on `bool`.
///
/// `bool` is always one byte, and single bytes don`t have endian order.
impl Endian for bool {
	fn from_be(self) -> Self { self }
	fn from_le(self) -> Self { self }
	fn to_be(self) -> Self { self }
	fn to_le(self) -> Self { self }
}

/// Implement on `char`.
///
/// `char` is four bytes wide. Delegate to `u32`'s implementation and transmute.
/// This is safe ONLY IF THE CONVERSION MAKES LOGICAL SENSE
/// `char` is Unicode codepoints, NOT integers, so not all values of `u32` are
/// valid values of `char`.
/// The `to_` functions will emit potentially invalid `char` values, and this is
/// to be expected. The `from_` functions, however, will panic if they are about
/// to emit an invalid `char` byte value.
impl Endian for char {
	/// Attempts to create a local `char` from a big-endian value.
	///
	/// This function WILL panic if the local value exceeds the maximum Unicode
	/// Scalar Value permissible.
	fn from_be(self) -> Self {
		let flip: u32 = (self as u32).from_be();
		if flip > ::std::char::MAX as u32 {
			panic!("A `char` cannot have a value of {:X}", flip);
		}
		unsafe { ::std::mem::transmute(flip) }
	}

	/// Attempts to create a local `char` from a little-endian value.
	///
	/// This function WILL panic if the local value exceeds the maximum Unicode
	/// Scalar Value permissible.
	fn from_le(self) -> Self {
		let flip: u32 = (self as u32).from_le();
		if flip > ::std::char::MAX as u32 {
			panic!("A `char` cannot have a value of {:X}", flip);
		}
		unsafe { ::std::mem::transmute(flip) }
	}

	/// Converts a local `char` to big-endian.
	///
	/// This may result in a byte value that is not a valid Unicode Scalar Value
	/// and the result of this transform should be passed into a `from_be()`
	/// before using it in anything that requires `char` semantics.
	fn to_be(self) -> Self {
		unsafe { ::std::mem::transmute((self as u32).to_be()) }
	}

	/// Converts a local `char` to little-endian.
	///
	/// This may result in a byte value that is not a valid Unicode Scalar Value
	/// and the result of this transform should be passed into a `from_le()`
	/// before using it in anything that requires `char` semantics.
	fn to_le(self) -> Self {
		unsafe { ::std::mem::transmute((self as u32).to_le()) }
	}
}

//  Implement on the integer primitives
implendian!(i8, u8, i16, u16, i32, u32, i64, u64, i128, u128);

//  Implement on floats
implendian_f!(f32, f64);

#[cfg(feature = "arrays")]
mod arrays;

mod slices;