blob: 6af948e1a092d460ae35887d1b74c24678b8727e [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! Macros used in network packet parsing and serialization.
macro_rules! __create_protocol_enum_inner {
// Create protocol enum when the Other variant is specified.
//
// A `From` implementation will be provided from `$repr`. The unspecified values will
// be mapped to the Other variant.
($(#[$attr:meta])* ($($vis:tt)*) enum $name:ident: $repr:ty {
$($variant:ident, $value:expr, $fmt:expr;)*
+ $delegate_name:ident($delegate_ty:ty);
_, $other_fmt:expr;
}) => {
$(#[$attr])*
$($vis)* enum $name {
$($variant,)*
$delegate_name($delegate_ty),
Other($repr),
}
impl From<$repr> for $name {
fn from(x: $repr) -> $name {
use core::convert::TryFrom;
match x {
$($value => $name::$variant,)*
x => <$delegate_ty>::try_from(x).map($name::$delegate_name).unwrap_or($name::Other(x)),
}
}
}
impl From<$delegate_ty> for $name {
fn from(x: $delegate_ty) -> $name {
$name::$delegate_name(x)
}
}
impl Into<$repr> for $name {
fn into(self) -> $repr {
match self {
$($name::$variant => $value,)*
$name::$delegate_name(x) => x.into(),
$name::Other(x) => x,
}
}
}
impl core::fmt::Display for $name {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
write!(
f,
"{}",
match self {
$($name::$variant => $fmt,)*
$name::$delegate_name(x) => return write!(f, "{}", x),
$name::Other(x) => return write!(f, $other_fmt, x),
}
)
}
}
impl core::fmt::Debug for $name {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
core::fmt::Display::fmt(self, f)
}
}
};
// Create protocol enum when the Other variant is not specified.
//
// In this case, a `TryFrom` implementation is provided from `$repr` instead of a `From`
// implementation.
($(#[$attr:meta])* ($($vis:tt)*) enum $name:ident: $repr:ty {
$($variant:ident, $value:expr, $fmt:expr;)*
+ $delegate_name:ident($delegate_ty:ty);
}) => {
$(#[$attr])*
$($vis)* enum $name {
$($variant,)*
$delegate_name($delegate_ty),
}
impl core::convert::TryFrom<$repr> for $name {
type Error = crate::error::UnrecognizedProtocolCode<$repr>;
fn try_from(x: $repr) -> Result<Self, Self::Error> {
use core::convert::TryFrom;
match x {
$($value => Ok($name::$variant),)*
x => $delegate_ty::try_from(x).map($name::$delegate_name),
}
}
}
impl Into<$repr> for $name {
fn into(self) -> $repr {
match self {
$($name::$variant => $value,)*
$name::$delegate_name(x) => x.into(),
}
}
}
impl core::fmt::Display for $name {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
write!(
f,
"{}",
match self {
$($name::$variant => $fmt,)*
$name::$delegate_name(x) => return write!(f, "{}", x),
}
)
}
}
impl core::fmt::Debug for $name {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
core::fmt::Display::fmt(self, f)
}
}
};
// Create protocol enum when the Other variant is specified.
//
// A `From` implementation will be provided from `$repr`. The unspecified values will
// be mapped to the Other variant.
($(#[$attr:meta])* ($($vis:tt)*) enum $name:ident: $repr:ty {
$($variant:ident, $value:expr, $fmt:expr;)*
_, $other_fmt:expr;
}) => {
$(#[$attr])*
$($vis)* enum $name {
$($variant,)*
Other($repr),
}
impl From<$repr> for $name {
fn from(x: $repr) -> $name {
match x {
$($value => $name::$variant,)*
x => $name::Other(x),
}
}
}
impl From<$name> for $repr {
fn from(name: $name) -> $repr {
match name {
$($name::$variant => $value,)*
$name::Other(x) => x,
}
}
}
impl core::fmt::Display for $name {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
write!(
f,
"{}",
match self {
$($name::$variant => $fmt,)*
$name::Other(x) => return write!(f, $other_fmt, x),
}
)
}
}
impl core::fmt::Debug for $name {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
core::fmt::Display::fmt(self, f)
}
}
};
// Create protocol enum when the Other variant is not specified.
//
// In this case, a `TryFrom` implementation is provided from `$repr` instead of a `From`
// implementation.
($(#[$attr:meta])* ($($vis:tt)*) enum $name:ident: $repr:ty {
$($variant:ident, $value:expr, $fmt:expr;)*
}) => {
$(#[$attr])*
$($vis)* enum $name {
$($variant,)*
}
impl core::convert::TryFrom<$repr> for $name {
type Error = crate::error::UnrecognizedProtocolCode<$repr>;
fn try_from(x: $repr) -> Result<Self, Self::Error> {
match x {
$($value => Ok($name::$variant),)*
x => Err(crate::error::UnrecognizedProtocolCode(x)),
}
}
}
impl From<$name> for $repr {
fn from(name: $name) -> $repr {
match name {
$($name::$variant => $value,)*
}
}
}
impl core::fmt::Display for $name {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
write!(
f,
"{}",
match self {
$($name::$variant => $fmt,)*
}
)
}
}
impl core::fmt::Debug for $name {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
core::fmt::Display::fmt(self, f)
}
}
};
}
/// Create an enum representing a protocol number (such as IP protocol or
/// EtherType).
///
/// `create_protocol_enum` takes an input that looks similar (although not
/// identical) to a normal enum definition:
///
/// ```rust,ignore
/// create_protocol_enum!(
/// /// IPv4 protocol number.
/// ///
/// /// The protocol numbers are specified in
/// /// https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml
/// #[allow(missing_docs)]
/// #[derive(Copy, Clone, Hash, Eq, PartialEq)]
/// pub enum Ipv4Proto: u8 {
/// Icmp, 1, "ICMP";
/// Igmp, 2, "IGMP";
/// Tcp, 6, "TCP";
/// Udp, 17, "UDP";
/// + Proto(IpProto);
/// _, "IPv4 protocol {}";
/// }
/// );
/// ```
///
/// Unlike a normal enum definition, this macro takes the following extra
/// information:
/// - The type of the underlying numerical representation of the protocol number
/// (`u8`, `u16`, etc)
/// - For each variant, besides the variant's name:
/// - The value of the protocol number associated with that variant
/// - A string representation used in implementations of `Display` and `Debug`
/// - A generic string representation used in the `Display` and `Debug` impls to
/// print unrecognized protocol numbers
///
/// Note that, in addition to the variants specified in the macro invocation, the
/// following special variants may be added:
/// - A variant which includes data may be added using the syntax `+
/// Variant(Type);`
/// - An `Other` variant which captures all values not given their own variants
/// may be added using the syntax `_, "format string {}"`
///
/// For a numerical type `U` (`u8`, `u16`, etc), impls of `Into<U>` are generated.
/// `From<U>` impls are generated if the `Other` variant is specified. If the
/// `Other` variant is not specified, `TryFrom<U>` will be generated instead.
macro_rules! create_protocol_enum {
($(#[$attr:meta])* enum $name:ident: $repr:ty {
$($variant:ident, $value:expr, $fmt:expr;)*
+ $delegate_name:ident($delegate_ty:ty);
_, $other_fmt:expr;
}) => {
// use `()` to explicitly forward the information about private items
__create_protocol_enum_inner!($(#[$attr])* () enum $name: $repr { $($variant, $value, $fmt;)* + $delegate_name($delegate_ty); _, $other_fmt; });
};
($(#[$attr:meta])* pub enum $name:ident: $repr:ty {
$($variant:ident, $value:expr, $fmt:expr;)*
+ $delegate_name:ident($delegate_ty:ty);
_, $other_fmt:expr;
}) => {
__create_protocol_enum_inner!($(#[$attr])* (pub) enum $name: $repr { $($variant, $value, $fmt;)* + $delegate_name($delegate_ty); _, $other_fmt; });
};
($(#[$attr:meta])* enum $name:ident: $repr:ty {
$($variant:ident, $value:expr, $fmt:expr;)*
+ $delegate_name:ident($delegate_ty:ty);
}) => {
// use `()` to explicitly forward the information about private items
__create_protocol_enum_inner!($(#[$attr])* () enum $name: $repr { $($variant, $value, $fmt;)* + $delegate_name($delegate_ty); });
};
($(#[$attr:meta])* pub enum $name:ident: $repr:ty {
$($variant:ident, $value:expr, $fmt:expr;)*
+ $delegate_name:ident($delegate_ty:ty);
}) => {
__create_protocol_enum_inner!($(#[$attr])* (pub) enum $name: $repr { $($variant, $value, $fmt;)* + $delegate_name($delegate_ty); });
};
($(#[$attr:meta])* enum $name:ident: $repr:ty {
$($variant:ident, $value:expr, $fmt:expr;)*
_, $other_fmt:expr;
}) => {
// use `()` to explicitly forward the information about private items
__create_protocol_enum_inner!($(#[$attr])* () enum $name: $repr { $($variant, $value, $fmt;)* _, $other_fmt; });
};
($(#[$attr:meta])* pub enum $name:ident: $repr:ty {
$($variant:ident, $value:expr, $fmt:expr;)*
_, $other_fmt:expr;
}) => {
__create_protocol_enum_inner!($(#[$attr])* (pub) enum $name: $repr { $($variant, $value, $fmt;)* _, $other_fmt; });
};
($(#[$attr:meta])* enum $name:ident: $repr:ty {
$($variant:ident, $value:expr, $fmt:expr;)*
}) => {
// use `()` to explicitly forward the information about private items
__create_protocol_enum_inner!($(#[$attr])* () enum $name: $repr { $($variant, $value, $fmt;)* });
};
($(#[$attr:meta])* pub enum $name:ident: $repr:ty {
$($variant:ident, $value:expr, $fmt:expr;)*
}) => {
__create_protocol_enum_inner!($(#[$attr])* (pub) enum $name: $repr { $($variant, $value, $fmt;)* });
};
}
/// Like `Result::expect`, but takes a format string and arguments which will be
/// evaluated only when panicking.
macro_rules! expect {
($e:expr, $fmt:literal $(, $args:expr)*) => {
$e.unwrap_or_else(|_| panic!($fmt $(, $args)*))
}
}
/// Declare a benchmark function.
///
/// The function will be named `$name`. If the `benchmark` feature is enabled,
/// it will be annotated with the `#[bench]` attribute, and the provided `$fn`
/// will be invoked with a `&mut test::Bencher` - in other words, a real
/// benchmark. If the `benchmark` feature is disabled, the function will be
/// annotated with the `#[test]` attribute, and the provided `$fn` will be
/// invoked with a `&mut TestBencher`, which has the effect of creating a test
/// that runs the benchmarked function for a single iteration.
///
/// Note that `$fn` doesn't have to be a named function - it can also be an
/// anonymous closure.
#[cfg(test)]
macro_rules! bench {
($name:ident, $fn:expr) => {
#[cfg(feature = "benchmark")]
#[bench]
fn $name(b: &mut test::Bencher) {
$fn(b);
}
// TODO(joshlf): Remove the `#[ignore]` once all benchmark tests pass.
#[cfg(not(feature = "benchmark"))]
#[test]
fn $name() {
$fn(&mut crate::testutil::benchmarks::TestBencher);
}
};
}