Skip to main content

binrw/
meta.rs

1//! Traits that expose information about the way types are parsed or serialised.
2//!
3//! The traits in this module *describe* how a [`BinRead`] or [`BinWrite`]
4//! implementation works; they do not *control* the implementation. They are
5//! automatically implemented for derived `BinRead` or `BinWrite`
6//! implementations, but can also be manually implemented if needed for types
7//! that manually implement `BinRead` and `BinWrite`.
8//!
9//! [`BinRead`]: crate::BinRead
10//! [`BinWrite`]: crate::BinWrite
11
12use crate::Endian;
13#[cfg(not(feature = "std"))]
14use alloc::{boxed::Box, vec::Vec};
15use core::marker::PhantomData;
16
17/// Types that require a magic number when parsed.
18///
19/// This trait is automatically defined on derived types with a
20/// [magic directive](crate::docs::attribute#magic).
21pub trait ReadMagic {
22    /// The type of the magic number.
23    type MagicType;
24
25    /// The magic number.
26    const MAGIC: Self::MagicType;
27}
28
29/// Types that write a magic number when serialised.
30///
31/// This trait is automatically defined on derived types with a
32/// [magic directive](crate::docs::attribute#magic).
33pub trait WriteMagic {
34    /// The type of the magic number.
35    type MagicType;
36
37    /// The magic number.
38    const MAGIC: Self::MagicType;
39}
40
41/// Types with explicit read endianness.
42///
43/// This trait is automatically defined on derived types with a
44/// [byte order directive](crate::docs::attribute#byte-order).
45pub trait ReadEndian {
46    /// The endianness of the type.
47    const ENDIAN: EndianKind;
48}
49
50/// Types with explicit write endianness.
51///
52/// This trait is automatically defined on derived types with a
53/// [byte order directive](crate::docs::attribute#byte-order).
54pub trait WriteEndian {
55    /// The endianness of the type.
56    const ENDIAN: EndianKind;
57}
58
59/// The kind of endianness used by a type.
60#[derive(Clone, Copy, Debug, Eq, PartialEq)]
61pub enum EndianKind {
62    /// The type has no endianness at all.
63    None,
64    /// The type uses a fixed endianness.
65    Endian(Endian),
66    /// The type uses an endianness that is dynamically determined at runtime
67    /// from an expression.
68    Runtime,
69    /// The type uses a heterogenous mix of endianness.
70    Mixed,
71}
72
73impl EndianKind {
74    /// Returns the fixed endianness of the type, if one exists.
75    #[must_use]
76    pub fn endian(self) -> Option<Endian> {
77        match self {
78            EndianKind::None | EndianKind::Runtime | EndianKind::Mixed => None,
79            EndianKind::Endian(endian) => Some(endian),
80        }
81    }
82}
83
84macro_rules! endian_impl {
85    ($($($Ty:ty)+ => $kind:expr),+ $(,)?) => {$($(
86        impl ReadEndian for $Ty {
87            const ENDIAN: EndianKind = $kind;
88        }
89
90        impl WriteEndian for $Ty {
91            const ENDIAN: EndianKind = $kind;
92        }
93    )+)+}
94}
95
96endian_impl!(() i8 u8 core::num::NonZeroU8 core::num::NonZeroI8 crate::strings::NullString => EndianKind::None);
97
98impl<T: ReadEndian + ?Sized> ReadEndian for Box<T> {
99    const ENDIAN: EndianKind = <T as ReadEndian>::ENDIAN;
100}
101
102impl<T: WriteEndian + ?Sized> WriteEndian for Box<T> {
103    const ENDIAN: EndianKind = <T as WriteEndian>::ENDIAN;
104}
105
106impl<T: ReadEndian> ReadEndian for [T] {
107    const ENDIAN: EndianKind = <T as ReadEndian>::ENDIAN;
108}
109
110impl<T: WriteEndian> WriteEndian for [T] {
111    const ENDIAN: EndianKind = <T as WriteEndian>::ENDIAN;
112}
113
114impl<T: ReadEndian, const N: usize> ReadEndian for [T; N] {
115    const ENDIAN: EndianKind = <T as ReadEndian>::ENDIAN;
116}
117
118impl<T: WriteEndian, const N: usize> WriteEndian for [T; N] {
119    const ENDIAN: EndianKind = <T as WriteEndian>::ENDIAN;
120}
121
122macro_rules! endian_generic_impl {
123    ($($Ty:ident)+) => {$(
124        impl<T: ReadEndian> ReadEndian for $Ty<T> {
125            const ENDIAN: EndianKind = <T as ReadEndian>::ENDIAN;
126        }
127
128        impl<T: WriteEndian> WriteEndian for $Ty<T> {
129            const ENDIAN: EndianKind = <T as WriteEndian>::ENDIAN;
130        }
131    )+}
132}
133
134endian_generic_impl!(Option Vec PhantomData);
135
136macro_rules! endian_tuple_impl {
137    ($type1:ident $(, $types:ident)*) => {
138        #[allow(non_camel_case_types)]
139        impl<$type1: ReadEndian, $($types: ReadEndian),*> ReadEndian for ($type1, $($types),*) {
140            const ENDIAN: EndianKind = EndianKind::Mixed;
141        }
142
143        #[allow(non_camel_case_types)]
144        impl<$type1: WriteEndian, $($types: WriteEndian),*> WriteEndian for ($type1, $($types),*) {
145            const ENDIAN: EndianKind = EndianKind::Mixed;
146        }
147
148        endian_tuple_impl!($($types),*);
149    };
150
151    () => {};
152}
153
154endian_tuple_impl!(
155    b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19, b20, b21,
156    b22, b23, b24, b25, b26, b27, b28, b29, b30, b31, b32
157);