packet_formats/
macros.rs

1// Copyright 2020 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Macros used in network packet parsing and serialization.
6
7macro_rules! __create_protocol_enum_inner {
8    // Create protocol enum when the Other variant is specified.
9    //
10    // A `From` implementation will be provided from `$repr`. The unspecified values will
11    // be mapped to the Other variant.
12    ($(#[$attr:meta])* ($($vis:tt)*) enum $name:ident: $repr:ty {
13        $($variant:ident, $value:expr, $fmt:expr;)*
14        + $delegate_name:ident($delegate_ty:ty);
15        _, $other_fmt:expr;
16    }) => {
17        $(#[$attr])*
18        $($vis)* enum $name {
19            $($variant,)*
20            $delegate_name($delegate_ty),
21            Other($repr),
22        }
23
24        impl From<$repr> for $name {
25            fn from(x: $repr) -> $name {
26                use core::convert::TryFrom;
27                match x {
28                    $($value => $name::$variant,)*
29                    x => <$delegate_ty>::try_from(x).map($name::$delegate_name).unwrap_or($name::Other(x)),
30                }
31            }
32        }
33
34        impl From<$delegate_ty> for $name {
35            fn from(x: $delegate_ty) -> $name {
36                $name::$delegate_name(x)
37            }
38        }
39
40        impl Into<$repr> for $name {
41            fn into(self) -> $repr {
42                match self {
43                    $($name::$variant => $value,)*
44                    $name::$delegate_name(x) => x.into(),
45                    $name::Other(x) => x,
46                }
47            }
48        }
49
50        impl core::fmt::Display for $name {
51            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
52                write!(
53                    f,
54                    "{}",
55                    match self {
56                        $($name::$variant => $fmt,)*
57                        $name::$delegate_name(x) => return write!(f, "{}", x),
58                        $name::Other(x) => return write!(f, $other_fmt, x),
59                    }
60                )
61            }
62        }
63
64        impl core::fmt::Debug for $name {
65            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
66                core::fmt::Display::fmt(self, f)
67            }
68        }
69    };
70
71    // Create protocol enum when the Other variant is not specified.
72    //
73    // In this case, a `TryFrom` implementation is provided from `$repr` instead of a `From`
74    // implementation.
75    ($(#[$attr:meta])* ($($vis:tt)*) enum $name:ident: $repr:ty {
76        $($variant:ident, $value:expr, $fmt:expr;)*
77        + $delegate_name:ident($delegate_ty:ty);
78    }) => {
79        $(#[$attr])*
80        $($vis)* enum $name {
81            $($variant,)*
82            $delegate_name($delegate_ty),
83        }
84
85        impl core::convert::TryFrom<$repr> for $name {
86            type Error = crate::error::UnrecognizedProtocolCode<$repr>;
87
88            fn try_from(x: $repr) -> Result<Self, Self::Error> {
89                use core::convert::TryFrom;
90                match x {
91                    $($value => Ok($name::$variant),)*
92                    x => $delegate_ty::try_from(x).map($name::$delegate_name),
93                }
94            }
95        }
96
97        impl Into<$repr> for $name {
98            fn into(self) -> $repr {
99                match self {
100                    $($name::$variant => $value,)*
101                    $name::$delegate_name(x) => x.into(),
102                }
103            }
104        }
105
106        impl core::fmt::Display for $name {
107            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
108                write!(
109                    f,
110                    "{}",
111                    match self {
112                        $($name::$variant => $fmt,)*
113                        $name::$delegate_name(x) => return write!(f, "{}", x),
114                    }
115                )
116            }
117        }
118
119        impl core::fmt::Debug for $name {
120            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
121                core::fmt::Display::fmt(self, f)
122            }
123        }
124    };
125
126    // Create protocol enum when the Other variant is specified.
127    //
128    // A `From` implementation will be provided from `$repr`. The unspecified values will
129    // be mapped to the Other variant.
130    ($(#[$attr:meta])* ($($vis:tt)*) enum $name:ident: $repr:ty {
131        $($variant:ident, $value:expr, $fmt:expr;)*
132        _, $other_fmt:expr;
133    }) => {
134        $(#[$attr])*
135        $($vis)* enum $name {
136            $($variant,)*
137            Other($repr),
138        }
139
140        impl From<$repr> for $name {
141            fn from(x: $repr) -> $name {
142                match x {
143                    $($value => $name::$variant,)*
144                    x => $name::Other(x),
145                }
146            }
147        }
148
149        impl From<$name> for $repr {
150            fn from(name: $name) -> $repr {
151                match name {
152                    $($name::$variant => $value,)*
153                    $name::Other(x) => x,
154                }
155            }
156        }
157
158        impl core::fmt::Display for $name {
159            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
160                write!(
161                    f,
162                    "{}",
163                    match self {
164                        $($name::$variant => $fmt,)*
165                        $name::Other(x) => return write!(f, $other_fmt, x),
166                    }
167                )
168            }
169        }
170
171        impl core::fmt::Debug for $name {
172            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
173                core::fmt::Display::fmt(self, f)
174            }
175        }
176    };
177
178    // Create protocol enum when the Other variant is not specified.
179    //
180    // In this case, a `TryFrom` implementation is provided from `$repr` instead of a `From`
181    // implementation.
182    ($(#[$attr:meta])* ($($vis:tt)*) enum $name:ident: $repr:ty {
183        $($variant:ident, $value:expr, $fmt:expr;)*
184    }) => {
185        $(#[$attr])*
186        $($vis)* enum $name {
187            $($variant,)*
188        }
189
190        impl core::convert::TryFrom<$repr> for $name {
191            type Error = crate::error::UnrecognizedProtocolCode<$repr>;
192
193            fn try_from(x: $repr) -> Result<Self, Self::Error> {
194                match x {
195                    $($value => Ok($name::$variant),)*
196                    x => Err(crate::error::UnrecognizedProtocolCode(x)),
197                }
198            }
199        }
200
201        impl From<$name> for $repr {
202            fn from(name: $name) -> $repr {
203                match name {
204                    $($name::$variant => $value,)*
205                }
206            }
207        }
208
209        impl core::fmt::Display for $name {
210            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
211                write!(
212                    f,
213                    "{}",
214                    match self {
215                        $($name::$variant => $fmt,)*
216                    }
217                )
218            }
219        }
220
221        impl core::fmt::Debug for $name {
222            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
223                core::fmt::Display::fmt(self, f)
224            }
225        }
226    };
227}
228
229/// Create an enum representing a protocol number (such as IP protocol or
230/// EtherType).
231///
232/// `create_protocol_enum` takes an input that looks similar (although not
233/// identical) to a normal enum definition:
234///
235/// ```rust,ignore
236/// create_protocol_enum!(
237///     /// IPv4 protocol number.
238///     ///
239///     /// The protocol numbers are specified in
240///     /// https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml
241///     #[allow(missing_docs)]
242///     #[derive(Copy, Clone, Hash, Eq, PartialEq)]
243///     pub enum Ipv4Proto: u8 {
244///         Icmp, 1, "ICMP";
245///         Igmp, 2, "IGMP";
246///         Tcp, 6, "TCP";
247///         Udp, 17, "UDP";
248///         + Proto(IpProto);
249///         _, "IPv4 protocol {}";
250///     }
251/// );
252/// ```
253///
254/// Unlike a normal enum definition, this macro takes the following extra
255/// information:
256/// - The type of the underlying numerical representation of the protocol number
257///   (`u8`, `u16`, etc)
258/// - For each variant, besides the variant's name:
259///   - The value of the protocol number associated with that variant
260///   - A string representation used in implementations of `Display` and `Debug`
261/// - A generic string representation used in the `Display` and `Debug` impls to
262///   print unrecognized protocol numbers
263///
264/// Note that, in addition to the variants specified in the macro invocation, the
265/// following special variants may be added:
266/// - A variant which includes data may be added using the syntax `+
267///   Variant(Type);`
268/// - An `Other` variant which captures all values not given their own variants
269///   may be added using the syntax `_, "format string {}"`
270///
271/// For a numerical type `U` (`u8`, `u16`, etc), impls of `Into<U>` are generated.
272/// `From<U>` impls are generated if the `Other` variant is specified. If the
273/// `Other` variant is not specified, `TryFrom<U>` will be generated instead.
274macro_rules! create_protocol_enum {
275    ($(#[$attr:meta])* enum $name:ident: $repr:ty {
276        $($variant:ident, $value:expr, $fmt:expr;)*
277        + $delegate_name:ident($delegate_ty:ty);
278        _, $other_fmt:expr;
279    }) => {
280        // use `()` to explicitly forward the information about private items
281        __create_protocol_enum_inner!($(#[$attr])* () enum $name: $repr { $($variant, $value, $fmt;)* + $delegate_name($delegate_ty); _, $other_fmt; });
282    };
283    ($(#[$attr:meta])* pub enum $name:ident: $repr:ty {
284        $($variant:ident, $value:expr, $fmt:expr;)*
285        + $delegate_name:ident($delegate_ty:ty);
286        _, $other_fmt:expr;
287    }) => {
288        __create_protocol_enum_inner!($(#[$attr])* (pub) enum $name: $repr { $($variant, $value, $fmt;)* + $delegate_name($delegate_ty); _, $other_fmt; });
289    };
290    ($(#[$attr:meta])* enum $name:ident: $repr:ty {
291        $($variant:ident, $value:expr, $fmt:expr;)*
292        + $delegate_name:ident($delegate_ty:ty);
293    }) => {
294        // use `()` to explicitly forward the information about private items
295        __create_protocol_enum_inner!($(#[$attr])* () enum $name: $repr { $($variant, $value, $fmt;)* + $delegate_name($delegate_ty); });
296    };
297    ($(#[$attr:meta])* pub enum $name:ident: $repr:ty {
298        $($variant:ident, $value:expr, $fmt:expr;)*
299        + $delegate_name:ident($delegate_ty:ty);
300    }) => {
301        __create_protocol_enum_inner!($(#[$attr])* (pub) enum $name: $repr { $($variant, $value, $fmt;)* + $delegate_name($delegate_ty); });
302    };
303
304    ($(#[$attr:meta])* enum $name:ident: $repr:ty {
305        $($variant:ident, $value:expr, $fmt:expr;)*
306        _, $other_fmt:expr;
307    }) => {
308        // use `()` to explicitly forward the information about private items
309        __create_protocol_enum_inner!($(#[$attr])* () enum $name: $repr { $($variant, $value, $fmt;)* _, $other_fmt; });
310    };
311    ($(#[$attr:meta])* pub enum $name:ident: $repr:ty {
312        $($variant:ident, $value:expr, $fmt:expr;)*
313        _, $other_fmt:expr;
314    }) => {
315        __create_protocol_enum_inner!($(#[$attr])* (pub) enum $name: $repr { $($variant, $value, $fmt;)* _, $other_fmt; });
316    };
317    ($(#[$attr:meta])* enum $name:ident: $repr:ty {
318        $($variant:ident, $value:expr, $fmt:expr;)*
319    }) => {
320        // use `()` to explicitly forward the information about private items
321        __create_protocol_enum_inner!($(#[$attr])* () enum $name: $repr { $($variant, $value, $fmt;)* });
322    };
323    ($(#[$attr:meta])* pub enum $name:ident: $repr:ty {
324        $($variant:ident, $value:expr, $fmt:expr;)*
325    }) => {
326        __create_protocol_enum_inner!($(#[$attr])* (pub) enum $name: $repr { $($variant, $value, $fmt;)* });
327    };
328}
329
330/// Declare a benchmark function.
331///
332/// The function will be named `$name`. If the `benchmark` feature is enabled,
333/// it will be annotated with the `#[bench]` attribute, and the provided `$fn`
334/// will be invoked with a `&mut test::Bencher` - in other words, a real
335/// benchmark. If the `benchmark` feature is disabled, the function will be
336/// annotated with the `#[test]` attribute, and the provided `$fn` will be
337/// invoked with a `&mut TestBencher`, which has the effect of creating a test
338/// that runs the benchmarked function for a single iteration.
339///
340/// Note that `$fn` doesn't have to be a named function - it can also be an
341/// anonymous closure.
342#[cfg(test)]
343macro_rules! bench {
344    ($name:ident, $fn:expr) => {
345        #[cfg(feature = "benchmark")]
346        #[bench]
347        fn $name(b: &mut test::Bencher) {
348            $fn(b);
349        }
350
351        // TODO(joshlf): Remove the `#[ignore]` once all benchmark tests pass.
352        #[cfg(not(feature = "benchmark"))]
353        #[test]
354        fn $name() {
355            $fn(&mut crate::testutil::benchmarks::TestBencher);
356        }
357    };
358}