rend-0.4.0/.cargo_vcs_info.json0000644000000001360000000000100117650ustar { "git": { "sha1": "53ab33c58c8d1426ac3db5a14a387b42190de365" }, "path_in_vcs": "" }rend-0.4.0/.gitignore000064400000000000000000000000250072674642500125720ustar 00000000000000/target Cargo.lock rend-0.4.0/.vscode/tasks.json000064400000000000000000000003630072674642500141700ustar 00000000000000{ "version": "2.0.0", "tasks": [ { "type": "cargo", "command": "build", "problemMatcher": [ "$rustc" ], "group": { "kind": "build", "isDefault": true }, "label": "rust: cargo build" } ] }rend-0.4.0/Cargo.toml0000644000000017620000000000100077710ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "rend" version = "0.4.0" authors = ["David Koloski "] description = "Endian-aware primitives for Rust" documentation = "https://docs.rs/rend" readme = "crates-io.md" keywords = [ "endian", "no_std", ] categories = [ "encoding", "no-std", ] license = "MIT" repository = "https://github.com/djkoloski/rend" [dependencies.bytecheck] version = "~0.6.7" optional = true default-features = false [features] default = ["std"] std = ["bytecheck/std"] validation = ["bytecheck"] rend-0.4.0/Cargo.toml.orig000064400000000000000000000012110072674642500134670ustar 00000000000000[package] name = "rend" version = "0.4.0" authors = ["David Koloski "] edition = "2018" description = "Endian-aware primitives for Rust" license = "MIT" documentation = "https://docs.rs/rend" repository = "https://github.com/djkoloski/rend" keywords = ["endian", "no_std"] categories = ["encoding", "no-std"] readme = "crates-io.md" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] bytecheck = { version = "~0.6.7", optional = true, default-features = false } [features] default = ["std"] std = ["bytecheck/std"] validation = ["bytecheck"] rend-0.4.0/LICENSE000064400000000000000000000020440072674642500116120ustar 00000000000000Copyright 2021 David Koloski Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. rend-0.4.0/README.md000064400000000000000000000023330072674642500120650ustar 00000000000000# rend   [![Latest Version]][crates.io] [![License]][license path] [Latest Version]: https://img.shields.io/crates/v/rend.svg [crates.io]: https://crates.io/crates/rend [License]: https://img.shields.io/badge/license-MIT-blue.svg [license path]: https://github.com/djkoloski/rend/blob/master/LICENSE rend is a library that provides endian-aware primitives for Rust. --- ## rend in action ```rust use rend::*; let little_int = i32_le::new(0x12345678); // Internal representation is little-endian assert_eq!([0x78, 0x56, 0x34, 0x12], unsafe { ::core::mem::transmute::<_, [u8; 4]>(little_int) }); // Can also be made with `.into()` let little_int: i32_le = 0x12345678.into(); // Still formats correctly assert_eq!("305419896", format!("{}", little_int)); assert_eq!("0x12345678", format!("0x{:x}", little_int)); let big_int = i32_be::new(0x12345678); // Internal representation is big-endian assert_eq!([0x12, 0x34, 0x56, 0x78], unsafe { ::core::mem::transmute::<_, [u8; 4]>(big_int) }); // Can also be made with `.into()` let big_int: i32_be = 0x12345678.into(); // Still formats correctly assert_eq!("305419896", format!("{}", big_int)); assert_eq!("0x12345678", format!("0x{:x}", big_int)); ```rend-0.4.0/build.rs000064400000000000000000000023470072674642500122600ustar 00000000000000use std::env; fn main() { let mut has_atomic32 = true; let mut has_atomic64 = true; let target = env::var("TARGET").unwrap(); // Full target triples that have specific limitations: match target.as_str() { "arm-linux-androideabi" | "asmjs-unknown-emscripten" | "wasm32-unknown-emscripten" | "wasm32-unknown-unknown" => has_atomic64 = false, _ => {} } // Architecture-specific limitations: let arch = target.split("-").next().unwrap_or(&target); match arch { // NOTE: Not all ARMv7 variants are listed here, as certain variants do actually provide // 64-bit atomics. (`armv7`, `armv7a`, and `armv7s`, specifically) "armv5te" | "mips" | "mipsel" | "powerpc" | "riscv32imac" | "thumbv7em" | "thumbv7m" | "thumbv8m.base" | "thumbv8m.main" | "armebv7r" | "armv7r" => has_atomic64 = false, "avr" | "riscv32i" | "riscv32imc" | "thumbv6m" => { has_atomic32 = false; has_atomic64 = false; } _ => {} } if has_atomic64 { println!("cargo:rustc-cfg=has_atomics_64"); } if has_atomic32 { println!("cargo:rustc-cfg=has_atomics"); } } rend-0.4.0/crates-io.md000064400000000000000000000016410072674642500130170ustar 00000000000000rend is a library that provides endian-aware primitives for Rust. --- ## rend in action ```rust use rend::*; let little_int = i32_le::new(0x12345678); // Internal representation is little-endian assert_eq!([0x78, 0x56, 0x34, 0x12], unsafe { ::core::mem::transmute::<_, [u8; 4]>(little_int) }); // Can also be made with `.into()` let little_int: i32_le = 0x12345678.into(); // Still formats correctly assert_eq!("305419896", format!("{}", little_int)); assert_eq!("0x12345678", format!("0x{:x}", little_int)); let big_int = i32_be::new(0x12345678); // Internal representation is big-endian assert_eq!([0x12, 0x34, 0x56, 0x78], unsafe { ::core::mem::transmute::<_, [u8; 4]>(big_int) }); // Can also be made with `.into()` let big_int: i32_be = 0x12345678.into(); // Still formats correctly assert_eq!("305419896", format!("{}", big_int)); assert_eq!("0x12345678", format!("0x{:x}", big_int)); ```rend-0.4.0/rustfmt.toml000064400000000000000000000000000072674642500131740ustar 00000000000000rend-0.4.0/src/impl_struct.rs000064400000000000000000000336040072674642500143150ustar 00000000000000#[cfg(has_atomics)] use ::core::sync::atomic::Ordering; #[cfg(has_atomics)] #[inline] pub(crate) fn failure_ordering_for(order: Ordering) -> Ordering { match order { Ordering::Relaxed => Ordering::Relaxed, Ordering::Release => Ordering::Relaxed, Ordering::Acquire => Ordering::Acquire, Ordering::AcqRel => Ordering::Acquire, Ordering::SeqCst => Ordering::SeqCst, order => order, } } macro_rules! impl_struct { (@$class:ident $endian:ident<$ne:ty> ($($const:ident)?)) => { impl $endian<$ne> { /// Creates a new value from a native-endian value #[inline] pub $($const)? fn new(value: $ne) -> Self { let value = from_native!(@$class $endian<$ne> value); Self { value: swap_bytes!(@$class $endian<$ne> value) } } /// Converts the value to a native-endian value #[inline] pub $($const)? fn value(self) -> $ne { let value = swap_bytes!(@$class $endian<$ne> self.value); to_native!(@$class $endian<$ne> value) } /// Creates a `NativeEndian` from this value #[inline] pub $($const)? fn to_ne(self) -> NativeEndian<$ne> { NativeEndian::<$ne>::new(self.value()) } /// Creates a `LittleEndian` from this value #[inline] pub $($const)? fn to_le(self) -> LittleEndian<$ne> { LittleEndian::<$ne>::new(self.value()) } /// Creates a `BigEndian` from this value #[inline] pub $($const)? fn to_be(self) -> BigEndian<$ne> { BigEndian::<$ne>::new(self.value()) } #[inline] #[allow(dead_code)] fn swap_endian(&mut self) { self.value = swap_bytes!(@$class $endian<$ne> self.value); } } }; (@signed_int $endian:ident<$ne:ty>) => { impl_struct!(@signed_int $endian<$ne> (const)); const _: () = { type Endian = $endian<$ne>; type Native = $ne; impl_binop!(Add::add); impl_binassign!(AddAssign::add_assign); impl_fmt!(Binary); impl_binop!(BitAnd::bitand); impl_binassign!(BitAndAssign::bitand_assign); impl_binop!(BitOr::bitor); impl_binassign!(BitOrAssign::bitor_assign); impl_binop!(BitXor::bitxor); impl_binassign!(BitXorAssign::bitxor_assign); impl_fmt!(Debug); impl_default!(); impl_fmt!(Display); impl_binop!(Div::div); impl_binassign!(DivAssign::div_assign); impl_eq!(); impl_from!(); impl_hash!(); impl_fmt!(LowerExp); impl_fmt!(LowerHex); impl_binop!(Mul::mul); impl_binassign!(MulAssign::mul_assign); impl_unop!(Neg::neg); impl_unop!(Not::not); impl_fmt!(Octal); impl_ord!(); impl_partial_eq!(); impl_partial_ord!(); impl_product!(); impl_binop!(Rem::rem); impl_binassign!(RemAssign::rem_assign); impl_binop!(Shl::shl); impl_binassign!(ShlAssign::shl_assign); impl_binop!(Shr::shr); impl_binassign!(ShrAssign::shr_assign); impl_binop!(Sub::sub); impl_binassign!(SubAssign::sub_assign); impl_sum!(); impl_fmt!(UpperExp); impl_fmt!(UpperHex); }; }; (@unsigned_int $endian:ident<$ne:ty>) => { impl_struct!(@unsigned_int $endian<$ne> (const)); const _: () = { type Endian = $endian<$ne>; type Native = $ne; impl_binop!(Add::add); impl_binassign!(AddAssign::add_assign); impl_fmt!(Binary); impl_binop!(BitAnd::bitand); impl_binassign!(BitAndAssign::bitand_assign); impl_binop!(BitOr::bitor); impl_binassign!(BitOrAssign::bitor_assign); impl_binop!(BitXor::bitxor); impl_binassign!(BitXorAssign::bitxor_assign); impl_fmt!(Debug); impl_default!(); impl_fmt!(Display); impl_binop!(Div::div); impl_binassign!(DivAssign::div_assign); impl_eq!(); impl_from!(); impl_hash!(); impl_fmt!(LowerExp); impl_fmt!(LowerHex); impl_binop!(Mul::mul); impl_binassign!(MulAssign::mul_assign); impl_unop!(Not::not); impl_fmt!(Octal); impl_ord!(); impl_partial_eq!(); impl_partial_ord!(); impl_product!(); impl_binop!(Rem::rem); impl_binassign!(RemAssign::rem_assign); impl_binop!(Shl::shl); impl_binassign!(ShlAssign::shl_assign); impl_binop!(Shr::shr); impl_binassign!(ShrAssign::shr_assign); impl_binop!(Sub::sub); impl_binassign!(SubAssign::sub_assign); impl_sum!(); impl_fmt!(UpperExp); impl_fmt!(UpperHex); }; }; (@float $endian:ident<$ne:ty>) => { impl_struct!(@float $endian<$ne> ()); const _: () = { type Endian = $endian<$ne>; type Native = $ne; impl_binop!(Add::add); impl_binassign!(AddAssign::add_assign); impl_fmt!(Debug); impl_default!(); impl_fmt!(Display); impl_binop!(Div::div); impl_binassign!(DivAssign::div_assign); impl_eq!(); impl_from!(); impl_fmt!(LowerExp); impl_binop!(Mul::mul); impl_binassign!(MulAssign::mul_assign); impl_unop!(Neg::neg); impl_partial_eq!(); impl_partial_ord!(); impl_product!(); impl_binop!(Rem::rem); impl_binassign!(RemAssign::rem_assign); impl_binop!(Sub::sub); impl_binassign!(SubAssign::sub_assign); impl_sum!(); impl_fmt!(UpperExp); }; }; (@char $endian:ident<$ne:ty>) => { impl_struct!(@char $endian<$ne> ()); const _: () = { type Endian = $endian<$ne>; type Native = $ne; impl_fmt!(Debug); impl_default!(); impl_fmt!(Display); impl_eq!(); impl_from!(); impl_hash!(); impl_ord!(); impl_partial_eq!(); impl_partial_ord!(); }; }; (@nonzero $endian:ident<$ne:ty> = $prim:ty) => { impl_struct!(@nonzero $endian<$ne> (const)); const _: () = { type Endian = $endian<$ne>; type Native = $ne; impl_fmt!(Binary); impl_binop!(@nonzero BitOr::bitor); impl_binassign!(@nonzero BitOrAssign::bitor_assign); impl_fmt!(Debug); impl_fmt!(Display); impl_eq!(); impl_from!(); impl_hash!(); impl_fmt!(LowerHex); impl_fmt!(Octal); impl_ord!(); impl_partial_eq!(); impl_partial_ord!(); impl_fmt!(UpperHex); }; }; (@atomic $endian:ident<$ne:ty> = $prim:ty) => { impl $endian<$ne> { /// Stores a value into the atomic integer if the current value is the same as the /// `current` value. #[inline] pub fn compare_exchange( &self, current: $prim, new: $prim, success: Ordering, failure: Ordering, ) -> Result<$prim, $prim> { match self.value.compare_exchange( swap_bytes!(@atomic $endian<$ne> current), swap_bytes!(@atomic $endian<$ne> new), success, failure, ) { Ok(x) => Ok(swap_bytes!(@atomic $endian<$ne> x)), Err(x) => Err(swap_bytes!(@atomic $endian<$ne> x)), } } /// Adds to the current value, returning the previous value. #[inline] pub fn fetch_add(&self, val: $prim, order: Ordering) -> $prim { self.fetch_update( order, $crate::impl_struct::failure_ordering_for(order), |x| Some(x + val), ).unwrap() } /// Bitwise "and" with the current value. #[inline] pub fn fetch_and(&self, val: $prim, order: Ordering) -> $prim { swap_bytes!(@atomic $endian<$ne> self.value.fetch_and(swap_bytes!(@atomic $endian<$ne> val), order) ) } /// Maximum with the current value. #[inline] pub fn fetch_max(&self, val: $prim, order: Ordering) -> $prim { self.fetch_update( order, $crate::impl_struct::failure_ordering_for(order), |x| Some(<$prim>::max(x, val)), ).unwrap() } /// Minimum with the current value. #[inline] pub fn fetch_min(&self, val: $prim, order: Ordering) -> $prim { self.fetch_update( order, $crate::impl_struct::failure_ordering_for(order), |x| Some(<$prim>::min(x, val)), ).unwrap() } /// Bitwise "nand" with the current value. #[inline] pub fn fetch_nand(&self, val: $prim, order: Ordering) -> $prim { swap_bytes!(@atomic $endian<$ne> self.value.fetch_nand(swap_bytes!(@atomic $endian<$ne> val), order) ) } /// Bitwise "or" with the current value. #[inline] pub fn fetch_or(&self, val: $prim, order: Ordering) -> $prim { swap_bytes!(@atomic $endian<$ne> self.value.fetch_or(swap_bytes!(@atomic $endian<$ne> val), order) ) } /// Subtracts from the current value, returning the previous value. #[inline] pub fn fetch_sub(&self, val: $prim, order: Ordering) -> $prim { self.fetch_update( order, $crate::impl_struct::failure_ordering_for(order), |x| Some(x - val), ).unwrap() } /// Fetches the value, and applies a function to it that returns an optional new value. /// Returns a `Result` of `Ok(previous_value)` if the function returned `Some(_)`, else /// `Err(previous_value)`. #[inline] pub fn fetch_update Option<$prim>>( &self, set_order: Ordering, fetch_order: Ordering, mut f: F, ) -> Result<$prim, $prim> { self.value.fetch_update(set_order, fetch_order, |x| { f(swap_bytes!(@atomic $endian<$ne> x)) .map(|x| swap_bytes!(@atomic $endian<$ne> x)) }) } /// Bitwise "xor" with the current value. #[inline] pub fn fetch_xor(&self, val: $prim, order: Ordering) -> $prim { swap_bytes!(@atomic $endian<$ne> self.value.fetch_xor(swap_bytes!(@atomic $endian<$ne> val), order) ) } /// Consumes the atomic and returns the contained value. #[inline] pub fn into_inner(self) -> $prim { swap_bytes!(@atomic $endian<$ne> self.value.into_inner()) } /// Loads a value from the atomic integer. #[inline] pub fn load(&self, order: Ordering) -> $prim { swap_bytes!(@atomic $endian<$ne> self.value.load(order)) } /// Creates a new atomic integer #[inline] pub const fn new(value: $prim) -> Self { Self { value: <$ne>::new(swap_bytes!(@atomic $endian<$ne> value)), } } /// Stores a value into the atomic integer. #[inline] pub fn store(&self, val: $prim, order: Ordering) { self.value.store(swap_bytes!(@atomic $endian<$ne> val), order); } /// Stores a value into the atomic integer, returning the previous value. #[inline] pub fn swap(&self, val: $prim, order: Ordering) -> $prim { swap_bytes!(@atomic $endian<$ne> self.value.swap(swap_bytes!(@atomic $endian<$ne> val), order) ) } } impl core::fmt::Debug for $endian<$ne> { #[inline] fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { swap_bytes!(@atomic $endian<$ne> self.load(Ordering::Relaxed)).fmt(f) } } impl Default for $endian<$ne> { #[inline] fn default() -> Self { Self::new(<$prim>::default()) } } impl From<$prim> for $endian<$ne> { #[inline] fn from(value: $prim) -> Self { Self::new(value) } } }; } rend-0.4.0/src/impl_traits.rs000064400000000000000000000163150072674642500142770ustar 00000000000000macro_rules! impl_unop { ($trait:ident::$fn:ident) => { impl ::core::ops::$trait for Endian { type Output = ::Output; #[inline] fn $fn(self) -> Self::Output { self.value().$fn() } } }; } macro_rules! impl_binop { ($trait:ident::$fn:ident) => { impl_binop!(@both $trait::$fn (Endian, Native)); impl_binop!(@both $trait::$fn (&'_ Endian, Native)); impl_binop!(@both $trait::$fn (Endian, &'_ Native)); impl_binop!(@both $trait::$fn (&'_ Endian, &'_ Native)); impl_binop!(@one $trait::$fn (Endian, Endian)); impl_binop!(@one $trait::$fn (&'_ Endian, Endian)); impl_binop!(@one $trait::$fn (Endian, &'_ Endian)); impl_binop!(@one $trait::$fn (&'_ Endian, &'_ Endian)); }; (@nonzero $trait:ident::$fn:ident) => { impl_binop!(@both $trait::$fn (Endian, Native)); impl_binop!(@both $trait::$fn (&'_ Endian, Native)); impl_binop!(@one $trait::$fn (Endian, Endian)); impl_binop!(@one $trait::$fn (&'_ Endian, Endian)); impl_binop!(@one $trait::$fn (Endian, &'_ Endian)); impl_binop!(@one $trait::$fn (&'_ Endian, &'_ Endian)); }; (@both $trait:ident::$fn:ident ($self:ty, $other:ty)) => { impl ::core::ops::$trait<$other> for $self { type Output = Native; #[inline] fn $fn(self, other: $other) -> Self::Output { self.value().$fn(other) } } impl ::core::ops::$trait<$self> for $other { type Output = Native; #[inline] fn $fn(self, other: $self) -> Self::Output { self.$fn(other.value()) } } }; (@one $trait:ident::$fn:ident ($self:ty, $other:ty)) => { impl ::core::ops::$trait<$other> for $self { type Output = Native; #[inline] fn $fn(self, other: $other) -> Self::Output { self.value().$fn(other.value()) } } }; } macro_rules! impl_binassign { ($trait:ident::$fn:ident) => { impl ::core::ops::$trait for Endian { #[inline] fn $fn(&mut self, other: Native) { self.swap_endian(); self.value.$fn(other); self.swap_endian(); } } impl ::core::ops::$trait for Endian { #[inline] fn $fn(&mut self, other: Endian) { self.swap_endian(); self.value.$fn(other.value()); self.swap_endian(); } } impl ::core::ops::$trait<&'_ Native> for Endian { #[inline] fn $fn(&mut self, other: &'_ Native) { self.swap_endian(); self.value.$fn(other); self.swap_endian(); } } impl ::core::ops::$trait<&'_ Endian> for Endian { #[inline] fn $fn(&mut self, other: &'_ Endian) { self.swap_endian(); self.value.$fn(other.value()); self.swap_endian(); } } }; (@nonzero $trait:ident::$fn:ident) => { impl ::core::ops::$trait for Endian { #[inline] fn $fn(&mut self, other: Native) { self.swap_endian(); self.value.$fn(other); self.swap_endian(); } } impl ::core::ops::$trait for Endian { #[inline] fn $fn(&mut self, other: Endian) { self.swap_endian(); self.value.$fn(other.value()); self.swap_endian(); } } }; } macro_rules! impl_fmt { ($trait:ident) => { impl ::core::fmt::$trait for Endian { #[inline] fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { ::core::fmt::$trait::fmt(&self.value(), f) } } }; } macro_rules! impl_default { () => { impl Default for Endian { #[inline] fn default() -> Self { Self::new(Native::default()) } } }; } macro_rules! impl_eq { () => { impl Eq for Endian {} }; } macro_rules! impl_from { () => { impl From for Endian { fn from(value: Native) -> Self { Self::new(value) } } impl<'a> From<&'a Native> for Endian { fn from(value: &'a Native) -> Self { Self::new(*value) } } impl From for Native { fn from(value: Endian) -> Self { value.value() } } impl<'a> From<&'a Endian> for Native { fn from(value: &'a Endian) -> Self { value.value() } } }; } macro_rules! impl_hash { () => { impl Hash for Endian { fn hash(&self, state: &mut H) { self.value().hash(state); } } }; } macro_rules! impl_ord { () => { impl Ord for Endian { #[inline] fn cmp(&self, other: &Self) -> ::core::cmp::Ordering { self.value().cmp(&other.value()) } } }; } macro_rules! impl_partial_eq { () => { impl PartialEq for Endian { #[inline] fn eq(&self, other: &Self) -> bool { self.value.eq(&other.value) } } impl PartialEq for Endian { #[inline] fn eq(&self, other: &Native) -> bool { self.value().eq(other) } } impl PartialEq for Native { #[inline] fn eq(&self, other: &Endian) -> bool { self.eq(&other.value()) } } }; } macro_rules! impl_partial_ord { () => { impl PartialOrd for Endian { #[inline] fn partial_cmp(&self, other: &Self) -> Option<::core::cmp::Ordering> { self.value().partial_cmp(&other.value()) } } impl PartialOrd for Endian { #[inline] fn partial_cmp(&self, other: &Native) -> Option<::core::cmp::Ordering> { self.value().partial_cmp(other) } } }; } macro_rules! impl_product { () => { impl ::core::iter::Product for Endian { #[inline] fn product>(iter: I) -> Self { Self::new(iter.map(|x| x.value()).product()) } } }; } macro_rules! impl_sum { () => { impl ::core::iter::Sum for Endian { #[inline] fn sum>(iter: I) -> Self { Self::new(iter.map(|x| x.value()).sum()) } } }; } rend-0.4.0/src/impl_validation.rs000064400000000000000000000037600072674642500151230ustar 00000000000000macro_rules! impl_validation { (@always $endian:ident<$ne:ty>) => { impl CheckBytes for $endian<$ne> { type Error = Infallible; #[inline] unsafe fn check_bytes<'a>( value: *const Self, _: &mut C, ) -> Result<&'a Self, Self::Error> { Ok(&*value) } } }; (@signed_int $endian:ident<$ne:ty>) => { impl_validation!(@always $endian<$ne>); }; (@unsigned_int $endian:ident<$ne:ty>) => { impl_validation!(@always $endian<$ne>); }; (@float $endian:ident<$ne:ty>) => { impl_validation!(@always $endian<$ne>); }; (@char $endian:ident<$ne:ty>) => { impl CheckBytes for $endian<$ne> { type Error = CharCheckError; #[inline] unsafe fn check_bytes<'a>( value: *const Self, context: &mut C, ) -> Result<&'a Self, Self::Error> { let as_u32 = &*$endian::::check_bytes(value.cast(), context)?; let c = as_u32.value(); <$ne>::from_u32(c).ok_or_else(|| CharCheckError { invalid_value: c })?; Ok(&*value) } } }; (@nonzero $endian:ident<$ne:ty> = $prim:ty) => { impl CheckBytes for $endian<$ne> { type Error = NonZeroCheckError; #[inline] unsafe fn check_bytes<'a>( value: *const Self, context: &mut C, ) -> Result<&'a Self, Self::Error> { if $endian::<$prim>::check_bytes(value.cast(), context)?.value() == 0 { Err(NonZeroCheckError::IsZero) } else { Ok(&*value) } } } }; (@atomic $endian:ident<$ne:ty> = $prim:ty) => { impl_validation!(@always $endian<$ne>); }; } rend-0.4.0/src/lib.rs000064400000000000000000000420340072674642500125130ustar 00000000000000//! # rend //! //! rend is a library that provides endian-aware primitives for Rust. //! //! It's similar in design to [`simple_endian`](https://crates.io/crates/simple_endian), but has //! support for more builtin types such as atomics and nonzero integers. It also has support for //! const functions since it does not rely on traits. //! //! rend does not provide endian-aware types for types that are inherently endian-agnostic, such as //! `bool` and `u8`. It does not provide endian-aware types for types that have an //! architecture-dependent size, such as `isize` and `usize`. It's also not extensible to custom //! types. //! //! rend is intended to be used to build portable types that can be shared between different //! architectures, especially with zero-copy deserialization. //! //! ## Features //! //! - `std`: Enables standard library support (enabled by default) //! - `validation`: Enables validation support through `bytecheck` //! //! ## Example: //! ``` //! use rend::*; //! //! let little_int = i32_le::new(0x12345678); //! // Internal representation is little-endian //! assert_eq!( //! [0x78, 0x56, 0x34, 0x12], //! unsafe { ::core::mem::transmute::<_, [u8; 4]>(little_int) } //! ); //! //! // Can also be made with `.into()` //! let little_int: i32_le = 0x12345678.into(); //! // Still formats correctly //! assert_eq!("305419896", format!("{}", little_int)); //! assert_eq!("0x12345678", format!("0x{:x}", little_int)); //! //! let big_int = i32_be::new(0x12345678); //! // Internal representation is big-endian //! assert_eq!( //! [0x12, 0x34, 0x56, 0x78], //! unsafe { ::core::mem::transmute::<_, [u8; 4]>(big_int) } //! ); //! //! // Can also be made with `.into()` //! let big_int: i32_be = 0x12345678.into(); //! // Still formats correctly //! assert_eq!("305419896", format!("{}", big_int)); //! assert_eq!("0x12345678", format!("0x{:x}", big_int)); //! ``` #![cfg_attr(not(feature = "std"), no_std)] #![deny( missing_docs, rustdoc::missing_crate_level_docs, rust_2018_compatibility, rust_2018_idioms, future_incompatible, nonstandard_style, unused, clippy::all )] #[macro_use] mod impl_struct; #[macro_use] mod impl_traits; #[cfg(feature = "validation")] #[macro_use] mod impl_validation; #[cfg(feature = "validation")] use bytecheck::{CharCheckError, CheckBytes, NonZeroCheckError}; #[cfg(feature = "validation")] use core::convert::Infallible; #[cfg(has_atomics)] use core::sync::atomic::{AtomicI16, AtomicI32, AtomicU16, AtomicU32, Ordering}; #[cfg(has_atomics_64)] use core::sync::atomic::{AtomicI64, AtomicU64}; use core::{ hash::{Hash, Hasher}, num::{ NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64, }, }; /// A type that has an associated cross-endian storage type. pub unsafe trait Primitive { /// An endian-agnostic type that can represent the primitve in both little- and big-endian /// forms. type Storage; } /// A wrapper for native-endian types. /// /// This is mostly useful for `const` conversions to big- and little-endian types in contexts where /// type inference is required. Because it's native-endian, the inner value is publicly exposed. #[derive(Clone, Copy)] #[repr(transparent)] pub struct NativeEndian { /// The value of the type pub value: T, } /// A wrapper for big-endian types. #[derive(Clone, Copy)] #[repr(transparent)] pub struct LittleEndian { value: T::Storage, } /// A wrapper for little-endian types. #[derive(Clone, Copy)] #[repr(transparent)] pub struct BigEndian { value: T::Storage, } macro_rules! swap_endian { (@NativeEndian $expr:expr) => {{ $expr }}; (@LittleEndian $expr:expr) => {{ #[cfg(target_endian = "little")] { $expr } #[cfg(target_endian = "big")] { $expr.swap_bytes() } }}; (@BigEndian $expr:expr) => {{ #[cfg(target_endian = "little")] { $expr.swap_bytes() } #[cfg(target_endian = "big")] { $expr } }}; } macro_rules! swap_bytes { (@signed_int $endian:ident<$ne:ty> $value:expr) => { swap_endian!(@$endian $value) }; (@unsigned_int $endian:ident<$ne:ty> $value:expr) => { swap_endian!(@$endian $value) }; (@float $endian:ident<$ne:ty> $value:expr) => { <$ne>::from_bits(swap_endian!(@$endian $value.to_bits())) }; (@char $endian:ident<$ne:ty> $value:expr) => { swap_endian!(@$endian $value) }; (@nonzero $endian:ident<$ne:ty> $value:expr) => { unsafe { <$ne>::new_unchecked(swap_endian!(@$endian $value.get())) } }; (@atomic $endian:ident<$ne:ty> $value:expr) => { swap_endian!(@$endian $value) }; } macro_rules! from_native { (@char NativeEndian<$ne:ty> $value:expr) => { $value }; (@char $endian:ident<$ne:ty> $value:expr) => { $value as u32 }; (@$class:ident $endian:ident<$ne:ty> $value:expr) => { $value }; } macro_rules! to_native { (@char NativeEndian<$ne:ty> $value:expr) => { $value }; (@char $endian:ident<$ne:ty> $value:expr) => { unsafe { char::from_u32_unchecked($value) } }; (@$class:ident $endian:ident<$ne:ty> $value:expr) => { $value }; } macro_rules! impl_endian { ( @$class:ident $native:ty $(= $prim:ty)?, $ne:ident = $ne_doc:literal, $le:ident = $le_doc:literal, $be:ident = $be_doc:literal ) => { impl_endian!(@$class $ne_doc NativeEndian<$native> as $ne $(= $prim)?); impl_endian!(@$class $le_doc LittleEndian<$native> as $le $(= $prim)?); impl_endian!(@$class $be_doc BigEndian<$native> as $be $(= $prim)?); }; (@$class:ident $doc:literal $endian:ident<$ne:ty> as $alias:ident $(= $prim:ty)?) => { impl_struct!(@$class $endian<$ne> $(= $prim)?); #[cfg(feature = "validation")] impl_validation!(@$class $endian<$ne> $(= $prim)?); #[doc = "Alias for "] #[doc = $doc] #[doc = "."] #[allow(non_camel_case_types)] pub type $alias = $endian<$ne>; }; } macro_rules! impl_primitive { ($($ty:ty),+ $(,)?) => { $( unsafe impl Primitive for $ty { type Storage = $ty; } )+ }; } impl_primitive!( i16, i32, i64, i128, u16, u32, u64, u128, f32, f64, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, ); #[cfg(has_atomics)] impl_primitive!(AtomicI16, AtomicI32, AtomicU16, AtomicU32); #[cfg(has_atomics_64)] impl_primitive!(AtomicI64, AtomicU64); unsafe impl Primitive for char { type Storage = u32; } impl_endian!( @signed_int i16, i16_ne = "`NativeEndian`", i16_le = "`LittleEndian`", i16_be = "`BigEndian`" ); impl_endian!( @signed_int i32, i32_ne = "`NativeEndian`", i32_le = "`LittleEndian`", i32_be = "`BigEndian`" ); impl_endian!( @signed_int i64, i64_ne = "`NativeEndian`", i64_le = "`LittleEndian`", i64_be = "`BigEndian`" ); impl_endian!( @signed_int i128, i128_ne = "`NativeEndian`", i128_le = "`LittleEndian`", i128_be = "`BigEndian`" ); impl_endian!( @unsigned_int u16, u16_ne = "`NativeEndian`", u16_le = "`LittleEndian`", u16_be = "`BigEndian`" ); impl_endian!( @unsigned_int u32, u32_ne = "`NativeEndian`", u32_le = "`LittleEndian`", u32_be = "`BigEndian`" ); impl_endian!( @unsigned_int u64, u64_ne = "`NativeEndian`", u64_le = "`LittleEndian`", u64_be = "`BigEndian`" ); impl_endian!( @unsigned_int u128, u128_ne = "`NativeEndian`", u128_le = "`LittleEndian`", u128_be = "`BigEndian`" ); impl_endian!( @float f32, f32_ne = "`NativeEndian`", f32_le = "`LittleEndian`", f32_be = "`BigEndian`" ); impl_endian!( @float f64, f64_ne = "`NativeEndian`", f64_le = "`LittleEndian`", f64_be = "`BigEndian`" ); impl_endian!( @char char, char_ne = "`NativeEndian`", char_le = "`LittleEndian`", char_be = "`BigEndian`" ); impl_endian!( @nonzero NonZeroI16 = i16, NonZeroI16_ne = "`NativeEndian`", NonZeroI16_le = "`LittleEndian`", NonZeroI16_be = "`BigEndian`" ); impl_endian!( @nonzero NonZeroI32 = i32, NonZeroI32_ne = "`NativeEndian`", NonZeroI32_le = "`LittleEndian`", NonZeroI32_be = "`BigEndian`" ); impl_endian!( @nonzero NonZeroI64 = i64, NonZeroI64_ne = "`NativeEndian`", NonZeroI64_le = "`LittleEndian`", NonZeroI64_be = "`BigEndian`" ); impl_endian!( @nonzero NonZeroI128 = i128, NonZeroI128_ne = "`NativeEndian`", NonZeroI128_le = "`LittleEndian`", NonZeroI128_be = "`BigEndian`" ); impl_endian!( @nonzero NonZeroU16 = u16, NonZeroU16_ne = "`NativeEndian`", NonZeroU16_le = "`LittleEndian`", NonZeroU16_be = "`BigEndian`" ); impl_endian!( @nonzero NonZeroU32 = u32, NonZeroU32_ne = "`NativeEndian`", NonZeroU32_le = "`LittleEndian`", NonZeroU32_be = "`BigEndian`" ); impl_endian!( @nonzero NonZeroU64 = u64, NonZeroU64_ne = "`NativeEndian`", NonZeroU64_le = "`LittleEndian`", NonZeroU64_be = "`BigEndian`" ); impl_endian!( @nonzero NonZeroU128 = u128, NonZeroU128_ne = "`NativeEndian`", NonZeroU128_le = "`LittleEndian`", NonZeroU128_be = "`BigEndian`" ); #[cfg(has_atomics)] impl_endian!( @atomic AtomicI16 = i16, AtomicI16_ne = "`NativeEndian`", AtomicI16_le = "`LittleEndian`", AtomicI16_be = "`BigEndian`" ); #[cfg(has_atomics)] impl_endian!( @atomic AtomicI32 = i32, AtomicI32_ne = "`NativeEndian`", AtomicI32_le = "`LittleEndian`", AtomicI32_be = "`BigEndian`" ); #[cfg(has_atomics_64)] impl_endian!( @atomic AtomicI64 = i64, AtomicI64_ne = "`NativeEndian`", AtomicI64_le = "`LittleEndian`", AtomicI64_be = "`BigEndian`" ); #[cfg(has_atomics)] impl_endian!( @atomic AtomicU16 = u16, AtomicU16_ne = "`NativeEndian`", AtomicU16_le = "`LittleEndian`", AtomicU16_be = "`BigEndian`" ); #[cfg(has_atomics)] impl_endian!( @atomic AtomicU32 = u32, AtomicU32_ne = "`NativeEndian`", AtomicU32_le = "`LittleEndian`", AtomicU32_be = "`BigEndian`" ); #[cfg(has_atomics_64)] impl_endian!( @atomic AtomicU64 = u64, AtomicU64_ne = "`NativeEndian`", AtomicU64_le = "`LittleEndian`", AtomicU64_be = "`BigEndian`" ); #[cfg(test)] mod tests { use crate::*; use core::mem; #[test] fn endian_representation() { unsafe { // i16 assert_eq!( [0x01, 0x02], mem::transmute::<_, [u8; 2]>(i16_be::new(0x0102)) ); assert_eq!( [0x02, 0x01], mem::transmute::<_, [u8; 2]>(i16_le::new(0x0102)) ); // i32 assert_eq!( [0x01, 0x02, 0x03, 0x04], mem::transmute::<_, [u8; 4]>(i32_be::new(0x01020304)) ); assert_eq!( [0x04, 0x03, 0x02, 0x01], mem::transmute::<_, [u8; 4]>(i32_le::new(0x01020304)) ); // i64 assert_eq!( [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08], mem::transmute::<_, [u8; 8]>(i64_be::new(0x0102030405060708)) ); assert_eq!( [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01], mem::transmute::<_, [u8; 8]>(i64_le::new(0x0102030405060708)) ); // i128 assert_eq!( [ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 ], mem::transmute::<_, [u8; 16]>(i128_be::new(0x0102030405060708090a0b0c0d0e0f10)) ); assert_eq!( [ 0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01 ], mem::transmute::<_, [u8; 16]>(i128_le::new(0x0102030405060708090a0b0c0d0e0f10)) ); // u16 assert_eq!( [0x01, 0x02], mem::transmute::<_, [u8; 2]>(u16_be::new(0x0102)) ); assert_eq!( [0x02, 0x01], mem::transmute::<_, [u8; 2]>(u16_le::new(0x0102)) ); // u32 assert_eq!( [0x01, 0x02, 0x03, 0x04], mem::transmute::<_, [u8; 4]>(u32_be::new(0x01020304)) ); assert_eq!( [0x04, 0x03, 0x02, 0x01], mem::transmute::<_, [u8; 4]>(u32_le::new(0x01020304)) ); // u64 assert_eq!( [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08], mem::transmute::<_, [u8; 8]>(u64_be::new(0x0102030405060708)) ); assert_eq!( [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01], mem::transmute::<_, [u8; 8]>(u64_le::new(0x0102030405060708)) ); // u128 assert_eq!( [ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 ], mem::transmute::<_, [u8; 16]>(u128_be::new(0x0102030405060708090a0b0c0d0e0f10)) ); assert_eq!( [ 0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01 ], mem::transmute::<_, [u8; 16]>(u128_le::new(0x0102030405060708090a0b0c0d0e0f10)) ); // f32 assert_eq!( [0x40, 0x49, 0x0f, 0xdb], mem::transmute::<_, [u8; 4]>(f32_be::new(core::f32::consts::PI)) ); assert_eq!( [0xdb, 0x0f, 0x49, 0x40], mem::transmute::<_, [u8; 4]>(f32_le::new(core::f32::consts::PI)) ); // f64 assert_eq!( [0x40, 0x09, 0x21, 0xfb, 0x54, 0x44, 0x2d, 0x18], mem::transmute::<_, [u8; 8]>(f64_be::new(core::f64::consts::PI)) ); assert_eq!( [0x18, 0x2d, 0x44, 0x54, 0xfb, 0x21, 0x09, 0x40], mem::transmute::<_, [u8; 8]>(f64_le::new(core::f64::consts::PI)) ); // char assert_eq!( [0x00, 0x01, 0xf3, 0x89], mem::transmute::<_, [u8; 4]>(char_be::new('🎉')) ); assert_eq!( [0x89, 0xf3, 0x01, 0x00], mem::transmute::<_, [u8; 4]>(char_le::new('🎉')) ); // AtomicU16 #[cfg(has_atomics)] assert_eq!( [0x01, 0x02], mem::transmute::<_, [u8; 2]>(AtomicU16_be::new(0x0102)) ); #[cfg(has_atomics)] assert_eq!( [0x02, 0x01], mem::transmute::<_, [u8; 2]>(AtomicU16_le::new(0x0102)) ); // AtomicU32 #[cfg(has_atomics)] assert_eq!( [0x01, 0x02, 0x03, 0x04], mem::transmute::<_, [u8; 4]>(AtomicU32_be::new(0x01020304)) ); #[cfg(has_atomics)] assert_eq!( [0x04, 0x03, 0x02, 0x01], mem::transmute::<_, [u8; 4]>(AtomicU32_le::new(0x01020304)) ); // AtomicU64 #[cfg(has_atomics_64)] assert_eq!( [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08], mem::transmute::<_, [u8; 8]>(AtomicU64_be::new(0x0102030405060708)) ); #[cfg(has_atomics_64)] assert_eq!( [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01], mem::transmute::<_, [u8; 8]>(AtomicU64_le::new(0x0102030405060708)) ); } } }