dot15d4_frame/ie/
payloads.rs

1use super::NestedInformationElementsIterator;
2use super::{Error, Result};
3
4/// A reader/writer for the IEEE 802.15.4 Payload Information Elements.
5#[derive(Debug, Eq, PartialEq)]
6pub struct PayloadInformationElement<T: AsRef<[u8]>> {
7    data: T,
8}
9
10impl<T: AsRef<[u8]>> PayloadInformationElement<T> {
11    /// Create a new [`PayloadInformationElement`] reader/writer from a given
12    /// buffer.
13    ///
14    /// # Errors
15    ///
16    /// Returns an error if the buffer is too short to contain a payload
17    /// information element.
18    pub fn new(data: T) -> Result<Self> {
19        let ie = Self::new_unchecked(data);
20
21        if !ie.check_len() {
22            return Err(Error);
23        }
24
25        Ok(ie)
26    }
27
28    /// Returns `false` if the buffer is too short to contain a payload
29    /// information element.
30    fn check_len(&self) -> bool {
31        self.data.as_ref().len() >= 2
32    }
33
34    /// Create a new [`PayloadInformationElement`] reader/writer from a given
35    /// buffer without length checking.
36    pub fn new_unchecked(data: T) -> Self {
37        Self { data }
38    }
39
40    /// Return the length field value.
41    #[allow(clippy::len_without_is_empty)]
42    pub fn len(&self) -> usize {
43        let b = &self.data.as_ref()[0..2];
44        u16::from_le_bytes([b[0], b[1]]) as usize & 0b1111111111
45    }
46
47    /// Return the [`PayloadGroupId`].
48    pub fn group_id(&self) -> PayloadGroupId {
49        let b = &self.data.as_ref()[0..2];
50        let id = (u16::from_le_bytes([b[0], b[1]]) >> 11) & 0b111;
51        PayloadGroupId::from(id as u8)
52    }
53
54    /// Return the content of this Header Information Element.
55    pub fn content(&self) -> &[u8] {
56        &self.data.as_ref()[2..][..self.len()]
57    }
58
59    /// Returns [`NestedInformationElementsIterator`] [`Iterator`].
60    ///
61    /// ## Panics
62    /// This method panics if the [`PayloadInformationElement`] is not an
63    /// [`MLME`] group.
64    ///
65    /// [`MLME`]: PayloadGroupId::Mlme
66    pub fn nested_information_elements(&self) -> NestedInformationElementsIterator {
67        assert!(self.group_id() == PayloadGroupId::Mlme);
68        NestedInformationElementsIterator::new(self.content())
69    }
70}
71
72impl<T: AsRef<[u8]> + AsMut<[u8]>> PayloadInformationElement<T> {
73    /// Clear the content of this Header Information Element.
74    pub fn clear(&mut self) {
75        self.data.as_mut().fill(0);
76    }
77
78    /// Set the length field value.
79    pub fn set_length(&mut self, len: u16) {
80        const MASK: u16 = 0b0000_0111_1111_1111;
81        let b = &mut self.data.as_mut()[0..2];
82        let value = u16::from_le_bytes([b[0], b[1]]) & !MASK;
83        let value = value | (len & MASK);
84        b.copy_from_slice(&value.to_le_bytes());
85    }
86
87    /// Set the [`PayloadGroupId`].
88    pub fn set_group_id(&mut self, id: PayloadGroupId) {
89        const MASK: u16 = 0b0111_1000_0000_0000;
90        let b = &mut self.data.as_mut()[0..2];
91        let value = u16::from_le_bytes([b[0], b[1]]) & !MASK;
92        let value = value | ((id as u16) << 11) | 0b1000_0000_0000_0000;
93        b.copy_from_slice(&value.to_le_bytes());
94    }
95
96    /// Return the content of this Header Information Element.
97    pub fn content_mut(&mut self) -> &mut [u8] {
98        &mut self.data.as_mut()[2..]
99    }
100}
101
102impl<T: AsRef<[u8]>> core::fmt::Display for PayloadInformationElement<T> {
103    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
104        match self.group_id() {
105            PayloadGroupId::Mlme => {
106                writeln!(f, "{:?}", self.group_id())?;
107
108                for nested in self.nested_information_elements() {
109                    writeln!(f, "  {}", nested)?;
110                }
111
112                Ok(())
113            }
114            id => write!(f, "{:?}({:0x?})", id, self.content()),
115        }
116    }
117}
118
119/// Payload Information Element ID.
120#[derive(Debug, Eq, PartialEq)]
121pub enum PayloadGroupId {
122    /// Encapsulated Service Data Unit Information Elements
123    Esdu = 0x00,
124    /// MAC sublayer Management Entity Information Elements
125    Mlme = 0x1,
126    /// Vendor specific Nested Information Elements
127    VendorSpecific = 0x02,
128    /// Payload Termination
129    PayloadTermination = 0x0f,
130    /// Unknown
131    Unknown,
132}
133
134impl From<u8> for PayloadGroupId {
135    fn from(value: u8) -> Self {
136        match value {
137            0x00 => Self::Esdu,
138            0x01 => Self::Mlme,
139            0x02 => Self::VendorSpecific,
140            0x0f => Self::PayloadTermination,
141            _ => Self::Unknown,
142        }
143    }
144}
145
146/// An [`Iterator`] over [`PayloadInformationElement`].
147#[derive(Debug)]
148pub struct PayloadInformationElementsIterator<'f> {
149    pub(crate) data: &'f [u8],
150    pub(crate) offset: usize,
151    pub(crate) terminated: bool,
152}
153
154impl PayloadInformationElementsIterator<'_> {
155    /// Return the offset of the iterator.
156    pub fn offset(&self) -> usize {
157        self.offset
158    }
159}
160
161impl<'f> Iterator for PayloadInformationElementsIterator<'f> {
162    type Item = PayloadInformationElement<&'f [u8]>;
163
164    fn next(&mut self) -> Option<Self::Item> {
165        if self.terminated {
166            None
167        } else {
168            let Ok(ie) = PayloadInformationElement::new(&self.data[self.offset..]) else {
169                self.terminated = true;
170                return None;
171            };
172
173            self.terminated = matches!(ie.group_id(), PayloadGroupId::PayloadTermination);
174
175            self.offset += ie.len() + 2;
176
177            if self.offset >= self.data.len() {
178                self.terminated = true;
179            }
180
181            Some(ie)
182        }
183    }
184}