coap_message_implementations/
option_iteration.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
//! Tools for iterating over encoded messages
//!
//! [OptPayloadReader] is the main struct of this module.

use crate::option_extension::take_extension;

/// Item type for an [OptPayloadReader]
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum OptItem<'a> {
    Option { number: u16, data: &'a [u8] },
    Payload(&'a [u8]),
    Error(&'static str),
}

/// Iteration cursor over a CoAP message's options and payload
///
/// This iterates over a contiguious memory area that contains encoded CoAP options and possibly a
/// payload marker followed by a payload.
///
/// It will yield any number of ascending options, possibly followed by a payload, followed by the
/// end of iteration.
///
/// An iteration item containing an Error puts the object into an indeterminate state; continuing
/// iteration will produce nonsentical results.
//
// FIXME become non-pub once coapwsmessage figures out a better way to use InMemoryMessage
pub struct OptPayloadReader<'a> {
    slice: &'a [u8],
    option_base: u16,
}

impl<'a> OptPayloadReader<'a> {
    /// Initialize this iterator over the given slice of memory.
    ///
    /// The message is assumed to be the start of a CoAP message; thus, option numbers start at
    /// zero.
    pub fn new(slice: &'a [u8]) -> Self {
        Self {
            slice,
            option_base: 0,
        }
    }
}

impl<'a> Iterator for OptPayloadReader<'a> {
    type Item = OptItem<'a>;

    fn next(&mut self) -> Option<OptItem<'a>> {
        let delta_len = *self.slice.first()?;
        self.slice = &self.slice[1..];

        if delta_len == 0xff {
            return Some(OptItem::Payload(core::mem::take(&mut self.slice)));
        }

        let mut delta = (delta_len as u16) >> 4;
        let mut len = (delta_len as u16) & 0x0f;

        if take_extension(&mut delta, &mut self.slice).is_err() {
            // FIXME here and following: Is there any ergonomics trick that allows returning
            // Some(...Error(...)) using the questionmark operator?
            return Some(OptItem::Error("Erroneous delta"));
        }
        if take_extension(&mut len, &mut self.slice).is_err() {
            return Some(OptItem::Error("Erroneous len"));
        }

        if let Some(s) = self.option_base.checked_add(delta) {
            self.option_base = s;
        } else {
            return Some(OptItem::Error("Options wrap"));
        }

        let len = len.into();

        if self.slice.len() < len {
            return Some(OptItem::Error("Too short for option"));
        }

        let (retslice, tail) = self.slice.split_at(len);
        self.slice = tail;

        Some(OptItem::Option {
            number: self.option_base,
            data: retslice,
        })
    }
}