Skip to main content

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        #[cfg(feature = "bytecheck")]
167        // SAFETY: `check_bytes` only returns `Ok` if the code point contained
168        // within the endian-aware `char` represents a valid `char`.
169        unsafe impl<C> bytecheck::CheckBytes<C> for $name
170        where
171            C: bytecheck::rancor::Fallible + ?Sized,
172            C::Error: bytecheck::rancor::Trace,
173            char: bytecheck::CheckBytes<C>,
174        {
175            #[inline]
176            unsafe fn check_bytes(
177                value: *const Self,
178                context: &mut C,
179            ) -> Result<(), C::Error> {
180                use bytecheck::rancor::ResultExt as _;
181
182                // SAFETY: `value` points to a `Self`, which has the same size
183                // as a `u32` and which the caller has guaranteed is valid for
184                // reads. All bit patterns are valid for `32`.
185                let u = unsafe {
186                    value.cast::<u32>().read_unaligned()
187                };
188                let c = swap_endian!($endian u);
189                // SAFETY: `value` points to a valid endian-aware `char` type if
190                // `c` is a valid `char`.
191                unsafe {
192                    char::check_bytes(&c as *const u32 as *const char, context)
193                        .with_trace(|| $crate::context::ValueCheckContext {
194                            inner_name: "char",
195                            outer_name: core::stringify!($name),
196                        })
197                }
198            }
199        }
200    };
201}
202
203define_unaligned_char!(char_ule: little);
204define_unaligned_char!(char_ube: big);
205
206macro_rules! define_unaligned_nonzero {
207    ($name:ident: $endian:ident $size:literal $prim:ty as $prim_int:ty) => {
208        define_unaligned_newtype!($name: $endian $size $prim);
209        impl_nonzero!($name: $endian $prim as $prim_int);
210
211        #[cfg(feature = "bytecheck")]
212        // SAFETY: `check_bytes` only returns `Ok` if `value` points to a valid
213        // non-zero value, which is the only requirement for `NonZero` integers.
214        unsafe impl<C> bytecheck::CheckBytes<C> for $name
215        where
216            C: bytecheck::rancor::Fallible + ?Sized,
217            C::Error: bytecheck::rancor::Trace,
218            $prim: bytecheck::CheckBytes<C>,
219        {
220            #[inline]
221            unsafe fn check_bytes(
222                value: *const Self,
223                context: &mut C,
224            ) -> Result<(), C::Error> {
225                use bytecheck::rancor::ResultExt as _;
226
227                // SAFETY: `value` points to a `Self`, which has the same size
228                // as a `$prim_int` and which the caller has guaranteed is valid
229                // for reads. All bit patterns are valid for `$prim_int`.
230                let value = unsafe {
231                    value.cast::<$prim_int>().read_unaligned()
232                };
233                let ptr = (&value as *const $prim_int).cast::<$prim>();
234                // SAFETY: `ptr` points to a `$prim_int` and so is guaranteed to
235                // be aligned and point to enough bytes to represent a `$prim`.
236                unsafe {
237                    <$prim>::check_bytes(ptr, context)
238                        .with_trace(|| $crate::context::ValueCheckContext {
239                            inner_name: core::stringify!($prim),
240                            outer_name: core::stringify!($name),
241                        })
242                }
243            }
244        }
245    };
246}
247
248macro_rules! define_unaligned_nonzeros {
249    ($(
250        $le:ident $be:ident:
251        $size:literal $prim:ty as $prim_int:ty
252    ),* $(,)?) => {
253        $(
254            define_unaligned_nonzero!($le: little $size $prim as $prim_int);
255            define_unaligned_nonzero!($be: big $size $prim as $prim_int);
256        )*
257    }
258}
259
260define_unaligned_nonzeros! {
261    NonZeroI16_ule NonZeroI16_ube: 2 NonZeroI16 as i16,
262    NonZeroI32_ule NonZeroI32_ube: 2 NonZeroI32 as i32,
263    NonZeroI64_ule NonZeroI64_ube: 4 NonZeroI64 as i64,
264    NonZeroI128_ule NonZeroI128_ube: 4 NonZeroI128 as i128,
265    NonZeroU16_ule NonZeroU16_ube: 8 NonZeroU16 as u16,
266    NonZeroU32_ule NonZeroU32_ube: 8 NonZeroU32 as u32,
267    NonZeroU64_ule NonZeroU64_ube: 16 NonZeroU64 as u64,
268    NonZeroU128_ule NonZeroU128_ube: 16 NonZeroU128 as u128,
269}
270
271#[cfg(test)]
272mod tests {
273    use core::mem::transmute;
274
275    use super::*;
276
277    #[test]
278    fn signed_integers() {
279        assert_size_align! {
280            i16_ube 2 1,
281            i16_ule 2 1,
282            i32_ube 4 1,
283            i32_ule 4 1,
284            i64_ube 8 1,
285            i64_ule 8 1,
286            i128_ube 16 1,
287            i128_ule 16 1,
288        }
289
290        unsafe {
291            // i16
292            assert_eq!(
293                [0x02, 0x01],
294                transmute::<_, [u8; 2]>(i16_ule::from_native(0x0102)),
295            );
296            assert_eq!(
297                [0x01, 0x02],
298                transmute::<_, [u8; 2]>(i16_ube::from_native(0x0102)),
299            );
300
301            // i32
302            assert_eq!(
303                [0x04, 0x03, 0x02, 0x01],
304                transmute::<_, [u8; 4]>(i32_ule::from_native(0x01020304)),
305            );
306            assert_eq!(
307                [0x01, 0x02, 0x03, 0x04],
308                transmute::<_, [u8; 4]>(i32_ube::from_native(0x01020304)),
309            );
310
311            // i64
312            assert_eq!(
313                [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01],
314                transmute::<_, [u8; 8]>(i64_ule::from_native(
315                    0x0102030405060708
316                )),
317            );
318            assert_eq!(
319                [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08],
320                transmute::<_, [u8; 8]>(i64_ube::from_native(
321                    0x0102030405060708
322                )),
323            );
324
325            // i128
326            assert_eq!(
327                [
328                    0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07,
329                    0x06, 0x05, 0x04, 0x03, 0x02, 0x01
330                ],
331                transmute::<_, [u8; 16]>(i128_ule::from_native(
332                    0x0102030405060708090a0b0c0d0e0f10
333                )),
334            );
335            assert_eq!(
336                [
337                    0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
338                    0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10
339                ],
340                transmute::<_, [u8; 16]>(i128_ube::from_native(
341                    0x0102030405060708090a0b0c0d0e0f10
342                )),
343            );
344        }
345    }
346
347    #[test]
348    fn unsigned_integers() {
349        assert_size_align! {
350            u16_ube 2 1,
351            u16_ule 2 1,
352            u32_ube 4 1,
353            u32_ule 4 1,
354            u64_ube 8 1,
355            u64_ule 8 1,
356            u128_ube 16 1,
357            u128_ule 16 1,
358        }
359
360        unsafe {
361            // u16
362            assert_eq!(
363                [0x02, 0x01],
364                transmute::<_, [u8; 2]>(u16_ule::from_native(0x0102)),
365            );
366            assert_eq!(
367                [0x01, 0x02],
368                transmute::<_, [u8; 2]>(u16_ube::from_native(0x0102)),
369            );
370
371            // u32
372            assert_eq!(
373                [0x04, 0x03, 0x02, 0x01],
374                transmute::<_, [u8; 4]>(u32_ule::from_native(0x01020304)),
375            );
376            assert_eq!(
377                [0x01, 0x02, 0x03, 0x04],
378                transmute::<_, [u8; 4]>(u32_ube::from_native(0x01020304)),
379            );
380
381            // u64
382            assert_eq!(
383                [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01],
384                transmute::<_, [u8; 8]>(u64_ule::from_native(
385                    0x0102030405060708
386                )),
387            );
388            assert_eq!(
389                [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08],
390                transmute::<_, [u8; 8]>(u64_ube::from_native(
391                    0x0102030405060708
392                )),
393            );
394
395            // u128
396            assert_eq!(
397                [
398                    0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07,
399                    0x06, 0x05, 0x04, 0x03, 0x02, 0x01
400                ],
401                transmute::<_, [u8; 16]>(u128_ule::from_native(
402                    0x0102030405060708090a0b0c0d0e0f10
403                )),
404            );
405            assert_eq!(
406                [
407                    0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
408                    0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10
409                ],
410                transmute::<_, [u8; 16]>(u128_ube::from_native(
411                    0x0102030405060708090a0b0c0d0e0f10
412                )),
413            );
414        }
415    }
416
417    #[test]
418    fn floats() {
419        assert_size_align! {
420            f32_ube 4 1,
421            f32_ule 4 1,
422            f64_ube 8 1,
423            f64_ule 8 1,
424        }
425
426        unsafe {
427            // f32
428            assert_eq!(
429                [0xdb, 0x0f, 0x49, 0x40],
430                transmute::<_, [u8; 4]>(f32_ule::from_native(
431                    core::f32::consts::PI
432                )),
433            );
434            assert_eq!(
435                [0x40, 0x49, 0x0f, 0xdb],
436                transmute::<_, [u8; 4]>(f32_ube::from_native(
437                    core::f32::consts::PI
438                )),
439            );
440
441            // f64
442            assert_eq!(
443                [0x18, 0x2d, 0x44, 0x54, 0xfb, 0x21, 0x09, 0x40],
444                transmute::<_, [u8; 8]>(f64_ule::from_native(
445                    core::f64::consts::PI
446                )),
447            );
448            assert_eq!(
449                [0x40, 0x09, 0x21, 0xfb, 0x54, 0x44, 0x2d, 0x18],
450                transmute::<_, [u8; 8]>(f64_ube::from_native(
451                    core::f64::consts::PI
452                )),
453            );
454
455            // char
456            assert_eq!(
457                [0x89, 0xf3, 0x01, 0x00],
458                transmute::<_, [u8; 4]>(char_ule::from_native('🎉')),
459            );
460            assert_eq!(
461                [0x00, 0x01, 0xf3, 0x89],
462                transmute::<_, [u8; 4]>(char_ube::from_native('🎉')),
463            );
464        }
465    }
466
467    #[test]
468    fn signed_non_zero() {
469        assert_size_align! {
470            NonZeroI16_ule 2 1,
471            NonZeroI16_ube 2 1,
472            NonZeroI32_ule 4 1,
473            NonZeroI32_ube 4 1,
474            NonZeroI64_ule 8 1,
475            NonZeroI64_ube 8 1,
476            NonZeroI128_ule 16 1,
477            NonZeroI128_ube 16 1,
478        }
479
480        unsafe {
481            // NonZeroI16
482            assert_eq!(
483                [0x02, 0x01],
484                transmute::<_, [u8; 2]>(NonZeroI16_ule::new_unchecked(0x0102)),
485            );
486            assert_eq!(
487                [0x01, 0x02],
488                transmute::<_, [u8; 2]>(NonZeroI16_ube::new_unchecked(0x0102)),
489            );
490
491            // NonZeroI32
492            assert_eq!(
493                [0x04, 0x03, 0x02, 0x01],
494                transmute::<_, [u8; 4]>(NonZeroI32_ule::new_unchecked(
495                    0x01020304
496                )),
497            );
498            assert_eq!(
499                [0x01, 0x02, 0x03, 0x04],
500                transmute::<_, [u8; 4]>(NonZeroI32_ube::new_unchecked(
501                    0x01020304
502                )),
503            );
504
505            // NonZeroI64
506            assert_eq!(
507                [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01],
508                transmute::<_, [u8; 8]>(NonZeroI64_ule::new_unchecked(
509                    0x0102030405060708
510                )),
511            );
512            assert_eq!(
513                [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08],
514                transmute::<_, [u8; 8]>(NonZeroI64_ube::new_unchecked(
515                    0x0102030405060708
516                )),
517            );
518
519            // NonZeroI128
520            assert_eq!(
521                [
522                    0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07,
523                    0x06, 0x05, 0x04, 0x03, 0x02, 0x01
524                ],
525                transmute::<_, [u8; 16]>(NonZeroI128_ule::new_unchecked(
526                    0x0102030405060708090a0b0c0d0e0f10
527                )),
528            );
529            assert_eq!(
530                [
531                    0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
532                    0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10
533                ],
534                transmute::<_, [u8; 16]>(NonZeroI128_ube::new_unchecked(
535                    0x0102030405060708090a0b0c0d0e0f10
536                )),
537            );
538        }
539    }
540
541    #[test]
542    fn unsigned_non_zero() {
543        assert_size_align! {
544            NonZeroU16_ule 2 1,
545            NonZeroU16_ube 2 1,
546            NonZeroU32_ule 4 1,
547            NonZeroU32_ube 4 1,
548            NonZeroU64_ule 8 1,
549            NonZeroU64_ube 8 1,
550            NonZeroU128_ule 16 1,
551            NonZeroU128_ube 16 1,
552        }
553
554        unsafe {
555            // NonZeroU16
556            assert_eq!(
557                [0x02, 0x01],
558                transmute::<_, [u8; 2]>(NonZeroU16_ule::new_unchecked(0x0102)),
559            );
560            assert_eq!(
561                [0x01, 0x02],
562                transmute::<_, [u8; 2]>(NonZeroU16_ube::new_unchecked(0x0102)),
563            );
564
565            // NonZeroU32
566            assert_eq!(
567                [0x04, 0x03, 0x02, 0x01],
568                transmute::<_, [u8; 4]>(NonZeroU32_ule::new_unchecked(
569                    0x01020304
570                )),
571            );
572            assert_eq!(
573                [0x01, 0x02, 0x03, 0x04],
574                transmute::<_, [u8; 4]>(NonZeroU32_ube::new_unchecked(
575                    0x01020304
576                )),
577            );
578
579            // NonZeroU64
580            assert_eq!(
581                [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01],
582                transmute::<_, [u8; 8]>(NonZeroU64_ule::new_unchecked(
583                    0x0102030405060708
584                )),
585            );
586            assert_eq!(
587                [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08],
588                transmute::<_, [u8; 8]>(NonZeroU64_ube::new_unchecked(
589                    0x0102030405060708
590                )),
591            );
592
593            // NonZeroU128
594            assert_eq!(
595                [
596                    0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07,
597                    0x06, 0x05, 0x04, 0x03, 0x02, 0x01
598                ],
599                transmute::<_, [u8; 16]>(NonZeroU128_ule::new_unchecked(
600                    0x0102030405060708090a0b0c0d0e0f10
601                )),
602            );
603            assert_eq!(
604                [
605                    0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
606                    0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10
607                ],
608                transmute::<_, [u8; 16]>(NonZeroU128_ube::new_unchecked(
609                    0x0102030405060708090a0b0c0d0e0f10
610                )),
611            );
612        }
613    }
614}