rend-0.5.2/.cargo/config.toml000064400000000000000000000000661046102023000140750ustar 00000000000000[build] rustdocflags = ["--cfg", "docsrs"] [env] rend-0.5.2/.cargo_vcs_info.json0000644000000001360000000000100117700ustar { "git": { "sha1": "86455add3bfce3879762c655ead34f750beb60a0" }, "path_in_vcs": "" }rend-0.5.2/.gitattributes000064400000000000000000000000171046102023000134510ustar 00000000000000* text=auto rend-0.5.2/.github/FUNDING.yml000064400000000000000000000000161046102023000137320ustar 00000000000000github: rkyv rend-0.5.2/.github/workflows/ci.yml000064400000000000000000000064721046102023000153040ustar 00000000000000name: CI on: push: pull_request: workflow_dispatch: schedule: - cron: "0 10 * * *" permissions: contents: read env: RUSTFLAGS: -Dwarnings jobs: features: name: Features / ${{ matrix.std }} ${{ matrix.derive }} runs-on: ubuntu-latest strategy: fail-fast: false matrix: external: - '' - bytecheck bytemuck-1 zerocopy-0_8 steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - run: cargo test --verbose --tests --no-default-features --features "${{ matrix.external }}" toolchain: name: Toolchain / ${{ matrix.toolchain }} ${{ matrix.opt }} runs-on: ubuntu-latest strategy: fail-fast: false matrix: toolchain: - stable - beta - nightly opt: - '' - --release steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.toolchain }} - run: cargo test --verbose ${{ matrix.opt }} miri: name: Miri / ${{ matrix.opt }} runs-on: ubuntu-latest strategy: fail-fast: false matrix: opt: - '' - --release steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@miri - run: cargo miri setup - run: cargo miri test ${{ matrix.opt }} --verbose env: MIRIFLAGS: -Zmiri-disable-stacked-borrows -Zmiri-tree-borrows test: name: Test / ${{ matrix.target }} ${{ matrix.opt }} runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: opt: - '' - --release include: - os: ubuntu-latest target: x86_64-unknown-linux-gnu - os: macos-latest target: aarch64-apple-darwin - os: windows-latest target: x86_64-pc-windows-msvc steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - run: cargo test ${{ matrix.opt }} cross: name: Cross / ${{ matrix.target }} runs-on: ubuntu-latest strategy: fail-fast: false matrix: target: - i686-unknown-linux-gnu - i586-unknown-linux-gnu - armv7-unknown-linux-gnueabihf - aarch64-unknown-linux-gnu - thumbv6m-none-eabi steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - run: cargo install cross - run: cross build --no-default-features --features "bytecheck bytemuck-1 zerocopy-0_8" --target ${{ matrix.target }} --verbose format: name: Format runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@nightly with: components: rustfmt - run: cargo fmt --check clippy: name: Clippy runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@nightly with: components: clippy - run: cargo clippy doc: name: Doc runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@nightly - run: cargo doc rend-0.5.2/.gitignore000064400000000000000000000000231046102023000125430ustar 00000000000000/target Cargo.lock rend-0.5.2/.vscode/tasks.json000064400000000000000000000003431046102023000141410ustar 00000000000000{ "version": "2.0.0", "tasks": [ { "type": "cargo", "command": "build", "problemMatcher": [ "$rustc" ], "group": { "kind": "build", "isDefault": true }, "label": "rust: cargo build" } ] }rend-0.5.2/Cargo.toml0000644000000026330000000000100077720ustar # 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 = "2021" name = "rend" version = "0.5.2" authors = ["David Koloski "] build = false autobins = false autoexamples = false autotests = false autobenches = false description = "Cross-platform, endian-aware primitives for Rust" documentation = "https://docs.rs/rend" readme = "README.md" keywords = [ "endian", "no_std", ] categories = [ "encoding", "no-std", ] license = "MIT" repository = "https://github.com/djkoloski/rend" [lib] name = "rend" path = "src/lib.rs" [dependencies.bytecheck] version = "0.8" optional = true default-features = false [dependencies.bytemuck-1] version = "1" optional = true default-features = false package = "bytemuck" [dependencies.zerocopy] version = "0.8" optional = true default-features = false [dependencies.zerocopy-derive] version = "0.8" optional = true default-features = false [features] default = [] zerocopy-0_8 = [ "dep:zerocopy", "dep:zerocopy-derive", ] rend-0.5.2/Cargo.toml.orig000064400000000000000000000017371046102023000134570ustar 00000000000000[package] name = "rend" description = "Cross-platform, endian-aware primitives for Rust" version = "0.5.2" authors = ["David Koloski "] edition = "2021" license = "MIT" readme = "README.md" repository = "https://github.com/djkoloski/rend" keywords = ["endian", "no_std"] categories = ["encoding", "no-std"] documentation = "https://docs.rs/rend" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] bytecheck = { version = "0.8", optional = true, default-features = false } bytemuck-1 = { package = "bytemuck", version = "1", optional = true, default-features = false } zerocopy = { version = "0.8", optional = true, default-features = false } zerocopy-derive = { version = "0.8", optional = true, default-features = false } [features] default = [] zerocopy-0_8 = ["dep:zerocopy", "dep:zerocopy-derive"] [patch.crates-io] bytecheck = { git = "https://github.com/rkyv/bytecheck" } rend-0.5.2/LICENSE000064400000000000000000000020351046102023000115650ustar 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.5.2/README.md000064400000000000000000000026351046102023000120450ustar 00000000000000# `rend` [![crates.io badge]][crates.io] [![docs badge]][docs] [![license badge]][license] [crates.io badge]: https://img.shields.io/crates/v/rend.svg [crates.io]: https://crates.io/crates/rend [docs badge]: https://img.shields.io/docsrs/rend [docs]: https://docs.rs/rend [license badge]: https://img.shields.io/badge/license-MIT-blue.svg [license]: https://github.com/rkyv/rend/blob/master/LICENSE rend provides cross-platform, endian-aware primitives for Rust. ## Documentation - [rend](https://docs.rs/rend), provides cross-platform, endian-aware primitives for Rust ## Example ```rust use core::mem::transmute; use rend::*; let little_int = i32_le::from_native(0x12345678); // Internal representation is little-endian assert_eq!( [0x78, 0x56, 0x34, 0x12], unsafe { 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::from_native(0x12345678); // Internal representation is big-endian assert_eq!( [0x12, 0x34, 0x56, 0x78], unsafe { 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.5.2/example.md000064400000000000000000000015551046102023000125430ustar 00000000000000```rust use core::mem::transmute; use rend::*; let little_int = i32_le::from_native(0x12345678); // Internal representation is little-endian assert_eq!( [0x78, 0x56, 0x34, 0x12], unsafe { 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::from_native(0x12345678); // Internal representation is big-endian assert_eq!( [0x12, 0x34, 0x56, 0x78], unsafe { 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.5.2/rustfmt.toml000064400000000000000000000006511046102023000131630ustar 00000000000000max_width = 80 comment_width = 80 wrap_comments = true group_imports = "StdExternalCrate" imports_granularity = "Crate" condense_wildcard_suffixes = true error_on_line_overflow = true error_on_unformatted = true format_code_in_doc_comments = true format_macro_matchers = true format_macro_bodies = true format_strings = true hex_literal_case = "Lower" normalize_comments = true use_field_init_shorthand = true rend-0.5.2/src/common.rs000064400000000000000000000357361046102023000132230ustar 00000000000000macro_rules! impl_integer { ($name:ident: $endian:ident $prim:ty) => { impl $name { #[doc = concat!( "Returns a `", stringify!($name), "` containing `value`.", )] #[inline] pub const fn from_native(value: $prim) -> Self { Self(swap_endian!($endian value)) } #[doc = concat!( "Returns a `", stringify!($prim), "` with the same value as `self`.", )] #[inline] pub const fn to_native(self) -> $prim { swap_endian!($endian self.0) } } }; } macro_rules! impl_signed_integer_traits { ($name:ident: $endian:ident $prim:ident) => { // SAFETY: An impl of `CheckBytes` with a `check_bytes` function that is // a no-op is sound for signed integers. unsafe_impl_check_bytes_noop!(for $name); // SAFETY: Signed integers are inhabited and allow all bit patterns, // fulfilling the requirements of `Zeroable` and `Pod`. unsafe_impl_zeroable!(for $name); unsafe_impl_pod!(for $name); impl_binop!(Add::add for $name: $prim); impl_binassign!(AddAssign::add_assign for $name: $prim); impl_clone_and_copy!(for $name); impl_fmt!(Binary for $name); impl_binop!(BitAnd::bitand for $name: $prim); impl_binassign!(BitAndAssign::bitand_assign for $name: $prim); impl_binop!(BitOr::bitor for $name: $prim); impl_binassign!(BitOrAssign::bitor_assign for $name: $prim); impl_binop!(BitXor::bitxor for $name: $prim); impl_binassign!(BitXorAssign::bitxor_assign for $name: $prim); impl_fmt!(Debug for $name); impl_default!(for $name: $prim); impl_fmt!(Display for $name); impl_binop!(Div::div for $name: $prim); impl_binassign!(DivAssign::div_assign for $name: $prim); impl_from!(for $name: $prim); impl_try_from_ptr_size!(isize for $name: $prim); impl_hash!(for $name); impl_fmt!(LowerExp for $name); impl_fmt!(LowerHex for $name); impl_binop!(Mul::mul for $name: $prim); impl_binassign!(MulAssign::mul_assign for $name: $prim); impl_unop!(Neg::neg for $name: $prim); impl_unop!(Not::not for $name: $prim); impl_fmt!(Octal for $name); impl_partial_eq_and_eq!(for $name: $prim); impl_partial_ord_and_ord!(for $name: $prim); impl_product_and_sum!(for $name); impl_binop!(Rem::rem for $name: $prim); impl_binassign!(RemAssign::rem_assign for $name: $prim); impl_binop!(Shl::shl for $name: $prim); impl_binassign!(ShlAssign::shl_assign for $name: $prim); impl_binop!(Shr::shr for $name: $prim); impl_binassign!(ShrAssign::shr_assign for $name: $prim); impl_binop!(Sub::sub for $name: $prim); impl_binassign!(SubAssign::sub_assign for $name: $prim); impl_fmt!(UpperExp for $name); impl_fmt!(UpperHex for $name); }; } macro_rules! impl_unsigned_integer_traits { ($name:ident: $endian:ident $prim:ident) => { // SAFETY: An impl of `CheckBytes` with a `check_bytes` function that is // a no-op is sound for unsigned integers. unsafe_impl_check_bytes_noop!(for $name); // SAFETY: Unsigned integers are inhabited and allow all bit patterns, // fulfilling the requirements of `Zeroable` and `Pod`. unsafe_impl_zeroable!(for $name); unsafe_impl_pod!(for $name); impl_binop!(Add::add for $name: $prim); impl_binassign!(AddAssign::add_assign for $name: $prim); impl_clone_and_copy!(for $name); impl_fmt!(Binary for $name); impl_binop!(BitAnd::bitand for $name: $prim); impl_binassign!(BitAndAssign::bitand_assign for $name: $prim); impl_binop!(BitOr::bitor for $name: $prim); impl_binassign!(BitOrAssign::bitor_assign for $name: $prim); impl_binop!(BitXor::bitxor for $name: $prim); impl_binassign!(BitXorAssign::bitxor_assign for $name: $prim); impl_fmt!(Debug for $name); impl_default!(for $name: $prim); impl_fmt!(Display for $name); impl_binop!(Div::div for $name: $prim); impl_binassign!(DivAssign::div_assign for $name: $prim); impl_from!(for $name: $prim); impl_try_from_ptr_size!(usize for $name: $prim); impl_hash!(for $name); impl_fmt!(LowerExp for $name); impl_fmt!(LowerHex for $name); impl_binop!(Mul::mul for $name: $prim); impl_binassign!(MulAssign::mul_assign for $name: $prim); impl_unop!(Not::not for $name: $prim); impl_fmt!(Octal for $name); impl_partial_eq_and_eq!(for $name: $prim); impl_partial_ord_and_ord!(for $name: $prim); impl_product_and_sum!(for $name); impl_binop!(Rem::rem for $name: $prim); impl_binassign!(RemAssign::rem_assign for $name: $prim); impl_binop!(Shl::shl for $name: $prim); impl_binassign!(ShlAssign::shl_assign for $name: $prim); impl_binop!(Shr::shr for $name: $prim); impl_binassign!(ShrAssign::shr_assign for $name: $prim); impl_binop!(Sub::sub for $name: $prim); impl_binassign!(SubAssign::sub_assign for $name: $prim); impl_fmt!(UpperExp for $name); impl_fmt!(UpperHex for $name); }; } macro_rules! impl_float { ($name:ident: $endian:ident $prim:ty as $prim_int:ty) => { impl $name { #[doc = concat!( "Returns a `", stringify!($name), "` containing `value`.", )] #[inline] pub const fn from_native(value: $prim) -> Self { use core::mem::transmute; // `transmute` is used here because `from_bits` and `to_bits` // are not stably const as of 1.81.0. #[allow(clippy::transmute_float_to_int)] // SAFETY: `$prim` and `$prim_int` have the same size and all // bit patterns are valid for both. let value = unsafe { transmute::<$prim, $prim_int>(value) }; let value = swap_endian!($endian value); #[allow(clippy::transmute_int_to_float)] // SAFETY: `$prim` and `$prim_int` have the same size and all // bit patterns are valid for both. let value = unsafe { transmute::<$prim_int, $prim>(value) }; Self(value) } #[doc = concat!( "Returns a `", stringify!($prim), "` with the same value as `self`.", )] #[inline] pub const fn to_native(self) -> $prim { use core::mem::transmute; // `transmute` is used here because `from_bits` and `to_bits` // are not stably const as of 1.81.0. #[allow(clippy::transmute_float_to_int)] // SAFETY: `$prim` and `$prim_int` have the same size and all // bit patterns are valid for both. let value = unsafe { transmute::<$prim, $prim_int>(self.0) }; let value = swap_endian!($endian value); #[allow(clippy::transmute_int_to_float)] // SAFETY: `$prim` and `$prim_int` have the same size and all // bit patterns are valid for both. unsafe { transmute::<$prim_int, $prim>(value) } } } // SAFETY: An impl of `CheckBytes` with a `check_bytes` function that is // a no-op is sound for floats. unsafe_impl_check_bytes_noop!(for $name); // SAFETY: `Pod` is implemented for `f32` and `f64` - as such, flipped // representations must also be `Pod`. unsafe_impl_zeroable!(for $name); unsafe_impl_pod!(for $name); impl_binop!(Add::add for $name: $prim); impl_binassign!(AddAssign::add_assign for $name: $prim); impl_clone_and_copy!(for $name); impl_fmt!(Debug for $name); impl_default!(for $name: $prim); impl_fmt!(Display for $name); impl_binop!(Div::div for $name: $prim); impl_binassign!(DivAssign::div_assign for $name: $prim); impl_from!(for $name: $prim); impl_fmt!(LowerExp for $name); impl_binop!(Mul::mul for $name: $prim); impl_binassign!(MulAssign::mul_assign for $name: $prim); impl_unop!(Neg::neg for $name: $prim); impl_partial_eq_and_eq!(for $name: $prim); impl_partial_ord!(for $name: $prim); impl_product_and_sum!(for $name); impl_binop!(Rem::rem for $name: $prim); impl_binassign!(RemAssign::rem_assign for $name: $prim); impl_binop!(Sub::sub for $name: $prim); impl_binassign!(SubAssign::sub_assign for $name: $prim); impl_fmt!(UpperExp for $name); }; } macro_rules! impl_char { ($name:ident: $endian:ident) => { impl $name { #[doc = concat!( "Returns a `", stringify!($name), "` containing `value`.", )] #[inline] pub const fn from_native(value: char) -> Self { Self(swap_endian!($endian value as u32)) } #[doc = concat!( "Returns a `", stringify!($prim), "` with the same value as `self`.", )] #[inline] pub const fn to_native(self) -> char { use core::mem::transmute; // `transmute` is used here because `from_u32_unchecked` is not // stably const as of 1.72.0. // SAFETY: `u32` and `char` have the same size and it is an // invariant of this type that it contains a valid `char` when // swapped to native endianness. unsafe { transmute::(swap_endian!($endian self.0)) } } } // SAFETY: An all-zero bits `char` is just the null char, whether you // read it forwards or backwards. unsafe_impl_zeroable!(for $name); // SAFETY: `char`s do not contain any uninit bytes. unsafe_impl_no_uninit!(for $name); impl_clone_and_copy!(for $name); impl_fmt!(Debug for $name); impl_default!(for $name: char); impl_fmt!(Display for $name); impl_from!(for $name: char); impl_hash!(for $name); impl_partial_eq_and_eq!(for $name: char); impl_partial_ord_and_ord!(for $name: char); #[cfg(feature = "bytecheck")] // SAFETY: `check_bytes` only returns `Ok` if the code point contained // within the endian-aware `char` represents a valid `char`. unsafe impl bytecheck::CheckBytes for $name where C: bytecheck::rancor::Fallible + ?Sized, C::Error: bytecheck::rancor::Trace, char: bytecheck::CheckBytes, { #[inline] unsafe fn check_bytes( value: *const Self, context: &mut C, ) -> Result<(), C::Error> { use bytecheck::rancor::ResultExt as _; // SAFETY: `value` points to a `Self`, which has the same size // as a `u32` and is at least as aligned as one. let u = unsafe { *value.cast::() }; let c = swap_endian!($endian u); // SAFETY: `value` points to a valid endian-aware `char` type if // `c` is a valid `char`. unsafe { char::check_bytes(&c as *const u32 as *const char, context) .with_trace(|| $crate::context::ValueCheckContext { inner_name: "char", outer_name: core::stringify!($name), }) } } } }; } macro_rules! impl_nonzero { ($name:ident: $endian:ident $prim:ty as $prim_int:ty) => { impl $name { /// Creates a non-zero if the given value is not zero. #[inline] pub const fn new(value: $prim_int) -> Option { if value != 0 { // SAFETY: `value` is not zero. Some(unsafe { Self::new_unchecked(value) }) } else { None } } /// Creates a non-zero without checking whether it is non-zero. This /// results in undefined behavior if the value is zero. /// /// # Safety /// /// The value must not be zero. #[inline] pub const unsafe fn new_unchecked(value: $prim_int) -> Self { // SAFETY: The caller has guaranteed that `value` is not zero. unsafe { Self(<$prim>::new_unchecked(swap_endian!($endian value))) } } /// Returns the value as a primitive type. #[inline] pub const fn get(self) -> $prim_int { swap_endian!($endian self.0.get()) } #[doc = concat!( "Returns a `", stringify!($name), "` containing `value`.", )] #[inline] pub const fn from_native(value: $prim) -> Self { // SAFETY: `value` is a non-zero integer and so `value.get()` // cannot return zero. unsafe { Self::new_unchecked(value.get()) } } #[doc = concat!( "Returns a `", stringify!($prim), "` with the same value as `self`.", )] #[inline] pub const fn to_native(self) -> $prim { // SAFETY: `self` is a non-zero integer and so `self.get()` // cannot return zero. unsafe { <$prim>::new_unchecked(self.get()) } } } // SAFETY: Non-zero integers do not contain any uninit bytes. unsafe_impl_no_uninit!(for $name); impl_clone_and_copy!(for $name); impl_fmt!(Binary for $name); impl_binop_nonzero!(BitOr::bitor for $name: $prim); impl_binassign_nonzero!(BitOrAssign::bitor_assign for $name: $prim); impl_fmt!(Debug for $name); impl_fmt!(Display for $name); impl_from!(for $name: $prim); impl_hash!(for $name); impl_fmt!(LowerHex for $name); impl_fmt!(Octal for $name); impl_partial_eq_and_eq!(for $name: $prim); impl_partial_ord_and_ord!(for $name: $prim); impl_fmt!(UpperHex for $name); }; } rend-0.5.2/src/context.rs000064400000000000000000000005741046102023000134070ustar 00000000000000use core::fmt; #[derive(Debug)] pub struct ValueCheckContext { pub inner_name: &'static str, pub outer_name: &'static str, } impl fmt::Display for ValueCheckContext { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "while checking {} value of {}", self.inner_name, self.outer_name, ) } } rend-0.5.2/src/lib.rs000064400000000000000000001150661046102023000124740ustar 00000000000000//! # rend //! //! rend provides cross-platform, endian-aware primitives for Rust. //! //! rend does not provide cross-platform alternatives for types that are //! inherently cross-platform, such as `bool` and `u8`. It also does not provide //! cross-platform alternatives for types that have an architecture-dependent //! size, such as `isize` and `usize`. rend does not support custom types. //! //! rend is intended to be used to build portable types that can be shared //! between different architectures. //! //! ## Features //! //! - `bytecheck`: Enables support for validating types using `bytecheck`. //! //! ## Crates //! //! - `zerocopy-0_8` //! //! ## Example: #![doc = include_str!("../example.md")] #![no_std] #![deny( future_incompatible, missing_docs, nonstandard_style, unsafe_op_in_unsafe_fn, unused, warnings, clippy::all, clippy::missing_safety_doc, clippy::undocumented_unsafe_blocks, rustdoc::broken_intra_doc_links, rustdoc::missing_crate_level_docs )] #![cfg_attr(all(docsrs, not(doctest)), feature(doc_cfg, doc_auto_cfg))] #[macro_use] mod common; #[cfg(feature = "bytecheck")] mod context; #[macro_use] mod traits; #[macro_use] mod util; pub mod unaligned; #[cfg(target_has_atomic = "16")] use core::sync::atomic::{AtomicI16, AtomicU16}; #[cfg(target_has_atomic = "32")] use core::sync::atomic::{AtomicI32, AtomicU32}; #[cfg(target_has_atomic = "64")] use core::sync::atomic::{AtomicI64, AtomicU64}; use core::{ num::{ NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64, }, sync::atomic::Ordering, }; // `rustfmt` keeps changing the indentation of the attributes in this macro. #[rustfmt::skip] macro_rules! define_newtype { ( $(#[$attr:meta])* $name:ident: $endian:ident $size_align:literal $prim:ty ) => { #[allow(non_camel_case_types)] #[doc = concat!( "A ", endian_name!($endian), "-endian `", stringify!($prim), "` with a guaranteed size and alignment of `", stringify!($size_align), "`.", )] $(#[$attr])* #[repr(C, align($size_align))] pub struct $name($prim); }; } macro_rules! define_signed_integer { ($name:ident: $endian:ident $size_align:literal $prim:ident) => { define_newtype!( #[cfg_attr( feature = "zerocopy-0_8", derive( zerocopy_derive::FromBytes, zerocopy_derive::IntoBytes, zerocopy_derive::Immutable, zerocopy_derive::KnownLayout, ), )] $name: $endian $size_align $prim ); impl_integer!($name: $endian $prim); impl_signed_integer_traits!($name: $endian $prim); }; } macro_rules! define_signed_integers { ($($le:ident $be:ident: $size_align:literal $prim:ident),* $(,)?) => { $( define_signed_integer!($le: little $size_align $prim); define_signed_integer!($be: big $size_align $prim); )* }; } define_signed_integers! { i16_le i16_be: 2 i16, i32_le i32_be: 4 i32, i64_le i64_be: 8 i64, i128_le i128_be: 16 i128, } macro_rules! define_unsigned_integer { ($name:ident: $endian:ident $size_align:literal $prim:ident) => { define_newtype!( #[cfg_attr( feature = "zerocopy-0_8", derive( zerocopy_derive::FromBytes, zerocopy_derive::IntoBytes, zerocopy_derive::Immutable, zerocopy_derive::KnownLayout, ), )] $name: $endian $size_align $prim ); impl_integer!($name: $endian $prim); impl_unsigned_integer_traits!($name: $endian $prim); } } macro_rules! define_unsigned_integers { ($($le:ident $be:ident: $size_align:literal $prim:ident),* $(,)?) => { $( define_unsigned_integer!($le: little $size_align $prim); define_unsigned_integer!($be: big $size_align $prim); )* }; } define_unsigned_integers! { u16_le u16_be: 2 u16, u32_le u32_be: 4 u32, u64_le u64_be: 8 u64, u128_le u128_be: 16 u128, } macro_rules! define_float { ( $name:ident: $endian:ident $size_align:literal $prim:ty as $prim_int:ty ) => { define_newtype!( #[cfg_attr( feature = "zerocopy-0_8", derive( zerocopy_derive::FromBytes, zerocopy_derive::IntoBytes, zerocopy_derive::Immutable, zerocopy_derive::KnownLayout, ), )] $name: $endian $size_align $prim ); impl_float!($name: $endian $prim as $prim_int); }; } macro_rules! define_floats { ($( $le:ident $be:ident: $size_align:literal $prim:ty as $prim_int:ty ),* $(,)?) => { $( define_float!($le: little $size_align $prim as $prim_int); define_float!($be: big $size_align $prim as $prim_int); )* }; } define_floats! { f32_le f32_be: 4 f32 as u32, f64_le f64_be: 8 f64 as u64, } macro_rules! define_char { ($name:ident: $endian:ident) => { define_newtype!( #[cfg_attr( feature = "zerocopy-0_8", derive( // The generated impl for `zerocopy::TryFromBytes` is overly // permissive. The derive macro doesn't understand that even // though this struct only contains a `u32`, it still has a // restricted set of valid bit patterns. Because // `zerocopy::TryFromBytes` has hidden, semver-breaking // members, I can't write a manual impl. So no impl for you. // // zerocopy_derive::TryFromBytes, zerocopy_derive::IntoBytes, zerocopy_derive::Immutable, zerocopy_derive::KnownLayout, ), )] $name: $endian 4 u32 ); impl_char!($name: $endian); }; } define_char!(char_le: little); define_char!(char_be: big); macro_rules! define_nonzero { ( $name:ident: $endian:ident $size_align:literal $prim:ty as $prim_int:ty ) => { define_newtype!( #[cfg_attr( feature = "zerocopy-0_8", derive( zerocopy_derive::TryFromBytes, zerocopy_derive::IntoBytes, zerocopy_derive::Immutable, zerocopy_derive::KnownLayout, ), )] $name: $endian $size_align $prim ); impl_nonzero!($name: $endian $prim as $prim_int); #[cfg(feature = "bytecheck")] // SAFETY: `check_bytes` only returns `Ok` if `value` points to a valid // non-zero value, which is the only requirement for `NonZero` integers. unsafe impl bytecheck::CheckBytes for $name where C: bytecheck::rancor::Fallible + ?Sized, C::Error: bytecheck::rancor::Trace, $prim: bytecheck::CheckBytes, { #[inline] unsafe fn check_bytes( value: *const Self, context: &mut C, ) -> Result<(), C::Error> { use bytecheck::rancor::ResultExt as _; // SAFETY: `value` points to a `Self`, which has the same size // as a `$prim` and is at least as aligned as one. Note that the // bit pattern for 0 is always the same regardless of // endianness. unsafe { <$prim>::check_bytes(value.cast(), context) .with_trace(|| $crate::context::ValueCheckContext { inner_name: core::stringify!($prim), outer_name: core::stringify!($name), }) } } } }; } macro_rules! define_nonzeros { ($( $le:ident $be:ident: $size_align:literal $prim:ty as $prim_int:ty ),* $(,)?) => { $( define_nonzero!($le: little $size_align $prim as $prim_int); define_nonzero!($be: big $size_align $prim as $prim_int); )* } } define_nonzeros! { NonZeroI16_le NonZeroI16_be: 2 NonZeroI16 as i16, NonZeroI32_le NonZeroI32_be: 4 NonZeroI32 as i32, NonZeroI64_le NonZeroI64_be: 8 NonZeroI64 as i64, NonZeroI128_le NonZeroI128_be: 16 NonZeroI128 as i128, NonZeroU16_le NonZeroU16_be: 2 NonZeroU16 as u16, NonZeroU32_le NonZeroU32_be: 4 NonZeroU32 as u32, NonZeroU64_le NonZeroU64_be: 8 NonZeroU64 as u64, NonZeroU128_le NonZeroU128_be: 16 NonZeroU128 as u128, } #[allow(dead_code)] const fn fetch_ordering(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, } } #[cfg(any( target_has_atomic = "16", target_has_atomic = "32", target_has_atomic = "64", ))] macro_rules! define_atomic { ( $name:ident: $endian:ident $size_align:literal $prim:ty as $prim_int:ty ) => { define_newtype!( #[cfg_attr( feature = "zerocopy-0_8", derive( zerocopy_derive::FromBytes, zerocopy_derive::IntoBytes, zerocopy_derive::KnownLayout, ), )] $name: $endian $size_align $prim ); impl $name { #[doc = concat!( "Returns a `", stringify!($name), "` containing `value`.", )] #[inline] pub const fn new(value: $prim_int) -> Self { Self(<$prim>::new(swap_endian!($endian value))) } } // SAFETY: An impl of `CheckBytes` with a `check_bytes` function that is // a no-op is sound for atomic integers. unsafe_impl_check_bytes_noop!(for $name); impl $name { /// Stores a value into the atomic integer if the current value is /// the same as the `current` value. /// #[doc = concat!( "See [`", stringify!($prim), "::compare_exchange`] for more information.", )] #[inline] pub fn compare_exchange( &self, current: $prim_int, new: $prim_int, success: Ordering, failure: Ordering, ) -> Result<$prim_int, $prim_int> { match self.0.compare_exchange( swap_endian!($endian current), swap_endian!($endian new), success, failure, ) { Ok(x) => Ok(swap_endian!($endian x)), Err(x) => Err(swap_endian!($endian x)), } } /// Stores a value into the atomic integer if the current value is /// the same as the `current` value. /// #[doc = concat!( "See [`", stringify!($prim), "::compare_exchange_weak`] for more information.", )] #[inline] pub fn compare_exchange_weak( &self, current: $prim_int, new: $prim_int, success: Ordering, failure: Ordering, ) -> Result<$prim_int, $prim_int> { match self.0.compare_exchange_weak( swap_endian!($endian current), swap_endian!($endian new), success, failure, ) { Ok(x) => Ok(swap_endian!($endian x)), Err(x) => Ok(swap_endian!($endian x)), } } /// Adds to the current value, returning the previous value. /// #[doc = concat!( "Because addition is not an endian-agnostic operation, ", "`fetch_add` is implemented in terms of [`", stringify!($prim), "::compare_exchange_weak`] on ", opposite_endian_name!($endian), "-endian targets. This may result in worse performance on ", "those targets.", )] /// #[doc = concat!( "See [`", stringify!($prim), "::fetch_add`] for more information.", )] #[inline] pub fn fetch_add( &self, val: $prim_int, order: Ordering, ) -> $prim_int { if_native_endian!( $endian self.0.fetch_add(val, order), self.fetch_update_fast( order, fetch_ordering(order), |x| x + val, ), ) } /// Bitwise "and" with the current value. /// #[doc = concat!( "See [`", stringify!($prim), "::fetch_and`] for more information.", )] #[inline] pub fn fetch_and( &self, val: $prim_int, order: Ordering, ) -> $prim_int { let val = swap_endian!($endian val); swap_endian!($endian self.0.fetch_and(val, order)) } /// Maximum with the current value. /// #[doc = concat!( "Because maximum is not an endian-agnostic operation, ", "`fetch_max` is implemented in terms of [`", stringify!($prim), "::compare_exchange_weak`] on ", opposite_endian_name!($endian), "-endian targets. This may result in worse performance on ", "those targets.", )] /// #[doc = concat!( "See [`", stringify!($prim), "::fetch_max`] for more information.", )] #[inline] pub fn fetch_max( &self, val: $prim_int, order: Ordering, ) -> $prim_int { if_native_endian!( $endian self.0.fetch_max(val, order), self.fetch_update_fast( order, fetch_ordering(order), |x| <$prim_int>::max(x, val), ), ) } /// Minimum with the current value. /// #[doc = concat!( "Because minimum is not an endian-agnostic operation, ", "`fetch_min` is implemented in terms of [`", stringify!($prim), "::compare_exchange_weak`] on ", opposite_endian_name!($endian), "-endian targets. This may result in worse performance on ", "those targets.", )] /// #[doc = concat!( "See [`", stringify!($prim), "::fetch_min`] for more information.", )] #[inline] pub fn fetch_min( &self, val: $prim_int, order: Ordering, ) -> $prim_int { if_native_endian!( $endian self.0.fetch_min(val, order), self.fetch_update_fast( order, fetch_ordering(order), |x| <$prim_int>::min(x, val), ), ) } /// Bitwise "nand" with the current value. /// #[doc = concat!( "See [`", stringify!($prim), "::fetch_nand`] for more information.", )] #[inline] pub fn fetch_nand( &self, val: $prim_int, order: Ordering, ) -> $prim_int { let val = swap_endian!($endian val); swap_endian!($endian self.0.fetch_nand(val, order)) } /// Bitwise "or" with the current value. /// #[doc = concat!( "See [`", stringify!($prim), "::fetch_or`] for more information.", )] #[inline] pub fn fetch_or( &self, val: $prim_int, order: Ordering, ) -> $prim_int { let val = swap_endian!($endian val); swap_endian!($endian self.0.fetch_or(val, order)) } /// Subtracts from the current value, returning the previous value. /// #[doc = concat!( "Because subtraction is not an endian-agnostic operation, ", "`fetch_sub` is implemented in terms of [`", stringify!($prim), "::compare_exchange_weak`] on ", opposite_endian_name!($endian), "-endian targets. This may result in worse performance on ", "those targets.", )] /// #[doc = concat!( "See [`", stringify!($prim), "::fetch_sub`] for more information.", )] #[inline] pub fn fetch_sub( &self, val: $prim_int, order: Ordering, ) -> $prim_int { if_native_endian!( $endian self.0.fetch_sub(val, order), self.fetch_update_fast( order, fetch_ordering(order), |x| x - val, ), ) } #[allow(dead_code)] #[inline(always)] fn fetch_update_fast $prim_int>( &self, set_order: Ordering, fetch_order: Ordering, f: F, ) -> $prim_int { let mut prev = swap_endian!($endian self.0.load(fetch_order)); loop { let next = swap_endian!($endian f(prev)); match self.0.compare_exchange_weak( prev, next, set_order, fetch_order, ) { Ok(x) => break x, Err(next_prev) => { prev = swap_endian!($endian next_prev); } } } } /// 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)`. /// #[doc = concat!( "See [`", stringify!($prim), "::fetch_update`] for more information.", )] #[inline] pub fn fetch_update Option<$prim_int>>( &self, set_order: Ordering, fetch_order: Ordering, mut f: F, ) -> Result<$prim_int, $prim_int> { self.0.fetch_update(set_order, fetch_order, |x| { f(swap_endian!($endian x)).map(|y| swap_endian!($endian y)) }) } /// Bitwise "xor" with the current value. /// #[doc = concat!( "See [`", stringify!($prim), "::fetch_xor`] for more information.", )] #[inline] pub fn fetch_xor( &self, val: $prim_int, order: Ordering, ) -> $prim_int { let val = swap_endian!($endian val); swap_endian!($endian self.0.fetch_xor(val, order)) } /// Consumes the atomic and returns the contained value. /// #[doc = concat!( "See [`", stringify!($prim), "::into_inner`] for more information.", )] #[inline] pub fn into_inner(self) -> $prim_int { swap_endian!($endian self.0.into_inner()) } /// Loads a value from the atomic integer. /// #[doc = concat!( "See [`", stringify!($prim), "::load`] for more information.", )] #[inline] pub fn load(&self, order: Ordering) -> $prim_int { swap_endian!($endian self.0.load(order)) } /// Stores a value into the atomic integer. /// #[doc = concat!( "See [`", stringify!($prim), "::store`] for more information.", )] #[inline] pub fn store(&self, val: $prim_int, order: Ordering) { self.0.store(swap_endian!($endian val), order); } /// Stores a value into the atomic integer, returning the previous /// value. /// #[doc = concat!( "See [`", stringify!($prim), "::swap`] for more information.", )] #[inline] pub fn swap(&self, val: $prim_int, order: Ordering) -> $prim_int { let val = swap_endian!($endian val); swap_endian!($endian self.0.swap(val, order)) } } impl core::fmt::Debug for $name { #[inline] fn fmt( &self, f: &mut core::fmt::Formatter<'_>, ) -> core::fmt::Result { swap_endian!($endian self.load(Ordering::Relaxed)).fmt(f) } } impl Default for $name { #[inline] fn default() -> Self { Self::new(<$prim_int>::default()) } } impl From<$prim_int> for $name { #[inline] fn from(value: $prim_int) -> Self { Self::new(value) } } } } #[cfg(any( target_has_atomic = "16", target_has_atomic = "32", target_has_atomic = "64", ))] macro_rules! define_atomics { ($( $le:ident $be:ident: $size_align:literal $prim:ty as $prim_int:ty ),* $(,)?) => { $( define_atomic!($le: little $size_align $prim as $prim_int); define_atomic!($be: big $size_align $prim as $prim_int); )* } } #[cfg(target_has_atomic = "16")] define_atomics! { AtomicI16_le AtomicI16_be: 2 AtomicI16 as i16, AtomicU16_le AtomicU16_be: 2 AtomicU16 as u16, } #[cfg(target_has_atomic = "32")] define_atomics! { AtomicI32_le AtomicI32_be: 4 AtomicI32 as i32, AtomicU32_le AtomicU32_be: 4 AtomicU32 as u32, } #[cfg(target_has_atomic = "64")] define_atomics! { AtomicI64_le AtomicI64_be: 8 AtomicI64 as i64, AtomicU64_le AtomicU64_be: 8 AtomicU64 as u64, } #[cfg(test)] mod tests { use core::mem::transmute; use super::*; #[test] fn signed_integers() { assert_size_align! { i16_be 2 2, i16_le 2 2, i32_be 4 4, i32_le 4 4, i64_be 8 8, i64_le 8 8, i128_be 16 16, i128_le 16 16, } unsafe { // i16 assert_eq!( [0x02, 0x01], transmute::<_, [u8; 2]>(i16_le::from_native(0x0102)), ); assert_eq!( [0x01, 0x02], transmute::<_, [u8; 2]>(i16_be::from_native(0x0102)), ); // i32 assert_eq!( [0x04, 0x03, 0x02, 0x01], transmute::<_, [u8; 4]>(i32_le::from_native(0x01020304)), ); assert_eq!( [0x01, 0x02, 0x03, 0x04], transmute::<_, [u8; 4]>(i32_be::from_native(0x01020304)), ); // i64 assert_eq!( [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01], transmute::<_, [u8; 8]>(i64_le::from_native( 0x0102030405060708 )), ); assert_eq!( [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08], transmute::<_, [u8; 8]>(i64_be::from_native( 0x0102030405060708 )), ); // i128 assert_eq!( [ 0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01 ], transmute::<_, [u8; 16]>(i128_le::from_native( 0x0102030405060708090a0b0c0d0e0f10 )), ); assert_eq!( [ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 ], transmute::<_, [u8; 16]>(i128_be::from_native( 0x0102030405060708090a0b0c0d0e0f10 )), ); } } #[test] fn unsigned_integers() { assert_size_align! { u16_be 2 2, u16_le 2 2, u32_be 4 4, u32_le 4 4, u64_be 8 8, u64_le 8 8, u128_be 16 16, u128_le 16 16, } unsafe { // u16 assert_eq!( [0x02, 0x01], transmute::<_, [u8; 2]>(u16_le::from_native(0x0102)), ); assert_eq!( [0x01, 0x02], transmute::<_, [u8; 2]>(u16_be::from_native(0x0102)), ); // u32 assert_eq!( [0x04, 0x03, 0x02, 0x01], transmute::<_, [u8; 4]>(u32_le::from_native(0x01020304)), ); assert_eq!( [0x01, 0x02, 0x03, 0x04], transmute::<_, [u8; 4]>(u32_be::from_native(0x01020304)), ); // u64 assert_eq!( [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01], transmute::<_, [u8; 8]>(u64_le::from_native( 0x0102030405060708 )), ); assert_eq!( [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08], transmute::<_, [u8; 8]>(u64_be::from_native( 0x0102030405060708 )), ); // u128 assert_eq!( [ 0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01 ], transmute::<_, [u8; 16]>(u128_le::from_native( 0x0102030405060708090a0b0c0d0e0f10 )), ); assert_eq!( [ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 ], transmute::<_, [u8; 16]>(u128_be::from_native( 0x0102030405060708090a0b0c0d0e0f10 )), ); } } #[test] fn floats() { assert_size_align! { f32_be 4 4, f32_le 4 4, f64_be 8 8, f64_le 8 8, } unsafe { // f32 assert_eq!( [0xdb, 0x0f, 0x49, 0x40], transmute::<_, [u8; 4]>(f32_le::from_native( core::f32::consts::PI )), ); assert_eq!( [0x40, 0x49, 0x0f, 0xdb], transmute::<_, [u8; 4]>(f32_be::from_native( core::f32::consts::PI )), ); // f64 assert_eq!( [0x18, 0x2d, 0x44, 0x54, 0xfb, 0x21, 0x09, 0x40], transmute::<_, [u8; 8]>(f64_le::from_native( core::f64::consts::PI )), ); assert_eq!( [0x40, 0x09, 0x21, 0xfb, 0x54, 0x44, 0x2d, 0x18], transmute::<_, [u8; 8]>(f64_be::from_native( core::f64::consts::PI )), ); // char assert_eq!( [0x89, 0xf3, 0x01, 0x00], transmute::<_, [u8; 4]>(char_le::from_native('🎉')), ); assert_eq!( [0x00, 0x01, 0xf3, 0x89], transmute::<_, [u8; 4]>(char_be::from_native('🎉')), ); } } #[test] fn signed_non_zero() { assert_size_align! { NonZeroI16_le 2 2, NonZeroI16_be 2 2, NonZeroI32_le 4 4, NonZeroI32_be 4 4, NonZeroI64_le 8 8, NonZeroI64_be 8 8, NonZeroI128_le 16 16, NonZeroI128_be 16 16, } unsafe { // NonZeroI16 assert_eq!( [0x02, 0x01], transmute::<_, [u8; 2]>(NonZeroI16_le::new_unchecked(0x0102)), ); assert_eq!( [0x01, 0x02], transmute::<_, [u8; 2]>(NonZeroI16_be::new_unchecked(0x0102)), ); // NonZeroI32 assert_eq!( [0x04, 0x03, 0x02, 0x01], transmute::<_, [u8; 4]>(NonZeroI32_le::new_unchecked( 0x01020304 )), ); assert_eq!( [0x01, 0x02, 0x03, 0x04], transmute::<_, [u8; 4]>(NonZeroI32_be::new_unchecked( 0x01020304 )), ); // NonZeroI64 assert_eq!( [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01], transmute::<_, [u8; 8]>(NonZeroI64_le::new_unchecked( 0x0102030405060708 )), ); assert_eq!( [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08], transmute::<_, [u8; 8]>(NonZeroI64_be::new_unchecked( 0x0102030405060708 )), ); // NonZeroI128 assert_eq!( [ 0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01 ], transmute::<_, [u8; 16]>(NonZeroI128_le::new_unchecked( 0x0102030405060708090a0b0c0d0e0f10 )), ); assert_eq!( [ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 ], transmute::<_, [u8; 16]>(NonZeroI128_be::new_unchecked( 0x0102030405060708090a0b0c0d0e0f10 )), ); } } #[test] fn unsigned_non_zero() { assert_size_align! { NonZeroU16_le 2 2, NonZeroU16_be 2 2, NonZeroU32_le 4 4, NonZeroU32_be 4 4, NonZeroU64_le 8 8, NonZeroU64_be 8 8, NonZeroU128_le 16 16, NonZeroU128_be 16 16, } unsafe { // NonZeroU16 assert_eq!( [0x02, 0x01], transmute::<_, [u8; 2]>(NonZeroU16_le::new_unchecked(0x0102)), ); assert_eq!( [0x01, 0x02], transmute::<_, [u8; 2]>(NonZeroU16_be::new_unchecked(0x0102)), ); // NonZeroU32 assert_eq!( [0x04, 0x03, 0x02, 0x01], transmute::<_, [u8; 4]>(NonZeroU32_le::new_unchecked( 0x01020304 )), ); assert_eq!( [0x01, 0x02, 0x03, 0x04], transmute::<_, [u8; 4]>(NonZeroU32_be::new_unchecked( 0x01020304 )), ); // NonZeroU64 assert_eq!( [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01], transmute::<_, [u8; 8]>(NonZeroU64_le::new_unchecked( 0x0102030405060708 )), ); assert_eq!( [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08], transmute::<_, [u8; 8]>(NonZeroU64_be::new_unchecked( 0x0102030405060708 )), ); // NonZeroU128 assert_eq!( [ 0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01 ], transmute::<_, [u8; 16]>(NonZeroU128_le::new_unchecked( 0x0102030405060708090a0b0c0d0e0f10 )), ); assert_eq!( [ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 ], transmute::<_, [u8; 16]>(NonZeroU128_be::new_unchecked( 0x0102030405060708090a0b0c0d0e0f10 )), ); } } #[cfg(feature = "bytecheck")] #[test] fn unaligned_non_zero() { use bytecheck::{ rancor::{Failure, Strategy}, CheckBytes, }; use unaligned::{u32_ule, NonZeroU32_ule}; let zero = u32_ule::from_native(0); let ptr = (&zero as *const u32_ule).cast::(); let mut unit = (); let context = Strategy::<_, Failure>::wrap(&mut unit); unsafe { >>::check_bytes( ptr, context, ) .unwrap_err(); } } #[cfg(target_has_atomic = "16")] #[test] fn atomics_16() { assert_size_align! { AtomicI16_le 2 2, AtomicI16_be 2 2, AtomicU16_le 2 2, AtomicU16_be 2 2, } unsafe { // AtomicI16 assert_eq!( [0x02, 0x01], transmute::<_, [u8; 2]>(AtomicI16_le::new(0x0102)), ); assert_eq!( [0x01, 0x02], transmute::<_, [u8; 2]>(AtomicI16_be::new(0x0102)), ); // AtomicU16 assert_eq!( [0x02, 0x01], transmute::<_, [u8; 2]>(AtomicU16_le::new(0x0102)), ); assert_eq!( [0x01, 0x02], transmute::<_, [u8; 2]>(AtomicU16_be::new(0x0102)), ); } } #[cfg(target_has_atomic = "32")] #[test] fn atomics_32() { assert_size_align! { AtomicI32_le 4 4, AtomicI32_be 4 4, AtomicU32_le 4 4, AtomicU32_be 4 4, } unsafe { // AtomicI32 assert_eq!( [0x04, 0x03, 0x02, 0x01], transmute::<_, [u8; 4]>(AtomicI32_le::new(0x01020304)), ); assert_eq!( [0x01, 0x02, 0x03, 0x04], transmute::<_, [u8; 4]>(AtomicI32_be::new(0x01020304)), ); // AtomicU32 assert_eq!( [0x04, 0x03, 0x02, 0x01], transmute::<_, [u8; 4]>(AtomicU32_le::new(0x01020304)), ); assert_eq!( [0x01, 0x02, 0x03, 0x04], transmute::<_, [u8; 4]>(AtomicU32_be::new(0x01020304)), ); } } #[cfg(target_has_atomic = "64")] #[test] fn atomics_64() { assert_size_align! { AtomicI64_le 8 8, AtomicI64_be 8 8, AtomicU64_le 8 8, AtomicU64_be 8 8, } unsafe { // AtomicI64 assert_eq!( [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01], transmute::<_, [u8; 8]>(AtomicI64_le::new(0x0102030405060708)), ); assert_eq!( [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08], transmute::<_, [u8; 8]>(AtomicI64_be::new(0x0102030405060708)), ); // AtomicU64 assert_eq!( [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01], transmute::<_, [u8; 8]>(AtomicU64_le::new(0x0102030405060708)), ); assert_eq!( [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08], transmute::<_, [u8; 8]>(AtomicU64_be::new(0x0102030405060708)), ); } } } rend-0.5.2/src/traits.rs000064400000000000000000000306361046102023000132330ustar 00000000000000macro_rules! impl_unop { ($trait:ident:: $fn:ident for $name:ident : $prim:ty) => { impl ::core::ops::$trait for $name { type Output = <$prim as ::core::ops::$trait>::Output; #[inline] fn $fn(self) -> Self::Output { self.to_native().$fn() } } }; } macro_rules! impl_binop_nonzero { ($trait:ident::$fn:ident for $name:ident: $prim:ty) => { impl_binop_both!($trait::$fn ($name, $prim) -> $prim); impl_binop_both!($trait::$fn (&'_ $name, $prim) -> $prim); impl_binop_one!($trait::$fn ($name, $name) -> $prim); impl_binop_one!($trait::$fn (&'_ $name, $name) -> $prim); impl_binop_one!($trait::$fn ($name, &'_ $name) -> $prim); impl_binop_one!($trait::$fn (&'_ $name, &'_ $name) -> $prim); }; } macro_rules! impl_binop_one { ($trait:ident:: $fn:ident($self:ty, $other:ty) -> $output:ty) => { impl ::core::ops::$trait<$other> for $self { type Output = $output; #[inline] fn $fn(self, other: $other) -> Self::Output { self.to_native().$fn(other.to_native()) } } }; } macro_rules! impl_binop_both { ($trait:ident:: $fn:ident($self:ty, $other:ty) -> $output:ty) => { impl ::core::ops::$trait<$other> for $self { type Output = $output; #[inline] fn $fn(self, other: $other) -> Self::Output { self.to_native().$fn(other) } } impl ::core::ops::$trait<$self> for $other { type Output = $output; #[inline] fn $fn(self, other: $self) -> Self::Output { self.$fn(other.to_native()) } } }; } macro_rules! impl_binop { ($trait:ident::$fn:ident for $name:ident: $prim:ty) => { impl_binop_both!($trait::$fn ($name, $prim) -> $prim); impl_binop_both!($trait::$fn (&'_ $name, $prim) -> $prim); impl_binop_both!($trait::$fn ($name, &'_ $prim) -> $prim); impl_binop_both!($trait::$fn (&'_ $name, &'_ $prim) -> $prim); impl_binop_one!($trait::$fn ($name, $name) -> $prim); impl_binop_one!($trait::$fn (&'_ $name, $name) -> $prim); impl_binop_one!($trait::$fn ($name, &'_ $name) -> $prim); impl_binop_one!($trait::$fn (&'_ $name, &'_ $name) -> $prim); }; } macro_rules! impl_binassign_nonzero { ($trait:ident:: $fn:ident for $name:ident : $prim:ty) => { impl ::core::ops::$trait<$prim> for $name { #[inline] fn $fn(&mut self, other: $prim) { let mut value = self.to_native(); value.$fn(other); *self = Self::from_native(value); } } impl ::core::ops::$trait<$name> for $name { #[inline] fn $fn(&mut self, other: $name) { let mut value = self.to_native(); value.$fn(other.to_native()); *self = Self::from_native(value); } } }; } macro_rules! impl_binassign { ($trait:ident:: $fn:ident for $name:ident : $prim:ty) => { impl ::core::ops::$trait<$prim> for $name { #[inline] fn $fn(&mut self, other: $prim) { let mut value = self.to_native(); value.$fn(other); *self = Self::from_native(value); } } impl ::core::ops::$trait<$name> for $name { #[inline] fn $fn(&mut self, other: $name) { let mut value = self.to_native(); value.$fn(other.to_native()); *self = Self::from_native(value); } } impl ::core::ops::$trait<&'_ $prim> for $name { #[inline] fn $fn(&mut self, other: &'_ $prim) { let mut value = self.to_native(); value.$fn(other); *self = Self::from_native(value); } } impl ::core::ops::$trait<&'_ $name> for $name { #[inline] fn $fn(&mut self, other: &'_ $name) { let mut value = self.to_native(); value.$fn(other.to_native()); *self = Self::from_native(value); } } }; } macro_rules! impl_clone_and_copy { (for $name:ident) => { impl Clone for $name { #[inline] fn clone(&self) -> Self { *self } } impl Copy for $name {} }; } macro_rules! impl_fmt { ($trait:ident for $name:ident) => { impl ::core::fmt::$trait for $name { #[inline] fn fmt( &self, f: &mut ::core::fmt::Formatter<'_>, ) -> ::core::fmt::Result { ::core::fmt::$trait::fmt(&self.to_native(), f) } } }; } macro_rules! impl_default { (for $name:ident : $prim:ty) => { impl Default for $name { #[inline] fn default() -> Self { Self::from_native(<$prim>::default()) } } }; } macro_rules! impl_from { (for $name:ident : $prim:ty) => { impl From<$prim> for $name { fn from(value: $prim) -> Self { Self::from_native(value) } } impl<'a> From<&'a $prim> for $name { fn from(value: &'a $prim) -> Self { Self::from_native(*value) } } impl From<$name> for $prim { fn from(value: $name) -> Self { value.to_native() } } impl<'a> From<&'a $name> for $prim { fn from(value: &'a $name) -> Self { value.to_native() } } }; } macro_rules! impl_try_from_ptr_size { ($size:ident for $name:ident: $prim:ident) => { impl TryFrom<$size> for $name { type Error = <$prim as TryFrom<$size>>::Error; #[inline] fn try_from(value: $size) -> Result { Ok(Self::from_native(<$prim>::try_from(value)?)) } } impl_try_into_ptr_size!($size for $name: $prim); }; } macro_rules! impl_try_into_ptr_size { (isize for $name:ident: i16) => { impl_into_ptr_size!(isize for $name); }; (usize for $name:ident: u16) => { impl_into_ptr_size!(usize for $name); }; ($size:ident for $name:ident: $prim:ident) => { impl TryFrom<$name> for $size { type Error = <$size as TryFrom<$prim>>::Error; #[inline] fn try_from(value: $name) -> Result { <$size>::try_from(value.to_native()) } } }; } macro_rules! impl_into_ptr_size { ($size:ident for $name:ident) => { impl From<$name> for $size { #[inline] fn from(value: $name) -> Self { <$size>::from(value.to_native()) } } }; } macro_rules! impl_hash { (for $name:ident) => { impl core::hash::Hash for $name { fn hash(&self, state: &mut H) { self.to_native().hash(state); } } }; } macro_rules! impl_partial_ord_and_ord { (for $name:ident : $prim:ty) => { impl PartialOrd for $name { #[inline] fn partial_cmp( &self, other: &Self, ) -> Option<::core::cmp::Ordering> { Some(self.cmp(other)) } } impl PartialOrd<$prim> for $name { #[inline] fn partial_cmp( &self, other: &$prim, ) -> Option<::core::cmp::Ordering> { self.to_native().partial_cmp(other) } } impl Ord for $name { #[inline] fn cmp(&self, other: &Self) -> ::core::cmp::Ordering { self.to_native().cmp(&other.to_native()) } } }; } macro_rules! impl_partial_eq_and_eq { (for $name:ident : $prim:ty) => { impl PartialEq for $name { #[inline] fn eq(&self, other: &Self) -> bool { let lhs = self.0; let rhs = other.0; lhs.eq(&rhs) } } impl PartialEq<$prim> for $name { #[inline] fn eq(&self, other: &$prim) -> bool { self.to_native().eq(other) } } impl PartialEq<$name> for $prim { #[inline] fn eq(&self, other: &$name) -> bool { self.eq(&other.to_native()) } } impl Eq for $name {} }; } macro_rules! impl_partial_ord { (for $name:ident : $prim:ty) => { impl PartialOrd for $name { #[inline] fn partial_cmp( &self, other: &Self, ) -> Option<::core::cmp::Ordering> { self.to_native().partial_cmp(&other.to_native()) } } impl PartialOrd<$prim> for $name { #[inline] fn partial_cmp( &self, other: &$prim, ) -> Option<::core::cmp::Ordering> { self.to_native().partial_cmp(other) } } }; } macro_rules! impl_product_and_sum { (for $name:ident) => { impl ::core::iter::Product for $name { #[inline] fn product>(iter: I) -> Self { Self::from_native(iter.map(|x| x.to_native()).product()) } } impl ::core::iter::Sum for $name { #[inline] fn sum>(iter: I) -> Self { Self::from_native(iter.map(|x| x.to_native()).sum()) } } }; } /// # Safety /// /// An impl of `CheckBytes` with a `check_bytes` function that is a no-op must /// be sound for `$name`. macro_rules! unsafe_impl_check_bytes_noop { (for $name:ident) => { #[cfg(feature = "bytecheck")] // SAFETY: All callers of this macro have guaranteed that all pointers // to `$name`s which are properly aligned and point to enough bytes to // represent the type also point to a valid instance of the type. unsafe impl bytecheck::CheckBytes for $name where C: bytecheck::rancor::Fallible + ?Sized, { #[inline] unsafe fn check_bytes( _: *const Self, _: &mut C, ) -> Result<(), C::Error> { // SAFETY: The invoker of this macro has guaranteed that an impl // of `CheckBytes` with a `check_bytes` function that is a no-op // is sound. Ok(()) } } }; } /// # Safety /// /// An all-zero bit pattern for `$name` must be a valid value. /// As a rule, any derivative type (e.g. `u64_le` or `i32_be`) of a native type /// (e.g. `u64`, `i32`) that is `Zeroable` will also be zeroable. macro_rules! unsafe_impl_zeroable { (for $name:ident) => { #[cfg(feature = "bytemuck-1")] unsafe impl bytemuck_1::Zeroable for $name {} }; } /// # Safety /// /// Read the safety requirements of [`bytemuck::Pod`]. /// In general, any type that is natively `Pod` (e.g. `u64`, `AtomicU32`) will /// be `Pod` even if wrapped (`u64_le`). /// /// It is required that `$name` has an impl for `Zeroable` for this macro to /// work. See [`unsafe_impl_zeroable!()`]. macro_rules! unsafe_impl_pod { (for $name:ident) => { #[cfg(feature = "bytemuck-1")] unsafe impl bytemuck_1::Pod for $name {} }; } /// # Safety /// /// Read the safety requirements of [`bytemuck::NoUninit`]. /// All primitive types are `NoUninit` and as a result, all of this crates's /// wrapped primitive types are also `NoUninit`. macro_rules! unsafe_impl_no_uninit { (for $name:ident) => { #[cfg(feature = "bytemuck-1")] unsafe impl bytemuck_1::NoUninit for $name {} }; } rend-0.5.2/src/unaligned.rs000064400000000000000000000452171046102023000136740ustar 00000000000000//! Cross-platform primitives with unaligned representations. use core::{ concat, num::{ NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64, }, }; // `rustfmt` keeps changing the indentation of the attributes in this macro. #[rustfmt::skip] macro_rules! define_unaligned_newtype { ( $(#[$attr:meta])* $name:ident: $endian:ident $size:literal $prim:ty ) => { #[allow(non_camel_case_types)] #[doc = concat!( "A ", endian_name!($endian), "-endian unaligned `", stringify!($prim), "` with a guaranteed size of `", stringify!($size), "` and alignment of `1`.", )] $(#[$attr])* #[repr(C, packed)] pub struct $name($prim); }; } macro_rules! define_unaligned_signed_integer { ($name:ident: $endian:ident $size:literal $prim:ident) => { define_unaligned_newtype!( #[cfg_attr( feature = "zerocopy-0_8", derive( zerocopy_derive::FromBytes, zerocopy_derive::IntoBytes, zerocopy_derive::Immutable, zerocopy_derive::KnownLayout, zerocopy_derive::Unaligned, ), )] $name: $endian $size $prim ); impl_integer!($name: $endian $prim); impl_signed_integer_traits!($name: $endian $prim); }; } macro_rules! define_unaligned_signed_integers { ($($le:ident $be:ident: $size:literal $prim:ident),* $(,)?) => { $( define_unaligned_signed_integer!($le: little $size $prim); define_unaligned_signed_integer!($be: big $size $prim); )* }; } define_unaligned_signed_integers! { i16_ule i16_ube: 2 i16, i32_ule i32_ube: 4 i32, i64_ule i64_ube: 8 i64, i128_ule i128_ube: 16 i128, } macro_rules! define_unaligned_unsigned_integer { ($name:ident: $endian:ident $size:literal $prim:ident) => { define_unaligned_newtype!( #[cfg_attr( feature = "zerocopy-0_8", derive( zerocopy_derive::FromBytes, zerocopy_derive::IntoBytes, zerocopy_derive::Immutable, zerocopy_derive::KnownLayout, zerocopy_derive::Unaligned, ), )] $name: $endian $size $prim ); impl_integer!($name: $endian $prim); impl_unsigned_integer_traits!($name: $endian $prim); } } macro_rules! define_unaligned_unsigned_integers { ($($le:ident $be:ident: $size:literal $prim:ident),* $(,)?) => { $( define_unaligned_unsigned_integer!($le: little $size $prim); define_unaligned_unsigned_integer!($be: big $size $prim); )* }; } define_unaligned_unsigned_integers! { u16_ule u16_ube: 2 u16, u32_ule u32_ube: 4 u32, u64_ule u64_ube: 8 u64, u128_ule u128_ube: 16 u128, } macro_rules! define_unaligned_float { ($name:ident: $endian:ident $size:literal $prim:ty as $prim_int:ty) => { define_unaligned_newtype!( #[cfg_attr( feature = "zerocopy-0_8", derive( zerocopy_derive::FromBytes, zerocopy_derive::IntoBytes, zerocopy_derive::Immutable, zerocopy_derive::KnownLayout, zerocopy_derive::Unaligned, ), )] $name: $endian $size $prim ); impl_float!($name: $endian $prim as $prim_int); }; } macro_rules! define_unaligned_floats { ($( $le:ident $be:ident: $size:literal $prim:ty as $prim_int:ty ),* $(,)?) => { $( define_unaligned_float!($le: little $size $prim as $prim_int); define_unaligned_float!($be: big $size $prim as $prim_int); )* }; } define_unaligned_floats! { f32_ule f32_ube: 4 f32 as u32, f64_ule f64_ube: 8 f64 as u64, } macro_rules! define_unaligned_char { ($name:ident: $endian:ident) => { define_unaligned_newtype!( #[cfg_attr( feature = "zerocopy-0_8", derive( // The generated impl for `zerocopy::TryFromBytes` is overly // permissive. The derive macro doesn't understand that even // though this struct only contains a `u32`, it still has a // restricted set of valid bit patterns. Because // `zerocopy::TryFromBytes` has hidden, semver-breaking // members, I can't write a manual impl. So no impl for you. // // zerocopy_derive::TryFromBytes, zerocopy_derive::IntoBytes, zerocopy_derive::Immutable, zerocopy_derive::KnownLayout, zerocopy_derive::Unaligned, ), )] $name: $endian 4 u32 ); impl_char!($name: $endian); }; } define_unaligned_char!(char_ule: little); define_unaligned_char!(char_ube: big); macro_rules! define_unaligned_nonzero { ($name:ident: $endian:ident $size:literal $prim:ty as $prim_int:ty) => { define_unaligned_newtype!($name: $endian $size $prim); impl_nonzero!($name: $endian $prim as $prim_int); #[cfg(feature = "bytecheck")] // SAFETY: `check_bytes` only returns `Ok` if `value` points to a valid // non-zero value, which is the only requirement for `NonZero` integers. unsafe impl bytecheck::CheckBytes for $name where C: bytecheck::rancor::Fallible + ?Sized, C::Error: bytecheck::rancor::Trace, $prim: bytecheck::CheckBytes, { #[inline] unsafe fn check_bytes( value: *const Self, context: &mut C, ) -> Result<(), C::Error> { use bytecheck::rancor::ResultExt as _; // SAFETY: `value` points to a `Self`, which has the same size // as a `$prim_int` and which the caller has guaranteed is valid // for reads. All bit patterns are valid for `$prim_int`. let value = unsafe { value.cast::<$prim_int>().read_unaligned() }; let ptr = (&value as *const $prim_int).cast::<$prim>(); // SAFETY: `ptr` points to a `$prim_int` and so is guaranteed to // be aligned and point to enough bytes to represent a `$prim`. unsafe { <$prim>::check_bytes(ptr, context) .with_trace(|| $crate::context::ValueCheckContext { inner_name: core::stringify!($prim), outer_name: core::stringify!($name), }) } } } }; } macro_rules! define_unaligned_nonzeros { ($( $le:ident $be:ident: $size:literal $prim:ty as $prim_int:ty ),* $(,)?) => { $( define_unaligned_nonzero!($le: little $size $prim as $prim_int); define_unaligned_nonzero!($be: big $size $prim as $prim_int); )* } } define_unaligned_nonzeros! { NonZeroI16_ule NonZeroI16_ube: 2 NonZeroI16 as i16, NonZeroI32_ule NonZeroI32_ube: 2 NonZeroI32 as i32, NonZeroI64_ule NonZeroI64_ube: 4 NonZeroI64 as i64, NonZeroI128_ule NonZeroI128_ube: 4 NonZeroI128 as i128, NonZeroU16_ule NonZeroU16_ube: 8 NonZeroU16 as u16, NonZeroU32_ule NonZeroU32_ube: 8 NonZeroU32 as u32, NonZeroU64_ule NonZeroU64_ube: 16 NonZeroU64 as u64, NonZeroU128_ule NonZeroU128_ube: 16 NonZeroU128 as u128, } #[cfg(test)] mod tests { use core::mem::transmute; use super::*; #[test] fn signed_integers() { assert_size_align! { i16_ube 2 1, i16_ule 2 1, i32_ube 4 1, i32_ule 4 1, i64_ube 8 1, i64_ule 8 1, i128_ube 16 1, i128_ule 16 1, } unsafe { // i16 assert_eq!( [0x02, 0x01], transmute::<_, [u8; 2]>(i16_ule::from_native(0x0102)), ); assert_eq!( [0x01, 0x02], transmute::<_, [u8; 2]>(i16_ube::from_native(0x0102)), ); // i32 assert_eq!( [0x04, 0x03, 0x02, 0x01], transmute::<_, [u8; 4]>(i32_ule::from_native(0x01020304)), ); assert_eq!( [0x01, 0x02, 0x03, 0x04], transmute::<_, [u8; 4]>(i32_ube::from_native(0x01020304)), ); // i64 assert_eq!( [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01], transmute::<_, [u8; 8]>(i64_ule::from_native( 0x0102030405060708 )), ); assert_eq!( [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08], transmute::<_, [u8; 8]>(i64_ube::from_native( 0x0102030405060708 )), ); // i128 assert_eq!( [ 0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01 ], transmute::<_, [u8; 16]>(i128_ule::from_native( 0x0102030405060708090a0b0c0d0e0f10 )), ); assert_eq!( [ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 ], transmute::<_, [u8; 16]>(i128_ube::from_native( 0x0102030405060708090a0b0c0d0e0f10 )), ); } } #[test] fn unsigned_integers() { assert_size_align! { u16_ube 2 1, u16_ule 2 1, u32_ube 4 1, u32_ule 4 1, u64_ube 8 1, u64_ule 8 1, u128_ube 16 1, u128_ule 16 1, } unsafe { // u16 assert_eq!( [0x02, 0x01], transmute::<_, [u8; 2]>(u16_ule::from_native(0x0102)), ); assert_eq!( [0x01, 0x02], transmute::<_, [u8; 2]>(u16_ube::from_native(0x0102)), ); // u32 assert_eq!( [0x04, 0x03, 0x02, 0x01], transmute::<_, [u8; 4]>(u32_ule::from_native(0x01020304)), ); assert_eq!( [0x01, 0x02, 0x03, 0x04], transmute::<_, [u8; 4]>(u32_ube::from_native(0x01020304)), ); // u64 assert_eq!( [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01], transmute::<_, [u8; 8]>(u64_ule::from_native( 0x0102030405060708 )), ); assert_eq!( [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08], transmute::<_, [u8; 8]>(u64_ube::from_native( 0x0102030405060708 )), ); // u128 assert_eq!( [ 0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01 ], transmute::<_, [u8; 16]>(u128_ule::from_native( 0x0102030405060708090a0b0c0d0e0f10 )), ); assert_eq!( [ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 ], transmute::<_, [u8; 16]>(u128_ube::from_native( 0x0102030405060708090a0b0c0d0e0f10 )), ); } } #[test] fn floats() { assert_size_align! { f32_ube 4 1, f32_ule 4 1, f64_ube 8 1, f64_ule 8 1, } unsafe { // f32 assert_eq!( [0xdb, 0x0f, 0x49, 0x40], transmute::<_, [u8; 4]>(f32_ule::from_native( core::f32::consts::PI )), ); assert_eq!( [0x40, 0x49, 0x0f, 0xdb], transmute::<_, [u8; 4]>(f32_ube::from_native( core::f32::consts::PI )), ); // f64 assert_eq!( [0x18, 0x2d, 0x44, 0x54, 0xfb, 0x21, 0x09, 0x40], transmute::<_, [u8; 8]>(f64_ule::from_native( core::f64::consts::PI )), ); assert_eq!( [0x40, 0x09, 0x21, 0xfb, 0x54, 0x44, 0x2d, 0x18], transmute::<_, [u8; 8]>(f64_ube::from_native( core::f64::consts::PI )), ); // char assert_eq!( [0x89, 0xf3, 0x01, 0x00], transmute::<_, [u8; 4]>(char_ule::from_native('🎉')), ); assert_eq!( [0x00, 0x01, 0xf3, 0x89], transmute::<_, [u8; 4]>(char_ube::from_native('🎉')), ); } } #[test] fn signed_non_zero() { assert_size_align! { NonZeroI16_ule 2 1, NonZeroI16_ube 2 1, NonZeroI32_ule 4 1, NonZeroI32_ube 4 1, NonZeroI64_ule 8 1, NonZeroI64_ube 8 1, NonZeroI128_ule 16 1, NonZeroI128_ube 16 1, } unsafe { // NonZeroI16 assert_eq!( [0x02, 0x01], transmute::<_, [u8; 2]>(NonZeroI16_ule::new_unchecked(0x0102)), ); assert_eq!( [0x01, 0x02], transmute::<_, [u8; 2]>(NonZeroI16_ube::new_unchecked(0x0102)), ); // NonZeroI32 assert_eq!( [0x04, 0x03, 0x02, 0x01], transmute::<_, [u8; 4]>(NonZeroI32_ule::new_unchecked( 0x01020304 )), ); assert_eq!( [0x01, 0x02, 0x03, 0x04], transmute::<_, [u8; 4]>(NonZeroI32_ube::new_unchecked( 0x01020304 )), ); // NonZeroI64 assert_eq!( [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01], transmute::<_, [u8; 8]>(NonZeroI64_ule::new_unchecked( 0x0102030405060708 )), ); assert_eq!( [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08], transmute::<_, [u8; 8]>(NonZeroI64_ube::new_unchecked( 0x0102030405060708 )), ); // NonZeroI128 assert_eq!( [ 0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01 ], transmute::<_, [u8; 16]>(NonZeroI128_ule::new_unchecked( 0x0102030405060708090a0b0c0d0e0f10 )), ); assert_eq!( [ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 ], transmute::<_, [u8; 16]>(NonZeroI128_ube::new_unchecked( 0x0102030405060708090a0b0c0d0e0f10 )), ); } } #[test] fn unsigned_non_zero() { assert_size_align! { NonZeroU16_ule 2 1, NonZeroU16_ube 2 1, NonZeroU32_ule 4 1, NonZeroU32_ube 4 1, NonZeroU64_ule 8 1, NonZeroU64_ube 8 1, NonZeroU128_ule 16 1, NonZeroU128_ube 16 1, } unsafe { // NonZeroU16 assert_eq!( [0x02, 0x01], transmute::<_, [u8; 2]>(NonZeroU16_ule::new_unchecked(0x0102)), ); assert_eq!( [0x01, 0x02], transmute::<_, [u8; 2]>(NonZeroU16_ube::new_unchecked(0x0102)), ); // NonZeroU32 assert_eq!( [0x04, 0x03, 0x02, 0x01], transmute::<_, [u8; 4]>(NonZeroU32_ule::new_unchecked( 0x01020304 )), ); assert_eq!( [0x01, 0x02, 0x03, 0x04], transmute::<_, [u8; 4]>(NonZeroU32_ube::new_unchecked( 0x01020304 )), ); // NonZeroU64 assert_eq!( [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01], transmute::<_, [u8; 8]>(NonZeroU64_ule::new_unchecked( 0x0102030405060708 )), ); assert_eq!( [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08], transmute::<_, [u8; 8]>(NonZeroU64_ube::new_unchecked( 0x0102030405060708 )), ); // NonZeroU128 assert_eq!( [ 0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01 ], transmute::<_, [u8; 16]>(NonZeroU128_ule::new_unchecked( 0x0102030405060708090a0b0c0d0e0f10 )), ); assert_eq!( [ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 ], transmute::<_, [u8; 16]>(NonZeroU128_ube::new_unchecked( 0x0102030405060708090a0b0c0d0e0f10 )), ); } } } rend-0.5.2/src/util.rs000064400000000000000000000030101046102023000126640ustar 00000000000000macro_rules! match_endian { (little $little:expr, $big:expr $(,)?) => { $little }; (big $little:expr, $big:expr $(,)?) => { $big }; } macro_rules! if_native_endian { ($endian:ident $true:expr, $false:expr $(,)?) => { match_endian!( $endian { #[cfg(target_endian = "little")] { $true } #[cfg(target_endian = "big")] { $false } }, { #[cfg(target_endian = "little")] { $false } #[cfg(target_endian = "big")] { $true } }, ) } } macro_rules! swap_endian { ($endian:ident $expr:expr) => { if_native_endian!($endian $expr, $expr.swap_bytes()) } } macro_rules! endian_name { ($endian:ident) => { match_endian!($endian "little", "big") }; } #[cfg(any( target_has_atomic = "16", target_has_atomic = "32", target_has_atomic = "64", ))] macro_rules! opposite_endian_name { ($endian:ident) => { match_endian!($endian "big", "little") }; } #[cfg(test)] macro_rules! assert_size_align { ($($name:ident $size:literal $align:literal),* $(,)?) => { $( assert_eq!(core::mem::size_of::<$name>(), $size); assert_eq!(core::mem::align_of::<$name>(), $align); )* } }