rend/
unaligned.rs

1//! Cross-platform primitives with unaligned representations.
2
3use 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]
13macro_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
34macro_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
54macro_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
63define_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
70macro_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
90macro_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
99define_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
106macro_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
125macro_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
137define_unaligned_floats! {
138    f32_ule f32_ube: 4 f32 as u32,
139    f64_ule f64_ube: 8 f64 as u64,
140}
141
142macro_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
168define_unaligned_char!(char_ule: little);
169define_unaligned_char!(char_ube: big);
170
171macro_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<C> bytecheck::CheckBytes<C> for $name
180        where
181            C: bytecheck::rancor::Fallible + ?Sized,
182            C::Error: bytecheck::rancor::Trace,
183            $prim: bytecheck::CheckBytes<C>,
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
213macro_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
225define_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)]
237mod 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}