// SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT//// Copyright 2019 The Fuchsia Authors//// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.// This file may not be copied, modified, or distributed except according to// those terms. //! Byte order-aware numeric primitives.//!//! This module contains equivalents of the native multi-byte integer types with//! no alignment requirement and supporting byte order conversions.//!//! For each native multi-byte integer type - `u16`, `i16`, `u32`, etc - and//! floating point type - `f32` and `f64` - an equivalent type is defined by//! this module - [`U16`], [`I16`], [`U32`], [`F32`], [`F64`], etc. Unlike their//! native counterparts, these types have alignment 1, and take a type parameter//! specifying the byte order in which the bytes are stored in memory. Each type//! implements this crate's relevant conversion and marker traits.//!//! These two properties, taken together, make these types useful for defining//! data structures whose memory layout matches a wire format such as that of a//! network protocol or a file format. Such formats often have multi-byte values//! at offsets that do not respect the alignment requirements of the equivalent//! native types, and stored in a byte order not necessarily the same as that of//! the target platform.//!//! Type aliases are provided for common byte orders in the [`big_endian`],//! [`little_endian`], [`network_endian`], and [`native_endian`] submodules.//! Note that network-endian is a synonym for big-endian.//!//! # Example//!//! One use of these types is for representing network packet formats, such as//! UDP://!//! ```rust//! use zerocopy::{*, byteorder::network_endian::U16};//! # use zerocopy_derive::*;//!//! #[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]//! #[repr(C)]//! struct UdpHeader {//! src_port: U16,//! dst_port: U16,//! length: U16,//! checksum: U16,//! }//!//! #[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]//! #[repr(C, packed)]//! struct UdpPacket {//! header: UdpHeader,//! body: [u8],//! }//!//! impl UdpPacket {//! fn parse(bytes: &[u8]) -> Option<&UdpPacket> {//! UdpPacket::ref_from_bytes(bytes).ok()//! }//! }//! ``` use core::{ convert::{TryFrom, TryInto}, fmt::{Binary, Debug, LowerHex, Octal, UpperHex}, hash::Hash, num::TryFromIntError,}; use super::*; /// A type-level representation of byte order.////// This type is implemented by [`BigEndian`] and [`LittleEndian`], which/// represent big-endian and little-endian byte order respectively. This module/// also provides a number of useful aliases for those types: [`NativeEndian`],/// [`NetworkEndian`], [`BE`], and [`LE`].////// `ByteOrder` types can be used to specify the byte order of the types in this/// module - for example, [`U32<BigEndian>`] is a 32-bit integer stored in/// big-endian byte order.////// [`U32<BigEndian>`]: U32pub trait ByteOrder: Copy + Clone + Debug + Display + Eq + PartialEq + Ord + PartialOrd + Hash + private::Sealed{ #[doc(hidden)] const ORDER: Order;} mod private { pub trait Sealed {} impl Sealed for super::BigEndian {} impl Sealed for super::LittleEndian {}} #[allow(missing_copy_implementations, missing_debug_implementations)]#[doc(hidden)]#[derive(PartialEq)]pub enum Order { BigEndian, LittleEndian,} /// Big-endian byte order.////// See [`ByteOrder`] for more details.#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]pub enum BigEndian {} impl ByteOrder for BigEndian { const ORDER: Order = Order::BigEndian;} impl Display for BigEndian { #[inline] fn fmt(&self, _: &mut Formatter<'_>) -> fmt::Result { match *self {} }} /// Little-endian byte order.////// See [`ByteOrder`] for more details.#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]pub enum LittleEndian {} impl ByteOrder for LittleEndian { const ORDER: Order = Order::LittleEndian;} impl Display for LittleEndian { #[inline] fn fmt(&self, _: &mut Formatter<'_>) -> fmt::Result { match *self {} }} /// The endianness used by this platform.////// This is a type alias for [`BigEndian`] or [`LittleEndian`] depending on the/// endianness of the target platform.#[cfg(target_endian = "big")]pub type NativeEndian = BigEndian; /// The endianness used by this platform.////// This is a type alias for [`BigEndian`] or [`LittleEndian`] depending on the/// endianness of the target platform.#[cfg(target_endian = "little")]pub type NativeEndian = LittleEndian; /// The endianness used in many network protocols.////// This is a type alias for [`BigEndian`].pub type NetworkEndian = BigEndian; /// A type alias for [`BigEndian`].pub type BE = BigEndian; /// A type alias for [`LittleEndian`].pub type LE = LittleEndian; macro_rules! impl_dbg_trait { ($name:ident, $native:ident) => { impl<O: ByteOrder> Debug for $name<O> { #[inline] fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { // This results in a format like "U16(42)". f.debug_tuple(stringify!($name)).field(&self.get()).finish() } } };} macro_rules! impl_dbg_traits { ($name:ident, $native:ident, "floating point number") => { #[cfg(not(no_fp_fmt_parse))] impl_dbg_trait!($name, $native); #[cfg(no_fp_fmt_parse)] impl<O: ByteOrder> Debug for $name<O> { #[inline] fn fmt(&self, _f: &mut Formatter<'_>) -> fmt::Result { panic!("floating point support is turned off"); } } }; ($name:ident, $native:ident, "unsigned integer") => { impl_dbg_traits!($name, $native, @all_types); }; ($name:ident, $native:ident, "signed integer") => { impl_dbg_traits!($name, $native, @all_types); }; ($name:ident, $native:ident, @all_types) => { impl_dbg_trait!($name, $native); };} macro_rules! impl_fmt_trait { ($name:ident, $native:ident, $trait:ident) => { impl<O: ByteOrder> $trait for $name<O> { #[inline(always)] fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { $trait::fmt(&self.get(), f) } } };} macro_rules! impl_fmt_traits { ($name:ident, $native:ident, "floating point number") => { #[cfg(not(no_fp_fmt_parse))] impl_fmt_trait!($name, $native, Display); }; ($name:ident, $native:ident, "unsigned integer") => { impl_fmt_traits!($name, $native, @all_types); }; ($name:ident, $native:ident, "signed integer") => { impl_fmt_traits!($name, $native, @all_types); }; ($name:ident, $native:ident, @all_types) => { impl_fmt_trait!($name, $native, Display); impl_fmt_trait!($name, $native, Octal); impl_fmt_trait!($name, $native, LowerHex); impl_fmt_trait!($name, $native, UpperHex); impl_fmt_trait!($name, $native, Binary); };} macro_rules! impl_ops_traits { ($name:ident, $native:ident, "floating point number") => { impl_ops_traits!($name, $native, @all_types); impl_ops_traits!($name, $native, @signed_integer_floating_point); impl<O: ByteOrder> PartialOrd for $name<O> { #[inline(always)] fn partial_cmp(&self, other: &Self) -> Option<Ordering> { self.get().partial_cmp(&other.get()) } } }; ($name:ident, $native:ident, "unsigned integer") => { impl_ops_traits!($name, $native, @signed_unsigned_integer); impl_ops_traits!($name, $native, @all_types); }; ($name:ident, $native:ident, "signed integer") => { impl_ops_traits!($name, $native, @signed_unsigned_integer); impl_ops_traits!($name, $native, @signed_integer_floating_point); impl_ops_traits!($name, $native, @all_types); }; ($name:ident, $native:ident, @signed_unsigned_integer) => { impl_ops_traits!(@without_byteorder_swap $name, $native, BitAnd, bitand, BitAndAssign, bitand_assign); impl_ops_traits!(@without_byteorder_swap $name, $native, BitOr, bitor, BitOrAssign, bitor_assign); impl_ops_traits!(@without_byteorder_swap $name, $native, BitXor, bitxor, BitXorAssign, bitxor_assign); impl_ops_traits!(@with_byteorder_swap $name, $native, Shl, shl, ShlAssign, shl_assign); impl_ops_traits!(@with_byteorder_swap $name, $native, Shr, shr, ShrAssign, shr_assign); impl<O> core::ops::Not for $name<O> { type Output = $name<O>; #[inline(always)] fn not(self) -> $name<O> { let self_native = $native::from_ne_bytes(self.0); $name((!self_native).to_ne_bytes(), PhantomData) } } impl<O: ByteOrder> PartialOrd for $name<O> { #[inline(always)] fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) } } impl<O: ByteOrder> Ord for $name<O> { #[inline(always)] fn cmp(&self, other: &Self) -> Ordering { self.get().cmp(&other.get()) } } impl<O: ByteOrder> PartialOrd<$native> for $name<O> { #[inline(always)] fn partial_cmp(&self, other: &$native) -> Option<Ordering> { self.get().partial_cmp(other) } } }; ($name:ident, $native:ident, @signed_integer_floating_point) => { impl<O: ByteOrder> core::ops::Neg for $name<O> { type Output = $name<O>; #[inline(always)] fn neg(self) -> $name<O> { let self_native: $native = self.get(); #[allow(clippy::arithmetic_side_effects)] $name::<O>::new(-self_native) } } }; ($name:ident, $native:ident, @all_types) => { impl_ops_traits!(@with_byteorder_swap $name, $native, Add, add, AddAssign, add_assign); impl_ops_traits!(@with_byteorder_swap $name, $native, Div, div, DivAssign, div_assign); impl_ops_traits!(@with_byteorder_swap $name, $native, Mul, mul, MulAssign, mul_assign); impl_ops_traits!(@with_byteorder_swap $name, $native, Rem, rem, RemAssign, rem_assign); impl_ops_traits!(@with_byteorder_swap $name, $native, Sub, sub, SubAssign, sub_assign); }; (@with_byteorder_swap $name:ident, $native:ident, $trait:ident, $method:ident, $trait_assign:ident, $method_assign:ident) => { impl<O: ByteOrder> core::ops::$trait<$name<O>> for $name<O> { type Output = $name<O>; #[inline(always)] fn $method(self, rhs: $name<O>) -> $name<O> { let self_native: $native = self.get(); let rhs_native: $native = rhs.get(); let result_native = core::ops::$trait::$method(self_native, rhs_native); $name::<O>::new(result_native) } } impl<O: ByteOrder> core::ops::$trait<$name<O>> for $native { type Output = $name<O>; #[inline(always)] fn $method(self, rhs: $name<O>) -> $name<O> { let rhs_native: $native = rhs.get(); let result_native = core::ops::$trait::$method(self, rhs_native); $name::<O>::new(result_native) } } impl<O: ByteOrder> core::ops::$trait<$native> for $name<O> { type Output = $name<O>; #[inline(always)] fn $method(self, rhs: $native) -> $name<O> { let self_native: $native = self.get(); let result_native = core::ops::$trait::$method(self_native, rhs); $name::<O>::new(result_native) } } impl<O: ByteOrder> core::ops::$trait_assign<$name<O>> for $name<O> { #[inline(always)] fn $method_assign(&mut self, rhs: $name<O>) { *self = core::ops::$trait::$method(*self, rhs); } } impl<O: ByteOrder> core::ops::$trait_assign<$name<O>> for $native { #[inline(always)] fn $method_assign(&mut self, rhs: $name<O>) { let rhs_native: $native = rhs.get(); *self = core::ops::$trait::$method(*self, rhs_native); } } impl<O: ByteOrder> core::ops::$trait_assign<$native> for $name<O> { #[inline(always)] fn $method_assign(&mut self, rhs: $native) { *self = core::ops::$trait::$method(*self, rhs); } } }; // Implement traits in terms of the same trait on the native type, but // without performing a byte order swap when both operands are byteorder // types. This only works for bitwise operations like `&`, `|`, etc. // // When only one operand is a byteorder type, we still need to perform a // byteorder swap. (@without_byteorder_swap $name:ident, $native:ident, $trait:ident, $method:ident, $trait_assign:ident, $method_assign:ident) => { impl<O: ByteOrder> core::ops::$trait<$name<O>> for $name<O> { type Output = $name<O>; #[inline(always)] fn $method(self, rhs: $name<O>) -> $name<O> { let self_native = $native::from_ne_bytes(self.0); let rhs_native = $native::from_ne_bytes(rhs.0); let result_native = core::ops::$trait::$method(self_native, rhs_native); $name(result_native.to_ne_bytes(), PhantomData) } } impl<O: ByteOrder> core::ops::$trait<$name<O>> for $native { type Output = $name<O>; #[inline(always)] fn $method(self, rhs: $name<O>) -> $name<O> { // No runtime cost - just byte packing let rhs_native = $native::from_ne_bytes(rhs.0); // (Maybe) runtime cost - byte order swap let slf_byteorder = $name::<O>::new(self); // No runtime cost - just byte packing let slf_native = $native::from_ne_bytes(slf_byteorder.0); // Runtime cost - perform the operation let result_native = core::ops::$trait::$method(slf_native, rhs_native); // No runtime cost - just byte unpacking $name(result_native.to_ne_bytes(), PhantomData) } } impl<O: ByteOrder> core::ops::$trait<$native> for $name<O> { type Output = $name<O>; #[inline(always)] fn $method(self, rhs: $native) -> $name<O> { // (Maybe) runtime cost - byte order swap let rhs_byteorder = $name::<O>::new(rhs); // No runtime cost - just byte packing let rhs_native = $native::from_ne_bytes(rhs_byteorder.0); // No runtime cost - just byte packing let slf_native = $native::from_ne_bytes(self.0); // Runtime cost - perform the operation let result_native = core::ops::$trait::$method(slf_native, rhs_native); // No runtime cost - just byte unpacking $name(result_native.to_ne_bytes(), PhantomData) } } impl<O: ByteOrder> core::ops::$trait_assign<$name<O>> for $name<O> { #[inline(always)] fn $method_assign(&mut self, rhs: $name<O>) { *self = core::ops::$trait::$method(*self, rhs); } } impl<O: ByteOrder> core::ops::$trait_assign<$name<O>> for $native { #[inline(always)] fn $method_assign(&mut self, rhs: $name<O>) { // (Maybe) runtime cost - byte order swap let rhs_native = rhs.get(); // Runtime cost - perform the operation *self = core::ops::$trait::$method(*self, rhs_native); } } impl<O: ByteOrder> core::ops::$trait_assign<$native> for $name<O> { #[inline(always)] fn $method_assign(&mut self, rhs: $native) { *self = core::ops::$trait::$method(*self, rhs); } } };} macro_rules! doc_comment { ($x:expr, $($tt:tt)*) => { #[doc = $x] $($tt)* };} macro_rules! define_max_value_constant { ($name:ident, $bytes:expr, "unsigned integer") => { /// The maximum value. /// /// This constant should be preferred to constructing a new value using /// `new`, as `new` may perform an endianness swap depending on the /// endianness `O` and the endianness of the platform. pub const MAX_VALUE: $name<O> = $name([0xFFu8; $bytes], PhantomData); }; // We don't provide maximum and minimum value constants for signed values // and floats because there's no way to do it generically - it would require // a different value depending on the value of the `ByteOrder` type // parameter. Currently, one workaround would be to provide implementations // for concrete implementations of that trait. In the long term, if we are // ever able to make the `new` constructor a const fn, we could use that // instead. ($name:ident, $bytes:expr, "signed integer") => {}; ($name:ident, $bytes:expr, "floating point number") => {};} macro_rules! define_type { ( $article:ident, $description:expr, $name:ident, $native:ident, $bits:expr, $bytes:expr, $from_be_fn:path, $to_be_fn:path, $from_le_fn:path, $to_le_fn:path, $number_kind:tt, [$($larger_native:ty),*], [$($larger_native_try:ty),*], [$($larger_byteorder:ident),*], [$($larger_byteorder_try:ident),*] ) => { doc_comment! { concat!($description, " stored in a given byte order. `", stringify!($name), "` is like the native `", stringify!($native), "` type withtwo major differences: First, it has no alignment requirement (its alignment is 1).Lines 1–500 of 1596. Load more
// SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT // // Copyright 2019 The Fuchsia Authors // // Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 // <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT // license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. // This file may not be copied, modified, or distributed except according to // those terms. //! Byte order-aware numeric primitives. //! //! This module contains equivalents of the native multi-byte integer types with //! no alignment requirement and supporting byte order conversions. //! //! For each native multi-byte integer type - `u16`, `i16`, `u32`, etc - and //! floating point type - `f32` and `f64` - an equivalent type is defined by //! this module - [`U16`], [`I16`], [`U32`], [`F32`], [`F64`], etc. Unlike their //! native counterparts, these types have alignment 1, and take a type parameter //! specifying the byte order in which the bytes are stored in memory. Each type //! implements this crate's relevant conversion and marker traits. //! //! These two properties, taken together, make these types useful for defining //! data structures whose memory layout matches a wire format such as that of a //! network protocol or a file format. Such formats often have multi-byte values //! at offsets that do not respect the alignment requirements of the equivalent //! native types, and stored in a byte order not necessarily the same as that of //! the target platform. //! //! Type aliases are provided for common byte orders in the [`big_endian`], //! [`little_endian`], [`network_endian`], and [`native_endian`] submodules. //! Note that network-endian is a synonym for big-endian. //! //! # Example //! //! One use of these types is for representing network packet formats, such as //! UDP: //! //! ```rust //! use zerocopy::{*, byteorder::network_endian::U16}; //! # use zerocopy_derive::*; //! //! #[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)] //! #[repr(C)] //! struct UdpHeader { //! src_port: U16, //! dst_port: U16, //! length: U16, //! checksum: U16, //! } //! //! #[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)] //! #[repr(C, packed)] //! struct UdpPacket { //! header: UdpHeader, //! body: [u8], //! } //! //! impl UdpPacket { //! fn parse(bytes: &[u8]) -> Option<&UdpPacket> { //! UdpPacket::ref_from_bytes(bytes).ok() //! } //! } //! ``` use core::{ convert::{TryFrom, TryInto}, fmt::{Binary, Debug, LowerHex, Octal, UpperHex}, hash::Hash, num::TryFromIntError, }; use super::*; /// A type-level representation of byte order. /// /// This type is implemented by [`BigEndian`] and [`LittleEndian`], which /// represent big-endian and little-endian byte order respectively. This module /// also provides a number of useful aliases for those types: [`NativeEndian`], /// [`NetworkEndian`], [`BE`], and [`LE`]. /// /// `ByteOrder` types can be used to specify the byte order of the types in this /// module - for example, [`U32<BigEndian>`] is a 32-bit integer stored in /// big-endian byte order. /// /// [`U32<BigEndian>`]: U32 pub trait ByteOrder: Copy + Clone + Debug + Display + Eq + PartialEq + Ord + PartialOrd + Hash + private::Sealed { #[doc(hidden)] const ORDER: Order; } mod private { pub trait Sealed {} impl Sealed for super::BigEndian {} impl Sealed for super::LittleEndian {} } #[allow(missing_copy_implementations, missing_debug_implementations)] #[doc(hidden)] #[derive(PartialEq)] pub enum Order { BigEndian, LittleEndian, } /// Big-endian byte order. /// /// See [`ByteOrder`] for more details. #[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] pub enum BigEndian {} impl ByteOrder for BigEndian { const ORDER: Order = Order::BigEndian; } impl Display for BigEndian { #[inline] fn fmt(&self, _: &mut Formatter<'_>) -> fmt::Result { match *self {} } } /// Little-endian byte order. /// /// See [`ByteOrder`] for more details. #[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] pub enum LittleEndian {} impl ByteOrder for LittleEndian { const ORDER: Order = Order::LittleEndian; } impl Display for LittleEndian { #[inline] fn fmt(&self, _: &mut Formatter<'_>) -> fmt::Result { match *self {} } } /// The endianness used by this platform. /// /// This is a type alias for [`BigEndian`] or [`LittleEndian`] depending on the /// endianness of the target platform. #[cfg(target_endian = "big")] pub type NativeEndian = BigEndian; /// The endianness used by this platform. /// /// This is a type alias for [`BigEndian`] or [`LittleEndian`] depending on the /// endianness of the target platform. #[cfg(target_endian = "little")] pub type NativeEndian = LittleEndian; /// The endianness used in many network protocols. /// /// This is a type alias for [`BigEndian`]. pub type NetworkEndian = BigEndian; /// A type alias for [`BigEndian`]. pub type BE = BigEndian; /// A type alias for [`LittleEndian`]. pub type LE = LittleEndian; macro_rules! impl_dbg_trait { ($name:ident, $native:ident) => { impl<O: ByteOrder> Debug for $name<O> { #[inline] fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { // This results in a format like "U16(42)". f.debug_tuple(stringify!($name)).field(&self.get()).finish() } } }; } macro_rules! impl_dbg_traits { ($name:ident, $native:ident, "floating point number") => { #[cfg(not(no_fp_fmt_parse))] impl_dbg_trait!($name, $native); #[cfg(no_fp_fmt_parse)] impl<O: ByteOrder> Debug for $name<O> { #[inline] fn fmt(&self, _f: &mut Formatter<'_>) -> fmt::Result { panic!("floating point support is turned off"); } } }; ($name:ident, $native:ident, "unsigned integer") => { impl_dbg_traits!($name, $native, @all_types); }; ($name:ident, $native:ident, "signed integer") => { impl_dbg_traits!($name, $native, @all_types); }; ($name:ident, $native:ident, @all_types) => { impl_dbg_trait!($name, $native); }; } macro_rules! impl_fmt_trait { ($name:ident, $native:ident, $trait:ident) => { impl<O: ByteOrder> $trait for $name<O> { #[inline(always)] fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { $trait::fmt(&self.get(), f) } } }; } macro_rules! impl_fmt_traits { ($name:ident, $native:ident, "floating point number") => { #[cfg(not(no_fp_fmt_parse))] impl_fmt_trait!($name, $native, Display); }; ($name:ident, $native:ident, "unsigned integer") => { impl_fmt_traits!($name, $native, @all_types); }; ($name:ident, $native:ident, "signed integer") => { impl_fmt_traits!($name, $native, @all_types); }; ($name:ident, $native:ident, @all_types) => { impl_fmt_trait!($name, $native, Display); impl_fmt_trait!($name, $native, Octal); impl_fmt_trait!($name, $native, LowerHex); impl_fmt_trait!($name, $native, UpperHex); impl_fmt_trait!($name, $native, Binary); }; } macro_rules! impl_ops_traits { ($name:ident, $native:ident, "floating point number") => { impl_ops_traits!($name, $native, @all_types); impl_ops_traits!($name, $native, @signed_integer_floating_point); impl<O: ByteOrder> PartialOrd for $name<O> { #[inline(always)] fn partial_cmp(&self, other: &Self) -> Option<Ordering> { self.get().partial_cmp(&other.get()) } } }; ($name:ident, $native:ident, "unsigned integer") => { impl_ops_traits!($name, $native, @signed_unsigned_integer); impl_ops_traits!($name, $native, @all_types); }; ($name:ident, $native:ident, "signed integer") => { impl_ops_traits!($name, $native, @signed_unsigned_integer); impl_ops_traits!($name, $native, @signed_integer_floating_point); impl_ops_traits!($name, $native, @all_types); }; ($name:ident, $native:ident, @signed_unsigned_integer) => { impl_ops_traits!(@without_byteorder_swap $name, $native, BitAnd, bitand, BitAndAssign, bitand_assign); impl_ops_traits!(@without_byteorder_swap $name, $native, BitOr, bitor, BitOrAssign, bitor_assign); impl_ops_traits!(@without_byteorder_swap $name, $native, BitXor, bitxor, BitXorAssign, bitxor_assign); impl_ops_traits!(@with_byteorder_swap $name, $native, Shl, shl, ShlAssign, shl_assign); impl_ops_traits!(@with_byteorder_swap $name, $native, Shr, shr, ShrAssign, shr_assign); impl<O> core::ops::Not for $name<O> { type Output = $name<O>; #[inline(always)] fn not(self) -> $name<O> { let self_native = $native::from_ne_bytes(self.0); $name((!self_native).to_ne_bytes(), PhantomData) } } impl<O: ByteOrder> PartialOrd for $name<O> { #[inline(always)] fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) } } impl<O: ByteOrder> Ord for $name<O> { #[inline(always)] fn cmp(&self, other: &Self) -> Ordering { self.get().cmp(&other.get()) } } impl<O: ByteOrder> PartialOrd<$native> for $name<O> { #[inline(always)] fn partial_cmp(&self, other: &$native) -> Option<Ordering> { self.get().partial_cmp(other) } } }; ($name:ident, $native:ident, @signed_integer_floating_point) => { impl<O: ByteOrder> core::ops::Neg for $name<O> { type Output = $name<O>; #[inline(always)] fn neg(self) -> $name<O> { let self_native: $native = self.get(); #[allow(clippy::arithmetic_side_effects)] $name::<O>::new(-self_native) } } }; ($name:ident, $native:ident, @all_types) => { impl_ops_traits!(@with_byteorder_swap $name, $native, Add, add, AddAssign, add_assign); impl_ops_traits!(@with_byteorder_swap $name, $native, Div, div, DivAssign, div_assign); impl_ops_traits!(@with_byteorder_swap $name, $native, Mul, mul, MulAssign, mul_assign); impl_ops_traits!(@with_byteorder_swap $name, $native, Rem, rem, RemAssign, rem_assign); impl_ops_traits!(@with_byteorder_swap $name, $native, Sub, sub, SubAssign, sub_assign); }; (@with_byteorder_swap $name:ident, $native:ident, $trait:ident, $method:ident, $trait_assign:ident, $method_assign:ident) => { impl<O: ByteOrder> core::ops::$trait<$name<O>> for $name<O> { type Output = $name<O>; #[inline(always)] fn $method(self, rhs: $name<O>) -> $name<O> { let self_native: $native = self.get(); let rhs_native: $native = rhs.get(); let result_native = core::ops::$trait::$method(self_native, rhs_native); $name::<O>::new(result_native) } } impl<O: ByteOrder> core::ops::$trait<$name<O>> for $native { type Output = $name<O>; #[inline(always)] fn $method(self, rhs: $name<O>) -> $name<O> { let rhs_native: $native = rhs.get(); let result_native = core::ops::$trait::$method(self, rhs_native); $name::<O>::new(result_native) } } impl<O: ByteOrder> core::ops::$trait<$native> for $name<O> { type Output = $name<O>; #[inline(always)] fn $method(self, rhs: $native) -> $name<O> { let self_native: $native = self.get(); let result_native = core::ops::$trait::$method(self_native, rhs); $name::<O>::new(result_native) } } impl<O: ByteOrder> core::ops::$trait_assign<$name<O>> for $name<O> { #[inline(always)] fn $method_assign(&mut self, rhs: $name<O>) { *self = core::ops::$trait::$method(*self, rhs); } } impl<O: ByteOrder> core::ops::$trait_assign<$name<O>> for $native { #[inline(always)] fn $method_assign(&mut self, rhs: $name<O>) { let rhs_native: $native = rhs.get(); *self = core::ops::$trait::$method(*self, rhs_native); } } impl<O: ByteOrder> core::ops::$trait_assign<$native> for $name<O> { #[inline(always)] fn $method_assign(&mut self, rhs: $native) { *self = core::ops::$trait::$method(*self, rhs); } } }; // Implement traits in terms of the same trait on the native type, but // without performing a byte order swap when both operands are byteorder // types. This only works for bitwise operations like `&`, `|`, etc. // // When only one operand is a byteorder type, we still need to perform a // byteorder swap. (@without_byteorder_swap $name:ident, $native:ident, $trait:ident, $method:ident, $trait_assign:ident, $method_assign:ident) => { impl<O: ByteOrder> core::ops::$trait<$name<O>> for $name<O> { type Output = $name<O>; #[inline(always)] fn $method(self, rhs: $name<O>) -> $name<O> { let self_native = $native::from_ne_bytes(self.0); let rhs_native = $native::from_ne_bytes(rhs.0); let result_native = core::ops::$trait::$method(self_native, rhs_native); $name(result_native.to_ne_bytes(), PhantomData) } } impl<O: ByteOrder> core::ops::$trait<$name<O>> for $native { type Output = $name<O>; #[inline(always)] fn $method(self, rhs: $name<O>) -> $name<O> { // No runtime cost - just byte packing let rhs_native = $native::from_ne_bytes(rhs.0); // (Maybe) runtime cost - byte order swap let slf_byteorder = $name::<O>::new(self); // No runtime cost - just byte packing let slf_native = $native::from_ne_bytes(slf_byteorder.0); // Runtime cost - perform the operation let result_native = core::ops::$trait::$method(slf_native, rhs_native); // No runtime cost - just byte unpacking $name(result_native.to_ne_bytes(), PhantomData) } } impl<O: ByteOrder> core::ops::$trait<$native> for $name<O> { type Output = $name<O>; #[inline(always)] fn $method(self, rhs: $native) -> $name<O> { // (Maybe) runtime cost - byte order swap let rhs_byteorder = $name::<O>::new(rhs); // No runtime cost - just byte packing let rhs_native = $native::from_ne_bytes(rhs_byteorder.0); // No runtime cost - just byte packing let slf_native = $native::from_ne_bytes(self.0); // Runtime cost - perform the operation let result_native = core::ops::$trait::$method(slf_native, rhs_native); // No runtime cost - just byte unpacking $name(result_native.to_ne_bytes(), PhantomData) } } impl<O: ByteOrder> core::ops::$trait_assign<$name<O>> for $name<O> { #[inline(always)] fn $method_assign(&mut self, rhs: $name<O>) { *self = core::ops::$trait::$method(*self, rhs); } } impl<O: ByteOrder> core::ops::$trait_assign<$name<O>> for $native { #[inline(always)] fn $method_assign(&mut self, rhs: $name<O>) { // (Maybe) runtime cost - byte order swap let rhs_native = rhs.get(); // Runtime cost - perform the operation *self = core::ops::$trait::$method(*self, rhs_native); } } impl<O: ByteOrder> core::ops::$trait_assign<$native> for $name<O> { #[inline(always)] fn $method_assign(&mut self, rhs: $native) { *self = core::ops::$trait::$method(*self, rhs); } } }; } macro_rules! doc_comment { ($x:expr, $($tt:tt)*) => { #[doc = $x] $($tt)* }; } macro_rules! define_max_value_constant { ($name:ident, $bytes:expr, "unsigned integer") => { /// The maximum value. /// /// This constant should be preferred to constructing a new value using /// `new`, as `new` may perform an endianness swap depending on the /// endianness `O` and the endianness of the platform. pub const MAX_VALUE: $name<O> = $name([0xFFu8; $bytes], PhantomData); }; // We don't provide maximum and minimum value constants for signed values // and floats because there's no way to do it generically - it would require // a different value depending on the value of the `ByteOrder` type // parameter. Currently, one workaround would be to provide implementations // for concrete implementations of that trait. In the long term, if we are // ever able to make the `new` constructor a const fn, we could use that // instead. ($name:ident, $bytes:expr, "signed integer") => {}; ($name:ident, $bytes:expr, "floating point number") => {}; } macro_rules! define_type { ( $article:ident, $description:expr, $name:ident, $native:ident, $bits:expr, $bytes:expr, $from_be_fn:path, $to_be_fn:path, $from_le_fn:path, $to_le_fn:path, $number_kind:tt, [$($larger_native:ty),*], [$($larger_native_try:ty),*], [$($larger_byteorder:ident),*], [$($larger_byteorder_try:ident),*] ) => { doc_comment! { concat!($description, " stored in a given byte order. `", stringify!($name), "` is like the native `", stringify!($native), "` type with two major differences: First, it has no alignment requirement (its alignment is 1).