Skip to main content

coap_message_implementations/
option_iteration.rs

1//! Tools for iterating over encoded messages
2//!
3//! [`OptPayloadReader`] is the main struct of this module.
4
5use crate::option_extension::take_extension;
6
7/// Item type for an [`OptPayloadReader`]
8#[derive(Debug)]
9#[cfg_attr(feature = "defmt", derive(defmt::Format))]
10pub enum OptItem<'a> {
11    Option { number: u16, data: &'a [u8] },
12    Payload(&'a [u8]),
13    Error(&'static str),
14}
15
16/// Iteration cursor over a CoAP message's options and payload
17///
18/// This iterates over a contiguous memory area that contains encoded CoAP options and possibly a
19/// payload marker followed by a payload.
20///
21/// It will yield any number of ascending options, possibly followed by a payload, followed by the
22/// end of iteration.
23///
24/// An iteration item containing an Error puts the object into an indeterminate state; continuing
25/// iteration will produce nonsentical results.
26//
27// FIXME become non-pub once coapwsmessage figures out a better way to use InMemoryMessage
28pub struct OptPayloadReader<'a> {
29    slice: &'a [u8],
30    option_base: u16,
31}
32
33impl<'a> OptPayloadReader<'a> {
34    /// Initialize this iterator over the given slice of memory.
35    ///
36    /// The message is assumed to be the start of a CoAP message; thus, option numbers start at
37    /// zero.
38    #[must_use]
39    pub fn new(slice: &'a [u8]) -> Self {
40        Self {
41            slice,
42            option_base: 0,
43        }
44    }
45
46    /// Re-construct this iterator over the given slice of memory.
47    pub(crate) fn new_from(slice: &'a [u8], option_base: u16) -> Self {
48        Self { slice, option_base }
49    }
50
51    /// Destruct into it's field components to get at `self.slice`.
52    pub(crate) fn destruct(self) -> (&'a [u8], u16) {
53        (self.slice, self.option_base)
54    }
55}
56
57impl<'a> Iterator for OptPayloadReader<'a> {
58    type Item = OptItem<'a>;
59
60    fn next(&mut self) -> Option<OptItem<'a>> {
61        let delta_len = *self.slice.first()?;
62        self.slice = &self.slice[1..];
63
64        if delta_len == 0xff {
65            return Some(OptItem::Payload(core::mem::take(&mut self.slice)));
66        }
67
68        let mut delta = u16::from(delta_len) >> 4;
69        let mut len = u16::from(delta_len) & 0x0f;
70
71        if take_extension(&mut delta, &mut self.slice).is_err() {
72            // FIXME here and following: Is there any ergonomics trick that allows returning
73            // Some(...Error(...)) using the questionmark operator?
74            return Some(OptItem::Error("Erroneous delta"));
75        }
76        if take_extension(&mut len, &mut self.slice).is_err() {
77            return Some(OptItem::Error("Erroneous len"));
78        }
79
80        if let Some(s) = self.option_base.checked_add(delta) {
81            self.option_base = s;
82        } else {
83            return Some(OptItem::Error("Options wrap"));
84        }
85
86        let len = len.into();
87
88        if self.slice.len() < len {
89            return Some(OptItem::Error("Too short for option"));
90        }
91
92        let (retslice, tail) = self.slice.split_at(len);
93        self.slice = tail;
94
95        Some(OptItem::Option {
96            number: self.option_base,
97            data: retslice,
98        })
99    }
100}