├── .gitattributes ├── .github ├── FUNDING.yml └── workflows │ └── ci.yml ├── .gitignore ├── .cargo └── config.toml ├── .vscode └── tasks.json ├── src ├── context.rs ├── util.rs ├── traits.rs ├── common.rs ├── unaligned.rs └── lib.rs ├── rustfmt.toml ├── example.md ├── LICENSE ├── Cargo.toml └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: rkyv 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | rustdocflags = ["--cfg", "docsrs"] 3 | 4 | [env] 5 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "cargo", 6 | "command": "build", 7 | "problemMatcher": [ 8 | "$rustc" 9 | ], 10 | "group": { 11 | "kind": "build", 12 | "isDefault": true 13 | }, 14 | "label": "rust: cargo build" 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /src/context.rs: -------------------------------------------------------------------------------- 1 | use core::fmt; 2 | 3 | #[derive(Debug)] 4 | pub struct ValueCheckContext { 5 | pub inner_name: &'static str, 6 | pub outer_name: &'static str, 7 | } 8 | 9 | impl fmt::Display for ValueCheckContext { 10 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 11 | write!( 12 | f, 13 | "while checking {} value of {}", 14 | self.inner_name, self.outer_name, 15 | ) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 80 2 | comment_width = 80 3 | wrap_comments = true 4 | group_imports = "StdExternalCrate" 5 | imports_granularity = "Crate" 6 | condense_wildcard_suffixes = true 7 | error_on_line_overflow = true 8 | error_on_unformatted = true 9 | format_code_in_doc_comments = true 10 | format_macro_matchers = true 11 | format_macro_bodies = true 12 | format_strings = true 13 | hex_literal_case = "Lower" 14 | normalize_comments = true 15 | use_field_init_shorthand = true 16 | -------------------------------------------------------------------------------- /example.md: -------------------------------------------------------------------------------- 1 | ```rust 2 | use core::mem::transmute; 3 | use rend::*; 4 | 5 | let little_int = i32_le::from_native(0x12345678); 6 | // Internal representation is little-endian 7 | assert_eq!( 8 | [0x78, 0x56, 0x34, 0x12], 9 | unsafe { transmute::<_, [u8; 4]>(little_int) } 10 | ); 11 | 12 | // Can also be made with `.into()` 13 | let little_int: i32_le = 0x12345678.into(); 14 | // Still formats correctly 15 | assert_eq!("305419896", format!("{}", little_int)); 16 | assert_eq!("0x12345678", format!("0x{:x}", little_int)); 17 | 18 | let big_int = i32_be::from_native(0x12345678); 19 | // Internal representation is big-endian 20 | assert_eq!( 21 | [0x12, 0x34, 0x56, 0x78], 22 | unsafe { transmute::<_, [u8; 4]>(big_int) } 23 | ); 24 | 25 | // Can also be made with `.into()` 26 | let big_int: i32_be = 0x12345678.into(); 27 | // Still formats correctly 28 | assert_eq!("305419896", format!("{}", big_int)); 29 | assert_eq!("0x12345678", format!("0x{:x}", big_int)); 30 | ``` 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2021 David Koloski 2 | 3 | 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: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | 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. 8 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rend" 3 | description = "Cross-platform, endian-aware primitives for Rust" 4 | version = "0.5.3" 5 | authors = ["David Koloski "] 6 | edition = "2021" 7 | license = "MIT" 8 | readme = "README.md" 9 | repository = "https://github.com/djkoloski/rend" 10 | keywords = ["endian", "no_std"] 11 | categories = ["encoding", "no-std"] 12 | documentation = "https://docs.rs/rend" 13 | 14 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 15 | 16 | [dependencies] 17 | bytecheck = { version = "0.8", optional = true, default-features = false } 18 | bytemuck-1 = { package = "bytemuck", version = "1", optional = true, default-features = false } 19 | zerocopy = { version = "0.8", optional = true, default-features = false } 20 | zerocopy-derive = { version = "0.8", optional = true, default-features = false } 21 | 22 | [features] 23 | default = [] 24 | zerocopy-0_8 = ["dep:zerocopy", "dep:zerocopy-derive"] 25 | 26 | [patch.crates-io] 27 | bytecheck = { git = "https://github.com/rkyv/bytecheck" } 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `rend` 2 | 3 | [![crates.io badge]][crates.io] [![docs badge]][docs] [![license badge]][license] 4 | 5 | [crates.io badge]: https://img.shields.io/crates/v/rend.svg 6 | [crates.io]: https://crates.io/crates/rend 7 | [docs badge]: https://img.shields.io/docsrs/rend 8 | [docs]: https://docs.rs/rend 9 | [license badge]: https://img.shields.io/badge/license-MIT-blue.svg 10 | [license]: https://github.com/rkyv/rend/blob/master/LICENSE 11 | 12 | rend provides cross-platform, endian-aware primitives for Rust. 13 | 14 | ## Documentation 15 | 16 | - [rend](https://docs.rs/rend), provides cross-platform, endian-aware primitives 17 | for Rust 18 | 19 | ## Example 20 | 21 | ```rust 22 | use core::mem::transmute; 23 | use rend::*; 24 | 25 | let little_int = i32_le::from_native(0x12345678); 26 | // Internal representation is little-endian 27 | assert_eq!( 28 | [0x78, 0x56, 0x34, 0x12], 29 | unsafe { transmute::<_, [u8; 4]>(little_int) } 30 | ); 31 | 32 | // Can also be made with `.into()` 33 | let little_int: i32_le = 0x12345678.into(); 34 | // Still formats correctly 35 | assert_eq!("305419896", format!("{}", little_int)); 36 | assert_eq!("0x12345678", format!("0x{:x}", little_int)); 37 | 38 | let big_int = i32_be::from_native(0x12345678); 39 | // Internal representation is big-endian 40 | assert_eq!( 41 | [0x12, 0x34, 0x56, 0x78], 42 | unsafe { transmute::<_, [u8; 4]>(big_int) } 43 | ); 44 | 45 | // Can also be made with `.into()` 46 | let big_int: i32_be = 0x12345678.into(); 47 | // Still formats correctly 48 | assert_eq!("305419896", format!("{}", big_int)); 49 | assert_eq!("0x12345678", format!("0x{:x}", big_int)); 50 | ``` 51 | -------------------------------------------------------------------------------- /src/util.rs: -------------------------------------------------------------------------------- 1 | macro_rules! match_endian { 2 | (little $little:expr, $big:expr $(,)?) => { 3 | $little 4 | }; 5 | (big $little:expr, $big:expr $(,)?) => { 6 | $big 7 | }; 8 | } 9 | 10 | macro_rules! if_native_endian { 11 | ($endian:ident $true:expr, $false:expr $(,)?) => { 12 | match_endian!( 13 | $endian 14 | { 15 | #[cfg(target_endian = "little")] 16 | { 17 | $true 18 | } 19 | #[cfg(target_endian = "big")] 20 | { 21 | $false 22 | } 23 | }, 24 | { 25 | #[cfg(target_endian = "little")] 26 | { 27 | $false 28 | } 29 | #[cfg(target_endian = "big")] 30 | { 31 | $true 32 | } 33 | }, 34 | ) 35 | } 36 | } 37 | 38 | macro_rules! swap_endian { 39 | ($endian:ident $expr:expr) => { 40 | if_native_endian!($endian $expr, $expr.swap_bytes()) 41 | } 42 | } 43 | 44 | macro_rules! endian_name { 45 | ($endian:ident) => { 46 | match_endian!($endian "little", "big") 47 | }; 48 | } 49 | 50 | #[cfg(any( 51 | target_has_atomic = "16", 52 | target_has_atomic = "32", 53 | target_has_atomic = "64", 54 | ))] 55 | macro_rules! opposite_endian_name { 56 | ($endian:ident) => { 57 | match_endian!($endian "big", "little") 58 | }; 59 | } 60 | 61 | #[cfg(test)] 62 | macro_rules! assert_size_align { 63 | ($($name:ident $size:literal $align:literal),* $(,)?) => { 64 | $( 65 | assert_eq!(core::mem::size_of::<$name>(), $size); 66 | assert_eq!(core::mem::align_of::<$name>(), $align); 67 | )* 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | pull_request: 6 | workflow_dispatch: 7 | schedule: 8 | - cron: "0 10 * * *" 9 | 10 | permissions: 11 | contents: read 12 | 13 | env: 14 | RUSTFLAGS: -Dwarnings 15 | 16 | jobs: 17 | features: 18 | name: Features / ${{ matrix.std }} ${{ matrix.derive }} 19 | runs-on: ubuntu-latest 20 | strategy: 21 | fail-fast: false 22 | matrix: 23 | external: 24 | - '' 25 | - bytecheck bytemuck-1 zerocopy-0_8 26 | 27 | steps: 28 | - uses: actions/checkout@v4 29 | - uses: dtolnay/rust-toolchain@stable 30 | - run: cargo test --verbose --tests --no-default-features --features "${{ matrix.external }}" 31 | 32 | toolchain: 33 | name: Toolchain / ${{ matrix.toolchain }} ${{ matrix.opt }} 34 | runs-on: ubuntu-latest 35 | strategy: 36 | fail-fast: false 37 | matrix: 38 | toolchain: 39 | - stable 40 | - beta 41 | - nightly 42 | opt: 43 | - '' 44 | - --release 45 | 46 | steps: 47 | - uses: actions/checkout@v4 48 | - uses: dtolnay/rust-toolchain@master 49 | with: 50 | toolchain: ${{ matrix.toolchain }} 51 | - run: cargo test --verbose ${{ matrix.opt }} 52 | 53 | miri: 54 | name: Miri / ${{ matrix.opt }} 55 | runs-on: ubuntu-latest 56 | strategy: 57 | fail-fast: false 58 | matrix: 59 | opt: 60 | - '' 61 | - --release 62 | 63 | steps: 64 | - uses: actions/checkout@v4 65 | - uses: dtolnay/rust-toolchain@miri 66 | - run: cargo miri setup 67 | - run: cargo miri test ${{ matrix.opt }} --verbose 68 | env: 69 | MIRIFLAGS: -Zmiri-disable-stacked-borrows -Zmiri-tree-borrows 70 | 71 | test: 72 | name: Test / ${{ matrix.target }} ${{ matrix.opt }} 73 | runs-on: ${{ matrix.os }} 74 | 75 | strategy: 76 | fail-fast: false 77 | matrix: 78 | opt: 79 | - '' 80 | - --release 81 | include: 82 | - os: ubuntu-latest 83 | target: x86_64-unknown-linux-gnu 84 | - os: macos-latest 85 | target: aarch64-apple-darwin 86 | - os: windows-latest 87 | target: x86_64-pc-windows-msvc 88 | 89 | steps: 90 | - uses: actions/checkout@v4 91 | - uses: dtolnay/rust-toolchain@stable 92 | - run: cargo test ${{ matrix.opt }} 93 | 94 | cross: 95 | name: Cross / ${{ matrix.target }} 96 | runs-on: ubuntu-latest 97 | 98 | strategy: 99 | fail-fast: false 100 | matrix: 101 | target: 102 | - i686-unknown-linux-gnu 103 | - i586-unknown-linux-gnu 104 | - armv7-unknown-linux-gnueabihf 105 | - aarch64-unknown-linux-gnu 106 | - thumbv6m-none-eabi 107 | 108 | steps: 109 | - uses: actions/checkout@v4 110 | - uses: dtolnay/rust-toolchain@stable 111 | - run: cargo install cross 112 | - run: cross build --no-default-features --features "bytecheck bytemuck-1 zerocopy-0_8" --target ${{ matrix.target }} --verbose 113 | 114 | format: 115 | name: Format 116 | runs-on: ubuntu-latest 117 | 118 | steps: 119 | - uses: actions/checkout@v4 120 | - uses: dtolnay/rust-toolchain@nightly 121 | with: 122 | components: rustfmt 123 | - run: cargo fmt --check 124 | 125 | clippy: 126 | name: Clippy 127 | runs-on: ubuntu-latest 128 | 129 | steps: 130 | - uses: actions/checkout@v4 131 | - uses: dtolnay/rust-toolchain@nightly 132 | with: 133 | components: clippy 134 | - run: cargo clippy 135 | 136 | doc: 137 | name: Doc 138 | runs-on: ubuntu-latest 139 | 140 | steps: 141 | - uses: actions/checkout@v4 142 | - uses: dtolnay/rust-toolchain@nightly 143 | - run: cargo doc 144 | -------------------------------------------------------------------------------- /src/traits.rs: -------------------------------------------------------------------------------- 1 | macro_rules! impl_unop { 2 | ($trait:ident:: $fn:ident for $name:ident : $prim:ty) => { 3 | impl ::core::ops::$trait for $name { 4 | type Output = <$prim as ::core::ops::$trait>::Output; 5 | 6 | #[inline] 7 | fn $fn(self) -> Self::Output { 8 | self.to_native().$fn() 9 | } 10 | } 11 | }; 12 | } 13 | 14 | macro_rules! impl_binop_nonzero { 15 | ($trait:ident::$fn:ident for $name:ident: $prim:ty) => { 16 | impl_binop_both!($trait::$fn ($name, $prim) -> $prim); 17 | impl_binop_both!($trait::$fn (&'_ $name, $prim) -> $prim); 18 | 19 | impl_binop_one!($trait::$fn ($name, $name) -> $prim); 20 | impl_binop_one!($trait::$fn (&'_ $name, $name) -> $prim); 21 | impl_binop_one!($trait::$fn ($name, &'_ $name) -> $prim); 22 | impl_binop_one!($trait::$fn (&'_ $name, &'_ $name) -> $prim); 23 | }; 24 | } 25 | 26 | macro_rules! impl_binop_one { 27 | ($trait:ident:: $fn:ident($self:ty, $other:ty) -> $output:ty) => { 28 | impl ::core::ops::$trait<$other> for $self { 29 | type Output = $output; 30 | 31 | #[inline] 32 | fn $fn(self, other: $other) -> Self::Output { 33 | self.to_native().$fn(other.to_native()) 34 | } 35 | } 36 | }; 37 | } 38 | 39 | macro_rules! impl_binop_both { 40 | ($trait:ident:: $fn:ident($self:ty, $other:ty) -> $output:ty) => { 41 | impl ::core::ops::$trait<$other> for $self { 42 | type Output = $output; 43 | 44 | #[inline] 45 | fn $fn(self, other: $other) -> Self::Output { 46 | self.to_native().$fn(other) 47 | } 48 | } 49 | 50 | impl ::core::ops::$trait<$self> for $other { 51 | type Output = $output; 52 | 53 | #[inline] 54 | fn $fn(self, other: $self) -> Self::Output { 55 | self.$fn(other.to_native()) 56 | } 57 | } 58 | }; 59 | } 60 | 61 | macro_rules! impl_binop { 62 | ($trait:ident::$fn:ident for $name:ident: $prim:ty) => { 63 | impl_binop_both!($trait::$fn ($name, $prim) -> $prim); 64 | impl_binop_both!($trait::$fn (&'_ $name, $prim) -> $prim); 65 | impl_binop_both!($trait::$fn ($name, &'_ $prim) -> $prim); 66 | impl_binop_both!($trait::$fn (&'_ $name, &'_ $prim) -> $prim); 67 | 68 | impl_binop_one!($trait::$fn ($name, $name) -> $prim); 69 | impl_binop_one!($trait::$fn (&'_ $name, $name) -> $prim); 70 | impl_binop_one!($trait::$fn ($name, &'_ $name) -> $prim); 71 | impl_binop_one!($trait::$fn (&'_ $name, &'_ $name) -> $prim); 72 | }; 73 | } 74 | 75 | macro_rules! impl_binassign_nonzero { 76 | ($trait:ident:: $fn:ident for $name:ident : $prim:ty) => { 77 | impl ::core::ops::$trait<$prim> for $name { 78 | #[inline] 79 | fn $fn(&mut self, other: $prim) { 80 | let mut value = self.to_native(); 81 | value.$fn(other); 82 | *self = Self::from_native(value); 83 | } 84 | } 85 | 86 | impl ::core::ops::$trait<$name> for $name { 87 | #[inline] 88 | fn $fn(&mut self, other: $name) { 89 | let mut value = self.to_native(); 90 | value.$fn(other.to_native()); 91 | *self = Self::from_native(value); 92 | } 93 | } 94 | }; 95 | } 96 | 97 | macro_rules! impl_binassign { 98 | ($trait:ident:: $fn:ident for $name:ident : $prim:ty) => { 99 | impl ::core::ops::$trait<$prim> for $name { 100 | #[inline] 101 | fn $fn(&mut self, other: $prim) { 102 | let mut value = self.to_native(); 103 | value.$fn(other); 104 | *self = Self::from_native(value); 105 | } 106 | } 107 | 108 | impl ::core::ops::$trait<$name> for $name { 109 | #[inline] 110 | fn $fn(&mut self, other: $name) { 111 | let mut value = self.to_native(); 112 | value.$fn(other.to_native()); 113 | *self = Self::from_native(value); 114 | } 115 | } 116 | 117 | impl ::core::ops::$trait<&'_ $prim> for $name { 118 | #[inline] 119 | fn $fn(&mut self, other: &'_ $prim) { 120 | let mut value = self.to_native(); 121 | value.$fn(other); 122 | *self = Self::from_native(value); 123 | } 124 | } 125 | 126 | impl ::core::ops::$trait<&'_ $name> for $name { 127 | #[inline] 128 | fn $fn(&mut self, other: &'_ $name) { 129 | let mut value = self.to_native(); 130 | value.$fn(other.to_native()); 131 | *self = Self::from_native(value); 132 | } 133 | } 134 | }; 135 | } 136 | 137 | macro_rules! impl_clone_and_copy { 138 | (for $name:ident) => { 139 | impl Clone for $name { 140 | #[inline] 141 | fn clone(&self) -> Self { 142 | *self 143 | } 144 | } 145 | 146 | impl Copy for $name {} 147 | }; 148 | } 149 | 150 | macro_rules! impl_fmt { 151 | ($trait:ident for $name:ident) => { 152 | impl ::core::fmt::$trait for $name { 153 | #[inline] 154 | fn fmt( 155 | &self, 156 | f: &mut ::core::fmt::Formatter<'_>, 157 | ) -> ::core::fmt::Result { 158 | ::core::fmt::$trait::fmt(&self.to_native(), f) 159 | } 160 | } 161 | }; 162 | } 163 | 164 | macro_rules! impl_default { 165 | (for $name:ident : $prim:ty) => { 166 | impl Default for $name { 167 | #[inline] 168 | fn default() -> Self { 169 | Self::from_native(<$prim>::default()) 170 | } 171 | } 172 | }; 173 | } 174 | 175 | macro_rules! impl_from { 176 | (for $name:ident : $prim:ty) => { 177 | impl From<$prim> for $name { 178 | fn from(value: $prim) -> Self { 179 | Self::from_native(value) 180 | } 181 | } 182 | 183 | impl<'a> From<&'a $prim> for $name { 184 | fn from(value: &'a $prim) -> Self { 185 | Self::from_native(*value) 186 | } 187 | } 188 | 189 | impl From<$name> for $prim { 190 | fn from(value: $name) -> Self { 191 | value.to_native() 192 | } 193 | } 194 | 195 | impl<'a> From<&'a $name> for $prim { 196 | fn from(value: &'a $name) -> Self { 197 | value.to_native() 198 | } 199 | } 200 | }; 201 | } 202 | 203 | macro_rules! impl_try_from_ptr_size { 204 | ($size:ident for $name:ident: $prim:ident) => { 205 | impl TryFrom<$size> for $name { 206 | type Error = <$prim as TryFrom<$size>>::Error; 207 | 208 | #[inline] 209 | fn try_from(value: $size) -> Result { 210 | Ok(Self::from_native(<$prim>::try_from(value)?)) 211 | } 212 | } 213 | 214 | impl_try_into_ptr_size!($size for $name: $prim); 215 | }; 216 | } 217 | 218 | macro_rules! impl_try_into_ptr_size { 219 | (isize for $name:ident: i16) => { 220 | impl_into_ptr_size!(isize for $name); 221 | }; 222 | 223 | (usize for $name:ident: u16) => { 224 | impl_into_ptr_size!(usize for $name); 225 | }; 226 | 227 | ($size:ident for $name:ident: $prim:ident) => { 228 | impl TryFrom<$name> for $size { 229 | type Error = <$size as TryFrom<$prim>>::Error; 230 | 231 | #[inline] 232 | fn try_from(value: $name) -> Result { 233 | <$size>::try_from(value.to_native()) 234 | } 235 | } 236 | }; 237 | } 238 | 239 | macro_rules! impl_into_ptr_size { 240 | ($size:ident for $name:ident) => { 241 | impl From<$name> for $size { 242 | #[inline] 243 | fn from(value: $name) -> Self { 244 | <$size>::from(value.to_native()) 245 | } 246 | } 247 | }; 248 | } 249 | 250 | macro_rules! impl_hash { 251 | (for $name:ident) => { 252 | impl core::hash::Hash for $name { 253 | fn hash(&self, state: &mut H) { 254 | self.to_native().hash(state); 255 | } 256 | } 257 | }; 258 | } 259 | 260 | macro_rules! impl_partial_ord_and_ord { 261 | (for $name:ident : $prim:ty) => { 262 | impl PartialOrd for $name { 263 | #[inline] 264 | fn partial_cmp( 265 | &self, 266 | other: &Self, 267 | ) -> Option<::core::cmp::Ordering> { 268 | Some(self.cmp(other)) 269 | } 270 | } 271 | 272 | impl PartialOrd<$prim> for $name { 273 | #[inline] 274 | fn partial_cmp( 275 | &self, 276 | other: &$prim, 277 | ) -> Option<::core::cmp::Ordering> { 278 | self.to_native().partial_cmp(other) 279 | } 280 | } 281 | 282 | impl Ord for $name { 283 | #[inline] 284 | fn cmp(&self, other: &Self) -> ::core::cmp::Ordering { 285 | self.to_native().cmp(&other.to_native()) 286 | } 287 | } 288 | }; 289 | } 290 | 291 | macro_rules! impl_partial_eq_and_eq { 292 | (for $name:ident : $prim:ty) => { 293 | impl PartialEq for $name { 294 | #[inline] 295 | fn eq(&self, other: &Self) -> bool { 296 | let lhs = self.0; 297 | let rhs = other.0; 298 | lhs.eq(&rhs) 299 | } 300 | } 301 | 302 | impl PartialEq<$prim> for $name { 303 | #[inline] 304 | fn eq(&self, other: &$prim) -> bool { 305 | self.to_native().eq(other) 306 | } 307 | } 308 | 309 | impl PartialEq<$name> for $prim { 310 | #[inline] 311 | fn eq(&self, other: &$name) -> bool { 312 | self.eq(&other.to_native()) 313 | } 314 | } 315 | 316 | impl Eq for $name {} 317 | }; 318 | } 319 | 320 | macro_rules! impl_partial_ord { 321 | (for $name:ident : $prim:ty) => { 322 | impl PartialOrd for $name { 323 | #[inline] 324 | fn partial_cmp( 325 | &self, 326 | other: &Self, 327 | ) -> Option<::core::cmp::Ordering> { 328 | self.to_native().partial_cmp(&other.to_native()) 329 | } 330 | } 331 | 332 | impl PartialOrd<$prim> for $name { 333 | #[inline] 334 | fn partial_cmp( 335 | &self, 336 | other: &$prim, 337 | ) -> Option<::core::cmp::Ordering> { 338 | self.to_native().partial_cmp(other) 339 | } 340 | } 341 | }; 342 | } 343 | 344 | macro_rules! impl_product_and_sum { 345 | (for $name:ident) => { 346 | impl ::core::iter::Product for $name { 347 | #[inline] 348 | fn product>(iter: I) -> Self { 349 | Self::from_native(iter.map(|x| x.to_native()).product()) 350 | } 351 | } 352 | 353 | impl ::core::iter::Sum for $name { 354 | #[inline] 355 | fn sum>(iter: I) -> Self { 356 | Self::from_native(iter.map(|x| x.to_native()).sum()) 357 | } 358 | } 359 | }; 360 | } 361 | 362 | /// # Safety 363 | /// 364 | /// An impl of `CheckBytes` with a `check_bytes` function that is a no-op must 365 | /// be sound for `$name`. 366 | macro_rules! unsafe_impl_check_bytes_noop { 367 | (for $name:ident) => { 368 | #[cfg(feature = "bytecheck")] 369 | // SAFETY: All callers of this macro have guaranteed that all pointers 370 | // to `$name`s which are properly aligned and point to enough bytes to 371 | // represent the type also point to a valid instance of the type. 372 | unsafe impl bytecheck::CheckBytes for $name 373 | where 374 | C: bytecheck::rancor::Fallible + ?Sized, 375 | { 376 | #[inline] 377 | unsafe fn check_bytes( 378 | _: *const Self, 379 | _: &mut C, 380 | ) -> Result<(), C::Error> { 381 | // SAFETY: The invoker of this macro has guaranteed that an impl 382 | // of `CheckBytes` with a `check_bytes` function that is a no-op 383 | // is sound. 384 | Ok(()) 385 | } 386 | } 387 | }; 388 | } 389 | 390 | /// # Safety 391 | /// 392 | /// An all-zero bit pattern for `$name` must be a valid value. 393 | /// As a rule, any derivative type (e.g. `u64_le` or `i32_be`) of a native type 394 | /// (e.g. `u64`, `i32`) that is `Zeroable` will also be zeroable. 395 | macro_rules! unsafe_impl_zeroable { 396 | (for $name:ident) => { 397 | #[cfg(feature = "bytemuck-1")] 398 | unsafe impl bytemuck_1::Zeroable for $name {} 399 | }; 400 | } 401 | 402 | /// # Safety 403 | /// 404 | /// Read the safety requirements of [`bytemuck::Pod`]. 405 | /// In general, any type that is natively `Pod` (e.g. `u64`, `AtomicU32`) will 406 | /// be `Pod` even if wrapped (`u64_le`). 407 | /// 408 | /// It is required that `$name` has an impl for `Zeroable` for this macro to 409 | /// work. See [`unsafe_impl_zeroable!()`]. 410 | macro_rules! unsafe_impl_pod { 411 | (for $name:ident) => { 412 | #[cfg(feature = "bytemuck-1")] 413 | unsafe impl bytemuck_1::Pod for $name {} 414 | }; 415 | } 416 | 417 | /// # Safety 418 | /// 419 | /// Read the safety requirements of [`bytemuck::NoUninit`]. 420 | /// All primitive types are `NoUninit` and as a result, all of this crates's 421 | /// wrapped primitive types are also `NoUninit`. 422 | macro_rules! unsafe_impl_no_uninit { 423 | (for $name:ident) => { 424 | #[cfg(feature = "bytemuck-1")] 425 | unsafe impl bytemuck_1::NoUninit for $name {} 426 | }; 427 | } 428 | -------------------------------------------------------------------------------- /src/common.rs: -------------------------------------------------------------------------------- 1 | macro_rules! impl_integer { 2 | ($name:ident: $endian:ident $prim:ty) => { 3 | impl $name { 4 | #[doc = concat!( 5 | "Returns a `", 6 | stringify!($name), 7 | "` containing `value`.", 8 | )] 9 | #[inline] 10 | pub const fn from_native(value: $prim) -> Self { 11 | Self(swap_endian!($endian value)) 12 | } 13 | 14 | #[doc = concat!( 15 | "Returns a `", 16 | stringify!($prim), 17 | "` with the same value as `self`.", 18 | )] 19 | #[inline] 20 | pub const fn to_native(self) -> $prim { 21 | swap_endian!($endian self.0) 22 | } 23 | } 24 | }; 25 | } 26 | 27 | macro_rules! impl_signed_integer_traits { 28 | ($name:ident: $endian:ident $prim:ident) => { 29 | // SAFETY: An impl of `CheckBytes` with a `check_bytes` function that is 30 | // a no-op is sound for signed integers. 31 | unsafe_impl_check_bytes_noop!(for $name); 32 | // SAFETY: Signed integers are inhabited and allow all bit patterns, 33 | // fulfilling the requirements of `Zeroable` and `Pod`. 34 | unsafe_impl_zeroable!(for $name); 35 | unsafe_impl_pod!(for $name); 36 | 37 | impl_binop!(Add::add for $name: $prim); 38 | impl_binassign!(AddAssign::add_assign for $name: $prim); 39 | impl_clone_and_copy!(for $name); 40 | impl_fmt!(Binary for $name); 41 | impl_binop!(BitAnd::bitand for $name: $prim); 42 | impl_binassign!(BitAndAssign::bitand_assign for $name: $prim); 43 | impl_binop!(BitOr::bitor for $name: $prim); 44 | impl_binassign!(BitOrAssign::bitor_assign for $name: $prim); 45 | impl_binop!(BitXor::bitxor for $name: $prim); 46 | impl_binassign!(BitXorAssign::bitxor_assign for $name: $prim); 47 | impl_fmt!(Debug for $name); 48 | impl_default!(for $name: $prim); 49 | impl_fmt!(Display for $name); 50 | impl_binop!(Div::div for $name: $prim); 51 | impl_binassign!(DivAssign::div_assign for $name: $prim); 52 | impl_from!(for $name: $prim); 53 | impl_try_from_ptr_size!(isize for $name: $prim); 54 | impl_hash!(for $name); 55 | impl_fmt!(LowerExp for $name); 56 | impl_fmt!(LowerHex for $name); 57 | impl_binop!(Mul::mul for $name: $prim); 58 | impl_binassign!(MulAssign::mul_assign for $name: $prim); 59 | impl_unop!(Neg::neg for $name: $prim); 60 | impl_unop!(Not::not for $name: $prim); 61 | impl_fmt!(Octal for $name); 62 | impl_partial_eq_and_eq!(for $name: $prim); 63 | impl_partial_ord_and_ord!(for $name: $prim); 64 | impl_product_and_sum!(for $name); 65 | impl_binop!(Rem::rem for $name: $prim); 66 | impl_binassign!(RemAssign::rem_assign for $name: $prim); 67 | impl_binop!(Shl::shl for $name: $prim); 68 | impl_binassign!(ShlAssign::shl_assign for $name: $prim); 69 | impl_binop!(Shr::shr for $name: $prim); 70 | impl_binassign!(ShrAssign::shr_assign for $name: $prim); 71 | impl_binop!(Sub::sub for $name: $prim); 72 | impl_binassign!(SubAssign::sub_assign for $name: $prim); 73 | impl_fmt!(UpperExp for $name); 74 | impl_fmt!(UpperHex for $name); 75 | }; 76 | } 77 | 78 | macro_rules! impl_unsigned_integer_traits { 79 | ($name:ident: $endian:ident $prim:ident) => { 80 | // SAFETY: An impl of `CheckBytes` with a `check_bytes` function that is 81 | // a no-op is sound for unsigned integers. 82 | unsafe_impl_check_bytes_noop!(for $name); 83 | // SAFETY: Unsigned integers are inhabited and allow all bit patterns, 84 | // fulfilling the requirements of `Zeroable` and `Pod`. 85 | unsafe_impl_zeroable!(for $name); 86 | unsafe_impl_pod!(for $name); 87 | 88 | impl_binop!(Add::add for $name: $prim); 89 | impl_binassign!(AddAssign::add_assign for $name: $prim); 90 | impl_clone_and_copy!(for $name); 91 | impl_fmt!(Binary for $name); 92 | impl_binop!(BitAnd::bitand for $name: $prim); 93 | impl_binassign!(BitAndAssign::bitand_assign for $name: $prim); 94 | impl_binop!(BitOr::bitor for $name: $prim); 95 | impl_binassign!(BitOrAssign::bitor_assign for $name: $prim); 96 | impl_binop!(BitXor::bitxor for $name: $prim); 97 | impl_binassign!(BitXorAssign::bitxor_assign for $name: $prim); 98 | impl_fmt!(Debug for $name); 99 | impl_default!(for $name: $prim); 100 | impl_fmt!(Display for $name); 101 | impl_binop!(Div::div for $name: $prim); 102 | impl_binassign!(DivAssign::div_assign for $name: $prim); 103 | impl_from!(for $name: $prim); 104 | impl_try_from_ptr_size!(usize for $name: $prim); 105 | impl_hash!(for $name); 106 | impl_fmt!(LowerExp for $name); 107 | impl_fmt!(LowerHex for $name); 108 | impl_binop!(Mul::mul for $name: $prim); 109 | impl_binassign!(MulAssign::mul_assign for $name: $prim); 110 | impl_unop!(Not::not for $name: $prim); 111 | impl_fmt!(Octal for $name); 112 | impl_partial_eq_and_eq!(for $name: $prim); 113 | impl_partial_ord_and_ord!(for $name: $prim); 114 | impl_product_and_sum!(for $name); 115 | impl_binop!(Rem::rem for $name: $prim); 116 | impl_binassign!(RemAssign::rem_assign for $name: $prim); 117 | impl_binop!(Shl::shl for $name: $prim); 118 | impl_binassign!(ShlAssign::shl_assign for $name: $prim); 119 | impl_binop!(Shr::shr for $name: $prim); 120 | impl_binassign!(ShrAssign::shr_assign for $name: $prim); 121 | impl_binop!(Sub::sub for $name: $prim); 122 | impl_binassign!(SubAssign::sub_assign for $name: $prim); 123 | impl_fmt!(UpperExp for $name); 124 | impl_fmt!(UpperHex for $name); 125 | }; 126 | } 127 | 128 | macro_rules! impl_float { 129 | ($name:ident: $endian:ident $prim:ty as $prim_int:ty) => { 130 | impl $name { 131 | #[doc = concat!( 132 | "Returns a `", 133 | stringify!($name), 134 | "` containing `value`.", 135 | )] 136 | #[inline] 137 | pub const fn from_native(value: $prim) -> Self { 138 | use core::mem::transmute; 139 | 140 | // `transmute` is used here because `from_bits` and `to_bits` 141 | // are not stably const as of 1.81.0. 142 | 143 | #[allow( 144 | unknown_lints, 145 | unnecessary_transmutes, 146 | )] 147 | // SAFETY: `$prim` and `$prim_int` have the same size and all 148 | // bit patterns are valid for both. 149 | let value = unsafe { transmute::<$prim, $prim_int>(value) }; 150 | let value = swap_endian!($endian value); 151 | #[allow( 152 | unknown_lints, 153 | unnecessary_transmutes, 154 | )] 155 | // SAFETY: `$prim` and `$prim_int` have the same size and all 156 | // bit patterns are valid for both. 157 | let value = unsafe { transmute::<$prim_int, $prim>(value) }; 158 | Self(value) 159 | } 160 | 161 | #[doc = concat!( 162 | "Returns a `", 163 | stringify!($prim), 164 | "` with the same value as `self`.", 165 | )] 166 | #[inline] 167 | pub const fn to_native(self) -> $prim { 168 | use core::mem::transmute; 169 | 170 | // `transmute` is used here because `from_bits` and `to_bits` 171 | // are not stably const as of 1.81.0. 172 | 173 | #[allow( 174 | unknown_lints, 175 | unnecessary_transmutes, 176 | )] 177 | // SAFETY: `$prim` and `$prim_int` have the same size and all 178 | // bit patterns are valid for both. 179 | let value = unsafe { transmute::<$prim, $prim_int>(self.0) }; 180 | let value = swap_endian!($endian value); 181 | #[allow( 182 | unknown_lints, 183 | unnecessary_transmutes, 184 | )] 185 | // SAFETY: `$prim` and `$prim_int` have the same size and all 186 | // bit patterns are valid for both. 187 | unsafe { transmute::<$prim_int, $prim>(value) } 188 | } 189 | } 190 | 191 | // SAFETY: An impl of `CheckBytes` with a `check_bytes` function that is 192 | // a no-op is sound for floats. 193 | unsafe_impl_check_bytes_noop!(for $name); 194 | // SAFETY: `Pod` is implemented for `f32` and `f64` - as such, flipped 195 | // representations must also be `Pod`. 196 | unsafe_impl_zeroable!(for $name); 197 | unsafe_impl_pod!(for $name); 198 | 199 | impl_binop!(Add::add for $name: $prim); 200 | impl_binassign!(AddAssign::add_assign for $name: $prim); 201 | impl_clone_and_copy!(for $name); 202 | impl_fmt!(Debug for $name); 203 | impl_default!(for $name: $prim); 204 | impl_fmt!(Display for $name); 205 | impl_binop!(Div::div for $name: $prim); 206 | impl_binassign!(DivAssign::div_assign for $name: $prim); 207 | impl_from!(for $name: $prim); 208 | impl_fmt!(LowerExp for $name); 209 | impl_binop!(Mul::mul for $name: $prim); 210 | impl_binassign!(MulAssign::mul_assign for $name: $prim); 211 | impl_unop!(Neg::neg for $name: $prim); 212 | impl_partial_eq_and_eq!(for $name: $prim); 213 | impl_partial_ord!(for $name: $prim); 214 | impl_product_and_sum!(for $name); 215 | impl_binop!(Rem::rem for $name: $prim); 216 | impl_binassign!(RemAssign::rem_assign for $name: $prim); 217 | impl_binop!(Sub::sub for $name: $prim); 218 | impl_binassign!(SubAssign::sub_assign for $name: $prim); 219 | impl_fmt!(UpperExp for $name); 220 | }; 221 | } 222 | 223 | macro_rules! impl_char { 224 | ($name:ident: $endian:ident) => { 225 | impl $name { 226 | #[doc = concat!( 227 | "Returns a `", 228 | stringify!($name), 229 | "` containing `value`.", 230 | )] 231 | #[inline] 232 | pub const fn from_native(value: char) -> Self { 233 | Self(swap_endian!($endian value as u32)) 234 | } 235 | 236 | #[doc = concat!( 237 | "Returns a `", 238 | stringify!($prim), 239 | "` with the same value as `self`.", 240 | )] 241 | #[inline] 242 | pub const fn to_native(self) -> char { 243 | use core::mem::transmute; 244 | 245 | // `transmute` is used here because `from_u32_unchecked` is not 246 | // stably const as of 1.72.0. 247 | 248 | #[allow(unknown_lints, unnecessary_transmutes)] 249 | // SAFETY: `u32` and `char` have the same size and it is an 250 | // invariant of this type that it contains a valid `char` when 251 | // swapped to native endianness. 252 | unsafe { transmute::(swap_endian!($endian self.0)) } 253 | } 254 | } 255 | 256 | // SAFETY: An all-zero bits `char` is just the null char, whether you 257 | // read it forwards or backwards. 258 | unsafe_impl_zeroable!(for $name); 259 | // SAFETY: `char`s do not contain any uninit bytes. 260 | unsafe_impl_no_uninit!(for $name); 261 | 262 | impl_clone_and_copy!(for $name); 263 | impl_fmt!(Debug for $name); 264 | impl_default!(for $name: char); 265 | impl_fmt!(Display for $name); 266 | impl_from!(for $name: char); 267 | impl_hash!(for $name); 268 | impl_partial_eq_and_eq!(for $name: char); 269 | impl_partial_ord_and_ord!(for $name: char); 270 | 271 | #[cfg(feature = "bytecheck")] 272 | // SAFETY: `check_bytes` only returns `Ok` if the code point contained 273 | // within the endian-aware `char` represents a valid `char`. 274 | unsafe impl bytecheck::CheckBytes for $name 275 | where 276 | C: bytecheck::rancor::Fallible + ?Sized, 277 | C::Error: bytecheck::rancor::Trace, 278 | char: bytecheck::CheckBytes, 279 | { 280 | #[inline] 281 | unsafe fn check_bytes( 282 | value: *const Self, 283 | context: &mut C, 284 | ) -> Result<(), C::Error> { 285 | use bytecheck::rancor::ResultExt as _; 286 | 287 | // SAFETY: `value` points to a `Self`, which has the same size 288 | // as a `u32` and is at least as aligned as one. 289 | let u = unsafe { *value.cast::() }; 290 | let c = swap_endian!($endian u); 291 | // SAFETY: `value` points to a valid endian-aware `char` type if 292 | // `c` is a valid `char`. 293 | unsafe { 294 | char::check_bytes(&c as *const u32 as *const char, context) 295 | .with_trace(|| $crate::context::ValueCheckContext { 296 | inner_name: "char", 297 | outer_name: core::stringify!($name), 298 | }) 299 | } 300 | } 301 | } 302 | }; 303 | } 304 | 305 | macro_rules! impl_nonzero { 306 | ($name:ident: $endian:ident $prim:ty as $prim_int:ty) => { 307 | impl $name { 308 | /// Creates a non-zero if the given value is not zero. 309 | #[inline] 310 | pub const fn new(value: $prim_int) -> Option { 311 | if value != 0 { 312 | // SAFETY: `value` is not zero. 313 | Some(unsafe { Self::new_unchecked(value) }) 314 | } else { 315 | None 316 | } 317 | } 318 | 319 | /// Creates a non-zero without checking whether it is non-zero. This 320 | /// results in undefined behavior if the value is zero. 321 | /// 322 | /// # Safety 323 | /// 324 | /// The value must not be zero. 325 | #[inline] 326 | pub const unsafe fn new_unchecked(value: $prim_int) -> Self { 327 | // SAFETY: The caller has guaranteed that `value` is not zero. 328 | unsafe { 329 | Self(<$prim>::new_unchecked(swap_endian!($endian value))) 330 | } 331 | } 332 | 333 | /// Returns the value as a primitive type. 334 | #[inline] 335 | pub const fn get(self) -> $prim_int { 336 | swap_endian!($endian self.0.get()) 337 | } 338 | 339 | #[doc = concat!( 340 | "Returns a `", 341 | stringify!($name), 342 | "` containing `value`.", 343 | )] 344 | #[inline] 345 | pub const fn from_native(value: $prim) -> Self { 346 | // SAFETY: `value` is a non-zero integer and so `value.get()` 347 | // cannot return zero. 348 | unsafe { Self::new_unchecked(value.get()) } 349 | } 350 | 351 | #[doc = concat!( 352 | "Returns a `", 353 | stringify!($prim), 354 | "` with the same value as `self`.", 355 | )] 356 | #[inline] 357 | pub const fn to_native(self) -> $prim { 358 | // SAFETY: `self` is a non-zero integer and so `self.get()` 359 | // cannot return zero. 360 | unsafe { <$prim>::new_unchecked(self.get()) } 361 | } 362 | } 363 | 364 | // SAFETY: Non-zero integers do not contain any uninit bytes. 365 | unsafe_impl_no_uninit!(for $name); 366 | 367 | impl_clone_and_copy!(for $name); 368 | impl_fmt!(Binary for $name); 369 | impl_binop_nonzero!(BitOr::bitor for $name: $prim); 370 | impl_binassign_nonzero!(BitOrAssign::bitor_assign for $name: $prim); 371 | impl_fmt!(Debug for $name); 372 | impl_fmt!(Display for $name); 373 | impl_from!(for $name: $prim); 374 | impl_hash!(for $name); 375 | impl_fmt!(LowerHex for $name); 376 | impl_fmt!(Octal for $name); 377 | impl_partial_eq_and_eq!(for $name: $prim); 378 | impl_partial_ord_and_ord!(for $name: $prim); 379 | impl_fmt!(UpperHex for $name); 380 | }; 381 | } 382 | -------------------------------------------------------------------------------- /src/unaligned.rs: -------------------------------------------------------------------------------- 1 | //! Cross-platform primitives with unaligned representations. 2 | 3 | use core::{ 4 | concat, 5 | num::{ 6 | NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroU128, 7 | NonZeroU16, NonZeroU32, NonZeroU64, 8 | }, 9 | }; 10 | 11 | // `rustfmt` keeps changing the indentation of the attributes in this macro. 12 | #[rustfmt::skip] 13 | macro_rules! define_unaligned_newtype { 14 | ( 15 | $(#[$attr:meta])* 16 | $name:ident: $endian:ident $size:literal $prim:ty 17 | ) => { 18 | #[allow(non_camel_case_types)] 19 | #[doc = concat!( 20 | "A ", 21 | endian_name!($endian), 22 | "-endian unaligned `", 23 | stringify!($prim), 24 | "` with a guaranteed size of `", 25 | stringify!($size), 26 | "` and alignment of `1`.", 27 | )] 28 | $(#[$attr])* 29 | #[repr(C, packed)] 30 | pub struct $name($prim); 31 | }; 32 | } 33 | 34 | macro_rules! define_unaligned_signed_integer { 35 | ($name:ident: $endian:ident $size:literal $prim:ident) => { 36 | define_unaligned_newtype!( 37 | #[cfg_attr( 38 | feature = "zerocopy-0_8", 39 | derive( 40 | zerocopy_derive::FromBytes, 41 | zerocopy_derive::IntoBytes, 42 | zerocopy_derive::Immutable, 43 | zerocopy_derive::KnownLayout, 44 | zerocopy_derive::Unaligned, 45 | ), 46 | )] 47 | $name: $endian $size $prim 48 | ); 49 | impl_integer!($name: $endian $prim); 50 | impl_signed_integer_traits!($name: $endian $prim); 51 | }; 52 | } 53 | 54 | macro_rules! define_unaligned_signed_integers { 55 | ($($le:ident $be:ident: $size:literal $prim:ident),* $(,)?) => { 56 | $( 57 | define_unaligned_signed_integer!($le: little $size $prim); 58 | define_unaligned_signed_integer!($be: big $size $prim); 59 | )* 60 | }; 61 | } 62 | 63 | define_unaligned_signed_integers! { 64 | i16_ule i16_ube: 2 i16, 65 | i32_ule i32_ube: 4 i32, 66 | i64_ule i64_ube: 8 i64, 67 | i128_ule i128_ube: 16 i128, 68 | } 69 | 70 | macro_rules! define_unaligned_unsigned_integer { 71 | ($name:ident: $endian:ident $size:literal $prim:ident) => { 72 | define_unaligned_newtype!( 73 | #[cfg_attr( 74 | feature = "zerocopy-0_8", 75 | derive( 76 | zerocopy_derive::FromBytes, 77 | zerocopy_derive::IntoBytes, 78 | zerocopy_derive::Immutable, 79 | zerocopy_derive::KnownLayout, 80 | zerocopy_derive::Unaligned, 81 | ), 82 | )] 83 | $name: $endian $size $prim 84 | ); 85 | impl_integer!($name: $endian $prim); 86 | impl_unsigned_integer_traits!($name: $endian $prim); 87 | } 88 | } 89 | 90 | macro_rules! define_unaligned_unsigned_integers { 91 | ($($le:ident $be:ident: $size:literal $prim:ident),* $(,)?) => { 92 | $( 93 | define_unaligned_unsigned_integer!($le: little $size $prim); 94 | define_unaligned_unsigned_integer!($be: big $size $prim); 95 | )* 96 | }; 97 | } 98 | 99 | define_unaligned_unsigned_integers! { 100 | u16_ule u16_ube: 2 u16, 101 | u32_ule u32_ube: 4 u32, 102 | u64_ule u64_ube: 8 u64, 103 | u128_ule u128_ube: 16 u128, 104 | } 105 | 106 | macro_rules! define_unaligned_float { 107 | ($name:ident: $endian:ident $size:literal $prim:ty as $prim_int:ty) => { 108 | define_unaligned_newtype!( 109 | #[cfg_attr( 110 | feature = "zerocopy-0_8", 111 | derive( 112 | zerocopy_derive::FromBytes, 113 | zerocopy_derive::IntoBytes, 114 | zerocopy_derive::Immutable, 115 | zerocopy_derive::KnownLayout, 116 | zerocopy_derive::Unaligned, 117 | ), 118 | )] 119 | $name: $endian $size $prim 120 | ); 121 | impl_float!($name: $endian $prim as $prim_int); 122 | }; 123 | } 124 | 125 | macro_rules! define_unaligned_floats { 126 | ($( 127 | $le:ident $be:ident: 128 | $size:literal $prim:ty as $prim_int:ty 129 | ),* $(,)?) => { 130 | $( 131 | define_unaligned_float!($le: little $size $prim as $prim_int); 132 | define_unaligned_float!($be: big $size $prim as $prim_int); 133 | )* 134 | }; 135 | } 136 | 137 | define_unaligned_floats! { 138 | f32_ule f32_ube: 4 f32 as u32, 139 | f64_ule f64_ube: 8 f64 as u64, 140 | } 141 | 142 | macro_rules! define_unaligned_char { 143 | ($name:ident: $endian:ident) => { 144 | define_unaligned_newtype!( 145 | #[cfg_attr( 146 | feature = "zerocopy-0_8", 147 | derive( 148 | // The generated impl for `zerocopy::TryFromBytes` is overly 149 | // permissive. The derive macro doesn't understand that even 150 | // though this struct only contains a `u32`, it still has a 151 | // restricted set of valid bit patterns. Because 152 | // `zerocopy::TryFromBytes` has hidden, semver-breaking 153 | // members, I can't write a manual impl. So no impl for you. 154 | // 155 | // zerocopy_derive::TryFromBytes, 156 | zerocopy_derive::IntoBytes, 157 | zerocopy_derive::Immutable, 158 | zerocopy_derive::KnownLayout, 159 | zerocopy_derive::Unaligned, 160 | ), 161 | )] 162 | $name: $endian 4 u32 163 | ); 164 | impl_char!($name: $endian); 165 | }; 166 | } 167 | 168 | define_unaligned_char!(char_ule: little); 169 | define_unaligned_char!(char_ube: big); 170 | 171 | macro_rules! define_unaligned_nonzero { 172 | ($name:ident: $endian:ident $size:literal $prim:ty as $prim_int:ty) => { 173 | define_unaligned_newtype!($name: $endian $size $prim); 174 | impl_nonzero!($name: $endian $prim as $prim_int); 175 | 176 | #[cfg(feature = "bytecheck")] 177 | // SAFETY: `check_bytes` only returns `Ok` if `value` points to a valid 178 | // non-zero value, which is the only requirement for `NonZero` integers. 179 | unsafe impl bytecheck::CheckBytes for $name 180 | where 181 | C: bytecheck::rancor::Fallible + ?Sized, 182 | C::Error: bytecheck::rancor::Trace, 183 | $prim: bytecheck::CheckBytes, 184 | { 185 | #[inline] 186 | unsafe fn check_bytes( 187 | value: *const Self, 188 | context: &mut C, 189 | ) -> Result<(), C::Error> { 190 | use bytecheck::rancor::ResultExt as _; 191 | 192 | // SAFETY: `value` points to a `Self`, which has the same size 193 | // as a `$prim_int` and which the caller has guaranteed is valid 194 | // for reads. All bit patterns are valid for `$prim_int`. 195 | let value = unsafe { 196 | value.cast::<$prim_int>().read_unaligned() 197 | }; 198 | let ptr = (&value as *const $prim_int).cast::<$prim>(); 199 | // SAFETY: `ptr` points to a `$prim_int` and so is guaranteed to 200 | // be aligned and point to enough bytes to represent a `$prim`. 201 | unsafe { 202 | <$prim>::check_bytes(ptr, context) 203 | .with_trace(|| $crate::context::ValueCheckContext { 204 | inner_name: core::stringify!($prim), 205 | outer_name: core::stringify!($name), 206 | }) 207 | } 208 | } 209 | } 210 | }; 211 | } 212 | 213 | macro_rules! define_unaligned_nonzeros { 214 | ($( 215 | $le:ident $be:ident: 216 | $size:literal $prim:ty as $prim_int:ty 217 | ),* $(,)?) => { 218 | $( 219 | define_unaligned_nonzero!($le: little $size $prim as $prim_int); 220 | define_unaligned_nonzero!($be: big $size $prim as $prim_int); 221 | )* 222 | } 223 | } 224 | 225 | define_unaligned_nonzeros! { 226 | NonZeroI16_ule NonZeroI16_ube: 2 NonZeroI16 as i16, 227 | NonZeroI32_ule NonZeroI32_ube: 2 NonZeroI32 as i32, 228 | NonZeroI64_ule NonZeroI64_ube: 4 NonZeroI64 as i64, 229 | NonZeroI128_ule NonZeroI128_ube: 4 NonZeroI128 as i128, 230 | NonZeroU16_ule NonZeroU16_ube: 8 NonZeroU16 as u16, 231 | NonZeroU32_ule NonZeroU32_ube: 8 NonZeroU32 as u32, 232 | NonZeroU64_ule NonZeroU64_ube: 16 NonZeroU64 as u64, 233 | NonZeroU128_ule NonZeroU128_ube: 16 NonZeroU128 as u128, 234 | } 235 | 236 | #[cfg(test)] 237 | mod tests { 238 | use core::mem::transmute; 239 | 240 | use super::*; 241 | 242 | #[test] 243 | fn signed_integers() { 244 | assert_size_align! { 245 | i16_ube 2 1, 246 | i16_ule 2 1, 247 | i32_ube 4 1, 248 | i32_ule 4 1, 249 | i64_ube 8 1, 250 | i64_ule 8 1, 251 | i128_ube 16 1, 252 | i128_ule 16 1, 253 | } 254 | 255 | unsafe { 256 | // i16 257 | assert_eq!( 258 | [0x02, 0x01], 259 | transmute::<_, [u8; 2]>(i16_ule::from_native(0x0102)), 260 | ); 261 | assert_eq!( 262 | [0x01, 0x02], 263 | transmute::<_, [u8; 2]>(i16_ube::from_native(0x0102)), 264 | ); 265 | 266 | // i32 267 | assert_eq!( 268 | [0x04, 0x03, 0x02, 0x01], 269 | transmute::<_, [u8; 4]>(i32_ule::from_native(0x01020304)), 270 | ); 271 | assert_eq!( 272 | [0x01, 0x02, 0x03, 0x04], 273 | transmute::<_, [u8; 4]>(i32_ube::from_native(0x01020304)), 274 | ); 275 | 276 | // i64 277 | assert_eq!( 278 | [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01], 279 | transmute::<_, [u8; 8]>(i64_ule::from_native( 280 | 0x0102030405060708 281 | )), 282 | ); 283 | assert_eq!( 284 | [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08], 285 | transmute::<_, [u8; 8]>(i64_ube::from_native( 286 | 0x0102030405060708 287 | )), 288 | ); 289 | 290 | // i128 291 | assert_eq!( 292 | [ 293 | 0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 294 | 0x06, 0x05, 0x04, 0x03, 0x02, 0x01 295 | ], 296 | transmute::<_, [u8; 16]>(i128_ule::from_native( 297 | 0x0102030405060708090a0b0c0d0e0f10 298 | )), 299 | ); 300 | assert_eq!( 301 | [ 302 | 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 303 | 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 304 | ], 305 | transmute::<_, [u8; 16]>(i128_ube::from_native( 306 | 0x0102030405060708090a0b0c0d0e0f10 307 | )), 308 | ); 309 | } 310 | } 311 | 312 | #[test] 313 | fn unsigned_integers() { 314 | assert_size_align! { 315 | u16_ube 2 1, 316 | u16_ule 2 1, 317 | u32_ube 4 1, 318 | u32_ule 4 1, 319 | u64_ube 8 1, 320 | u64_ule 8 1, 321 | u128_ube 16 1, 322 | u128_ule 16 1, 323 | } 324 | 325 | unsafe { 326 | // u16 327 | assert_eq!( 328 | [0x02, 0x01], 329 | transmute::<_, [u8; 2]>(u16_ule::from_native(0x0102)), 330 | ); 331 | assert_eq!( 332 | [0x01, 0x02], 333 | transmute::<_, [u8; 2]>(u16_ube::from_native(0x0102)), 334 | ); 335 | 336 | // u32 337 | assert_eq!( 338 | [0x04, 0x03, 0x02, 0x01], 339 | transmute::<_, [u8; 4]>(u32_ule::from_native(0x01020304)), 340 | ); 341 | assert_eq!( 342 | [0x01, 0x02, 0x03, 0x04], 343 | transmute::<_, [u8; 4]>(u32_ube::from_native(0x01020304)), 344 | ); 345 | 346 | // u64 347 | assert_eq!( 348 | [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01], 349 | transmute::<_, [u8; 8]>(u64_ule::from_native( 350 | 0x0102030405060708 351 | )), 352 | ); 353 | assert_eq!( 354 | [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08], 355 | transmute::<_, [u8; 8]>(u64_ube::from_native( 356 | 0x0102030405060708 357 | )), 358 | ); 359 | 360 | // u128 361 | assert_eq!( 362 | [ 363 | 0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 364 | 0x06, 0x05, 0x04, 0x03, 0x02, 0x01 365 | ], 366 | transmute::<_, [u8; 16]>(u128_ule::from_native( 367 | 0x0102030405060708090a0b0c0d0e0f10 368 | )), 369 | ); 370 | assert_eq!( 371 | [ 372 | 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 373 | 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 374 | ], 375 | transmute::<_, [u8; 16]>(u128_ube::from_native( 376 | 0x0102030405060708090a0b0c0d0e0f10 377 | )), 378 | ); 379 | } 380 | } 381 | 382 | #[test] 383 | fn floats() { 384 | assert_size_align! { 385 | f32_ube 4 1, 386 | f32_ule 4 1, 387 | f64_ube 8 1, 388 | f64_ule 8 1, 389 | } 390 | 391 | unsafe { 392 | // f32 393 | assert_eq!( 394 | [0xdb, 0x0f, 0x49, 0x40], 395 | transmute::<_, [u8; 4]>(f32_ule::from_native( 396 | core::f32::consts::PI 397 | )), 398 | ); 399 | assert_eq!( 400 | [0x40, 0x49, 0x0f, 0xdb], 401 | transmute::<_, [u8; 4]>(f32_ube::from_native( 402 | core::f32::consts::PI 403 | )), 404 | ); 405 | 406 | // f64 407 | assert_eq!( 408 | [0x18, 0x2d, 0x44, 0x54, 0xfb, 0x21, 0x09, 0x40], 409 | transmute::<_, [u8; 8]>(f64_ule::from_native( 410 | core::f64::consts::PI 411 | )), 412 | ); 413 | assert_eq!( 414 | [0x40, 0x09, 0x21, 0xfb, 0x54, 0x44, 0x2d, 0x18], 415 | transmute::<_, [u8; 8]>(f64_ube::from_native( 416 | core::f64::consts::PI 417 | )), 418 | ); 419 | 420 | // char 421 | assert_eq!( 422 | [0x89, 0xf3, 0x01, 0x00], 423 | transmute::<_, [u8; 4]>(char_ule::from_native('🎉')), 424 | ); 425 | assert_eq!( 426 | [0x00, 0x01, 0xf3, 0x89], 427 | transmute::<_, [u8; 4]>(char_ube::from_native('🎉')), 428 | ); 429 | } 430 | } 431 | 432 | #[test] 433 | fn signed_non_zero() { 434 | assert_size_align! { 435 | NonZeroI16_ule 2 1, 436 | NonZeroI16_ube 2 1, 437 | NonZeroI32_ule 4 1, 438 | NonZeroI32_ube 4 1, 439 | NonZeroI64_ule 8 1, 440 | NonZeroI64_ube 8 1, 441 | NonZeroI128_ule 16 1, 442 | NonZeroI128_ube 16 1, 443 | } 444 | 445 | unsafe { 446 | // NonZeroI16 447 | assert_eq!( 448 | [0x02, 0x01], 449 | transmute::<_, [u8; 2]>(NonZeroI16_ule::new_unchecked(0x0102)), 450 | ); 451 | assert_eq!( 452 | [0x01, 0x02], 453 | transmute::<_, [u8; 2]>(NonZeroI16_ube::new_unchecked(0x0102)), 454 | ); 455 | 456 | // NonZeroI32 457 | assert_eq!( 458 | [0x04, 0x03, 0x02, 0x01], 459 | transmute::<_, [u8; 4]>(NonZeroI32_ule::new_unchecked( 460 | 0x01020304 461 | )), 462 | ); 463 | assert_eq!( 464 | [0x01, 0x02, 0x03, 0x04], 465 | transmute::<_, [u8; 4]>(NonZeroI32_ube::new_unchecked( 466 | 0x01020304 467 | )), 468 | ); 469 | 470 | // NonZeroI64 471 | assert_eq!( 472 | [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01], 473 | transmute::<_, [u8; 8]>(NonZeroI64_ule::new_unchecked( 474 | 0x0102030405060708 475 | )), 476 | ); 477 | assert_eq!( 478 | [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08], 479 | transmute::<_, [u8; 8]>(NonZeroI64_ube::new_unchecked( 480 | 0x0102030405060708 481 | )), 482 | ); 483 | 484 | // NonZeroI128 485 | assert_eq!( 486 | [ 487 | 0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 488 | 0x06, 0x05, 0x04, 0x03, 0x02, 0x01 489 | ], 490 | transmute::<_, [u8; 16]>(NonZeroI128_ule::new_unchecked( 491 | 0x0102030405060708090a0b0c0d0e0f10 492 | )), 493 | ); 494 | assert_eq!( 495 | [ 496 | 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 497 | 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 498 | ], 499 | transmute::<_, [u8; 16]>(NonZeroI128_ube::new_unchecked( 500 | 0x0102030405060708090a0b0c0d0e0f10 501 | )), 502 | ); 503 | } 504 | } 505 | 506 | #[test] 507 | fn unsigned_non_zero() { 508 | assert_size_align! { 509 | NonZeroU16_ule 2 1, 510 | NonZeroU16_ube 2 1, 511 | NonZeroU32_ule 4 1, 512 | NonZeroU32_ube 4 1, 513 | NonZeroU64_ule 8 1, 514 | NonZeroU64_ube 8 1, 515 | NonZeroU128_ule 16 1, 516 | NonZeroU128_ube 16 1, 517 | } 518 | 519 | unsafe { 520 | // NonZeroU16 521 | assert_eq!( 522 | [0x02, 0x01], 523 | transmute::<_, [u8; 2]>(NonZeroU16_ule::new_unchecked(0x0102)), 524 | ); 525 | assert_eq!( 526 | [0x01, 0x02], 527 | transmute::<_, [u8; 2]>(NonZeroU16_ube::new_unchecked(0x0102)), 528 | ); 529 | 530 | // NonZeroU32 531 | assert_eq!( 532 | [0x04, 0x03, 0x02, 0x01], 533 | transmute::<_, [u8; 4]>(NonZeroU32_ule::new_unchecked( 534 | 0x01020304 535 | )), 536 | ); 537 | assert_eq!( 538 | [0x01, 0x02, 0x03, 0x04], 539 | transmute::<_, [u8; 4]>(NonZeroU32_ube::new_unchecked( 540 | 0x01020304 541 | )), 542 | ); 543 | 544 | // NonZeroU64 545 | assert_eq!( 546 | [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01], 547 | transmute::<_, [u8; 8]>(NonZeroU64_ule::new_unchecked( 548 | 0x0102030405060708 549 | )), 550 | ); 551 | assert_eq!( 552 | [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08], 553 | transmute::<_, [u8; 8]>(NonZeroU64_ube::new_unchecked( 554 | 0x0102030405060708 555 | )), 556 | ); 557 | 558 | // NonZeroU128 559 | assert_eq!( 560 | [ 561 | 0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 562 | 0x06, 0x05, 0x04, 0x03, 0x02, 0x01 563 | ], 564 | transmute::<_, [u8; 16]>(NonZeroU128_ule::new_unchecked( 565 | 0x0102030405060708090a0b0c0d0e0f10 566 | )), 567 | ); 568 | assert_eq!( 569 | [ 570 | 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 571 | 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 572 | ], 573 | transmute::<_, [u8; 16]>(NonZeroU128_ube::new_unchecked( 574 | 0x0102030405060708090a0b0c0d0e0f10 575 | )), 576 | ); 577 | } 578 | } 579 | } 580 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # rend 2 | //! 3 | //! rend provides cross-platform, endian-aware primitives for Rust. 4 | //! 5 | //! rend does not provide cross-platform alternatives for types that are 6 | //! inherently cross-platform, such as `bool` and `u8`. It also does not provide 7 | //! cross-platform alternatives for types that have an architecture-dependent 8 | //! size, such as `isize` and `usize`. rend does not support custom types. 9 | //! 10 | //! rend is intended to be used to build portable types that can be shared 11 | //! between different architectures. 12 | //! 13 | //! ## Features 14 | //! 15 | //! - `bytecheck`: Enables support for validating types using `bytecheck`. 16 | //! 17 | //! ## Crates 18 | //! 19 | //! - `zerocopy-0_8` 20 | //! 21 | //! ## Example: 22 | #![doc = include_str!("../example.md")] 23 | #![no_std] 24 | #![deny( 25 | future_incompatible, 26 | missing_docs, 27 | nonstandard_style, 28 | unsafe_op_in_unsafe_fn, 29 | unused, 30 | warnings, 31 | clippy::all, 32 | clippy::missing_safety_doc, 33 | clippy::undocumented_unsafe_blocks, 34 | rustdoc::broken_intra_doc_links, 35 | rustdoc::missing_crate_level_docs 36 | )] 37 | #![cfg_attr(all(docsrs, not(doctest)), feature(doc_cfg))] 38 | 39 | #[macro_use] 40 | mod common; 41 | #[cfg(feature = "bytecheck")] 42 | mod context; 43 | #[macro_use] 44 | mod traits; 45 | #[macro_use] 46 | mod util; 47 | 48 | pub mod unaligned; 49 | 50 | #[cfg(target_has_atomic = "16")] 51 | use core::sync::atomic::{AtomicI16, AtomicU16}; 52 | #[cfg(target_has_atomic = "32")] 53 | use core::sync::atomic::{AtomicI32, AtomicU32}; 54 | #[cfg(target_has_atomic = "64")] 55 | use core::sync::atomic::{AtomicI64, AtomicU64}; 56 | use core::{ 57 | num::{ 58 | NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroU128, 59 | NonZeroU16, NonZeroU32, NonZeroU64, 60 | }, 61 | sync::atomic::Ordering, 62 | }; 63 | 64 | // `rustfmt` keeps changing the indentation of the attributes in this macro. 65 | #[rustfmt::skip] 66 | macro_rules! define_newtype { 67 | ( 68 | $(#[$attr:meta])* 69 | $name:ident: $endian:ident $size_align:literal $prim:ty 70 | ) => { 71 | #[allow(non_camel_case_types)] 72 | #[doc = concat!( 73 | "A ", 74 | endian_name!($endian), 75 | "-endian `", 76 | stringify!($prim), 77 | "` with a guaranteed size and alignment of `", 78 | stringify!($size_align), 79 | "`.", 80 | )] 81 | $(#[$attr])* 82 | #[repr(C, align($size_align))] 83 | pub struct $name($prim); 84 | }; 85 | } 86 | 87 | macro_rules! define_signed_integer { 88 | ($name:ident: $endian:ident $size_align:literal $prim:ident) => { 89 | define_newtype!( 90 | #[cfg_attr( 91 | feature = "zerocopy-0_8", 92 | derive( 93 | zerocopy_derive::FromBytes, 94 | zerocopy_derive::IntoBytes, 95 | zerocopy_derive::Immutable, 96 | zerocopy_derive::KnownLayout, 97 | ), 98 | )] 99 | $name: $endian $size_align $prim 100 | ); 101 | impl_integer!($name: $endian $prim); 102 | impl_signed_integer_traits!($name: $endian $prim); 103 | }; 104 | } 105 | 106 | macro_rules! define_signed_integers { 107 | ($($le:ident $be:ident: $size_align:literal $prim:ident),* $(,)?) => { 108 | $( 109 | define_signed_integer!($le: little $size_align $prim); 110 | define_signed_integer!($be: big $size_align $prim); 111 | )* 112 | }; 113 | } 114 | 115 | define_signed_integers! { 116 | i16_le i16_be: 2 i16, 117 | i32_le i32_be: 4 i32, 118 | i64_le i64_be: 8 i64, 119 | i128_le i128_be: 16 i128, 120 | } 121 | 122 | macro_rules! define_unsigned_integer { 123 | ($name:ident: $endian:ident $size_align:literal $prim:ident) => { 124 | define_newtype!( 125 | #[cfg_attr( 126 | feature = "zerocopy-0_8", 127 | derive( 128 | zerocopy_derive::FromBytes, 129 | zerocopy_derive::IntoBytes, 130 | zerocopy_derive::Immutable, 131 | zerocopy_derive::KnownLayout, 132 | ), 133 | )] 134 | $name: $endian $size_align $prim 135 | ); 136 | impl_integer!($name: $endian $prim); 137 | impl_unsigned_integer_traits!($name: $endian $prim); 138 | } 139 | } 140 | 141 | macro_rules! define_unsigned_integers { 142 | ($($le:ident $be:ident: $size_align:literal $prim:ident),* $(,)?) => { 143 | $( 144 | define_unsigned_integer!($le: little $size_align $prim); 145 | define_unsigned_integer!($be: big $size_align $prim); 146 | )* 147 | }; 148 | } 149 | 150 | define_unsigned_integers! { 151 | u16_le u16_be: 2 u16, 152 | u32_le u32_be: 4 u32, 153 | u64_le u64_be: 8 u64, 154 | u128_le u128_be: 16 u128, 155 | } 156 | 157 | macro_rules! define_float { 158 | ( 159 | $name:ident: 160 | $endian:ident $size_align:literal $prim:ty as $prim_int:ty 161 | ) => { 162 | define_newtype!( 163 | #[cfg_attr( 164 | feature = "zerocopy-0_8", 165 | derive( 166 | zerocopy_derive::FromBytes, 167 | zerocopy_derive::IntoBytes, 168 | zerocopy_derive::Immutable, 169 | zerocopy_derive::KnownLayout, 170 | ), 171 | )] 172 | $name: $endian $size_align $prim 173 | ); 174 | impl_float!($name: $endian $prim as $prim_int); 175 | }; 176 | } 177 | 178 | macro_rules! define_floats { 179 | ($( 180 | $le:ident $be:ident: 181 | $size_align:literal $prim:ty as $prim_int:ty 182 | ),* $(,)?) => { 183 | $( 184 | define_float!($le: little $size_align $prim as $prim_int); 185 | define_float!($be: big $size_align $prim as $prim_int); 186 | )* 187 | }; 188 | } 189 | 190 | define_floats! { 191 | f32_le f32_be: 4 f32 as u32, 192 | f64_le f64_be: 8 f64 as u64, 193 | } 194 | 195 | macro_rules! define_char { 196 | ($name:ident: $endian:ident) => { 197 | define_newtype!( 198 | #[cfg_attr( 199 | feature = "zerocopy-0_8", 200 | derive( 201 | // The generated impl for `zerocopy::TryFromBytes` is overly 202 | // permissive. The derive macro doesn't understand that even 203 | // though this struct only contains a `u32`, it still has a 204 | // restricted set of valid bit patterns. Because 205 | // `zerocopy::TryFromBytes` has hidden, semver-breaking 206 | // members, I can't write a manual impl. So no impl for you. 207 | // 208 | // zerocopy_derive::TryFromBytes, 209 | zerocopy_derive::IntoBytes, 210 | zerocopy_derive::Immutable, 211 | zerocopy_derive::KnownLayout, 212 | ), 213 | )] 214 | $name: $endian 4 u32 215 | ); 216 | impl_char!($name: $endian); 217 | }; 218 | } 219 | 220 | define_char!(char_le: little); 221 | define_char!(char_be: big); 222 | 223 | macro_rules! define_nonzero { 224 | ( 225 | $name:ident: 226 | $endian:ident $size_align:literal $prim:ty as $prim_int:ty 227 | ) => { 228 | define_newtype!( 229 | #[cfg_attr( 230 | feature = "zerocopy-0_8", 231 | derive( 232 | zerocopy_derive::TryFromBytes, 233 | zerocopy_derive::IntoBytes, 234 | zerocopy_derive::Immutable, 235 | zerocopy_derive::KnownLayout, 236 | ), 237 | )] 238 | $name: $endian $size_align $prim 239 | ); 240 | impl_nonzero!($name: $endian $prim as $prim_int); 241 | 242 | #[cfg(feature = "bytecheck")] 243 | // SAFETY: `check_bytes` only returns `Ok` if `value` points to a valid 244 | // non-zero value, which is the only requirement for `NonZero` integers. 245 | unsafe impl bytecheck::CheckBytes for $name 246 | where 247 | C: bytecheck::rancor::Fallible + ?Sized, 248 | C::Error: bytecheck::rancor::Trace, 249 | $prim: bytecheck::CheckBytes, 250 | { 251 | #[inline] 252 | unsafe fn check_bytes( 253 | value: *const Self, 254 | context: &mut C, 255 | ) -> Result<(), C::Error> { 256 | use bytecheck::rancor::ResultExt as _; 257 | 258 | // SAFETY: `value` points to a `Self`, which has the same size 259 | // as a `$prim` and is at least as aligned as one. Note that the 260 | // bit pattern for 0 is always the same regardless of 261 | // endianness. 262 | unsafe { 263 | <$prim>::check_bytes(value.cast(), context) 264 | .with_trace(|| $crate::context::ValueCheckContext { 265 | inner_name: core::stringify!($prim), 266 | outer_name: core::stringify!($name), 267 | }) 268 | } 269 | } 270 | } 271 | }; 272 | } 273 | 274 | macro_rules! define_nonzeros { 275 | ($( 276 | $le:ident $be:ident: 277 | $size_align:literal $prim:ty as $prim_int:ty 278 | ),* $(,)?) => { 279 | $( 280 | define_nonzero!($le: little $size_align $prim as $prim_int); 281 | define_nonzero!($be: big $size_align $prim as $prim_int); 282 | )* 283 | } 284 | } 285 | 286 | define_nonzeros! { 287 | NonZeroI16_le NonZeroI16_be: 2 NonZeroI16 as i16, 288 | NonZeroI32_le NonZeroI32_be: 4 NonZeroI32 as i32, 289 | NonZeroI64_le NonZeroI64_be: 8 NonZeroI64 as i64, 290 | NonZeroI128_le NonZeroI128_be: 16 NonZeroI128 as i128, 291 | NonZeroU16_le NonZeroU16_be: 2 NonZeroU16 as u16, 292 | NonZeroU32_le NonZeroU32_be: 4 NonZeroU32 as u32, 293 | NonZeroU64_le NonZeroU64_be: 8 NonZeroU64 as u64, 294 | NonZeroU128_le NonZeroU128_be: 16 NonZeroU128 as u128, 295 | } 296 | 297 | #[allow(dead_code)] 298 | const fn fetch_ordering(order: Ordering) -> Ordering { 299 | match order { 300 | Ordering::Relaxed => Ordering::Relaxed, 301 | Ordering::Release => Ordering::Relaxed, 302 | Ordering::Acquire => Ordering::Acquire, 303 | Ordering::AcqRel => Ordering::Acquire, 304 | Ordering::SeqCst => Ordering::SeqCst, 305 | order => order, 306 | } 307 | } 308 | 309 | #[cfg(any( 310 | target_has_atomic = "16", 311 | target_has_atomic = "32", 312 | target_has_atomic = "64", 313 | ))] 314 | macro_rules! define_atomic { 315 | ( 316 | $name:ident: 317 | $endian:ident $size_align:literal $prim:ty as $prim_int:ty 318 | ) => { 319 | define_newtype!( 320 | #[cfg_attr( 321 | feature = "zerocopy-0_8", 322 | derive( 323 | zerocopy_derive::FromBytes, 324 | zerocopy_derive::IntoBytes, 325 | zerocopy_derive::KnownLayout, 326 | ), 327 | )] 328 | $name: $endian $size_align $prim 329 | ); 330 | 331 | impl $name { 332 | #[doc = concat!( 333 | "Returns a `", 334 | stringify!($name), 335 | "` containing `value`.", 336 | )] 337 | #[inline] 338 | pub const fn new(value: $prim_int) -> Self { 339 | Self(<$prim>::new(swap_endian!($endian value))) 340 | } 341 | } 342 | 343 | // SAFETY: An impl of `CheckBytes` with a `check_bytes` function that is 344 | // a no-op is sound for atomic integers. 345 | unsafe_impl_check_bytes_noop!(for $name); 346 | 347 | impl $name { 348 | /// Stores a value into the atomic integer if the current value is 349 | /// the same as the `current` value. 350 | /// 351 | #[doc = concat!( 352 | "See [`", 353 | stringify!($prim), 354 | "::compare_exchange`] for more information.", 355 | )] 356 | #[inline] 357 | pub fn compare_exchange( 358 | &self, 359 | current: $prim_int, 360 | new: $prim_int, 361 | success: Ordering, 362 | failure: Ordering, 363 | ) -> Result<$prim_int, $prim_int> { 364 | match self.0.compare_exchange( 365 | swap_endian!($endian current), 366 | swap_endian!($endian new), 367 | success, 368 | failure, 369 | ) { 370 | Ok(x) => Ok(swap_endian!($endian x)), 371 | Err(x) => Err(swap_endian!($endian x)), 372 | } 373 | } 374 | 375 | /// Stores a value into the atomic integer if the current value is 376 | /// the same as the `current` value. 377 | /// 378 | #[doc = concat!( 379 | "See [`", 380 | stringify!($prim), 381 | "::compare_exchange_weak`] for more information.", 382 | )] 383 | #[inline] 384 | pub fn compare_exchange_weak( 385 | &self, 386 | current: $prim_int, 387 | new: $prim_int, 388 | success: Ordering, 389 | failure: Ordering, 390 | ) -> Result<$prim_int, $prim_int> { 391 | match self.0.compare_exchange_weak( 392 | swap_endian!($endian current), 393 | swap_endian!($endian new), 394 | success, 395 | failure, 396 | ) { 397 | Ok(x) => Ok(swap_endian!($endian x)), 398 | Err(x) => Ok(swap_endian!($endian x)), 399 | } 400 | } 401 | 402 | /// Adds to the current value, returning the previous value. 403 | /// 404 | #[doc = concat!( 405 | "Because addition is not an endian-agnostic operation, ", 406 | "`fetch_add` is implemented in terms of [`", 407 | stringify!($prim), 408 | "::compare_exchange_weak`] on ", 409 | opposite_endian_name!($endian), 410 | "-endian targets. This may result in worse performance on ", 411 | "those targets.", 412 | )] 413 | /// 414 | #[doc = concat!( 415 | "See [`", 416 | stringify!($prim), 417 | "::fetch_add`] for more information.", 418 | )] 419 | #[inline] 420 | pub fn fetch_add( 421 | &self, 422 | val: $prim_int, 423 | order: Ordering, 424 | ) -> $prim_int { 425 | if_native_endian!( 426 | $endian 427 | self.0.fetch_add(val, order), 428 | self.fetch_update_fast( 429 | order, 430 | fetch_ordering(order), 431 | |x| x + val, 432 | ), 433 | ) 434 | } 435 | 436 | /// Bitwise "and" with the current value. 437 | /// 438 | #[doc = concat!( 439 | "See [`", 440 | stringify!($prim), 441 | "::fetch_and`] for more information.", 442 | )] 443 | #[inline] 444 | pub fn fetch_and( 445 | &self, 446 | val: $prim_int, 447 | order: Ordering, 448 | ) -> $prim_int { 449 | let val = swap_endian!($endian val); 450 | swap_endian!($endian self.0.fetch_and(val, order)) 451 | } 452 | 453 | /// Maximum with the current value. 454 | /// 455 | #[doc = concat!( 456 | "Because maximum is not an endian-agnostic operation, ", 457 | "`fetch_max` is implemented in terms of [`", 458 | stringify!($prim), 459 | "::compare_exchange_weak`] on ", 460 | opposite_endian_name!($endian), 461 | "-endian targets. This may result in worse performance on ", 462 | "those targets.", 463 | )] 464 | /// 465 | #[doc = concat!( 466 | "See [`", 467 | stringify!($prim), 468 | "::fetch_max`] for more information.", 469 | )] 470 | #[inline] 471 | pub fn fetch_max( 472 | &self, 473 | val: $prim_int, 474 | order: Ordering, 475 | ) -> $prim_int { 476 | if_native_endian!( 477 | $endian 478 | self.0.fetch_max(val, order), 479 | self.fetch_update_fast( 480 | order, 481 | fetch_ordering(order), 482 | |x| <$prim_int>::max(x, val), 483 | ), 484 | ) 485 | } 486 | 487 | /// Minimum with the current value. 488 | /// 489 | #[doc = concat!( 490 | "Because minimum is not an endian-agnostic operation, ", 491 | "`fetch_min` is implemented in terms of [`", 492 | stringify!($prim), 493 | "::compare_exchange_weak`] on ", 494 | opposite_endian_name!($endian), 495 | "-endian targets. This may result in worse performance on ", 496 | "those targets.", 497 | )] 498 | /// 499 | #[doc = concat!( 500 | "See [`", 501 | stringify!($prim), 502 | "::fetch_min`] for more information.", 503 | )] 504 | #[inline] 505 | pub fn fetch_min( 506 | &self, 507 | val: $prim_int, 508 | order: Ordering, 509 | ) -> $prim_int { 510 | if_native_endian!( 511 | $endian 512 | self.0.fetch_min(val, order), 513 | self.fetch_update_fast( 514 | order, 515 | fetch_ordering(order), 516 | |x| <$prim_int>::min(x, val), 517 | ), 518 | ) 519 | } 520 | 521 | /// Bitwise "nand" with the current value. 522 | /// 523 | #[doc = concat!( 524 | "See [`", 525 | stringify!($prim), 526 | "::fetch_nand`] for more information.", 527 | )] 528 | #[inline] 529 | pub fn fetch_nand( 530 | &self, 531 | val: $prim_int, 532 | order: Ordering, 533 | ) -> $prim_int { 534 | let val = swap_endian!($endian val); 535 | swap_endian!($endian self.0.fetch_nand(val, order)) 536 | } 537 | 538 | /// Bitwise "or" with the current value. 539 | /// 540 | #[doc = concat!( 541 | "See [`", 542 | stringify!($prim), 543 | "::fetch_or`] for more information.", 544 | )] 545 | #[inline] 546 | pub fn fetch_or( 547 | &self, 548 | val: $prim_int, 549 | order: Ordering, 550 | ) -> $prim_int { 551 | let val = swap_endian!($endian val); 552 | swap_endian!($endian self.0.fetch_or(val, order)) 553 | } 554 | 555 | /// Subtracts from the current value, returning the previous value. 556 | /// 557 | #[doc = concat!( 558 | "Because subtraction is not an endian-agnostic operation, ", 559 | "`fetch_sub` is implemented in terms of [`", 560 | stringify!($prim), 561 | "::compare_exchange_weak`] on ", 562 | opposite_endian_name!($endian), 563 | "-endian targets. This may result in worse performance on ", 564 | "those targets.", 565 | )] 566 | /// 567 | #[doc = concat!( 568 | "See [`", 569 | stringify!($prim), 570 | "::fetch_sub`] for more information.", 571 | )] 572 | #[inline] 573 | pub fn fetch_sub( 574 | &self, 575 | val: $prim_int, 576 | order: Ordering, 577 | ) -> $prim_int { 578 | if_native_endian!( 579 | $endian 580 | self.0.fetch_sub(val, order), 581 | self.fetch_update_fast( 582 | order, 583 | fetch_ordering(order), 584 | |x| x - val, 585 | ), 586 | ) 587 | } 588 | 589 | #[allow(dead_code)] 590 | #[inline(always)] 591 | fn fetch_update_fast $prim_int>( 592 | &self, 593 | set_order: Ordering, 594 | fetch_order: Ordering, 595 | f: F, 596 | ) -> $prim_int { 597 | let mut prev = swap_endian!($endian self.0.load(fetch_order)); 598 | loop { 599 | let next = swap_endian!($endian f(prev)); 600 | match self.0.compare_exchange_weak( 601 | prev, 602 | next, 603 | set_order, 604 | fetch_order, 605 | ) { 606 | Ok(x) => break x, 607 | Err(next_prev) => { 608 | prev = swap_endian!($endian next_prev); 609 | } 610 | } 611 | } 612 | } 613 | 614 | /// Fetches the value, and applies a function to it that returns an 615 | /// optional new value. Returns a `Result` of `Ok(previous_value)` 616 | /// if the function returned `Some(_)`, else `Err(previous_value)`. 617 | /// 618 | #[doc = concat!( 619 | "See [`", 620 | stringify!($prim), 621 | "::fetch_update`] for more information.", 622 | )] 623 | #[inline] 624 | pub fn fetch_update Option<$prim_int>>( 625 | &self, 626 | set_order: Ordering, 627 | fetch_order: Ordering, 628 | mut f: F, 629 | ) -> Result<$prim_int, $prim_int> { 630 | self.0.fetch_update(set_order, fetch_order, |x| { 631 | f(swap_endian!($endian x)).map(|y| swap_endian!($endian y)) 632 | }) 633 | } 634 | 635 | /// Bitwise "xor" with the current value. 636 | /// 637 | #[doc = concat!( 638 | "See [`", 639 | stringify!($prim), 640 | "::fetch_xor`] for more information.", 641 | )] 642 | #[inline] 643 | pub fn fetch_xor( 644 | &self, 645 | val: $prim_int, 646 | order: Ordering, 647 | ) -> $prim_int { 648 | let val = swap_endian!($endian val); 649 | swap_endian!($endian self.0.fetch_xor(val, order)) 650 | } 651 | 652 | /// Consumes the atomic and returns the contained value. 653 | /// 654 | #[doc = concat!( 655 | "See [`", 656 | stringify!($prim), 657 | "::into_inner`] for more information.", 658 | )] 659 | #[inline] 660 | pub fn into_inner(self) -> $prim_int { 661 | swap_endian!($endian self.0.into_inner()) 662 | } 663 | 664 | /// Loads a value from the atomic integer. 665 | /// 666 | #[doc = concat!( 667 | "See [`", 668 | stringify!($prim), 669 | "::load`] for more information.", 670 | )] 671 | #[inline] 672 | pub fn load(&self, order: Ordering) -> $prim_int { 673 | swap_endian!($endian self.0.load(order)) 674 | } 675 | 676 | /// Stores a value into the atomic integer. 677 | /// 678 | #[doc = concat!( 679 | "See [`", 680 | stringify!($prim), 681 | "::store`] for more information.", 682 | )] 683 | #[inline] 684 | pub fn store(&self, val: $prim_int, order: Ordering) { 685 | self.0.store(swap_endian!($endian val), order); 686 | } 687 | 688 | /// Stores a value into the atomic integer, returning the previous 689 | /// value. 690 | /// 691 | #[doc = concat!( 692 | "See [`", 693 | stringify!($prim), 694 | "::swap`] for more information.", 695 | )] 696 | #[inline] 697 | pub fn swap(&self, val: $prim_int, order: Ordering) -> $prim_int { 698 | let val = swap_endian!($endian val); 699 | swap_endian!($endian self.0.swap(val, order)) 700 | } 701 | } 702 | 703 | impl core::fmt::Debug for $name { 704 | #[inline] 705 | fn fmt( 706 | &self, 707 | f: &mut core::fmt::Formatter<'_>, 708 | ) -> core::fmt::Result { 709 | swap_endian!($endian self.load(Ordering::Relaxed)).fmt(f) 710 | } 711 | } 712 | 713 | impl Default for $name { 714 | #[inline] 715 | fn default() -> Self { 716 | Self::new(<$prim_int>::default()) 717 | } 718 | } 719 | 720 | impl From<$prim_int> for $name { 721 | #[inline] 722 | fn from(value: $prim_int) -> Self { 723 | Self::new(value) 724 | } 725 | } 726 | } 727 | } 728 | 729 | #[cfg(any( 730 | target_has_atomic = "16", 731 | target_has_atomic = "32", 732 | target_has_atomic = "64", 733 | ))] 734 | macro_rules! define_atomics { 735 | ($( 736 | $le:ident $be:ident: 737 | $size_align:literal $prim:ty as $prim_int:ty 738 | ),* $(,)?) => { 739 | $( 740 | define_atomic!($le: little $size_align $prim as $prim_int); 741 | define_atomic!($be: big $size_align $prim as $prim_int); 742 | )* 743 | } 744 | } 745 | 746 | #[cfg(target_has_atomic = "16")] 747 | define_atomics! { 748 | AtomicI16_le AtomicI16_be: 2 AtomicI16 as i16, 749 | AtomicU16_le AtomicU16_be: 2 AtomicU16 as u16, 750 | } 751 | 752 | #[cfg(target_has_atomic = "32")] 753 | define_atomics! { 754 | AtomicI32_le AtomicI32_be: 4 AtomicI32 as i32, 755 | AtomicU32_le AtomicU32_be: 4 AtomicU32 as u32, 756 | } 757 | 758 | #[cfg(target_has_atomic = "64")] 759 | define_atomics! { 760 | AtomicI64_le AtomicI64_be: 8 AtomicI64 as i64, 761 | AtomicU64_le AtomicU64_be: 8 AtomicU64 as u64, 762 | } 763 | 764 | #[cfg(test)] 765 | mod tests { 766 | use core::mem::transmute; 767 | 768 | use super::*; 769 | 770 | #[test] 771 | fn signed_integers() { 772 | assert_size_align! { 773 | i16_be 2 2, 774 | i16_le 2 2, 775 | i32_be 4 4, 776 | i32_le 4 4, 777 | i64_be 8 8, 778 | i64_le 8 8, 779 | i128_be 16 16, 780 | i128_le 16 16, 781 | } 782 | 783 | unsafe { 784 | // i16 785 | assert_eq!( 786 | [0x02, 0x01], 787 | transmute::<_, [u8; 2]>(i16_le::from_native(0x0102)), 788 | ); 789 | assert_eq!( 790 | [0x01, 0x02], 791 | transmute::<_, [u8; 2]>(i16_be::from_native(0x0102)), 792 | ); 793 | 794 | // i32 795 | assert_eq!( 796 | [0x04, 0x03, 0x02, 0x01], 797 | transmute::<_, [u8; 4]>(i32_le::from_native(0x01020304)), 798 | ); 799 | assert_eq!( 800 | [0x01, 0x02, 0x03, 0x04], 801 | transmute::<_, [u8; 4]>(i32_be::from_native(0x01020304)), 802 | ); 803 | 804 | // i64 805 | assert_eq!( 806 | [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01], 807 | transmute::<_, [u8; 8]>(i64_le::from_native( 808 | 0x0102030405060708 809 | )), 810 | ); 811 | assert_eq!( 812 | [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08], 813 | transmute::<_, [u8; 8]>(i64_be::from_native( 814 | 0x0102030405060708 815 | )), 816 | ); 817 | 818 | // i128 819 | assert_eq!( 820 | [ 821 | 0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 822 | 0x06, 0x05, 0x04, 0x03, 0x02, 0x01 823 | ], 824 | transmute::<_, [u8; 16]>(i128_le::from_native( 825 | 0x0102030405060708090a0b0c0d0e0f10 826 | )), 827 | ); 828 | assert_eq!( 829 | [ 830 | 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 831 | 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 832 | ], 833 | transmute::<_, [u8; 16]>(i128_be::from_native( 834 | 0x0102030405060708090a0b0c0d0e0f10 835 | )), 836 | ); 837 | } 838 | } 839 | 840 | #[test] 841 | fn unsigned_integers() { 842 | assert_size_align! { 843 | u16_be 2 2, 844 | u16_le 2 2, 845 | u32_be 4 4, 846 | u32_le 4 4, 847 | u64_be 8 8, 848 | u64_le 8 8, 849 | u128_be 16 16, 850 | u128_le 16 16, 851 | } 852 | 853 | unsafe { 854 | // u16 855 | assert_eq!( 856 | [0x02, 0x01], 857 | transmute::<_, [u8; 2]>(u16_le::from_native(0x0102)), 858 | ); 859 | assert_eq!( 860 | [0x01, 0x02], 861 | transmute::<_, [u8; 2]>(u16_be::from_native(0x0102)), 862 | ); 863 | 864 | // u32 865 | assert_eq!( 866 | [0x04, 0x03, 0x02, 0x01], 867 | transmute::<_, [u8; 4]>(u32_le::from_native(0x01020304)), 868 | ); 869 | assert_eq!( 870 | [0x01, 0x02, 0x03, 0x04], 871 | transmute::<_, [u8; 4]>(u32_be::from_native(0x01020304)), 872 | ); 873 | 874 | // u64 875 | assert_eq!( 876 | [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01], 877 | transmute::<_, [u8; 8]>(u64_le::from_native( 878 | 0x0102030405060708 879 | )), 880 | ); 881 | assert_eq!( 882 | [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08], 883 | transmute::<_, [u8; 8]>(u64_be::from_native( 884 | 0x0102030405060708 885 | )), 886 | ); 887 | 888 | // u128 889 | assert_eq!( 890 | [ 891 | 0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 892 | 0x06, 0x05, 0x04, 0x03, 0x02, 0x01 893 | ], 894 | transmute::<_, [u8; 16]>(u128_le::from_native( 895 | 0x0102030405060708090a0b0c0d0e0f10 896 | )), 897 | ); 898 | assert_eq!( 899 | [ 900 | 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 901 | 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 902 | ], 903 | transmute::<_, [u8; 16]>(u128_be::from_native( 904 | 0x0102030405060708090a0b0c0d0e0f10 905 | )), 906 | ); 907 | } 908 | } 909 | 910 | #[test] 911 | fn floats() { 912 | assert_size_align! { 913 | f32_be 4 4, 914 | f32_le 4 4, 915 | f64_be 8 8, 916 | f64_le 8 8, 917 | } 918 | 919 | unsafe { 920 | // f32 921 | assert_eq!( 922 | [0xdb, 0x0f, 0x49, 0x40], 923 | transmute::<_, [u8; 4]>(f32_le::from_native( 924 | core::f32::consts::PI 925 | )), 926 | ); 927 | assert_eq!( 928 | [0x40, 0x49, 0x0f, 0xdb], 929 | transmute::<_, [u8; 4]>(f32_be::from_native( 930 | core::f32::consts::PI 931 | )), 932 | ); 933 | 934 | // f64 935 | assert_eq!( 936 | [0x18, 0x2d, 0x44, 0x54, 0xfb, 0x21, 0x09, 0x40], 937 | transmute::<_, [u8; 8]>(f64_le::from_native( 938 | core::f64::consts::PI 939 | )), 940 | ); 941 | assert_eq!( 942 | [0x40, 0x09, 0x21, 0xfb, 0x54, 0x44, 0x2d, 0x18], 943 | transmute::<_, [u8; 8]>(f64_be::from_native( 944 | core::f64::consts::PI 945 | )), 946 | ); 947 | 948 | // char 949 | assert_eq!( 950 | [0x89, 0xf3, 0x01, 0x00], 951 | transmute::<_, [u8; 4]>(char_le::from_native('🎉')), 952 | ); 953 | assert_eq!( 954 | [0x00, 0x01, 0xf3, 0x89], 955 | transmute::<_, [u8; 4]>(char_be::from_native('🎉')), 956 | ); 957 | } 958 | } 959 | 960 | #[test] 961 | fn signed_non_zero() { 962 | assert_size_align! { 963 | NonZeroI16_le 2 2, 964 | NonZeroI16_be 2 2, 965 | NonZeroI32_le 4 4, 966 | NonZeroI32_be 4 4, 967 | NonZeroI64_le 8 8, 968 | NonZeroI64_be 8 8, 969 | NonZeroI128_le 16 16, 970 | NonZeroI128_be 16 16, 971 | } 972 | 973 | unsafe { 974 | // NonZeroI16 975 | assert_eq!( 976 | [0x02, 0x01], 977 | transmute::<_, [u8; 2]>(NonZeroI16_le::new_unchecked(0x0102)), 978 | ); 979 | assert_eq!( 980 | [0x01, 0x02], 981 | transmute::<_, [u8; 2]>(NonZeroI16_be::new_unchecked(0x0102)), 982 | ); 983 | 984 | // NonZeroI32 985 | assert_eq!( 986 | [0x04, 0x03, 0x02, 0x01], 987 | transmute::<_, [u8; 4]>(NonZeroI32_le::new_unchecked( 988 | 0x01020304 989 | )), 990 | ); 991 | assert_eq!( 992 | [0x01, 0x02, 0x03, 0x04], 993 | transmute::<_, [u8; 4]>(NonZeroI32_be::new_unchecked( 994 | 0x01020304 995 | )), 996 | ); 997 | 998 | // NonZeroI64 999 | assert_eq!( 1000 | [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01], 1001 | transmute::<_, [u8; 8]>(NonZeroI64_le::new_unchecked( 1002 | 0x0102030405060708 1003 | )), 1004 | ); 1005 | assert_eq!( 1006 | [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08], 1007 | transmute::<_, [u8; 8]>(NonZeroI64_be::new_unchecked( 1008 | 0x0102030405060708 1009 | )), 1010 | ); 1011 | 1012 | // NonZeroI128 1013 | assert_eq!( 1014 | [ 1015 | 0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 1016 | 0x06, 0x05, 0x04, 0x03, 0x02, 0x01 1017 | ], 1018 | transmute::<_, [u8; 16]>(NonZeroI128_le::new_unchecked( 1019 | 0x0102030405060708090a0b0c0d0e0f10 1020 | )), 1021 | ); 1022 | assert_eq!( 1023 | [ 1024 | 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 1025 | 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 1026 | ], 1027 | transmute::<_, [u8; 16]>(NonZeroI128_be::new_unchecked( 1028 | 0x0102030405060708090a0b0c0d0e0f10 1029 | )), 1030 | ); 1031 | } 1032 | } 1033 | 1034 | #[test] 1035 | fn unsigned_non_zero() { 1036 | assert_size_align! { 1037 | NonZeroU16_le 2 2, 1038 | NonZeroU16_be 2 2, 1039 | NonZeroU32_le 4 4, 1040 | NonZeroU32_be 4 4, 1041 | NonZeroU64_le 8 8, 1042 | NonZeroU64_be 8 8, 1043 | NonZeroU128_le 16 16, 1044 | NonZeroU128_be 16 16, 1045 | } 1046 | 1047 | unsafe { 1048 | // NonZeroU16 1049 | assert_eq!( 1050 | [0x02, 0x01], 1051 | transmute::<_, [u8; 2]>(NonZeroU16_le::new_unchecked(0x0102)), 1052 | ); 1053 | assert_eq!( 1054 | [0x01, 0x02], 1055 | transmute::<_, [u8; 2]>(NonZeroU16_be::new_unchecked(0x0102)), 1056 | ); 1057 | 1058 | // NonZeroU32 1059 | assert_eq!( 1060 | [0x04, 0x03, 0x02, 0x01], 1061 | transmute::<_, [u8; 4]>(NonZeroU32_le::new_unchecked( 1062 | 0x01020304 1063 | )), 1064 | ); 1065 | assert_eq!( 1066 | [0x01, 0x02, 0x03, 0x04], 1067 | transmute::<_, [u8; 4]>(NonZeroU32_be::new_unchecked( 1068 | 0x01020304 1069 | )), 1070 | ); 1071 | 1072 | // NonZeroU64 1073 | assert_eq!( 1074 | [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01], 1075 | transmute::<_, [u8; 8]>(NonZeroU64_le::new_unchecked( 1076 | 0x0102030405060708 1077 | )), 1078 | ); 1079 | assert_eq!( 1080 | [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08], 1081 | transmute::<_, [u8; 8]>(NonZeroU64_be::new_unchecked( 1082 | 0x0102030405060708 1083 | )), 1084 | ); 1085 | 1086 | // NonZeroU128 1087 | assert_eq!( 1088 | [ 1089 | 0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 1090 | 0x06, 0x05, 0x04, 0x03, 0x02, 0x01 1091 | ], 1092 | transmute::<_, [u8; 16]>(NonZeroU128_le::new_unchecked( 1093 | 0x0102030405060708090a0b0c0d0e0f10 1094 | )), 1095 | ); 1096 | assert_eq!( 1097 | [ 1098 | 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 1099 | 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 1100 | ], 1101 | transmute::<_, [u8; 16]>(NonZeroU128_be::new_unchecked( 1102 | 0x0102030405060708090a0b0c0d0e0f10 1103 | )), 1104 | ); 1105 | } 1106 | } 1107 | 1108 | #[cfg(feature = "bytecheck")] 1109 | #[test] 1110 | fn unaligned_non_zero() { 1111 | use bytecheck::{ 1112 | rancor::{Failure, Strategy}, 1113 | CheckBytes, 1114 | }; 1115 | use unaligned::{u32_ule, NonZeroU32_ule}; 1116 | 1117 | let zero = u32_ule::from_native(0); 1118 | let ptr = (&zero as *const u32_ule).cast::(); 1119 | let mut unit = (); 1120 | let context = Strategy::<_, Failure>::wrap(&mut unit); 1121 | unsafe { 1122 | >>::check_bytes( 1123 | ptr, context, 1124 | ) 1125 | .unwrap_err(); 1126 | } 1127 | } 1128 | 1129 | #[cfg(target_has_atomic = "16")] 1130 | #[test] 1131 | fn atomics_16() { 1132 | assert_size_align! { 1133 | AtomicI16_le 2 2, 1134 | AtomicI16_be 2 2, 1135 | AtomicU16_le 2 2, 1136 | AtomicU16_be 2 2, 1137 | } 1138 | 1139 | unsafe { 1140 | // AtomicI16 1141 | assert_eq!( 1142 | [0x02, 0x01], 1143 | transmute::<_, [u8; 2]>(AtomicI16_le::new(0x0102)), 1144 | ); 1145 | assert_eq!( 1146 | [0x01, 0x02], 1147 | transmute::<_, [u8; 2]>(AtomicI16_be::new(0x0102)), 1148 | ); 1149 | 1150 | // AtomicU16 1151 | assert_eq!( 1152 | [0x02, 0x01], 1153 | transmute::<_, [u8; 2]>(AtomicU16_le::new(0x0102)), 1154 | ); 1155 | assert_eq!( 1156 | [0x01, 0x02], 1157 | transmute::<_, [u8; 2]>(AtomicU16_be::new(0x0102)), 1158 | ); 1159 | } 1160 | } 1161 | 1162 | #[cfg(target_has_atomic = "32")] 1163 | #[test] 1164 | fn atomics_32() { 1165 | assert_size_align! { 1166 | AtomicI32_le 4 4, 1167 | AtomicI32_be 4 4, 1168 | AtomicU32_le 4 4, 1169 | AtomicU32_be 4 4, 1170 | } 1171 | 1172 | unsafe { 1173 | // AtomicI32 1174 | assert_eq!( 1175 | [0x04, 0x03, 0x02, 0x01], 1176 | transmute::<_, [u8; 4]>(AtomicI32_le::new(0x01020304)), 1177 | ); 1178 | assert_eq!( 1179 | [0x01, 0x02, 0x03, 0x04], 1180 | transmute::<_, [u8; 4]>(AtomicI32_be::new(0x01020304)), 1181 | ); 1182 | 1183 | // AtomicU32 1184 | assert_eq!( 1185 | [0x04, 0x03, 0x02, 0x01], 1186 | transmute::<_, [u8; 4]>(AtomicU32_le::new(0x01020304)), 1187 | ); 1188 | assert_eq!( 1189 | [0x01, 0x02, 0x03, 0x04], 1190 | transmute::<_, [u8; 4]>(AtomicU32_be::new(0x01020304)), 1191 | ); 1192 | } 1193 | } 1194 | 1195 | #[cfg(target_has_atomic = "64")] 1196 | #[test] 1197 | fn atomics_64() { 1198 | assert_size_align! { 1199 | AtomicI64_le 8 8, 1200 | AtomicI64_be 8 8, 1201 | AtomicU64_le 8 8, 1202 | AtomicU64_be 8 8, 1203 | } 1204 | 1205 | unsafe { 1206 | // AtomicI64 1207 | assert_eq!( 1208 | [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01], 1209 | transmute::<_, [u8; 8]>(AtomicI64_le::new(0x0102030405060708)), 1210 | ); 1211 | assert_eq!( 1212 | [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08], 1213 | transmute::<_, [u8; 8]>(AtomicI64_be::new(0x0102030405060708)), 1214 | ); 1215 | 1216 | // AtomicU64 1217 | assert_eq!( 1218 | [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01], 1219 | transmute::<_, [u8; 8]>(AtomicU64_le::new(0x0102030405060708)), 1220 | ); 1221 | assert_eq!( 1222 | [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08], 1223 | transmute::<_, [u8; 8]>(AtomicU64_be::new(0x0102030405060708)), 1224 | ); 1225 | } 1226 | } 1227 | } 1228 | --------------------------------------------------------------------------------