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,
})
}
}