coap_message_implementations/
heap.rs

1//! This module provides [HeapMessage], an implementation of all the message traits backed by heap
2//! memory.
3//!
4//! It serves both to easily get an owned copy of a message (using [`HeapMessage::new()`] and
5//! [`MinimalWritableMessage::set_from_message()`]), especially if that needs frequent manipulation
6//! of options, and as an example of how to the message traits can be implemented.
7//!
8//! This module is available only if the `alloc` feature is enabled on this crate.
9//!
10//! Cross references:
11//!
12//! * The [coap-lite] crate's Packet struct is very similar to this, to the point where their
13//!   [MutableWritableMessage] implementations are identical. They differ by whether or not they
14//!   carry CoAP-over-UDP header information.
15//!
16//! * This implementation used to be part of the [coap_message] crate before its 0.3 version.
17//!
18//! [coap-lite]: https://crates.io/crates/coap-lite
19
20extern crate alloc;
21use alloc::{vec, vec::Vec};
22use core::convert::Infallible;
23
24use coap_message::{
25    MinimalWritableMessage, MutableWritableMessage, ReadableMessage, SeekWritableMessage,
26    WithSortedOptions,
27};
28
29/// A heap CoAP message backed by allocated memory
30///
31/// It stores its payload in a [`Vec`], and uses a [`BTreeMap<_, Vec<_>>`](alloc::collections::BTreeMap) to
32/// store all the individual option values.
33///
34/// It offers a few methods for direct manipulation of options, even out of sequence, that can not
35/// be expected from a general message buffer and are thus not captured in traits.
36///
37/// ```
38/// # use coap_message_implementations::heap::HeapMessage;
39/// use coap_message::{MinimalWritableMessage, ReadableMessage};
40/// let mut m = HeapMessage::new();
41/// m.set_code(1);
42/// m.add_option(11, b".well-known");
43/// m.add_option(11, b"core");
44///
45/// let mut m2 = HeapMessage::new();
46/// m2.set_from_message(&m);
47/// assert!(m.code() == 1);
48/// ```
49#[derive(Debug)]
50pub struct HeapMessage {
51    code: u8,
52    options: alloc::collections::BTreeMap<u16, Vec<Vec<u8>>>,
53    payload: Vec<u8>,
54}
55
56impl HeapMessage {
57    pub fn new() -> Self {
58        Self {
59            code: 0,
60            options: alloc::collections::BTreeMap::new(),
61            payload: vec![],
62        }
63    }
64
65    /// Replace the occurrence'th value of the optnum option in the message
66    ///
67    /// Panics if there's not ``occurrence + 1`` options of that number.
68    pub fn change_option(&mut self, optnum: u16, occurrence: usize, value: impl Into<Vec<u8>>) {
69        self.options.get_mut(&optnum).unwrap()[occurrence] = value.into();
70    }
71
72    /// Remove the occurrence'th option of option number optnum
73    ///
74    /// Panics if there's not ``occurrence + 1`` options of that number.
75    pub fn remove_option(&mut self, optnum: u16, occurrence: usize) {
76        let vec = self.options.get_mut(&optnum).unwrap();
77        vec.remove(occurrence);
78        if vec.len() == 0 {
79            self.options.remove(&optnum);
80        }
81    }
82}
83
84impl MinimalWritableMessage for HeapMessage {
85    type AddOptionError = Infallible;
86    type SetPayloadError = Infallible;
87    type UnionError = Infallible;
88
89    type Code = u8;
90    type OptionNumber = u16;
91
92    fn set_code(&mut self, code: u8) {
93        self.code = code;
94    }
95
96    fn add_option(&mut self, optnum: u16, data: &[u8]) -> Result<(), Infallible> {
97        self.options
98            .entry(optnum)
99            .or_insert(vec![])
100            .push(data.to_vec());
101        Ok(())
102    }
103
104    fn set_payload(&mut self, payload: &[u8]) -> Result<(), Infallible> {
105        self.payload = payload.to_vec();
106        Ok(())
107    }
108}
109
110impl MutableWritableMessage for HeapMessage {
111    fn available_space(&self) -> usize {
112        core::usize::MAX
113    }
114
115    fn payload_mut_with_len(&mut self, len: usize) -> Result<&mut [u8], Infallible> {
116        self.payload.resize(len, 0);
117        Ok(&mut self.payload)
118    }
119
120    fn truncate(&mut self, len: usize) -> Result<(), Infallible> {
121        self.payload.truncate(len);
122        Ok(())
123    }
124
125    fn mutate_options<F>(&mut self, mut callback: F)
126    where
127        F: FnMut(Self::OptionNumber, &mut [u8]),
128    {
129        for (&number, ref mut values) in self.options.iter_mut() {
130            for v in values.iter_mut() {
131                callback(number.into(), v);
132            }
133        }
134    }
135}
136
137impl SeekWritableMessage for HeapMessage {}
138
139pub struct MessageOption<'a> {
140    number: u16,
141    value: &'a [u8],
142}
143
144impl<'a> coap_message::MessageOption for MessageOption<'a> {
145    fn number(&self) -> u16 {
146        self.number
147    }
148    fn value(&self) -> &[u8] {
149        self.value
150    }
151}
152
153pub struct ReadCursor<'a> {
154    iter: alloc::collections::btree_map::Iter<'a, u16, Vec<Vec<u8>>>,
155    popped: (u16, &'a [Vec<u8>]),
156}
157
158impl<'a> Iterator for ReadCursor<'a> {
159    type Item = MessageOption<'a>;
160    fn next(&mut self) -> Option<MessageOption<'a>> {
161        if self.popped.1.len() == 0 {
162            self.popped = self.iter.next().map(|(k, v)| (*k, v.as_slice()))?;
163
164            // Avoided by construction
165            debug_assert!(
166                self.popped.1.len() > 0,
167                "HeapMessage had present but empty option"
168            );
169        }
170
171        let (first, rest) = self.popped.1.split_at(1);
172        self.popped.1 = rest;
173        Some(MessageOption {
174            number: self.popped.0,
175            value: first[0].as_slice(),
176        })
177    }
178}
179
180impl WithSortedOptions for HeapMessage {
181    // By virtue of the order of options used in BTreeMap
182}
183
184impl ReadableMessage for HeapMessage {
185    type Code = u8;
186    type MessageOption<'a> = MessageOption<'a>;
187    type OptionsIter<'a> = ReadCursor<'a>;
188
189    fn code(&self) -> u8 {
190        self.code
191    }
192    fn payload(&self) -> &[u8] {
193        &self.payload
194    }
195    fn options<'m>(&'m self) -> ReadCursor<'m> {
196        ReadCursor {
197            iter: self.options.iter(),
198            popped: (0, &[]),
199        }
200    }
201}