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;