From 000f86f5470401e4d0d8824ec976738fb8a35bb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kr=C3=B6ning?= Date: Thu, 23 May 2024 17:00:48 +0200 Subject: [PATCH] initial implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Kröning --- .dockerignore | 1 + .github/workflows/ci.yml | 59 ++ Cargo.lock | 85 +++ Cargo.toml | 21 +- Dockerfile | 9 + README.md | 61 ++ rustfmt.toml | 2 + src/internal_macros.rs | 67 ++ src/lib.rs | 1502 ++++++++++++++++++++++++++++++++++++++ 9 files changed, 1806 insertions(+), 1 deletion(-) create mode 120000 .dockerignore create mode 100644 .github/workflows/ci.yml create mode 100644 Dockerfile create mode 100644 README.md create mode 100644 rustfmt.toml create mode 100644 src/internal_macros.rs diff --git a/.dockerignore b/.dockerignore new file mode 120000 index 0000000..3e4e48b --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +.gitignore \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..b2a201f --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,59 @@ +name: CI + +on: + push: + pull_request: + merge_group: + +env: + RUSTFLAGS: -Dwarnings + RUSTDOCFLAGS: -Dwarnings + +jobs: + clippy: + name: Clippy + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - run: cargo clippy --all-features + + doc: + name: Doc + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - run: cargo doc --all-features + + fmt: + name: Format + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@nightly + with: + components: rustfmt + - run: cargo fmt --all --check + + test: + name: Test + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - run: cargo test --all-features + + test-s390x: + name: Test S390x + runs-on: ubuntu-latest + steps: + - uses: docker/setup-qemu-action@v3 + with: + platforms: linux/s390x + - uses: docker/setup-buildx-action@v3 + with: + platforms: linux/s390x + - uses: docker/build-push-action@v5 + with: + platforms: linux/s390x diff --git a/Cargo.lock b/Cargo.lock index 221852b..a3c01fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,91 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + +[[package]] +name = "bytemuck" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78834c15cb5d5efe3452d58b1e8ba890dd62d21907f867f383358198e56ebca5" + +[[package]] +name = "bytemuck_derive" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4da9a32f3fed317401fa3c862968128267c3106685286e15d5aaa3d7389c2f60" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "endian-num" version = "0.0.0" +dependencies = [ + "bitflags", + "bytemuck", + "bytemuck_derive", + "zerocopy", + "zerocopy-derive", +] + +[[package]] +name = "proc-macro2" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "2.0.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2863d96a84c6439701d7a38f9de935ec562c8832cc55d1dde0f513b52fad106" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "zerocopy" +version = "0.7.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml index 2d1c286..eae89ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,8 +1,27 @@ [package] name = "endian-num" version = "0.0.0" +authors = ["Martin Kröning "] edition = "2021" -description = "Reserved." +description = "Byte-order-aware numeric types." +repository = "https://github.com/rust-osdev/endian-num" license = "MIT OR Apache-2.0" +keywords = ["byte", "endian", "big-endian", "little-endian", "binary"] +categories = ["encoding", "rust-patterns", "no-std::no-alloc"] + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] [dependencies] +bitflags = { version = "2", optional = true } +bytemuck = { version = "1", optional = true } +bytemuck_derive = { version = "1", optional = true } +zerocopy = { version = "0.7", optional = true, default-features = false } +zerocopy-derive = { version = "0.7", optional = true } + +[features] +bitflags = ["dep:bitflags"] +bytemuck = ["dep:bytemuck", "dep:bytemuck_derive"] +linux-types = [] +zerocopy = ["dep:zerocopy", "dep:zerocopy-derive"] diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..65f3b03 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,9 @@ +FROM rust:latest +ENV CARGO_INCREMENTAL=0 \ + CARGO_TERM_COLOR=always + +WORKDIR /root/endian-num +COPY . . +RUN set -ex; \ + cargo test --all-features; \ + cargo clean diff --git a/README.md b/README.md new file mode 100644 index 0000000..c36a737 --- /dev/null +++ b/README.md @@ -0,0 +1,61 @@ +# endian-num + +[![Crates.io](https://img.shields.io/crates/v/endian-num)](https://crates.io/crates/endian-num) +[![docs.rs](https://img.shields.io/docsrs/endian-num)](https://docs.rs/endian-num) +[![CI](https://github.com/rust-osdev/endian-num/actions/workflows/ci.yml/badge.svg)](https://github.com/rust-osdev/endian-num/actions/workflows/ci.yml) + +This crate provides the [`Be`] (big-endian) and [`Le`] (little-endian) byte-order-aware numeric types. + +[`Be`]: https://docs.rs/endian-num/latest/endian_num/struct.Be.html +[`Le`]: https://docs.rs/endian-num/latest/endian_num/struct.Le.html + +The core API looks _roughly_ like this (correspondingly for `Be`): + +```rust +#[repr(transparent)] +pub struct Le(pub T); + +impl Le { + pub const fn from_ne(n: T) -> Self; + pub const fn from_be(n: Be) -> Self; + + pub const fn to_ne(self) -> T; + pub const fn to_be(self) -> Be; + + pub const fn to_be_bytes(self) -> [u8; mem::size_of::()]; + pub const fn to_le_bytes(self) -> [u8; mem::size_of::()]; + pub const fn to_ne_bytes(self) -> [u8; mem::size_of::()]; + + pub const fn from_be_bytes(bytes: [u8; mem::size_of::()]) -> Self; + pub const fn from_le_bytes(bytes: [u8; mem::size_of::()]) -> Self; + pub const fn from_ne_bytes(bytes: [u8; mem::size_of::()]) -> Self; +} +``` + +The types also implement appropriate traits from [`core::cmp`], [`core::convert`], [`core::fmt`], and [`core::ops`] and provide additional helper methods for computations. + +For API documentation, see the [docs]. + +[docs]: https://docs.rs/endian-num +[`core::cmp`]: https://doc.rust-lang.org/stable/core/cmp/index.html +[`core::convert`]: https://doc.rust-lang.org/stable/core/convert/index.html +[`core::fmt`]: https://doc.rust-lang.org/stable/core/fmt/index.html +[`core::ops`]: https://doc.rust-lang.org/stable/core/ops/index.html + + +## License + +Licensed under either of + + * Apache License, Version 2.0 + ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license + ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..64d94de --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,2 @@ +group_imports = "StdExternalCrate" +imports_granularity = "Module" diff --git a/src/internal_macros.rs b/src/internal_macros.rs new file mode 100644 index 0000000..2b56d1d --- /dev/null +++ b/src/internal_macros.rs @@ -0,0 +1,67 @@ +//! Based on . + +// implements the unary operator "op &T" +// based on "op T" where T is expected to be `Copy`able +macro_rules! forward_ref_unop { + (impl $Trait:ident, $method:ident for $T:ty) => { + impl $Trait for &$T { + type Output = <$T as $Trait>::Output; + + #[inline] + #[track_caller] + fn $method(self) -> <$T as $Trait>::Output { + $Trait::$method(*self) + } + } + }; +} + +// implements binary operators "&T op Rhs", "T op &Rhs", "&T op &Rhs" +// based on "T op Rhs" where T and Rhs are expected to be `Copy`able +macro_rules! forward_ref_binop { + (impl $Trait:ident<$Rhs:ty>, $method:ident for $T:ty) => { + impl<'a> $Trait<$Rhs> for &'a $T { + type Output = <$T as $Trait<$Rhs>>::Output; + + #[inline] + #[track_caller] + fn $method(self, rhs: $Rhs) -> <$T as $Trait<$Rhs>>::Output { + $Trait::$method(*self, rhs) + } + } + + impl $Trait<&$Rhs> for $T { + type Output = <$T as $Trait<$Rhs>>::Output; + + #[inline] + #[track_caller] + fn $method(self, rhs: &$Rhs) -> <$T as $Trait<$Rhs>>::Output { + $Trait::$method(self, *rhs) + } + } + + impl $Trait<&$Rhs> for &$T { + type Output = <$T as $Trait<$Rhs>>::Output; + + #[inline] + #[track_caller] + fn $method(self, rhs: &$Rhs) -> <$T as $Trait<$Rhs>>::Output { + $Trait::$method(*self, *rhs) + } + } + }; +} + +// implements "T op= &Rhs", based on "T op= Rhs" +// where Rhs is expected to be `Copy`able +macro_rules! forward_ref_op_assign { + (impl $Trait:ident<$Rhs:ty>, $method:ident for $T:ty) => { + impl $Trait<&$Rhs> for $T { + #[inline] + #[track_caller] + fn $method(&mut self, rhs: &$Rhs) { + $Trait::$method(self, *rhs); + } + } + }; +} diff --git a/src/lib.rs b/src/lib.rs index e69de29..f563cc0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -0,0 +1,1502 @@ +//! Byte-order-aware numeric types. +//! +//! This crate provides the [`Be`] (big-endian) and [`Le`] (little-endian) byte-order-aware numeric types. +//! +//! The types aim to provide a similar API to other numeric types from [`core::num`], such as [`NonZero`], [`Saturating`], and [`Wrapping`]. +//! +//! [`NonZero`]: core::num::NonZero +//! [`Saturating`]: core::num::Saturating +//! [`Wrapping`]: core::num::Wrapping +//! +//! The core API looks _roughly_ like this (correspondingly for `Be`): +//! +//! ```ignore +//! #[repr(transparent)] +//! pub struct Le(pub T); +//! +//! impl Le { +//! pub const fn from_ne(n: T) -> Self; +//! pub const fn from_be(n: Be) -> Self; +//! +//! pub const fn to_ne(self) -> T; +//! pub const fn to_be(self) -> Be; +//! +//! pub const fn to_be_bytes(self) -> [u8; mem::size_of::()]; +//! pub const fn to_le_bytes(self) -> [u8; mem::size_of::()]; +//! pub const fn to_ne_bytes(self) -> [u8; mem::size_of::()]; +//! +//! pub const fn from_be_bytes(bytes: [u8; mem::size_of::()]) -> Self; +//! pub const fn from_le_bytes(bytes: [u8; mem::size_of::()]) -> Self; +//! pub const fn from_ne_bytes(bytes: [u8; mem::size_of::()]) -> Self; +//! } +//! ``` +//! +//! The types also implement appropriate traits from [`core::cmp`], [`core::convert`], [`core::fmt`], and [`core::ops`] and provide additional helper methods for computations. +//! +//! In addition to widening and byte-reordering [`From`] implementations, the endian number types implement conversions to and from arrays of smaller number types of the same ordering. +//! This is useful in situations, where a larger field has to be treated as multiple smaller field. +//! +//! # Examples +//! +//! ``` +//! use endian_num::Le; +//! +//! let a = Le::::from_ne(0x1A); +//! let b = Le::::from_ne(0x2B00); +//! +//! assert_eq!((a + b).to_le_bytes(), [0x1A, 0x2B, 0x00, 0x00]); +//! ``` +//! +//! # Optional features +//! +//! This crate has the following optional features: +//! +//! - [`bitflags`] — `Be` and `Le` implement [`Bits`], [`ParseHex`], and [`WriteHex`]. +//! - [`bytemuck`] — `Be` and `Le` implement [`Zeroable`] and [`Pod`]. +//! - `linux-types` — Type aliases like in [`linux/types.h`], such as [`le32`]. +//! - [`zerocopy`] — `Be` and `Le` implement [`FromZeroes`], [`FromBytes`], and [`AsBytes`]. +//! +//! [`Bits`]: bitflags::Bits +//! [`ParseHex`]: bitflags::parser::ParseHex +//! [`WriteHex`]: bitflags::parser::WriteHex +//! [`Zeroable`]: bytemuck::Zeroable +//! [`Pod`]: bytemuck::Pod +//! [`linux/types.h`]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/uapi/linux/types.h?h=v6.9#n36 +//! [`FromZeroes`]: zerocopy::FromZeroes +//! [`FromBytes`]: zerocopy::FromBytes +//! [`AsBytes`]: zerocopy::AsBytes +//! +//! # Related crates +//! +//! - [endian-type](https://crates.io/crates/endian-type) +//! - [endian-type-rs](https://crates.io/crates/endian-type-rs) — Depends on `num`. +//! - [endiantype](https://crates.io/crates/endiantype) +//! - [nora_endian](https://crates.io/crates/nora_endian) +//! - [simple_endian](https://crates.io/crates/simple_endian) — Also provides `f32`, `f64`, and `bool` types. +//! - [`zerocopy::byteorder`] — These types are [`Unaligned`](zerocopy::Unaligned), which makes them unsuitable for volatile memory operations. +//! +//! [`zerocopy::byteorder`]: https://docs.rs/zerocopy/0.7/zerocopy/byteorder/index.html + +#![no_std] +#![cfg_attr(docsrs, feature(doc_auto_cfg))] +#![warn(missing_docs)] +#![warn(rust_2018_idioms)] + +#[macro_use] +mod internal_macros; + +use core::cmp::Ordering; +use core::iter::{Product, Sum}; +use core::num::TryFromIntError; +use core::ops::{ + Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Div, DivAssign, + Mul, MulAssign, Neg, Not, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, Sub, SubAssign, +}; +use core::{fmt, mem}; + +/// An integer stored in big-endian byte order. +/// +/// # Examples +/// +/// ``` +/// use endian_num::Be; +/// +/// let n = 0x1Au32; +/// +/// if cfg!(target_endian = "big") { +/// assert_eq!(Be::::from_ne(n).0, n); +/// } else { +/// assert_eq!(Be::::from_ne(n).0, n.swap_bytes()); +/// } +/// ``` +#[cfg_attr( + feature = "bytemuck", + derive(bytemuck_derive::Zeroable, bytemuck_derive::Pod) +)] +#[cfg_attr( + feature = "zerocopy", + derive( + zerocopy_derive::FromZeroes, + zerocopy_derive::FromBytes, + zerocopy_derive::AsBytes + ) +)] +#[derive(Default, Hash, PartialEq, Eq, Clone, Copy)] +#[repr(transparent)] +pub struct Be(pub T); + +/// An integer stored in little-endian byte order. +/// +/// # Examples +/// +/// ``` +/// use endian_num::Le; +/// +/// let n = 0x1Au32; +/// +/// if cfg!(target_endian = "little") { +/// assert_eq!(Le::::from_ne(n).0, n); +/// } else { +/// assert_eq!(Le::::from_ne(n).0, n.swap_bytes()); +/// } +/// ``` +#[cfg_attr( + feature = "bytemuck", + derive(bytemuck_derive::Zeroable, bytemuck_derive::Pod) +)] +#[cfg_attr( + feature = "zerocopy", + derive( + zerocopy_derive::FromZeroes, + zerocopy_derive::FromBytes, + zerocopy_derive::AsBytes + ) +)] +#[derive(Default, Hash, PartialEq, Eq, Clone, Copy)] +#[repr(transparent)] +pub struct Le(pub T); + +macro_rules! impl_fmt { + (impl $Trait:ident for Xe) => { + impl_fmt!(impl $Trait for Be); + impl_fmt!(impl $Trait for Le); + }; + (impl $Trait:ident for $SelfT:ident) => { + impl fmt::$Trait for $SelfT + where + Self: Copy + Into, + T: fmt::$Trait, + { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (*self).into().fmt(f) + } + } + }; +} + +impl_fmt! { impl Debug for Xe } +impl_fmt! { impl Display for Xe } +impl_fmt! { impl Binary for Xe } +impl_fmt! { impl Octal for Xe } +impl_fmt! { impl LowerHex for Xe } +impl_fmt! { impl UpperHex for Xe } +impl_fmt! { impl LowerExp for Xe } +impl_fmt! { impl UpperExp for Xe } + +macro_rules! unop_impl { + (impl $Trait:ident, $method:ident for $T:ty) => { + impl $Trait for $T { + type Output = Self; + + #[inline] + #[track_caller] + fn $method(self) -> Self::Output { + Self::Output::from_ne($Trait::$method(self.to_ne())) + } + } + }; +} + +macro_rules! binop_impl { + (impl $Trait:ident, $method:ident for $T:ty) => { + impl $Trait for $T { + type Output = Self; + + #[inline] + #[track_caller] + fn $method(self, rhs: Self) -> Self::Output { + Self::Output::from_ne($Trait::$method(self.to_ne(), rhs.to_ne())) + } + } + }; + (impl $Trait:ident<$Rhs:ty>, $method:ident for $T:ty) => { + impl $Trait<$Rhs> for $T { + type Output = Self; + + #[inline] + #[track_caller] + fn $method(self, rhs: $Rhs) -> Self::Output { + Self::Output::from_ne($Trait::$method(self.to_ne(), rhs)) + } + } + }; +} + +macro_rules! op_assign_impl { + (impl $Trait:ident<$Rhs:ty>, $method:ident for $T:ty, $binop:path) => { + impl $Trait<$Rhs> for $T { + #[inline] + #[track_caller] + fn $method(&mut self, rhs: $Rhs) { + *self = $binop(*self, rhs); + } + } + }; +} + +macro_rules! endian_impl { + ($(Xe<$T:ty>)*) => {$( + endian_impl! { Be<$T> } + endian_impl! { Le<$T> } + )*}; + ($($Xe:ident<$T:ty>)*) => {$( + impl PartialOrd for $Xe<$T> { + #[inline] + fn partial_cmp(&self, rhs: &Self) -> Option { + Some(self.cmp(rhs)) + } + } + + impl Ord for $Xe<$T> { + #[inline] + fn cmp(&self, rhs: &Self) -> Ordering { + Ord::cmp(&self.to_ne(), &rhs.to_ne()) + } + } + + unop_impl! { impl Not, not for $Xe<$T> } + + forward_ref_unop! { impl Not, not for $Xe<$T> } + + binop_impl! { impl Add, add for $Xe<$T> } + binop_impl! { impl BitAnd, bitand for $Xe<$T> } + binop_impl! { impl BitOr, bitor for $Xe<$T> } + binop_impl! { impl BitXor, bitxor for $Xe<$T> } + binop_impl! { impl Div, div for $Xe<$T> } + binop_impl! { impl Mul, mul for $Xe<$T> } + binop_impl! { impl Rem, rem for $Xe<$T> } + binop_impl! { impl Sub, sub for $Xe<$T> } + + forward_ref_binop! { impl Add<$Xe<$T>>, add for $Xe<$T> } + forward_ref_binop! { impl BitAnd<$Xe<$T>>, bitand for $Xe<$T> } + forward_ref_binop! { impl BitOr<$Xe<$T>>, bitor for $Xe<$T> } + forward_ref_binop! { impl BitXor<$Xe<$T>>, bitxor for $Xe<$T> } + forward_ref_binop! { impl Div<$Xe<$T>>, div for $Xe<$T> } + forward_ref_binop! { impl Mul<$Xe<$T>>, mul for $Xe<$T> } + forward_ref_binop! { impl Rem<$Xe<$T>>, rem for $Xe<$T> } + forward_ref_binop! { impl Sub<$Xe<$T>>, sub for $Xe<$T> } + + op_assign_impl! { impl AddAssign, add_assign for $Xe<$T>, Add::add } + op_assign_impl! { impl BitAndAssign, bitand_assign for $Xe<$T>, BitAnd::bitand } + op_assign_impl! { impl BitOrAssign, bitor_assign for $Xe<$T>, BitOr::bitor } + op_assign_impl! { impl BitXorAssign, bitxor_assign for $Xe<$T>, BitXor::bitxor } + op_assign_impl! { impl DivAssign, div_assign for $Xe<$T>, Div::div } + op_assign_impl! { impl MulAssign, mul_assign for $Xe<$T>, Mul::mul } + op_assign_impl! { impl RemAssign, rem_assign for $Xe<$T>, Rem::rem } + op_assign_impl! { impl SubAssign, sub_assign for $Xe<$T>, Sub::sub } + + forward_ref_op_assign! { impl AddAssign<$Xe<$T>>, add_assign for $Xe<$T> } + forward_ref_op_assign! { impl BitAndAssign<$Xe<$T>>, bitand_assign for $Xe<$T> } + forward_ref_op_assign! { impl BitOrAssign<$Xe<$T>>, bitor_assign for $Xe<$T> } + forward_ref_op_assign! { impl BitXorAssign<$Xe<$T>>, bitxor_assign for $Xe<$T> } + forward_ref_op_assign! { impl DivAssign<$Xe<$T>>, div_assign for $Xe<$T> } + forward_ref_op_assign! { impl MulAssign<$Xe<$T>>, mul_assign for $Xe<$T> } + forward_ref_op_assign! { impl RemAssign<$Xe<$T>>, rem_assign for $Xe<$T> } + forward_ref_op_assign! { impl SubAssign<$Xe<$T>>, sub_assign for $Xe<$T> } + + binop_impl! { impl Shl, shl for $Xe<$T> } + binop_impl! { impl Shl, shl for $Xe<$T> } + binop_impl! { impl Shl, shl for $Xe<$T> } + binop_impl! { impl Shl, shl for $Xe<$T> } + binop_impl! { impl Shl, shl for $Xe<$T> } + binop_impl! { impl Shl, shl for $Xe<$T> } + binop_impl! { impl Shl, shl for $Xe<$T> } + binop_impl! { impl Shl, shl for $Xe<$T> } + binop_impl! { impl Shl, shl for $Xe<$T> } + binop_impl! { impl Shl, shl for $Xe<$T> } + binop_impl! { impl Shl, shl for $Xe<$T> } + binop_impl! { impl Shl, shl for $Xe<$T> } + + binop_impl! { impl Shr, shr for $Xe<$T> } + binop_impl! { impl Shr, shr for $Xe<$T> } + binop_impl! { impl Shr, shr for $Xe<$T> } + binop_impl! { impl Shr, shr for $Xe<$T> } + binop_impl! { impl Shr, shr for $Xe<$T> } + binop_impl! { impl Shr, shr for $Xe<$T> } + binop_impl! { impl Shr, shr for $Xe<$T> } + binop_impl! { impl Shr, shr for $Xe<$T> } + binop_impl! { impl Shr, shr for $Xe<$T> } + binop_impl! { impl Shr, shr for $Xe<$T> } + binop_impl! { impl Shr, shr for $Xe<$T> } + binop_impl! { impl Shr, shr for $Xe<$T> } + + forward_ref_binop! { impl Shl, shl for $Xe<$T> } + forward_ref_binop! { impl Shl, shl for $Xe<$T> } + forward_ref_binop! { impl Shl, shl for $Xe<$T> } + forward_ref_binop! { impl Shl, shl for $Xe<$T> } + forward_ref_binop! { impl Shl, shl for $Xe<$T> } + forward_ref_binop! { impl Shl, shl for $Xe<$T> } + forward_ref_binop! { impl Shl, shl for $Xe<$T> } + forward_ref_binop! { impl Shl, shl for $Xe<$T> } + forward_ref_binop! { impl Shl, shl for $Xe<$T> } + forward_ref_binop! { impl Shl, shl for $Xe<$T> } + forward_ref_binop! { impl Shl, shl for $Xe<$T> } + forward_ref_binop! { impl Shl, shl for $Xe<$T> } + + forward_ref_binop! { impl Shr, shr for $Xe<$T> } + forward_ref_binop! { impl Shr, shr for $Xe<$T> } + forward_ref_binop! { impl Shr, shr for $Xe<$T> } + forward_ref_binop! { impl Shr, shr for $Xe<$T> } + forward_ref_binop! { impl Shr, shr for $Xe<$T> } + forward_ref_binop! { impl Shr, shr for $Xe<$T> } + forward_ref_binop! { impl Shr, shr for $Xe<$T> } + forward_ref_binop! { impl Shr, shr for $Xe<$T> } + forward_ref_binop! { impl Shr, shr for $Xe<$T> } + forward_ref_binop! { impl Shr, shr for $Xe<$T> } + forward_ref_binop! { impl Shr, shr for $Xe<$T> } + forward_ref_binop! { impl Shr, shr for $Xe<$T> } + + op_assign_impl! { impl ShlAssign, shl_assign for $Xe<$T>, Shl::shl } + op_assign_impl! { impl ShlAssign, shl_assign for $Xe<$T>, Shl::shl } + op_assign_impl! { impl ShlAssign, shl_assign for $Xe<$T>, Shl::shl } + op_assign_impl! { impl ShlAssign, shl_assign for $Xe<$T>, Shl::shl } + op_assign_impl! { impl ShlAssign, shl_assign for $Xe<$T>, Shl::shl } + op_assign_impl! { impl ShlAssign, shl_assign for $Xe<$T>, Shl::shl } + op_assign_impl! { impl ShlAssign, shl_assign for $Xe<$T>, Shl::shl } + op_assign_impl! { impl ShlAssign, shl_assign for $Xe<$T>, Shl::shl } + op_assign_impl! { impl ShlAssign, shl_assign for $Xe<$T>, Shl::shl } + op_assign_impl! { impl ShlAssign, shl_assign for $Xe<$T>, Shl::shl } + op_assign_impl! { impl ShlAssign, shl_assign for $Xe<$T>, Shl::shl } + op_assign_impl! { impl ShlAssign, shl_assign for $Xe<$T>, Shl::shl } + + op_assign_impl! { impl ShrAssign, shr_assign for $Xe<$T>, Shr::shr } + op_assign_impl! { impl ShrAssign, shr_assign for $Xe<$T>, Shr::shr } + op_assign_impl! { impl ShrAssign, shr_assign for $Xe<$T>, Shr::shr } + op_assign_impl! { impl ShrAssign, shr_assign for $Xe<$T>, Shr::shr } + op_assign_impl! { impl ShrAssign, shr_assign for $Xe<$T>, Shr::shr } + op_assign_impl! { impl ShrAssign, shr_assign for $Xe<$T>, Shr::shr } + op_assign_impl! { impl ShrAssign, shr_assign for $Xe<$T>, Shr::shr } + op_assign_impl! { impl ShrAssign, shr_assign for $Xe<$T>, Shr::shr } + op_assign_impl! { impl ShrAssign, shr_assign for $Xe<$T>, Shr::shr } + op_assign_impl! { impl ShrAssign, shr_assign for $Xe<$T>, Shr::shr } + op_assign_impl! { impl ShrAssign, shr_assign for $Xe<$T>, Shr::shr } + op_assign_impl! { impl ShrAssign, shr_assign for $Xe<$T>, Shr::shr } + + forward_ref_op_assign! { impl ShlAssign, shl_assign for $Xe<$T> } + forward_ref_op_assign! { impl ShlAssign, shl_assign for $Xe<$T> } + forward_ref_op_assign! { impl ShlAssign, shl_assign for $Xe<$T> } + forward_ref_op_assign! { impl ShlAssign, shl_assign for $Xe<$T> } + forward_ref_op_assign! { impl ShlAssign, shl_assign for $Xe<$T> } + forward_ref_op_assign! { impl ShlAssign, shl_assign for $Xe<$T> } + forward_ref_op_assign! { impl ShlAssign, shl_assign for $Xe<$T> } + forward_ref_op_assign! { impl ShlAssign, shl_assign for $Xe<$T> } + forward_ref_op_assign! { impl ShlAssign, shl_assign for $Xe<$T> } + forward_ref_op_assign! { impl ShlAssign, shl_assign for $Xe<$T> } + forward_ref_op_assign! { impl ShlAssign, shl_assign for $Xe<$T> } + forward_ref_op_assign! { impl ShlAssign, shl_assign for $Xe<$T> } + + forward_ref_op_assign! { impl ShrAssign, shr_assign for $Xe<$T> } + forward_ref_op_assign! { impl ShrAssign, shr_assign for $Xe<$T> } + forward_ref_op_assign! { impl ShrAssign, shr_assign for $Xe<$T> } + forward_ref_op_assign! { impl ShrAssign, shr_assign for $Xe<$T> } + forward_ref_op_assign! { impl ShrAssign, shr_assign for $Xe<$T> } + forward_ref_op_assign! { impl ShrAssign, shr_assign for $Xe<$T> } + forward_ref_op_assign! { impl ShrAssign, shr_assign for $Xe<$T> } + forward_ref_op_assign! { impl ShrAssign, shr_assign for $Xe<$T> } + forward_ref_op_assign! { impl ShrAssign, shr_assign for $Xe<$T> } + forward_ref_op_assign! { impl ShrAssign, shr_assign for $Xe<$T> } + forward_ref_op_assign! { impl ShrAssign, shr_assign for $Xe<$T> } + forward_ref_op_assign! { impl ShrAssign, shr_assign for $Xe<$T> } + )*}; +} + +endian_impl! { Xe Xe Xe Xe Xe Xe Xe Xe Xe Xe Xe Xe } + +macro_rules! impl_from { + (Xe<$Small:ty> => Xe<$Large:ty>) => { + impl_from!(Be<$Small> => Be<$Large>); + impl_from!(Le<$Small> => Le<$Large>); + }; + ($Small:ty => $Large:ty) => { + impl From<$Small> for $Large { + #[doc = concat!("Converts [`", stringify!($Small), "`] to [`", stringify!($Large), "`] losslessly.")] + #[inline] + fn from(small: $Small) -> Self { + Self::from_ne(small.to_ne().into()) + } + } + }; +} + +// unsigned integer -> unsigned integer +impl_from!(Xe => Xe); +impl_from!(Xe => Xe); +impl_from!(Xe => Xe); +impl_from!(Xe => Xe); +impl_from!(Xe => Xe); +impl_from!(Xe => Xe); +impl_from!(Xe => Xe); +impl_from!(Xe => Xe); +impl_from!(Xe => Xe); +impl_from!(Xe => Xe); +impl_from!(Xe => Xe); + +// signed integer -> signed integer +impl_from!(Xe => Xe); +impl_from!(Xe => Xe); +impl_from!(Xe => Xe); +impl_from!(Xe => Xe); +impl_from!(Xe => Xe); +impl_from!(Xe => Xe); +impl_from!(Xe => Xe); +impl_from!(Xe => Xe); +impl_from!(Xe => Xe); +impl_from!(Xe => Xe); +impl_from!(Xe => Xe); + +// unsigned integer -> signed integer +impl_from!(Xe => Xe); +impl_from!(Xe => Xe); +impl_from!(Xe => Xe); +impl_from!(Xe => Xe); +impl_from!(Xe => Xe); +impl_from!(Xe => Xe); +impl_from!(Xe => Xe); +impl_from!(Xe => Xe); +impl_from!(Xe => Xe); +impl_from!(Xe => Xe); + +// The C99 standard defines bounds on INTPTR_MIN, INTPTR_MAX, and UINTPTR_MAX +// which imply that pointer-sized integers must be at least 16 bits: +// https://port70.net/~nsz/c/c99/n1256.html#7.18.2.4 +impl_from!(Xe => Xe); +impl_from!(Xe => Xe); +impl_from!(Xe => Xe); + +macro_rules! impl_try_from { + (Xe<$source:ty> => $(Xe<$target:ty>),+) => {$( + impl_try_from!(Be<$source> => Be<$target>); + impl_try_from!(Le<$source> => Le<$target>); + )*}; + ($Xe:ident<$source:ty> => $Xe2:ident<$target:ty>) => { + impl TryFrom<$Xe<$source>> for $Xe<$target> { + type Error = TryFromIntError; + + /// Try to create the target number type from a source + /// number type. This returns an error if the source value + /// is outside of the range of the target type. + #[inline] + fn try_from(u: $Xe<$source>) -> Result { + <$target>::try_from(u.to_ne()).map(Self::from_ne) + } + } + }; +} + +// unsigned integer -> unsigned integer +impl_try_from!(Xe => Xe); +impl_try_from!(Xe => Xe, Xe); +impl_try_from!(Xe => Xe, Xe, Xe); +impl_try_from!(Xe => Xe, Xe, Xe, Xe); + +// signed integer -> signed integer +impl_try_from!(Xe => Xe); +impl_try_from!(Xe => Xe, Xe); +impl_try_from!(Xe => Xe, Xe, Xe); +impl_try_from!(Xe => Xe, Xe, Xe, Xe); + +// unsigned integer -> signed integer +impl_try_from!(Xe => Xe); +impl_try_from!(Xe => Xe, Xe); +impl_try_from!(Xe => Xe, Xe, Xe); +impl_try_from!(Xe => Xe, Xe, Xe, Xe); +impl_try_from!(Xe => Xe, Xe, Xe, Xe, Xe); + +// signed integer -> unsigned integer +impl_try_from!(Xe => Xe, Xe, Xe, Xe, Xe); +impl_try_from!(Xe => Xe); +impl_try_from!(Xe => Xe, Xe, Xe, Xe); +impl_try_from!(Xe => Xe, Xe); +impl_try_from!(Xe => Xe, Xe, Xe); +impl_try_from!(Xe => Xe, Xe, Xe); +impl_try_from!(Xe => Xe, Xe); +impl_try_from!(Xe => Xe, Xe, Xe, Xe); +impl_try_from!(Xe => Xe); + +// usize/isize +impl_try_from!(Xe => Xe); +impl_try_from!(Xe => Xe); + +macro_rules! impl_sum_product { + ($(Xe<$T:ty>)*) => {$( + impl_sum_product!(Be<$T>); + impl_sum_product!(Le<$T>); + )*}; + ($Xe:ty) => ( + impl Sum for $Xe { + fn sum>(iter: I) -> Self { + iter.fold(<$Xe>::from_ne(0), |a, b| a + b) + } + } + + impl Product for $Xe { + fn product>(iter: I) -> Self { + iter.fold(<$Xe>::from_ne(1), |a, b| a * b) + } + } + + impl<'a> Sum<&'a $Xe> for $Xe { + fn sum>(iter: I) -> Self { + iter.fold(<$Xe>::from_ne(0), |a, b| a + b) + } + } + + impl<'a> Product<&'a $Xe> for $Xe { + fn product>(iter: I) -> Self { + iter.fold(<$Xe>::from_ne(1), |a, b| a * b) + } + } + ); +} + +impl_sum_product! { Xe Xe Xe Xe Xe Xe Xe Xe Xe Xe Xe Xe } + +#[rustfmt::skip] +macro_rules! rot { + (u8) => { 2 }; + (u16) => { 4 }; + (u32) => { 8 }; + (u64) => { 12 }; + (u128) => { 16 }; + (usize) => { rot!(u64) }; + (i8) => { rot!(u8) }; + (i16) => { rot!(u16) }; + (i32) => { rot!(u32) }; + (i64) => { rot!(u64) }; + (i128) => { rot!(u128) }; + (isize) => { rot!(usize) }; +} + +#[rustfmt::skip] +macro_rules! rot_op { + (u8) => { "0x82" }; + (u16) => { "0xa003" }; + (u32) => { "0x10000b3" }; + (u64) => { "0xaa00000000006e1" }; + (u128) => { "0x13f40000000000000000000000004f76" }; + (usize) => { rot_op!(u64) }; + (i8) => { "-0x7e" }; + (i16) => { "-0x5ffd" }; + (i32) => { rot_op!(u32) }; + (i64) => { rot_op!(u64) }; + (i128) => { rot_op!(u128) }; + (isize) => { rot_op!(usize) }; +} + +#[rustfmt::skip] +macro_rules! rot_result { + (u8) => { "0xa" }; + (u16) => { "0x3a" }; + (u32) => { "0xb301" }; + (u64) => { "0x6e10aa" }; + (u128) => { "0x4f7613f4" }; + (usize) => { rot_result!(u64) }; + (i8) => { rot_result!(u8) }; + (i16) => { rot_result!(u16) }; + (i32) => { rot_result!(u32) }; + (i64) => { rot_result!(u64) }; + (i128) => { rot_result!(u128) }; + (isize) => { rot_result!(usize) }; +} + +#[rustfmt::skip] +macro_rules! swap_op { + (u8) => { "0x12" }; + (u16) => { "0x1234" }; + (u32) => { "0x12345678" }; + (u64) => { "0x1234567890123456" }; + (u128) => { "0x12345678901234567890123456789012" }; + (usize) => { swap_op!(u64) }; + (i8) => { swap_op!(u8) }; + (i16) => { swap_op!(u16) }; + (i32) => { swap_op!(u32) }; + (i64) => { swap_op!(u64) }; + (i128) => { swap_op!(u128) }; + (isize) => { swap_op!(usize) }; +} + +#[rustfmt::skip] +macro_rules! swapped { + (u8) => { "0x12" }; + (u16) => { "0x3412" }; + (u32) => { "0x78563412" }; + (u64) => { "0x5634129078563412" }; + (u128) => { "0x12907856341290785634129078563412" }; + (usize) => { swapped!(u64) }; + (i8) => { swapped!(u8) }; + (i16) => { swapped!(u16) }; + (i32) => { swapped!(u32) }; + (i64) => { swapped!(u64) }; + (i128) => { swapped!(u128) }; + (isize) => { swapped!(usize) }; +} + +#[rustfmt::skip] +macro_rules! reversed { + (u8) => { "0x48" }; + (u16) => { "0x2c48" }; + (u32) => { "0x1e6a2c48" }; + (u64) => { "0x6a2c48091e6a2c48" }; + (u128) => { "0x48091e6a2c48091e6a2c48091e6a2c48" }; + (usize) => { reversed!(u64) }; + (i8) => { reversed!(u8) }; + (i16) => { reversed!(u16) }; + (i32) => { reversed!(u32) }; + (i64) => { reversed!(u64) }; + (i128) => { reversed!(u128) }; + (isize) => { reversed!(usize) }; +} + +macro_rules! be_bytes { + (u8) => { "[0x12]" }; + (u16) => { "[0x12, 0x34]" }; + (u32) => { "[0x12, 0x34, 0x56, 0x78]" }; + (u64) => { "[0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56]" }; + (u128) => { "[0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12]" }; + (usize) => { be_bytes!(u64) }; + (i8) => { be_bytes!(u8) }; + (i16) => { be_bytes!(u16) }; + (i32) => { be_bytes!(u32) }; + (i64) => { be_bytes!(u64) }; + (i128) => { be_bytes!(u128) }; + (isize) => { be_bytes!(usize) }; +} + +macro_rules! le_bytes { + (u8) => { "[0x12]" }; + (u16) => { "[0x34, 0x12]" }; + (u32) => { "[0x78, 0x56, 0x34, 0x12]" }; + (u64) => { "[0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34, 0x12]" }; + (u128) => { "[0x12, 0x90, 0x78, 0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34, 0x12]" }; + (usize) => { le_bytes!(u64) }; + (i8) => { le_bytes!(u8) }; + (i16) => { le_bytes!(u16) }; + (i32) => { le_bytes!(u32) }; + (i64) => { le_bytes!(u64) }; + (i128) => { le_bytes!(u128) }; + (isize) => { le_bytes!(usize) }; +} + +macro_rules! endian_int_impl { + ($(Xe<$T:ident>)*) => {$( + endian_int_impl! { Be<$T>, from_be, to_be, "big", Le, from_le, to_le, "little" } + endian_int_impl! { Le<$T>, from_le, to_le, "little", Be, from_be, to_be, "big" } + )*}; + ($Xe:ident<$T:ident>, $from_xe:ident, $to_xe:ident, $order:literal, $Other:ident, $from_other:ident, $to_other:ident, $order_other:literal) => { + impl $Xe<$T> { + /// The smallest value that can be represented by this integer type. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("use endian_num::", stringify!($Xe), ";")] + /// + #[doc = concat!("assert_eq!(", stringify!($Xe), "::<", stringify!($T), ">::MIN, ", stringify!($Xe), "::<", stringify!($T), ">::from_ne(", stringify!($T), "::MIN));")] + /// ``` + pub const MIN: Self = Self::from_ne(<$T>::MIN); + + /// The largest value that can be represented by this integer type. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("use endian_num::", stringify!($Xe), ";")] + /// + #[doc = concat!("assert_eq!(", stringify!($Xe), "::<", stringify!($T), ">::MAX, ", stringify!($Xe), "::<", stringify!($T), ">::from_ne(", stringify!($T), "::MAX));")] + /// ``` + pub const MAX: Self = Self::from_ne(<$T>::MAX); + + /// The size of this integer type in bits. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("use endian_num::", stringify!($Xe), ";")] + /// + #[doc = concat!("assert_eq!(", stringify!($Xe), "::<", stringify!($T), ">::BITS, ", stringify!($T), "::BITS);")] + /// ``` + pub const BITS: u32 = <$T>::BITS; + + #[doc = concat!("Creates a new ", $order, "-endian integer from a native-endian integer.")] + /// + #[doc = concat!("On ", $order, " endian, this is a no-op. On ", $order_other, " endian, the bytes are swapped.")] + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("use endian_num::", stringify!($Xe), ";")] + /// + #[doc = concat!("let n = 0x1A", stringify!($T), ";")] + /// + #[doc = concat!("if cfg!(target_endian = \"", $order, "\") {")] + #[doc = concat!(" assert_eq!(", stringify!($Xe), "::<", stringify!($T), ">::from_ne(n).0, n);")] + /// } else { + #[doc = concat!(" assert_eq!(", stringify!($Xe), "::<", stringify!($T), ">::from_ne(n).0, n.swap_bytes());")] + /// } + /// ``` + #[must_use] + #[inline] + pub const fn from_ne(n: $T) -> Self { + Self(n.$to_xe()) + } + + #[doc = concat!("Creates a new ", $order, "-endian integer from a ", $order_other, "-endian integer.")] + /// + /// This always swaps the bytes. + /// + /// # Examples + /// + /// ``` + #[doc = concat!("use endian_num::{Be, Le};")] + /// + #[doc = concat!("let n = 0x1A", stringify!($T), ";")] + /// + #[doc = concat!("assert_eq!(", stringify!($Xe), "::<", stringify!($T), ">::", stringify!($from_other), "(", stringify!($Other), "(n)).0, n.swap_bytes());")] + /// ``` + #[must_use] + #[inline] + pub const fn $from_other(n: $Other<$T>) -> Self { + Self(n.0.swap_bytes()) + } + + /// Returns the integer in native-endian byte order. + /// + #[doc = concat!("On ", $order, " endian, this is a no-op. On ", $order_other, " endian, the bytes are swapped.")] + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("use endian_num::", stringify!($Xe), ";")] + /// + #[doc = concat!("let n = 0x1A", stringify!($T), ";")] + /// + #[doc = concat!("if cfg!(target_endian = \"", $order, "\") {")] + #[doc = concat!(" assert_eq!(", stringify!($Xe), "(n).to_ne(), n);")] + /// } else { + #[doc = concat!(" assert_eq!(", stringify!($Xe), "(n).to_ne(), n.swap_bytes());")] + /// } + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn to_ne(self) -> $T { + <$T>::$from_xe(self.0) + } + + #[doc = concat!("Returns the integer in ", $order_other, "-endian byte order.")] + /// + /// This always swaps the bytes. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("use endian_num::", stringify!($Xe), ";")] + /// + #[doc = concat!("let n = 0x1A", stringify!($T), ";")] + /// + #[doc = concat!("assert_eq!(", stringify!($Xe), "(n).", stringify!($to_other), "().0, n.swap_bytes());")] + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn $to_other(self) -> $Other<$T> { + $Other(self.0.swap_bytes()) + } + + /// Return the memory representation of this integer as a byte array in big-endian (network) byte order. + /// + /// # Examples + /// + /// ``` + #[doc = concat!("use endian_num::", stringify!($Xe), ";")] + /// + #[doc = concat!("let bytes = ", stringify!($Xe), "::<", stringify!($T), ">::from_ne(", swap_op!($T), ").to_be_bytes();")] + #[doc = concat!("assert_eq!(bytes, ", be_bytes!($T), ");")] + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn to_be_bytes(self) -> [u8; mem::size_of::()] { + self.to_ne().to_be_bytes() + } + + /// Return the memory representation of this integer as a byte array in little-endian byte order. + /// + /// # Examples + /// + /// ``` + #[doc = concat!("use endian_num::", stringify!($Xe), ";")] + /// + #[doc = concat!("let bytes = ", stringify!($Xe), "::<", stringify!($T), ">::from_ne(", swap_op!($T), ").to_le_bytes();")] + #[doc = concat!("assert_eq!(bytes, ", le_bytes!($T), ");")] + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn to_le_bytes(self) -> [u8; mem::size_of::()] { + self.to_ne().to_le_bytes() + } + + /// Return the memory representation of this integer as a byte array in + /// native byte order. + /// + /// As the target platform's native endianness is used, portable code + /// should use [`to_be_bytes`] or [`to_le_bytes`], as appropriate, + /// instead. + /// + /// [`to_be_bytes`]: Self::to_be_bytes + /// [`to_le_bytes`]: Self::to_le_bytes + /// + /// # Examples + /// + /// ``` + #[doc = concat!("use endian_num::", stringify!($Xe), ";")] + /// + #[doc = concat!("let bytes = ", stringify!($Xe), "::<", stringify!($T), ">::from_ne(", swap_op!($T), ").to_ne_bytes();")] + /// assert_eq!( + /// bytes, + /// if cfg!(target_endian = "big") { + #[doc = concat!(" ", be_bytes!($T))] + /// } else { + #[doc = concat!(" ", le_bytes!($T))] + /// } + /// ); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn to_ne_bytes(self) -> [u8; mem::size_of::()] { + self.to_ne().to_ne_bytes() + } + + #[doc = concat!("Create a ", $order, " endian integer value from its representation as a byte array in ", $order, " endian.")] + /// + /// # Examples + /// + /// ``` + #[doc = concat!("use endian_num::", stringify!($Xe), ";")] + /// + #[doc = concat!("let value = ", stringify!($Xe), "::<", stringify!($T), ">::from_be_bytes(", be_bytes!($T), ");")] + #[doc = concat!("assert_eq!(value.to_ne(), ", swap_op!($T), ");")] + /// ``` + /// + /// When starting from a slice rather than an array, fallible conversion APIs can be used: + /// + /// ``` + #[doc = concat!("use endian_num::", stringify!($Xe), ";")] + /// + #[doc = concat!("fn read_be_", stringify!($T), "(input: &mut &[u8]) -> ", stringify!($Xe), "<", stringify!($T), "> {")] + #[doc = concat!(" let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($Xe), "<", stringify!($T), ">>());")] + /// *input = rest; + #[doc = concat!(" ", stringify!($Xe), "::<", stringify!($T), ">::from_be_bytes(int_bytes.try_into().unwrap())")] + /// } + /// ``` + #[must_use] + #[inline] + pub const fn from_be_bytes(bytes: [u8; mem::size_of::()]) -> Self { + Self::from_ne(<$T>::from_be_bytes(bytes)) + } + + #[doc = concat!("Create a ", $order, " endian integer value from its representation as a byte array in ", $order_other, " endian.")] + /// + /// # Examples + /// + /// ``` + #[doc = concat!("use endian_num::", stringify!($Xe), ";")] + /// + #[doc = concat!("let value = ", stringify!($Xe), "::<", stringify!($T), ">::from_le_bytes(", le_bytes!($T), ");")] + #[doc = concat!("assert_eq!(value.to_ne(), ", swap_op!($T), ");")] + /// ``` + /// + /// When starting from a slice rather than an array, fallible conversion APIs can be used: + /// + /// ``` + #[doc = concat!("use endian_num::", stringify!($Xe), ";")] + /// + #[doc = concat!("fn read_le_", stringify!($T), "(input: &mut &[u8]) -> ", stringify!($Xe), "<", stringify!($T), "> {")] + #[doc = concat!(" let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($Xe), "<", stringify!($T), ">>());")] + /// *input = rest; + #[doc = concat!(" ", stringify!($Xe), "::<", stringify!($T), ">::from_le_bytes(int_bytes.try_into().unwrap())")] + /// } + /// ``` + #[must_use] + #[inline] + pub const fn from_le_bytes(bytes: [u8; mem::size_of::()]) -> Self { + Self::from_ne(<$T>::from_le_bytes(bytes)) + } + + #[doc = concat!("Create a ", $order, " endian integer value from its memory representation as a byte array in native endianness.")] + /// + /// As the target platform's native endianness is used, portable code + /// likely wants to use [`from_be_bytes`] or [`from_le_bytes`], as + /// appropriate instead. + /// + /// [`from_be_bytes`]: Self::from_be_bytes + /// [`from_le_bytes`]: Self::from_le_bytes + /// + /// # Examples + /// + /// ``` + #[doc = concat!("use endian_num::", stringify!($Xe), ";")] + /// + #[doc = concat!("let value = ", stringify!($Xe), "::<", stringify!($T), ">::from_ne_bytes(if cfg!(target_endian = \"big\") {")] + #[doc = concat!(" ", be_bytes!($T))] + /// } else { + #[doc = concat!(" ", le_bytes!($T))] + /// }); + #[doc = concat!("assert_eq!(value.to_ne(), ", swap_op!($T), ");")] + /// ``` + /// + /// When starting from a slice rather than an array, fallible conversion APIs can be used: + /// + /// ``` + #[doc = concat!("use endian_num::", stringify!($Xe), ";")] + /// + #[doc = concat!("fn read_ne_", stringify!($T), "(input: &mut &[u8]) -> ", stringify!($Xe), "<", stringify!($T), "> {")] + #[doc = concat!(" let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($Xe), "<", stringify!($T), ">>());")] + /// *input = rest; + #[doc = concat!(" ", stringify!($Xe), "::<", stringify!($T), ">::from_ne_bytes(int_bytes.try_into().unwrap())")] + /// } + /// ``` + #[must_use] + #[inline] + pub const fn from_ne_bytes(bytes: [u8; mem::size_of::()]) -> Self { + Self::from_ne(<$T>::from_ne_bytes(bytes)) + } + + /// Shifts the bits to the left by a specified amount, `n`, + /// wrapping the truncated bits to the end of the resulting integer. + /// + /// Please note this isn't the same operation as the `<<` shifting operator! + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("use endian_num::", stringify!($Xe), ";")] + /// + #[doc = concat!("let n = ", stringify!($Xe), "::<", stringify!($T), ">::from_ne(", rot_op!($T), ");")] + #[doc = concat!("let m = ", stringify!($Xe), "::<", stringify!($T), ">::from_ne(", rot_result!($T), ");")] + /// + #[doc = concat!("assert_eq!(n.rotate_left(", rot!($T), "), m);")] + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn rotate_left(self, n: u32) -> Self { + Self::from_ne(self.to_ne().rotate_left(n)) + } + + /// Shifts the bits to the right by a specified amount, `n`, + /// wrapping the truncated bits to the beginning of the resulting + /// integer. + /// + /// Please note this isn't the same operation as the `>>` shifting operator! + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("use endian_num::", stringify!($Xe), ";")] + /// + #[doc = concat!("let n = ", stringify!($Xe), "::<", stringify!($T), ">::from_ne(", rot_result!($T), ");")] + #[doc = concat!("let m = ", stringify!($Xe), "::<", stringify!($T), ">::from_ne(", rot_op!($T), ");")] + /// + #[doc = concat!("assert_eq!(n.rotate_right(", rot!($T), "), m);")] + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn rotate_right(self, n: u32) -> Self { + Self::from_ne(self.to_ne().rotate_right(n)) + } + + /// Reverses the byte order of the integer. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("use endian_num::", stringify!($Xe), ";")] + /// + #[doc = concat!("let n = ", stringify!($Xe), "(", swap_op!($T), stringify!($T), ");")] + /// let m = n.swap_bytes(); + /// + #[doc = concat!("assert_eq!(m, ", stringify!($Xe), "(", swapped!($T), "));")] + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn swap_bytes(self) -> Self { + Self(self.0.swap_bytes()) + } + + /// Reverses the order of bits in the integer. + /// + /// The least significant bit becomes the most significant bit, + /// second least-significant bit becomes second most-significant bit, etc. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("use endian_num::", stringify!($Xe), ";")] + /// + #[doc = concat!("let n = ", stringify!($Xe), "(", swap_op!($T), stringify!($T), ");")] + /// let m = n.reverse_bits(); + /// + #[doc = concat!("assert_eq!(m, ", stringify!($Xe), "(", reversed!($T), "));")] + #[doc = concat!("assert_eq!(", stringify!($Xe), "(0), ", stringify!($Xe), "(0", stringify!($T), ").reverse_bits());")] + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn reverse_bits(self) -> Self { + Self(self.0.reverse_bits()) + } + + /// Raises self to the power of `exp`, using exponentiation by squaring. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("use endian_num::", stringify!($Xe), ";")] + /// + #[doc = concat!("assert_eq!(", stringify!($Xe), "::<", stringify!($T), ">::from_ne(2).pow(5).to_ne(), 32);")] + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn pow(self, exp: u32) -> Self { + Self::from_ne(self.to_ne().pow(exp)) + } + } + + impl From<$T> for $Xe<$T> { + #[inline] + fn from(value: $T) -> Self { + Self::from_ne(value) + } + } + + impl From<$Xe<$T>> for $T { + #[inline] + fn from(value: $Xe<$T>) -> Self { + value.to_ne() + } + } + + impl From<$Other<$T>> for $Xe<$T> { + #[inline] + fn from(value: $Other<$T>) -> Self { + Self::$from_other(value) + } + } + + #[cfg(feature = "bitflags")] + impl bitflags::Bits for $Xe<$T> { + const EMPTY: Self = Self::MIN; + + const ALL: Self = Self::MAX; + } + + #[cfg(feature = "bitflags")] + impl bitflags::parser::ParseHex for $Xe<$T> + { + fn parse_hex(input: &str) -> Result { + <$T>::parse_hex(input).map($Xe::<$T>::from_ne) + } + } + + #[cfg(feature = "bitflags")] + impl bitflags::parser::WriteHex for $Xe<$T> + { + fn write_hex(&self, writer: W) -> fmt::Result { + self.to_ne().write_hex(writer) + } + } + }; +} + +endian_int_impl! { Xe Xe Xe Xe Xe Xe Xe Xe Xe Xe Xe Xe } + +macro_rules! endian_int_impl_signed { + ($(Xe<$T:ty>)*) => {$( + endian_int_impl_signed! { Be<$T> } + endian_int_impl_signed! { Le<$T> } + )*}; + ($Xe:ident<$T:ty>) => { + unop_impl! { impl Neg, neg for $Xe<$T> } + forward_ref_unop! { impl Neg, neg for $Xe<$T> } + + impl $Xe<$T> { + /// Computes the absolute value of self. + /// + #[doc = concat!("See [`", stringify!($T), "::abs`]")] + /// for documentation on overflow behavior. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("use endian_num::", stringify!($Xe), ";")] + /// + #[doc = concat!("assert_eq!(", stringify!($Xe), "::<", stringify!($T), ">::from_ne(10).abs(), ", stringify!($Xe), "::<", stringify!($T), ">::from_ne(10));")] + #[doc = concat!("assert_eq!(", stringify!($Xe), "::<", stringify!($T), ">::from_ne(-10).abs(), ", stringify!($Xe), "::<", stringify!($T), ">::from_ne(10));")] + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn abs(self) -> Self { + Self::from_ne(self.to_ne().abs()) + } + + /// Returns a number representing sign of `self`. + /// + /// - `0` if the number is zero + /// - `1` if the number is positive + /// - `-1` if the number is negative + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("use endian_num::", stringify!($Xe), ";")] + /// + #[doc = concat!("assert_eq!(", stringify!($Xe), "::<", stringify!($T), ">::from_ne(10).signum().to_ne(), 1);")] + #[doc = concat!("assert_eq!(", stringify!($Xe), "::<", stringify!($T), ">::from_ne(0).signum().to_ne(), 0);")] + #[doc = concat!("assert_eq!(", stringify!($Xe), "::<", stringify!($T), ">::from_ne(-10).signum().to_ne(), -1);")] + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn signum(self) -> Self { + Self::from_ne(self.to_ne().signum()) + } + + /// Returns `true` if `self` is positive and `false` if the number is zero or + /// negative. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("use endian_num::", stringify!($Xe), ";")] + /// + #[doc = concat!("assert!(", stringify!($Xe), "::<", stringify!($T), ">::from_ne(10).is_positive());")] + #[doc = concat!("assert!(!", stringify!($Xe), "::<", stringify!($T), ">::from_ne(-10).is_positive());")] + /// ``` + #[must_use] + #[inline] + pub const fn is_positive(self) -> bool { + self.to_ne().is_positive() + } + + /// Returns `true` if `self` is negative and `false` if the number is zero or + /// positive. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("use endian_num::", stringify!($Xe), ";")] + /// + #[doc = concat!("assert!(", stringify!($Xe), "::<", stringify!($T), ">::from_ne(-10).is_negative());")] + #[doc = concat!("assert!(!", stringify!($Xe), "::<", stringify!($T), ">::from_ne(10).is_negative());")] + /// ``` + #[must_use] + #[inline] + pub const fn is_negative(self) -> bool { + self.to_ne().is_negative() + } + + /// Returns the number of ones in the binary representation of `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("use endian_num::", stringify!($Xe), ";")] + /// + #[doc = concat!("let n = ", stringify!($Xe), "(0b100_0000", stringify!($T), ");")] + /// + /// assert_eq!(n.count_ones(), 1); + /// ``` + #[doc(alias = "popcount")] + #[doc(alias = "popcnt")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn count_ones(self) -> u32 { + self.0.count_ones() + } + + /// Returns the number of zeros in the binary representation of `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("use endian_num::", stringify!($Xe), ";")] + /// + #[doc = concat!("assert_eq!(", stringify!($Xe), "::<", stringify!($T), ">::MAX.count_zeros(), 1);")] + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn count_zeros(self) -> u32 { + self.0.count_zeros() + } + + /// Returns the number of leading zeros in the binary representation of `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("use endian_num::", stringify!($Xe), ";")] + /// + #[doc = concat!("let n = ", stringify!($Xe), "::<", stringify!($T), ">::from_ne(-1);")] + /// + /// assert_eq!(n.leading_zeros(), 0); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn leading_zeros(self) -> u32 { + self.to_ne().leading_zeros() + } + + /// Returns the number of trailing zeros in the binary representation of `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("use endian_num::", stringify!($Xe), ";")] + /// + #[doc = concat!("let n = ", stringify!($Xe), "::<", stringify!($T), ">::from_ne(-4);")] + /// + /// assert_eq!(n.trailing_zeros(), 2); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn trailing_zeros(self) -> u32 { + self.to_ne().trailing_zeros() + } + } + }; +} + +endian_int_impl_signed! { Xe Xe Xe Xe Xe Xe } + +macro_rules! endian_int_impl_unsigned { + ($(Xe<$T:ty>)*) => {$( + endian_int_impl_unsigned! { Be<$T> } + endian_int_impl_unsigned! { Le<$T> } + )*}; + ($Xe:ident<$T:ty>) => { + impl $Xe<$T> { + /// Returns the number of ones in the binary representation of `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("use endian_num::", stringify!($Xe), ";")] + /// + #[doc = concat!("let n = ", stringify!($Xe), "(0b01001100", stringify!($T), ");")] + /// + /// assert_eq!(n.count_ones(), 3); + /// ``` + #[doc(alias = "popcount")] + #[doc(alias = "popcnt")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn count_ones(self) -> u32 { + self.0.count_ones() + } + + /// Returns the number of zeros in the binary representation of `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("use endian_num::", stringify!($Xe), ";")] + /// + #[doc = concat!("assert_eq!(", stringify!($Xe), "::<", stringify!($T), ">::MAX.count_zeros(), 0);")] + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn count_zeros(self) -> u32 { + self.0.count_zeros() + } + + /// Returns the number of leading zeros in the binary representation of `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("use endian_num::", stringify!($Xe), ";")] + /// + #[doc = concat!("let n = ", stringify!($Xe), "::<", stringify!($T), ">::from_ne(", stringify!($T), "::MAX >> 2);")] + /// + /// assert_eq!(n.leading_zeros(), 2); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn leading_zeros(self) -> u32 { + self.to_ne().leading_zeros() + } + + /// Returns the number of trailing zeros in the binary representation of `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("use endian_num::", stringify!($Xe), ";")] + /// + #[doc = concat!("let n = ", stringify!($Xe), "::<", stringify!($T), ">::from_ne(0b0101000);")] + /// + /// assert_eq!(n.trailing_zeros(), 3); + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn trailing_zeros(self) -> u32 { + self.to_ne().trailing_zeros() + } + + /// Returns `true` if and only if `self == 2^k` for some `k`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("use endian_num::", stringify!($Xe), ";")] + /// + #[doc = concat!("assert!(", stringify!($Xe), "::<", stringify!($T), ">::from_ne(16).is_power_of_two());")] + #[doc = concat!("assert!(!", stringify!($Xe), "::<", stringify!($T), ">::from_ne(10).is_power_of_two());")] + /// ``` + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn is_power_of_two(self) -> bool { + self.to_ne().is_power_of_two() + } + } + }; +} + +endian_int_impl_unsigned! { Xe Xe Xe Xe Xe Xe } + +macro_rules! impl_from_array { + (Xe<$Large:ty>, [Xe<$Small:ty>; $i:literal]) => { + impl_from_array!(Be<$Large>, [Be<$Small>; $i], "big"); + impl_from_array!(Le<$Large>, [Le<$Small>; $i], "little"); + }; + ($Large:ty, [$Small:ty; $i:literal], $order:literal) => { + impl From<[$Small; $i]> for $Large { + #[doc = concat!("Create an integer from its representation as a [`", stringify!($Small), "`] array in ", $order, " endian.")] + #[inline] + fn from(value: [$Small; $i]) -> Self { + // SAFETY: integers are plain old datatypes so we can always transmute to them + unsafe { mem::transmute(value) } + } + } + + impl From<$Large> for [$Small; $i] { + #[doc = concat!("Return the memory representation of this integer as a [`", stringify!($Small), "`] array in ", $order, "-endian byte order.")] + #[inline] + fn from(value: $Large) -> Self { + // SAFETY: integers are plain old datatypes so we can always transmute them to + // arrays of bytes + unsafe { mem::transmute(value) } + } + } + }; +} + +impl_from_array!(Xe, [Xe; 2]); +impl_from_array!(Xe, [Xe; 4]); +impl_from_array!(Xe, [Xe; 2]); +impl_from_array!(Xe, [Xe; 8]); +impl_from_array!(Xe, [Xe; 4]); +impl_from_array!(Xe, [Xe; 2]); +impl_from_array!(Xe, [Xe; 16]); +impl_from_array!(Xe, [Xe; 8]); +impl_from_array!(Xe, [Xe; 4]); +impl_from_array!(Xe, [Xe; 2]); + +macro_rules! type_alias { + (#[$cfg:meta], $alias:ident = $SelfT:ty, $bits:expr, $order:expr) => { + #[doc = concat!("A ", stringify!($bits), "-bit unsigned integer stored in ", $order, "-endian byte order.")] + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("use endian_num::", stringify!($alias), ";")] + /// + #[doc = concat!("let n = 0x1A;")] + /// + #[doc = concat!("if cfg!(target_endian = \"", $order, "\") {")] + #[doc = concat!(" assert_eq!(", stringify!($alias), "::from_ne(n).0, n);")] + /// } else { + #[doc = concat!(" assert_eq!(", stringify!($alias), "::from_ne(n).0, n.swap_bytes());")] + /// } + /// ``` + #[$cfg] + #[allow(non_camel_case_types)] + pub type $alias = $SelfT; + }; +} + +type_alias!(#[cfg(feature = "linux-types")], be16 = Be, 16, "big"); +type_alias!(#[cfg(feature = "linux-types")], be32 = Be, 32, "big"); +type_alias!(#[cfg(feature = "linux-types")], be64 = Be, 64, "big"); +type_alias!(#[cfg(feature = "linux-types")], be128 = Be, 128, "big"); +type_alias!(#[cfg(feature = "linux-types")], le16 = Le, 16, "little"); +type_alias!(#[cfg(feature = "linux-types")], le32 = Le, 32, "little"); +type_alias!(#[cfg(feature = "linux-types")], le64 = Le, 64, "little"); +type_alias!(#[cfg(feature = "linux-types")], le128 = Le, 128, "little");