fxt/
header.rs

1// Copyright 2023 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#[macro_export]
6macro_rules! trace_header {
7    ($name:ident (max_size_bit: $upper_size_bit:literal) ($size_ty:ty) $(($header_ty:expr))? {
8        $($field_ty:ty, $getter:ident: $start_bit:literal, $end_bit:literal;)*
9    }) => {
10        trace_header!(
11            $name (max_size_bit: $upper_size_bit) ($size_ty) $(($header_ty))?
12            {
13                $($field_ty, $getter: $start_bit, $end_bit;)*
14            } => |_h| Ok(())
15        );
16    };
17    ($name:ident $(($header_ty:expr))? {
18        $($field_ty:ty, $getter:ident: $start_bit:literal, $end_bit:literal;)*
19    }) => {
20        trace_header!(
21            $name $(($header_ty))?
22            {
23                $($field_ty, $getter: $start_bit, $end_bit;)*
24            } => |_h| Ok(())
25        );
26    };
27    (
28        $name:ident $(($header_ty:expr))? {
29            $($field_ty:ty, $getter:ident: $start_bit:literal, $end_bit:literal;)*
30        } => |$header:ident $(: $header_arg_ty:ty)?| $verify:expr
31    ) => {
32        trace_header!(
33            $name (max_size_bit: 15) (u16) $(($header_ty))?
34            {
35                $($field_ty, $getter: $start_bit, $end_bit;)*
36            } => |$header $(: $header_arg_ty)?| $verify
37        );
38    };
39    (
40        $name:ident (max_size_bit: $upper_size_bit:literal) ($size_ty:ty) $(($header_ty:expr))? {
41            $($field_ty:ty, $getter:ident: $start_bit:literal, $end_bit:literal;)*
42        } => |$header:ident $(: $header_arg_ty:ty)?| $verify:expr
43    ) => {
44        // We invoke the bitfield macros ourselves here so we can use derives on the type.
45        #[derive(Clone, Copy, Eq, PartialEq)]
46        pub struct $name(u64);
47
48        bitfield::bitfield_bitrange! { struct $name(u64) }
49
50        // NB: bitfield macros flip the start and end bits compared to ours.
51        impl std::fmt::Debug for $name {
52            bitfield::bitfield_debug! {
53                struct $name;
54                u8, raw_type, _: 3, 0;
55                $size_ty, size_words, _: $upper_size_bit, 4;
56                $($field_ty, $getter, _: $end_bit, $start_bit;)*
57            }
58        }
59
60        impl $name {
61            paste::paste! { bitfield::bitfield_fields! {
62                u64;
63                pub u8, raw_type, set_raw_type: 3, 0;
64                pub $size_ty, size_words, set_size_words: $upper_size_bit, 4;
65                $(pub $field_ty, $getter, [<set_ $getter>]: $end_bit, $start_bit;)*
66            }}
67
68            #[allow(unused, unused_mut)]
69            pub(crate) fn empty() -> Self {
70                let mut header = Self(0);
71                $(header.set_raw_type($header_ty);)?
72                header
73            }
74
75            fn new(bits: u64) -> Result<Self, crate::ParseError> {
76                let header = Self(bits);
77
78                $(if header.raw_type() != $header_ty {
79                    return Err(crate::ParseError::WrongType {
80                        context: stringify!($name),
81                        expected: $header_ty,
82                        observed: header.raw_type(),
83                    });
84                })?
85
86                // Run invoker-defined verification and return if it's an error.
87                let res: Result<(), crate::ParseError> = (|$header $(: $header_arg_ty)?| $verify)(&header);
88                res?;
89
90                Ok(header)
91            }
92
93            #[allow(unused)] // Some headers are converted from others and don't need to be parsed.
94            fn parse(buf: &[u8]) -> crate::ParseResult<'_, Self> {
95                use nom::Parser;
96                nom::combinator::map_res(nom::number::streaming::le_u64, |h| Self::new(h)).parse(buf)
97            }
98
99            #[allow(unused)] // Not all headers come with payloads, some we use to probe for types.
100            fn take_payload<'a>(&self, buf: &'a [u8]) -> crate::ParseResult<'a, &'a [u8]> {
101                if self.size_words() == 0 {
102                    return Err(nom::Err::Failure(crate::ParseError::InvalidSize));
103                }
104                let size_bytes_without_header = (self.size_words() as usize - 1) * 8;
105                if size_bytes_without_header > buf.len() {
106                    let needed = size_bytes_without_header - buf.len();
107                    return Err(nom::Err::Incomplete(nom::Needed::Size(
108                        std::num::NonZero::new(needed).unwrap())
109                    ));
110                }
111                let (payload, rem) = buf.split_at(size_bytes_without_header);
112                Ok((rem, payload))
113            }
114        }
115
116        impl crate::header::TraceHeader for $name {
117            fn set_size_words(&mut self, n: u16) {
118                self.set_size_words(n.try_into().unwrap());
119            }
120            fn to_le_bytes(&self) -> [u8; 8] {
121                self.0.to_le_bytes()
122            }
123        }
124    };
125}
126
127pub(crate) trait TraceHeader {
128    fn set_size_words(&mut self, n: u16);
129    fn to_le_bytes(&self) -> [u8; 8];
130}