rend/
lib.rs

1//! # rend
2//!
3//! rend is a library that provides endian-aware primitives for Rust.
4//!
5//! It's similar in design to [`simple_endian`](https://crates.io/crates/simple_endian), but has
6//! support for more builtin types such as atomics and nonzero integers. It also has support for
7//! const functions since it does not rely on traits.
8//!
9//! rend does not provide endian-aware types for types that are inherently endian-agnostic, such as
10//! `bool` and `u8`. It does not provide endian-aware types for types that have an
11//! architecture-dependent size, such as `isize` and `usize`. It's also not extensible to custom
12//! types.
13//!
14//! rend is intended to be used to build portable types that can be shared between different
15//! architectures, especially with zero-copy deserialization.
16//!
17//! ## Features
18//!
19//! - `std`: Enables standard library support (enabled by default)
20//! - `validation`: Enables validation support through `bytecheck`
21//!
22//! ## Example:
23//! ```
24//! use rend::*;
25//!
26//! let little_int = i32_le::new(0x12345678);
27//! // Internal representation is little-endian
28//! assert_eq!(
29//!     [0x78, 0x56, 0x34, 0x12],
30//!     unsafe { ::core::mem::transmute::<_, [u8; 4]>(little_int) }
31//! );
32//!
33//! // Can also be made with `.into()`
34//! let little_int: i32_le = 0x12345678.into();
35//! // Still formats correctly
36//! assert_eq!("305419896", format!("{}", little_int));
37//! assert_eq!("0x12345678", format!("0x{:x}", little_int));
38//!
39//! let big_int = i32_be::new(0x12345678);
40//! // Internal representation is big-endian
41//! assert_eq!(
42//!     [0x12, 0x34, 0x56, 0x78],
43//!     unsafe { ::core::mem::transmute::<_, [u8; 4]>(big_int) }
44//! );
45//!
46//! // Can also be made with `.into()`
47//! let big_int: i32_be = 0x12345678.into();
48//! // Still formats correctly
49//! assert_eq!("305419896", format!("{}", big_int));
50//! assert_eq!("0x12345678", format!("0x{:x}", big_int));
51//! ```
52
53#![cfg_attr(not(feature = "std"), no_std)]
54#![deny(
55    missing_docs,
56    rustdoc::missing_crate_level_docs,
57    rust_2018_compatibility,
58    rust_2018_idioms,
59    future_incompatible,
60    nonstandard_style,
61    unused,
62    clippy::all
63)]
64
65#[macro_use]
66mod impl_struct;
67#[macro_use]
68mod impl_traits;
69#[cfg(feature = "validation")]
70#[macro_use]
71mod impl_validation;
72#[cfg(feature = "bytemuck")]
73mod impl_bytemuck;
74
75#[cfg(feature = "validation")]
76use bytecheck::{CharCheckError, CheckBytes, NonZeroCheckError};
77#[cfg(feature = "validation")]
78use core::convert::Infallible;
79#[cfg(has_atomics)]
80use core::sync::atomic::{AtomicI16, AtomicI32, AtomicU16, AtomicU32, Ordering};
81#[cfg(has_atomics_64)]
82use core::sync::atomic::{AtomicI64, AtomicU64};
83use core::{
84    hash::{Hash, Hasher},
85    num::{
86        NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroU128, NonZeroU16, NonZeroU32,
87        NonZeroU64,
88    },
89};
90
91/// A type that has an associated cross-endian storage type.
92pub unsafe trait Primitive {
93    /// An endian-agnostic type that can represent the primitve in both little- and big-endian
94    /// forms.
95    type Storage;
96}
97
98/// A wrapper for native-endian types.
99///
100/// This is mostly useful for `const` conversions to big- and little-endian types in contexts where
101/// type inference is required. Because it's native-endian, the inner value is publicly exposed.
102#[derive(Clone, Copy)]
103#[cfg_attr(feature = "bytemuck", derive(bytemuck::Zeroable, bytemuck::Pod))]
104#[repr(transparent)]
105pub struct NativeEndian<T> {
106    /// The value of the type
107    pub value: T,
108}
109
110/// A wrapper for big-endian types.
111#[derive(Clone, Copy)]
112#[repr(transparent)]
113pub struct LittleEndian<T: Primitive> {
114    value: T::Storage,
115}
116
117/// A wrapper for little-endian types.
118#[derive(Clone, Copy)]
119#[repr(transparent)]
120pub struct BigEndian<T: Primitive> {
121    value: T::Storage,
122}
123
124macro_rules! swap_endian {
125    (@NativeEndian $expr:expr) => {{
126        $expr
127    }};
128    (@LittleEndian $expr:expr) => {{
129        #[cfg(target_endian = "little")]
130        {
131            $expr
132        }
133        #[cfg(target_endian = "big")]
134        {
135            $expr.swap_bytes()
136        }
137    }};
138    (@BigEndian $expr:expr) => {{
139        #[cfg(target_endian = "little")]
140        {
141            $expr.swap_bytes()
142        }
143        #[cfg(target_endian = "big")]
144        {
145            $expr
146        }
147    }};
148}
149
150macro_rules! swap_bytes {
151    (@signed_int $endian:ident<$ne:ty> $value:expr) => {
152        swap_endian!(@$endian $value)
153    };
154    (@unsigned_int $endian:ident<$ne:ty> $value:expr) => {
155        swap_endian!(@$endian $value)
156    };
157    (@float $endian:ident<$ne:ty> $value:expr) => {
158        <$ne>::from_bits(swap_endian!(@$endian $value.to_bits()))
159    };
160    (@char $endian:ident<$ne:ty> $value:expr) => {
161        swap_endian!(@$endian $value)
162    };
163    (@nonzero $endian:ident<$ne:ty> $value:expr) => {
164        unsafe { <$ne>::new_unchecked(swap_endian!(@$endian $value.get())) }
165    };
166    (@atomic $endian:ident<$ne:ty> $value:expr) => {
167        swap_endian!(@$endian $value)
168    };
169}
170
171macro_rules! from_native {
172    (@char NativeEndian<$ne:ty> $value:expr) => {
173        $value
174    };
175    (@char $endian:ident<$ne:ty> $value:expr) => {
176        $value as u32
177    };
178    (@$class:ident $endian:ident<$ne:ty> $value:expr) => {
179        $value
180    };
181}
182
183macro_rules! to_native {
184    (@char NativeEndian<$ne:ty> $value:expr) => {
185        $value
186    };
187    (@char $endian:ident<$ne:ty> $value:expr) => {
188        unsafe { char::from_u32_unchecked($value) }
189    };
190    (@$class:ident $endian:ident<$ne:ty> $value:expr) => {
191        $value
192    };
193}
194
195macro_rules! impl_endian {
196    (
197        @$class:ident $native:ty $(= $prim:ty)?,
198        $ne:ident = $ne_doc:literal,
199        $le:ident = $le_doc:literal,
200        $be:ident = $be_doc:literal
201    ) => {
202        impl_endian!(@$class $ne_doc NativeEndian<$native> as $ne $(= $prim)?);
203        impl_endian!(@$class $le_doc LittleEndian<$native> as $le $(= $prim)?);
204        impl_endian!(@$class $be_doc BigEndian<$native> as $be $(= $prim)?);
205    };
206    (@$class:ident $doc:literal $endian:ident<$ne:ty> as $alias:ident $(= $prim:ty)?) => {
207        impl_struct!(@$class $endian<$ne> $(= $prim)?);
208        #[cfg(feature = "validation")]
209        impl_validation!(@$class $endian<$ne> $(= $prim)?);
210        #[doc = "Alias for "]
211        #[doc = $doc]
212        #[doc = "."]
213        #[allow(non_camel_case_types)]
214        pub type $alias = $endian<$ne>;
215    };
216}
217
218macro_rules! impl_primitive {
219    ($($ty:ty),+ $(,)?) => {
220        $(
221            unsafe impl Primitive for $ty {
222                type Storage = $ty;
223            }
224        )+
225    };
226}
227
228impl_primitive!(
229    i16,
230    i32,
231    i64,
232    i128,
233    u16,
234    u32,
235    u64,
236    u128,
237    f32,
238    f64,
239    NonZeroI16,
240    NonZeroI32,
241    NonZeroI64,
242    NonZeroI128,
243    NonZeroU16,
244    NonZeroU32,
245    NonZeroU64,
246    NonZeroU128,
247);
248
249#[cfg(has_atomics)]
250impl_primitive!(AtomicI16, AtomicI32, AtomicU16, AtomicU32);
251
252#[cfg(has_atomics_64)]
253impl_primitive!(AtomicI64, AtomicU64);
254
255unsafe impl Primitive for char {
256    type Storage = u32;
257}
258
259impl_endian!(
260    @signed_int i16,
261    i16_ne = "`NativeEndian<i16>`",
262    i16_le = "`LittleEndian<i16>`",
263    i16_be = "`BigEndian<i16>`"
264);
265impl_endian!(
266    @signed_int i32,
267    i32_ne = "`NativeEndian<i32>`",
268    i32_le = "`LittleEndian<i32>`",
269    i32_be = "`BigEndian<i32>`"
270);
271impl_endian!(
272    @signed_int i64,
273    i64_ne = "`NativeEndian<i64>`",
274    i64_le = "`LittleEndian<i64>`",
275    i64_be = "`BigEndian<i64>`"
276);
277impl_endian!(
278    @signed_int i128,
279    i128_ne = "`NativeEndian<i128>`",
280    i128_le = "`LittleEndian<i128>`",
281    i128_be = "`BigEndian<i128>`"
282);
283impl_endian!(
284    @unsigned_int u16,
285    u16_ne = "`NativeEndian<u16>`",
286    u16_le = "`LittleEndian<u16>`",
287    u16_be = "`BigEndian<u16>`"
288);
289impl_endian!(
290    @unsigned_int u32,
291    u32_ne = "`NativeEndian<u32>`",
292    u32_le = "`LittleEndian<u32>`",
293    u32_be = "`BigEndian<u32>`"
294);
295impl_endian!(
296    @unsigned_int u64,
297    u64_ne = "`NativeEndian<u64>`",
298    u64_le = "`LittleEndian<u64>`",
299    u64_be = "`BigEndian<u64>`"
300);
301impl_endian!(
302    @unsigned_int u128,
303    u128_ne = "`NativeEndian<u128>`",
304    u128_le = "`LittleEndian<u128>`",
305    u128_be = "`BigEndian<u128>`"
306);
307
308impl_endian!(
309    @float f32,
310    f32_ne = "`NativeEndian<f32>`",
311    f32_le = "`LittleEndian<f32>`",
312    f32_be = "`BigEndian<f32>`"
313);
314impl_endian!(
315    @float f64,
316    f64_ne = "`NativeEndian<f64>`",
317    f64_le = "`LittleEndian<f64>`",
318    f64_be = "`BigEndian<f64>`"
319);
320
321impl_endian!(
322    @char char,
323    char_ne = "`NativeEndian<char>`",
324    char_le = "`LittleEndian<char>`",
325    char_be = "`BigEndian<char>`"
326);
327
328impl_endian!(
329    @nonzero NonZeroI16 = i16,
330    NonZeroI16_ne = "`NativeEndian<NonZeroI16>`",
331    NonZeroI16_le = "`LittleEndian<NonZeroI16>`",
332    NonZeroI16_be = "`BigEndian<NonZeroI16>`"
333);
334impl_endian!(
335    @nonzero NonZeroI32 = i32,
336    NonZeroI32_ne = "`NativeEndian<NonZeroI32>`",
337    NonZeroI32_le = "`LittleEndian<NonZeroI32>`",
338    NonZeroI32_be = "`BigEndian<NonZeroI32>`"
339);
340impl_endian!(
341    @nonzero NonZeroI64 = i64,
342    NonZeroI64_ne = "`NativeEndian<NonZeroI64>`",
343    NonZeroI64_le = "`LittleEndian<NonZeroI64>`",
344    NonZeroI64_be = "`BigEndian<NonZeroI64>`"
345);
346impl_endian!(
347    @nonzero NonZeroI128 = i128,
348    NonZeroI128_ne = "`NativeEndian<NonZeroI128>`",
349    NonZeroI128_le = "`LittleEndian<NonZeroI128>`",
350    NonZeroI128_be = "`BigEndian<NonZeroI128>`"
351);
352impl_endian!(
353    @nonzero NonZeroU16 = u16,
354    NonZeroU16_ne = "`NativeEndian<NonZeroU16>`",
355    NonZeroU16_le = "`LittleEndian<NonZeroU16>`",
356    NonZeroU16_be = "`BigEndian<NonZeroU16>`"
357);
358impl_endian!(
359    @nonzero NonZeroU32 = u32,
360    NonZeroU32_ne = "`NativeEndian<NonZeroU32>`",
361    NonZeroU32_le = "`LittleEndian<NonZeroU32>`",
362    NonZeroU32_be = "`BigEndian<NonZeroU32>`"
363);
364impl_endian!(
365    @nonzero NonZeroU64 = u64,
366    NonZeroU64_ne = "`NativeEndian<NonZeroU64>`",
367    NonZeroU64_le = "`LittleEndian<NonZeroU64>`",
368    NonZeroU64_be = "`BigEndian<NonZeroU64>`"
369);
370impl_endian!(
371    @nonzero NonZeroU128 = u128,
372    NonZeroU128_ne = "`NativeEndian<NonZeroU128>`",
373    NonZeroU128_le = "`LittleEndian<NonZeroU128>`",
374    NonZeroU128_be = "`BigEndian<NonZeroU128>`"
375);
376
377#[cfg(has_atomics)]
378impl_endian!(
379    @atomic AtomicI16 = i16,
380    AtomicI16_ne = "`NativeEndian<AtomicI16>`",
381    AtomicI16_le = "`LittleEndian<AtomicI16>`",
382    AtomicI16_be = "`BigEndian<AtomicI16>`"
383);
384#[cfg(has_atomics)]
385impl_endian!(
386    @atomic AtomicI32 = i32,
387    AtomicI32_ne = "`NativeEndian<AtomicI32>`",
388    AtomicI32_le = "`LittleEndian<AtomicI32>`",
389    AtomicI32_be = "`BigEndian<AtomicI32>`"
390);
391#[cfg(has_atomics_64)]
392impl_endian!(
393    @atomic AtomicI64 = i64,
394    AtomicI64_ne = "`NativeEndian<AtomicI64>`",
395    AtomicI64_le = "`LittleEndian<AtomicI64>`",
396    AtomicI64_be = "`BigEndian<AtomicI64>`"
397);
398#[cfg(has_atomics)]
399impl_endian!(
400    @atomic AtomicU16 = u16,
401    AtomicU16_ne = "`NativeEndian<AtomicU16>`",
402    AtomicU16_le = "`LittleEndian<AtomicU16>`",
403    AtomicU16_be = "`BigEndian<AtomicU16>`"
404);
405#[cfg(has_atomics)]
406impl_endian!(
407    @atomic AtomicU32 = u32,
408    AtomicU32_ne = "`NativeEndian<AtomicU32>`",
409    AtomicU32_le = "`LittleEndian<AtomicU32>`",
410    AtomicU32_be = "`BigEndian<AtomicU32>`"
411);
412#[cfg(has_atomics_64)]
413impl_endian!(
414    @atomic AtomicU64 = u64,
415    AtomicU64_ne = "`NativeEndian<AtomicU64>`",
416    AtomicU64_le = "`LittleEndian<AtomicU64>`",
417    AtomicU64_be = "`BigEndian<AtomicU64>`"
418);
419
420#[cfg(test)]
421mod tests {
422    use crate::*;
423    use core::mem;
424
425    #[test]
426    fn endian_representation() {
427        unsafe {
428            // i16
429            assert_eq!(
430                [0x01, 0x02],
431                mem::transmute::<_, [u8; 2]>(i16_be::new(0x0102))
432            );
433            assert_eq!(
434                [0x02, 0x01],
435                mem::transmute::<_, [u8; 2]>(i16_le::new(0x0102))
436            );
437
438            // i32
439            assert_eq!(
440                [0x01, 0x02, 0x03, 0x04],
441                mem::transmute::<_, [u8; 4]>(i32_be::new(0x01020304))
442            );
443            assert_eq!(
444                [0x04, 0x03, 0x02, 0x01],
445                mem::transmute::<_, [u8; 4]>(i32_le::new(0x01020304))
446            );
447
448            // i64
449            assert_eq!(
450                [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08],
451                mem::transmute::<_, [u8; 8]>(i64_be::new(0x0102030405060708))
452            );
453            assert_eq!(
454                [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01],
455                mem::transmute::<_, [u8; 8]>(i64_le::new(0x0102030405060708))
456            );
457
458            // i128
459            assert_eq!(
460                [
461                    0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
462                    0x0e, 0x0f, 0x10
463                ],
464                mem::transmute::<_, [u8; 16]>(i128_be::new(0x0102030405060708090a0b0c0d0e0f10))
465            );
466            assert_eq!(
467                [
468                    0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04,
469                    0x03, 0x02, 0x01
470                ],
471                mem::transmute::<_, [u8; 16]>(i128_le::new(0x0102030405060708090a0b0c0d0e0f10))
472            );
473
474            // u16
475            assert_eq!(
476                [0x01, 0x02],
477                mem::transmute::<_, [u8; 2]>(u16_be::new(0x0102))
478            );
479            assert_eq!(
480                [0x02, 0x01],
481                mem::transmute::<_, [u8; 2]>(u16_le::new(0x0102))
482            );
483
484            // u32
485            assert_eq!(
486                [0x01, 0x02, 0x03, 0x04],
487                mem::transmute::<_, [u8; 4]>(u32_be::new(0x01020304))
488            );
489            assert_eq!(
490                [0x04, 0x03, 0x02, 0x01],
491                mem::transmute::<_, [u8; 4]>(u32_le::new(0x01020304))
492            );
493
494            // u64
495            assert_eq!(
496                [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08],
497                mem::transmute::<_, [u8; 8]>(u64_be::new(0x0102030405060708))
498            );
499            assert_eq!(
500                [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01],
501                mem::transmute::<_, [u8; 8]>(u64_le::new(0x0102030405060708))
502            );
503
504            // u128
505            assert_eq!(
506                [
507                    0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
508                    0x0e, 0x0f, 0x10
509                ],
510                mem::transmute::<_, [u8; 16]>(u128_be::new(0x0102030405060708090a0b0c0d0e0f10))
511            );
512            assert_eq!(
513                [
514                    0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04,
515                    0x03, 0x02, 0x01
516                ],
517                mem::transmute::<_, [u8; 16]>(u128_le::new(0x0102030405060708090a0b0c0d0e0f10))
518            );
519
520            // f32
521            assert_eq!(
522                [0x40, 0x49, 0x0f, 0xdb],
523                mem::transmute::<_, [u8; 4]>(f32_be::new(core::f32::consts::PI))
524            );
525            assert_eq!(
526                [0xdb, 0x0f, 0x49, 0x40],
527                mem::transmute::<_, [u8; 4]>(f32_le::new(core::f32::consts::PI))
528            );
529
530            // f64
531            assert_eq!(
532                [0x40, 0x09, 0x21, 0xfb, 0x54, 0x44, 0x2d, 0x18],
533                mem::transmute::<_, [u8; 8]>(f64_be::new(core::f64::consts::PI))
534            );
535            assert_eq!(
536                [0x18, 0x2d, 0x44, 0x54, 0xfb, 0x21, 0x09, 0x40],
537                mem::transmute::<_, [u8; 8]>(f64_le::new(core::f64::consts::PI))
538            );
539
540            // char
541            assert_eq!(
542                [0x00, 0x01, 0xf3, 0x89],
543                mem::transmute::<_, [u8; 4]>(char_be::new('🎉'))
544            );
545            assert_eq!(
546                [0x89, 0xf3, 0x01, 0x00],
547                mem::transmute::<_, [u8; 4]>(char_le::new('🎉'))
548            );
549
550            // AtomicU16
551            #[cfg(has_atomics)]
552            assert_eq!(
553                [0x01, 0x02],
554                mem::transmute::<_, [u8; 2]>(AtomicU16_be::new(0x0102))
555            );
556            #[cfg(has_atomics)]
557            assert_eq!(
558                [0x02, 0x01],
559                mem::transmute::<_, [u8; 2]>(AtomicU16_le::new(0x0102))
560            );
561
562            // AtomicU32
563            #[cfg(has_atomics)]
564            assert_eq!(
565                [0x01, 0x02, 0x03, 0x04],
566                mem::transmute::<_, [u8; 4]>(AtomicU32_be::new(0x01020304))
567            );
568            #[cfg(has_atomics)]
569            assert_eq!(
570                [0x04, 0x03, 0x02, 0x01],
571                mem::transmute::<_, [u8; 4]>(AtomicU32_le::new(0x01020304))
572            );
573
574            // AtomicU64
575            #[cfg(has_atomics_64)]
576            assert_eq!(
577                [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08],
578                mem::transmute::<_, [u8; 8]>(AtomicU64_be::new(0x0102030405060708))
579            );
580            #[cfg(has_atomics_64)]
581            assert_eq!(
582                [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01],
583                mem::transmute::<_, [u8; 8]>(AtomicU64_le::new(0x0102030405060708))
584            );
585        }
586    }
587}