format_struct/
lib.rs

1//! A crate for quick and easy format structure definitions for use in binary file parsing.
2//!
3//! # Usage
4//!
5//! This crate should be used by invoking the provided [`format_struct`] macro like this:
6//!
7//! ```rust
8//! use format_struct::{format_struct, ReprByteSlice};
9//!
10//! // Here we define a small structure.
11//! format_struct! {
12//!     struct little Test {
13//!         foo: u8,
14//!         bar: u32,
15//!         baz: [u8; 2],
16//!     }
17//! }
18//!
19//! # pub fn main() {
20//! // This is the data we want to parse:
21//! let data = &[
22//!     0x42u8, // this goes into foo
23//!     0x39, 0x05, 0x00, 0x00, // this goes into bar
24//!     0xaa, 0x55, // this goes into baz
25//! ][..];
26//!
27//! // This is completely zero-cost since the implementation is just a transmute.
28//! let s = Test::from_byte_slice(data).unwrap();
29//!
30//! // Each integer field access compiles to a single unaligned memory access instruction.
31//! assert_eq!(s.foo, 0x42);
32//! assert_eq!(s.bar.get(), 1337);
33//! assert_eq!(&s.baz, &[0xaa, 0x55]);
34//! # }
35//! ```
36
37#![no_std]
38#![deny(missing_docs)]
39#![deny(missing_debug_implementations)]
40#![deny(rust_2018_idioms)]
41#![deny(unreachable_pub)]
42#![deny(clippy::unwrap_used)]
43#![warn(clippy::missing_errors_doc)]
44#![warn(clippy::missing_panics_doc)]
45
46#[cfg(feature = "std")]
47extern crate std;
48
49pub mod endian;
50
51use core::mem::MaybeUninit;
52use endian::FixedEndian;
53pub use endian::{BigEndian, Endian, LittleEndian};
54
55/// The error type returned when a byte slice of size that is either not equal to or not a multiple
56/// of the target type's size is transmuted into that type.
57#[derive(Copy, Clone, Debug)]
58pub struct UnalignedSizeError;
59
60impl core::fmt::Display for UnalignedSizeError {
61    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
62        f.write_str("byte slice is not aligned to the structure's size")
63    }
64}
65
66#[cfg(feature = "std")]
67impl std::error::Error for UnalignedSizeError {}
68
69/// The error type returned when a type is transmuted into a byte slice and the multiple of the
70/// slice's length and the type's size overflows `isize`.
71#[derive(Copy, Clone, Debug)]
72pub struct SliceSizeOverflowError;
73
74impl core::fmt::Display for SliceSizeOverflowError {
75    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
76        f.write_str("multiple of slice count and type size overflows isize")
77    }
78}
79
80#[cfg(feature = "std")]
81impl std::error::Error for SliceSizeOverflowError {}
82
83/// Safely multiplies a size of a type with a count.
84///
85/// # Errors
86///
87/// Returns [`SliceSizeOverflowError`] in case the resulting value overflows [`isize`]
88pub const fn safe_count_to_size<T: Sized>(count: usize) -> Result<usize, SliceSizeOverflowError> {
89    const MAX_SIZE: usize = isize::MAX as usize;
90
91    if let Some(size) = core::mem::size_of::<T>().checked_mul(count) {
92        if size <= MAX_SIZE {
93            Ok(size)
94        } else {
95            Err(SliceSizeOverflowError)
96        }
97    } else {
98        Err(SliceSizeOverflowError)
99    }
100}
101
102/// Checks that the specified size if a multiple of a type's size and returns the size divided by
103/// the type's size.
104///
105/// # Errors
106///
107/// Returns [`UnalignedSizeError`] in case the provided size is not aligned or `T` is zero sized type.
108pub const fn safe_size_to_count<T: Sized>(size: usize) -> Result<usize, UnalignedSizeError> {
109    if size % core::mem::size_of::<T>() == 0 {
110        if let Some(count) = size.checked_div(core::mem::size_of::<T>()) {
111            Ok(count)
112        } else {
113            Err(UnalignedSizeError)
114        }
115    } else {
116        Err(UnalignedSizeError)
117    }
118}
119
120/// An **unsafe** trait for types that may be safely transmuted from and to byte slices.
121///
122/// This trait is usually automatically implemented by the [`format_struct`] macro so there is no
123/// need to implement it manually.
124///
125/// All the trait's methods could be implemented automatically but are not due to limitations of the
126/// Rust's generics: using `Self` in a const context (array size on our case) isn't possible in
127/// traits. Since the trait isn't meant to be implemented manually that is considered a non-issue.
128///
129/// # Safety
130///
131/// Types implementing the trait must be safe to transmute from an arbitrary byte slice of the same
132/// size as the type itself. The alignment for the type must be 1.
133pub unsafe trait ReprByteSlice: Sized {
134    /// Transmutes an immutable byte slice reference into an immutable `Self` reference.
135    ///
136    /// # Errors
137    ///
138    /// Returns an error in case the size doesn't match the type's size.
139    fn from_byte_slice(s: &[u8]) -> Result<&Self, UnalignedSizeError>;
140
141    /// Transmutes a mutable byte slice reference into a mutable `Self` reference.
142    ///
143    /// # Errors
144    ///
145    /// Returns an error in case the size doesn't match the type's size.
146    fn from_byte_slice_mut(s: &mut [u8]) -> Result<&mut Self, UnalignedSizeError>;
147
148    /// Transmutes an immutable reference to `self` into an immutable reference to a byte slice.
149    fn as_byte_slice(&self) -> &[u8];
150
151    /// Transmutes a mutable reference to `self` into a mutable reference to a byte slice.
152    fn as_byte_slice_mut(&mut self) -> &mut [u8];
153
154    /// Transmutes an immutable byte slice reference into an immutable to a slice of `Self`.
155    ///
156    /// # Errors
157    ///
158    /// Returns an error in case the size isn't a multiple of the type's size.
159    fn slice_from_byte_slice(s: &[u8]) -> Result<&[Self], UnalignedSizeError>;
160
161    /// Transmutes a mutable byte slice reference into a mutable to a slice of `Self`.
162    ///
163    /// # Errors
164    ///
165    /// Returns an error in case the size isn't a multiple of the type's size.
166    fn slice_from_byte_slice_mut(s: &mut [u8]) -> Result<&mut [Self], UnalignedSizeError>;
167
168    /// Transmutes an immutable reference to a slice of `Self` into an immutable reference to a byte
169    /// slice.
170    ///
171    /// # Errors
172    ///
173    /// Returns [`SliceSizeOverflowError`] in case the product of the slice length and the type's
174    /// size would be larger than [`isize::MAX`].
175    fn slice_as_byte_slice(slice: &[Self]) -> Result<&[u8], SliceSizeOverflowError>;
176
177    /// Transmutes a mutable reference to a slice of `Self` into a mutable reference to a byte
178    /// slice.
179    ///
180    /// # Errors
181    ///
182    /// Returns [`SliceSizeOverflowError`] in case the product of the slice length and the type's
183    /// size would be larger than [`isize::MAX`].
184    fn slice_as_byte_slice_mut(s: &mut [Self]) -> Result<&mut [u8], SliceSizeOverflowError>;
185
186    /// Transmutes an immutable reference to a slice of [`MaybeUninit<u8>`] into an immutable
187    /// reference to a slice of [`MaybeUninit<Self>`].
188    ///
189    /// # Errors
190    ///
191    /// Returns an error in case the size isn't a multiple of the type's size.
192    fn uninit_slice_from_byte_slice(
193        bytes: &[MaybeUninit<u8>],
194    ) -> Result<&[MaybeUninit<Self>], UnalignedSizeError>;
195
196    /// Transmutes a mutable reference to a slice of [`MaybeUninit<u8>`] into a mutable reference
197    /// to a slice of [`MaybeUninit<Self>`].
198    ///
199    /// # Errors
200    ///
201    /// Returns an error in case the size isn't a multiple of the type's size.
202    fn uninit_slice_from_byte_slice_mut(
203        bytes: &mut [MaybeUninit<u8>],
204    ) -> Result<&mut [MaybeUninit<Self>], UnalignedSizeError>;
205
206    /// Transmutes an immutable reference to a slice of [`MaybeUninit<Self>`] into an immutable
207    /// reference to a slice of [`MaybeUninit<u8>`].
208    ///
209    /// # Errors
210    ///
211    /// Returns [`SliceSizeOverflowError`] in case the product of the slice length and the type's
212    /// size would be larger than [`isize::MAX`].
213    fn uninit_slice_as_byte_slice(
214        slice: &[MaybeUninit<Self>],
215    ) -> Result<&[MaybeUninit<u8>], SliceSizeOverflowError>;
216
217    /// Transmutes a mutable reference to a slice of [`MaybeUninit<u8>`] into a mutable reference
218    /// to a slice of [`MaybeUninit<Self>`].
219    ///
220    /// # Errors
221    ///
222    /// Returns [`SliceSizeOverflowError`] in case the product of the slice length and the type's
223    /// size would be larger than [`isize::MAX`].
224    fn uninit_slice_as_byte_slice_mut(
225        s: &mut [MaybeUninit<Self>],
226    ) -> Result<&mut [MaybeUninit<u8>], SliceSizeOverflowError>;
227}
228
229macro_rules! define_int_wrapper {
230    ($ty:ident, $name:ident) => {
231        #[doc = concat!(
232            "A type that wraps a byte array to be decoded into a `", stringify!($ty), "`.\n\n"
233        )]
234        /// The generic parameter represents the endianness used to decode the wrapped value. In
235        /// case the value is expected to have fixed endianness, either [`BigEndian`] or
236        /// [`LittleEndian`] types should be used, otherwise the [`Endian`] type.
237        #[derive(Copy, Clone, Eq, PartialEq, Hash)]
238        #[repr(C)]
239        pub struct $name<E>([u8; ($ty::BITS as usize) / 8], ::core::marker::PhantomData<E>);
240
241        impl<E> $name<E> {
242            #[doc = concat!("Converts a byte array into a [`", stringify!($name), "`].")]
243            pub const fn from_bytes(bytes: [u8; ($ty::BITS as usize) / 8]) -> Self {
244                Self(bytes, ::core::marker::PhantomData)
245            }
246
247            #[doc = concat!("Converts a [`", stringify!($name), "`] into a byte array.")]
248            pub const fn into_bytes(self) -> [u8; ($ty::BITS as usize) / 8] {
249                self.0
250            }
251        }
252
253        $crate::format_struct!(@impl_conv $name<E> size (($ty::BITS as usize) / 8));
254
255        impl $name<Endian> {
256            #[doc = concat!(
257                "Constructs a [`", stringify!($name), "`] wrapper type from a `", stringify!($ty),
258                "` value using the specified endianness."
259            )]
260            #[inline]
261            pub const fn new_with_endian(value: $ty, endian: Endian) -> Self {
262                let bytes = match endian {
263                    Endian::Little => value.to_le_bytes(),
264                    Endian::Big => value.to_be_bytes(),
265                };
266
267                Self(bytes, ::core::marker::PhantomData)
268            }
269
270            #[doc = concat!(
271                "Extracts a `", stringify!($ty), "` value from a [`", stringify!($name),
272                "`] wrapper using the specified endianness."
273            )]
274            #[inline]
275            pub const fn get_with_endian(self, endian: Endian) -> $ty {
276                match endian {
277                    Endian::Little => $ty::from_le_bytes(self.0),
278                    Endian::Big => $ty::from_be_bytes(self.0),
279                }
280            }
281        }
282
283        impl<E: FixedEndian> $name<E> {
284            #[doc = concat!(
285                "Constructs a [`", stringify!($name), "`] wrapper type from a `", stringify!($ty),
286                "` value using the type's fixed endianness."
287            )]
288            #[inline]
289            pub const fn new(value: $ty) -> Self {
290                let bytes = match E::ENDIAN {
291                    Endian::Little => value.to_le_bytes(),
292                    Endian::Big => value.to_be_bytes(),
293                };
294
295                Self(bytes, ::core::marker::PhantomData)
296            }
297
298            #[doc = concat!(
299                "Extracts a `", stringify!($ty), "` value from a [`", stringify!($name),
300                "`] wrapper using the type's fixed endianness."
301            )]
302            #[inline]
303            pub const fn get(self) -> $ty {
304                match E::ENDIAN {
305                    Endian::Little => $ty::from_le_bytes(self.0),
306                    Endian::Big => $ty::from_be_bytes(self.0),
307                }
308            }
309        }
310
311        impl<E> ::core::default::Default for $name<E> {
312            fn default() -> Self {
313                Self(Default::default(), ::core::marker::PhantomData)
314            }
315        }
316
317        impl<E: FixedEndian> From<$ty> for $name<E> {
318            fn from(value: $ty) -> Self {
319                Self::new(value)
320            }
321        }
322
323        impl core::fmt::Debug for $name<LittleEndian> {
324            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
325                core::fmt::Debug::fmt(&self.get(), f)
326            }
327        }
328
329        impl core::fmt::Debug for $name<BigEndian> {
330            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
331                core::fmt::Debug::fmt(&self.get(), f)
332            }
333        }
334
335        impl core::fmt::Debug for $name<Endian> {
336            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
337                f.debug_tuple(stringify!($name))
338                    .field(&self.0)
339                    .finish()
340            }
341        }
342    };
343}
344
345/// Defines a structure that can be transmuted from/into a byte slice for parsing/constructing binary formats in a
346/// zero-copy way.
347///
348/// The macro achieves this by replacing all multibyte integers with wrapper types that are byte
349/// arrays internally and only allowing integer and fixed size array fields in a structure.
350///
351/// Accepted syntax is similar to a standard structure definition in Rust with some differences:
352///
353/// * The `struct` keyword is followed by either `little` or `big` keywords if you want fixed
354///   endianness or `dynamic` keyword if you want dynamic endianness.
355/// * Fields of the generated structure may only have documentation meta, other meta types are
356///   disallowed.
357///
358/// # Examples
359///
360/// ```rust
361/// # use format_struct::format_struct;
362/// format_struct! {
363///     /// A little-endian test structure.
364///     #[derive(Default, Clone)]
365///     pub struct little Test {
366///         /// this byte is public
367///         pub byte: u8,
368///         short: u16,
369///         word: i32,
370///         dword: i64,
371///         qword: u128,
372///         byte_arr: [u8; 16],
373///     }
374/// }
375/// ```
376///
377/// It is also possible to define multiple structures in one macro invocation:
378///
379/// ```rust
380/// # use format_struct::format_struct;
381/// format_struct! {
382///     struct little Foo {
383///         byte: u8,
384///     }
385///
386///     struct big Bar {
387///         a: u64,
388///     }
389///
390///     pub struct little Baz {
391///         z: [u8; 33],
392///     }
393/// }
394/// ```
395///
396/// # Allowed field types
397///
398/// Integer types (`u8`, `u16`, `u32`, `u64`, `u128` and their signed counterparts) are allowed. Multi-byte integer
399/// types are converted to wrapper types with alignment 1 (u16 to [`U16`], u32 to [`U32`], ...).
400///
401/// Statically-sized arrays of types implementing [`ReprByteSlice`] are allowed as well making it possible to nest
402/// structures defined by the [`format_struct`] macros.
403///
404/// Example:
405/// ```rust
406/// # use format_struct::format_struct;
407/// format_struct! {
408///     #[derive(Clone, Debug)]
409///     struct little TopLevel {
410///         foo: u32,
411///         nested: Nested,
412///     }
413///
414///     #[derive(Clone, Debug)]
415///     struct big Nested {
416///         bar: u32,
417///     }
418/// }
419/// ```
420///
421/// Note that nested structures are only affected by their endian specifiers. In the example above field `bar` of
422/// the `Nested` structure will be big-endian even when wrapped into the `TopLevel` structure.
423///
424/// # Layout
425///
426/// The fields in the structure are laid out in declaration order without any padding. That means that the following
427/// structure will take 7 bytes instead of 16 you might expect:
428///
429/// ```rust
430/// # use format_struct::format_struct;
431/// format_struct! {
432///     struct little SmallStruct {
433///         byte: u8,
434///         dword: u64,
435///     }
436/// }
437/// ```
438#[macro_export]
439macro_rules! format_struct {
440    ($($(#[$m:meta])* $vis:vis struct $endian:tt $name:ident {
441        $($(#[doc = $field_doc:literal])* $field_vis:vis $field_name:ident: $ty:tt),*,
442    })+) => {
443        $(
444            #[repr(C)]
445            $(#[$m])*
446            $vis struct $name {
447                $($(#[doc = $field_doc])*
448                $field_vis $field_name: $crate::format_struct!(@wrapper_type $endian $ty)),*
449            }
450
451            const _: fn() = || {
452                fn assert_impl_repr_byte_slice<T: ?Sized + $crate::ReprByteSlice>() {}
453
454                $(assert_impl_repr_byte_slice::<$crate::format_struct!(@wrapper_type $endian $ty)>();)*
455            };
456
457            impl $name {
458                #[doc = concat!("Converts a byte array into a [`", stringify!($name), "`].")]
459                pub const fn from_bytes(bytes: [u8; ::core::mem::size_of::<Self>()]) -> Self {
460                    unsafe { ::core::mem::transmute(bytes) }
461                }
462
463                #[doc = concat!(
464                    "Converts an immutable reference to a byte array of the same size as [`",
465                    stringify!($name), " into an immutable reference to [`", stringify!($name),
466                    "`]."
467                )]
468                pub const fn from_byte_array(bytes: &[u8; ::core::mem::size_of::<Self>()]) -> &Self {
469                    unsafe { &*(bytes.as_ptr() as *const Self) }
470                }
471
472                #[doc = concat!(
473                    "Converts a mutable reference to a byte array of the same size as [`",
474                    stringify!($name), "`] into a mutable reference to [`", stringify!($name),
475                    "`]."
476                )]
477                pub fn from_byte_array_mut(bytes: &mut [u8; ::core::mem::size_of::<Self>()]) -> &mut Self {
478                    unsafe { &mut *(bytes.as_mut_ptr() as *mut Self) }
479                }
480
481                #[doc = concat!("Converts a [`", stringify!($name), "`] into a byte array.")]
482                pub const fn into_bytes(self) -> [u8; ::core::mem::size_of::<Self>()] {
483                    unsafe { ::core::mem::transmute(self) }
484                }
485
486                #[doc = concat!(
487                    "Converts an immutable reference to a byte array of the same size as [`",
488                    stringify!($name), " into an immutable reference to [`", stringify!($name),
489                    "`]."
490                )]
491                pub const fn as_byte_array(&self) -> &[u8; ::core::mem::size_of::<Self>()] {
492                    unsafe { &*(self as *const Self as *const [u8; ::core::mem::size_of::<Self>()]) }
493                }
494
495                #[doc = concat!(
496                    "Converts a mutable reference to a byte array of the same size as [`",
497                    stringify!($name), "`] into a mutable reference to [`", stringify!($name),
498                    "`]."
499                )]
500                pub fn as_byte_array_mut(&mut self) -> &mut [u8; ::core::mem::size_of::<Self>()] {
501                    unsafe { &mut *(self as *mut Self as *mut [u8; ::core::mem::size_of::<Self>()]) }
502                }
503            }
504
505            $crate::format_struct!(@impl_conv $name size ::core::mem::size_of::<$name>());
506        )+
507    };
508    (@impl_conv $name:tt$(<$gen:ident$(: $trait:ident)?$(, const $const_gen:ident: usize)?>)? size $size_expr:expr) => {
509        unsafe impl$(<$gen$(: $crate::$trait)? $(, const $const_gen: usize)?>)? $crate::ReprByteSlice for $name$(<$gen $(, $const_gen)?>)? {
510            fn from_byte_slice(s: &[u8]) -> ::core::result::Result<&Self, $crate::UnalignedSizeError> {
511                let bytes: &[u8; $size_expr] = ::core::convert::TryInto::try_into(s).map_err(|_| $crate::UnalignedSizeError)?;
512                let ptr = bytes.as_ptr() as *const Self;
513
514                ::core::result::Result::Ok(unsafe { &*ptr })
515            }
516
517            fn from_byte_slice_mut(s: &mut [u8]) -> ::core::result::Result<&mut Self, $crate::UnalignedSizeError> {
518                let bytes: &mut [u8; $size_expr] = ::core::convert::TryInto::try_into(s).map_err(|_| $crate::UnalignedSizeError)?;
519                let ptr = bytes.as_ptr() as *mut Self;
520
521                ::core::result::Result::Ok(unsafe { &mut *ptr })
522            }
523
524            fn as_byte_slice(&self) -> &[u8] {
525                let data = self as *const Self as *const u8;
526                let len = ::core::mem::size_of::<Self>();
527                unsafe { ::core::slice::from_raw_parts(data, len) }
528            }
529
530            fn as_byte_slice_mut(&mut self) -> &mut [u8] {
531                let data = self as *mut Self as *mut u8;
532                let len = ::core::mem::size_of::<Self>();
533                unsafe { ::core::slice::from_raw_parts_mut(data, len) }
534            }
535
536            fn slice_from_byte_slice(s: &[u8]) -> ::core::result::Result<&[Self], $crate::UnalignedSizeError> {
537                if s.is_empty() {
538                    ::core::result::Result::Ok(&[])
539                } else {
540                    let size = $crate::safe_size_to_count::<Self>(s.len())?;
541                    let ptr = s.as_ptr() as *const Self;
542
543                    ::core::result::Result::Ok(unsafe { ::core::slice::from_raw_parts(ptr, size) })
544                }
545            }
546
547            fn slice_from_byte_slice_mut(s: &mut [u8]) -> ::core::result::Result<&mut [Self], $crate::UnalignedSizeError> {
548                if s.is_empty() {
549                    ::core::result::Result::Ok(&mut [])
550                } else {
551                    let size = $crate::safe_size_to_count::<Self>(s.len())?;
552                    let ptr = s.as_mut_ptr() as *mut Self;
553
554                    ::core::result::Result::Ok(unsafe { ::core::slice::from_raw_parts_mut(ptr, size) })
555                }
556            }
557
558            fn slice_as_byte_slice(slice: &[Self]) -> ::core::result::Result<&[u8], $crate::SliceSizeOverflowError> {
559                let data = slice.as_ptr() as *const u8;
560                let len = $crate::safe_count_to_size::<Self>(slice.len())?;
561                ::core::result::Result::Ok(unsafe { ::core::slice::from_raw_parts(data, len) })
562            }
563
564            fn slice_as_byte_slice_mut(slice: &mut [Self]) -> ::core::result::Result<&mut [u8], $crate::SliceSizeOverflowError> {
565                let data = slice.as_ptr() as *mut u8;
566                let len = $crate::safe_count_to_size::<Self>(slice.len())?;
567                ::core::result::Result::Ok(unsafe { ::core::slice::from_raw_parts_mut(data, len) })
568            }
569
570            fn uninit_slice_from_byte_slice(
571                s: &[::core::mem::MaybeUninit<u8>]
572            ) -> ::core::result::Result<&[::core::mem::MaybeUninit<Self>], $crate::UnalignedSizeError> {
573                if s.is_empty() {
574                    ::core::result::Result::Ok(&[])
575                } else {
576                    let size = $crate::safe_size_to_count::<Self>(s.len())?;
577                    let ptr = s.as_ptr() as *const ::core::mem::MaybeUninit<Self>;
578
579                    ::core::result::Result::Ok(unsafe { ::core::slice::from_raw_parts(ptr, size) })
580                }
581            }
582
583            fn uninit_slice_from_byte_slice_mut(
584                s: &mut [::core::mem::MaybeUninit<u8>]
585            ) -> ::core::result::Result<&mut [::core::mem::MaybeUninit<Self>], $crate::UnalignedSizeError> {
586                if s.is_empty() {
587                    ::core::result::Result::Ok(&mut [])
588                } else {
589                    let size = $crate::safe_size_to_count::<Self>(s.len())?;
590                    let ptr = s.as_mut_ptr() as *mut ::core::mem::MaybeUninit<Self>;
591
592                    ::core::result::Result::Ok(unsafe { ::core::slice::from_raw_parts_mut(ptr, size) })
593                }
594            }
595
596            fn uninit_slice_as_byte_slice(
597                slice: &[::core::mem::MaybeUninit<Self>]
598            ) -> ::core::result::Result<&[::core::mem::MaybeUninit<u8>], $crate::SliceSizeOverflowError> {
599                let data = slice.as_ptr() as *const ::core::mem::MaybeUninit<u8>;
600                let len = ::core::mem::size_of::<Self>().checked_mul(slice.len()).expect("");
601                ::core::result::Result::Ok(unsafe { ::core::slice::from_raw_parts(data, len) })
602            }
603
604            fn uninit_slice_as_byte_slice_mut(
605                slice: &mut [::core::mem::MaybeUninit<Self>]
606            ) -> ::core::result::Result<&mut [::core::mem::MaybeUninit<u8>], $crate::SliceSizeOverflowError> {
607                let data = slice.as_ptr() as *mut ::core::mem::MaybeUninit<u8>;
608                let len = ::core::mem::size_of::<Self>().checked_mul(slice.len()).unwrap();
609                ::core::result::Result::Ok(unsafe { ::core::slice::from_raw_parts_mut(data, len) })
610            }
611        }
612    };
613    (@endian_type little) => {$crate::LittleEndian};
614    (@endian_type big) => {$crate::BigEndian};
615    (@endian_type dynamic) => {$crate::Endian};
616    (@wrapper_type $endian:tt [$ty:ident; $($n:tt)+]) => {
617        [$crate::format_struct!(@wrapper_type $endian $ty); $($n)+]
618    };
619    (@wrapper_type $endian:tt u8) => {u8};
620    (@wrapper_type $endian:tt i8) => {i8};
621    (@wrapper_type $endian:tt u16) => {$crate::U16<$crate::format_struct!(@endian_type $endian)>};
622    (@wrapper_type $endian:tt i16) => {$crate::I16<$crate::format_struct!(@endian_type $endian)>};
623    (@wrapper_type $endian:tt u32) => {$crate::U32<$crate::format_struct!(@endian_type $endian)>};
624    (@wrapper_type $endian:tt i32) => {$crate::I32<$crate::format_struct!(@endian_type $endian)>};
625    (@wrapper_type $endian:tt u64) => {$crate::U64<$crate::format_struct!(@endian_type $endian)>};
626    (@wrapper_type $endian:tt i64) => {$crate::I64<$crate::format_struct!(@endian_type $endian)>};
627    (@wrapper_type $endian:tt u128) => {$crate::U128<$crate::format_struct!(@endian_type $endian)>};
628    (@wrapper_type $endian:tt i128) => {$crate::I128<$crate::format_struct!(@endian_type $endian)>};
629    (@wrapper_type $endian:tt $ty:ty) => {$ty};
630}
631
632format_struct!(@impl_conv u8 size 1);
633define_int_wrapper!(u16, U16);
634define_int_wrapper!(i16, I16);
635define_int_wrapper!(u32, U32);
636define_int_wrapper!(i32, I32);
637define_int_wrapper!(u64, U64);
638define_int_wrapper!(i64, I64);
639define_int_wrapper!(u128, U128);
640define_int_wrapper!(i128, I128);
641
642type Arr<T, const N: usize> = [T; N];
643format_struct!(@impl_conv Arr<T: ReprByteSlice, const N: usize> size N);
644
645#[cfg(test)]
646#[allow(unused, unreachable_pub)]
647mod tests {
648    use super::*;
649    use core::{marker::PhantomData, mem::MaybeUninit};
650
651    const CONSTANT_SIZE: usize = 16;
652
653    format_struct! {
654        #[derive(Default, Clone)]
655        struct little TestLe {
656            /// this is a byte
657            /// this is a multiline comment
658            #[doc = "this is the third line"]
659            byte: u8,
660            short: u16,
661            word: u32,
662            dword: u64,
663            qword: u128,
664            byte_arr: [u8; 16],
665            short_arr: [u16; 16],
666        }
667
668        #[derive(Default, Clone)]
669        struct big TestBe {
670            pub byte: u8,
671            short: u16,
672            word: u32,
673            dword: u64,
674            qword: u128,
675            byte_arr: [u8; 16],
676            short_arr: [u16; 16],
677        }
678
679        #[derive(Default, Clone)]
680        struct dynamic TestDyn {
681            byte: u8,
682            short: u16,
683            word: u32,
684            dword: u64,
685            qword: u128,
686            byte_arr: [u8; const { 16 }],
687            short_arr: [u16; CONSTANT_SIZE],
688        }
689
690        #[derive(Default, Clone)]
691        struct little TestNested {
692            regular: u8,
693            single: TestBe,
694            array: [TestLe; 2],
695        }
696    }
697
698    #[test]
699    fn test_access_short_arr() {
700        let mut test_le = TestLe::default();
701
702        for (i, s) in test_le.short_arr.iter_mut().enumerate() {
703            *s = U16((i as u16).to_le_bytes(), PhantomData);
704        }
705
706        assert_eq!(test_le.short_arr[5].get(), 5);
707    }
708
709    #[test]
710    fn test_access_u8() {
711        let mut test = TestLe::default();
712
713        test.byte = 42;
714        assert_eq!(test.byte, 42);
715    }
716
717    #[test]
718    fn test_access_u16() {
719        let mut test_le = TestLe::default();
720        test_le.short = U16::new(1337);
721        assert_eq!(test_le.short.get(), 1337);
722        assert_eq!(test_le.short.0, 1337u16.to_le_bytes());
723
724        let mut test_be = TestBe::default();
725        test_be.short = U16::new(1337);
726        assert_eq!(test_be.short.get(), 1337);
727        assert_eq!(test_be.short.0, 1337u16.to_be_bytes());
728    }
729
730    #[test]
731    fn test_access_u32() {
732        let mut test_le = TestLe::default();
733        test_le.word = U32::new(13371337);
734        assert_eq!(test_le.word.get(), 13371337);
735        assert_eq!(test_le.word.0, 13371337u32.to_le_bytes());
736
737        let mut test_be = TestBe::default();
738        test_be.word = U32::new(13371337);
739        assert_eq!(test_be.word.get(), 13371337);
740        assert_eq!(test_be.word.0, 13371337u32.to_be_bytes());
741    }
742
743    #[test]
744    fn test_access_u64() {
745        let mut test_le = TestLe::default();
746        test_le.dword = U64::new(1337133713371337);
747        assert_eq!(test_le.dword.get(), 1337133713371337);
748        assert_eq!(test_le.dword.0, 1337133713371337u64.to_le_bytes());
749
750        let mut test_be = TestBe::default();
751        test_be.dword = U64::new(1337133713371337);
752        assert_eq!(test_be.dword.get(), 1337133713371337);
753        assert_eq!(test_be.dword.0, 1337133713371337u64.to_be_bytes());
754    }
755
756    #[test]
757    fn test_access_u128() {
758        let mut test_le = TestLe::default();
759        test_le.qword = U128::new(13371337133713371337133713371337);
760        assert_eq!(test_le.qword.get(), 13371337133713371337133713371337u128);
761        assert_eq!(
762            test_le.qword.0,
763            13371337133713371337133713371337u128.to_le_bytes()
764        );
765
766        let mut test_be = TestBe::default();
767        test_be.qword = U128::new(13371337133713371337133713371337u128);
768        assert_eq!(test_be.qword.get(), 13371337133713371337133713371337);
769        assert_eq!(
770            test_be.qword.0,
771            13371337133713371337133713371337u128.to_be_bytes()
772        );
773    }
774
775    #[test]
776    fn test_uninit() {
777        let mut test = [
778            MaybeUninit::<TestLe>::uninit(),
779            MaybeUninit::<TestLe>::uninit(),
780        ];
781        TestLe::uninit_slice_as_byte_slice(&test[..]).unwrap()[0];
782    }
783}