1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
//! Traits that expose information about the way types are parsed or serialised.
//!
//! The traits in this module *describe* how a [`BinRead`] or [`BinWrite`]
//! implementation works; they do not *control* the implementation. They are
//! automatically implemented for derived `BinRead` or `BinWrite`
//! implementations, but can also be manually implemented if needed for types
//! that manually implement `BinRead` and `BinWrite`.
//!
//! [`BinRead`]: crate::BinRead
//! [`BinWrite`]: crate::BinWrite

use crate::Endian;
#[cfg(not(feature = "std"))]
use alloc::{boxed::Box, vec::Vec};
use core::marker::PhantomData;

/// Types that require a magic number when parsed.
///
/// This trait is automatically defined on derived types with a
/// [magic directive](crate::docs::attribute#magic).
pub trait ReadMagic {
    /// The type of the magic number.
    type MagicType;

    /// The magic number.
    const MAGIC: Self::MagicType;
}

/// Types that write a magic number when serialised.
///
/// This trait is automatically defined on derived types with a
/// [magic directive](crate::docs::attribute#magic).
pub trait WriteMagic {
    /// The type of the magic number.
    type MagicType;

    /// The magic number.
    const MAGIC: Self::MagicType;
}

/// Types with explicit read endianness.
///
/// This trait is automatically defined on derived types with a
/// [byte order directive](crate::docs::attribute#byte-order).
pub trait ReadEndian {
    /// The endianness of the type.
    const ENDIAN: EndianKind;
}

/// Types with explicit write endianness.
///
/// This trait is automatically defined on derived types with a
/// [byte order directive](crate::docs::attribute#byte-order).
pub trait WriteEndian {
    /// The endianness of the type.
    const ENDIAN: EndianKind;
}

/// The kind of endianness used by a type.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum EndianKind {
    /// The type has no endianness at all.
    None,
    /// The type uses a fixed endianness.
    Endian(Endian),
    /// The type uses an endianness that is dynamically determined at runtime
    /// from an expression.
    Runtime,
    /// The type uses a heterogenous mix of endianness.
    Mixed,
}

impl EndianKind {
    /// Returns the fixed endianness of the type, if one exists.
    #[must_use]
    pub fn endian(self) -> Option<Endian> {
        match self {
            EndianKind::None | EndianKind::Runtime | EndianKind::Mixed => None,
            EndianKind::Endian(endian) => Some(endian),
        }
    }
}

macro_rules! endian_impl {
    ($($($Ty:ty)+ => $kind:expr),+ $(,)?) => {$($(
        impl ReadEndian for $Ty {
            const ENDIAN: EndianKind = $kind;
        }

        impl WriteEndian for $Ty {
            const ENDIAN: EndianKind = $kind;
        }
    )+)+}
}

endian_impl!(() i8 u8 core::num::NonZeroU8 core::num::NonZeroI8 crate::strings::NullString => EndianKind::None);

impl<T: ReadEndian + ?Sized> ReadEndian for Box<T> {
    const ENDIAN: EndianKind = <T as ReadEndian>::ENDIAN;
}

impl<T: WriteEndian + ?Sized> WriteEndian for Box<T> {
    const ENDIAN: EndianKind = <T as WriteEndian>::ENDIAN;
}

impl<T: ReadEndian> ReadEndian for [T] {
    const ENDIAN: EndianKind = <T as ReadEndian>::ENDIAN;
}

impl<T: WriteEndian> WriteEndian for [T] {
    const ENDIAN: EndianKind = <T as WriteEndian>::ENDIAN;
}

impl<T: ReadEndian, const N: usize> ReadEndian for [T; N] {
    const ENDIAN: EndianKind = <T as ReadEndian>::ENDIAN;
}

impl<T: WriteEndian, const N: usize> WriteEndian for [T; N] {
    const ENDIAN: EndianKind = <T as WriteEndian>::ENDIAN;
}

macro_rules! endian_generic_impl {
    ($($Ty:ident)+) => {$(
        impl<T: ReadEndian> ReadEndian for $Ty<T> {
            const ENDIAN: EndianKind = <T as ReadEndian>::ENDIAN;
        }

        impl<T: WriteEndian> WriteEndian for $Ty<T> {
            const ENDIAN: EndianKind = <T as WriteEndian>::ENDIAN;
        }
    )+}
}

endian_generic_impl!(Option Vec PhantomData);

macro_rules! endian_tuple_impl {
    ($type1:ident $(, $types:ident)*) => {
        #[allow(non_camel_case_types)]
        impl<$type1: ReadEndian, $($types: ReadEndian),*> ReadEndian for ($type1, $($types),*) {
            const ENDIAN: EndianKind = EndianKind::Mixed;
        }

        #[allow(non_camel_case_types)]
        impl<$type1: WriteEndian, $($types: WriteEndian),*> WriteEndian for ($type1, $($types),*) {
            const ENDIAN: EndianKind = EndianKind::Mixed;
        }

        endian_tuple_impl!($($types),*);
    };

    () => {};
}

endian_tuple_impl!(
    b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19, b20, b21,
    b22, b23, b24, b25, b26, b27, b28, b29, b30, b31, b32
);