coap_message_implementations/
inmemory.rs

1//! Implementation of [coap_message::ReadableMessage] based on a serialized message
2//!
3//! [Message] is the main struct of this module -- if the message is available as a slice that
4//! lives as long as the message, that's the type to use. Otherwise, implement [EncodedMessage] on
5//! your data, and wrap it in an [EncodedMessageView]. (The [Message] is nothing else than the
6//! options/payload slice plus the code in a struct).
7//!
8//! Note that the [`crate::inmemory_write`] has a similar mechanism but does without an equivalent
9//! "view"; at the next breaking revision, those might be unified.
10#![cfg_attr(feature = "downcast", allow(unsafe_code))]
11
12use coap_message::*;
13
14use crate::option_iteration::*;
15
16/// An iterator producing [MessageOption]s by running along an encoded CoAP message options stream
17/// by using a [OptPayloadReader] and discarding the payload.
18pub struct OptionsIter<'a>(pub(crate) OptPayloadReader<'a>);
19
20/// Option value used by this library to indicate a format error in the message that was not
21/// detected by the time option / payload processing was started.
22///
23/// As this option is critical and proxy-unsafe, no application can expect to successfully process
24/// any message that contains this option. (The number is from the experimental-use range;
25/// implementations using this module will simply not support that option in experiments, as they
26/// can not distinguish it from an option formatting error).
27pub const OPTION_INVALID: u16 = u16::MAX;
28
29impl<'a> Iterator for OptionsIter<'a> {
30    type Item = MessageOption<'a>;
31
32    fn next(&mut self) -> Option<MessageOption<'a>> {
33        match self.0.next() {
34            Some(OptItem::Option { number, data }) => Some(MessageOption {
35                number,
36                value: data,
37            }),
38            Some(OptItem::Error { .. }) => Some(MessageOption {
39                number: OPTION_INVALID,
40                value: &[],
41            }),
42            // No need to recurse or loop -- it's always the last one
43            Some(OptItem::Payload(_)) => None,
44            None => None,
45        }
46    }
47}
48
49/// A simple [coap_message::MessageOption] implementation for memory-mapped CoAP messages
50pub struct MessageOption<'a> {
51    number: u16,
52    value: &'a [u8],
53}
54
55impl coap_message::MessageOption for MessageOption<'_> {
56    fn number(&self) -> u16 {
57        self.number
58    }
59    fn value(&self) -> &[u8] {
60        self.value
61    }
62}
63
64/// A CoAP message that resides in contiguous readable memory
65///
66/// This implementation does not attempt to do any early validation. On encoding errors discovered
67/// at runtime, it simply emits the critical-and-not-safe-to-forward CoAP option 65535
68/// ([OPTION_INVALID]), which to the indication indicates that something went wrong
69///
70/// Message is covariant over its lifetime because it only reads from there. FIXME: It should
71/// express that using a Deref or something like that.
72///
73/// # Implementation details
74///
75/// When used with [coap_message::helpers::RefWithStaticType], it always uses its `'static`
76/// counterpart as the corresponding type ID. Its [`Self::downcast_from`] method uses this, and
77/// restores the lifetime based on the given impl Trait reference's lifetime using its covariance
78/// property.
79#[derive(Clone, Debug)]
80pub struct Message<'a> {
81    inner: EncodedMessageView<SliceMessage<'a>>,
82}
83
84#[derive(Clone, Debug)]
85#[cfg_attr(feature = "defmt", derive(defmt::Format))]
86struct SliceMessage<'a> {
87    code: u8,
88    options_and_payload: &'a [u8],
89}
90
91impl EncodedMessage for SliceMessage<'_> {
92    fn code(&self) -> u8 {
93        self.code
94    }
95    fn options_and_payload(&self) -> &[u8] {
96        self.options_and_payload
97    }
98}
99
100impl<'a> Message<'a> {
101    pub fn new(code: u8, options_and_payload: &'a [u8]) -> Self {
102        Self {
103            inner: EncodedMessageView::new(SliceMessage {
104                code,
105                options_and_payload,
106            }),
107        }
108    }
109
110    #[cfg(feature = "downcast")]
111    pub fn downcast_from<M: ReadableMessage>(generic: &'a M) -> Option<&'a Self> {
112        let (reference, type_id) = generic.with_static_type_annotation()?.into_inner();
113        if type_id != core::any::TypeId::of::<Message<'static>>() {
114            return None;
115        }
116        // One of us, one of us.
117        let ptr = reference as *const M as *const Self;
118        // SAFETY: The RefWithStaticType matching our type ID will only be constructed if M is
119        // Message, and whatever Message<'b> it was before, it will live at least for 'a (because
120        // we got a &'a Message<'b> and is covariant.
121        Some(unsafe { &*ptr })
122    }
123}
124
125impl ReadableMessage for Message<'_> {
126    type Code = u8;
127    type MessageOption<'a>
128        = MessageOption<'a>
129    where
130        Self: 'a;
131    type OptionsIter<'a>
132        = OptionsIter<'a>
133    where
134        Self: 'a;
135
136    fn code(&self) -> u8 {
137        self.inner.code()
138    }
139
140    fn payload(&self) -> &[u8] {
141        self.inner.payload()
142    }
143
144    fn options(&self) -> OptionsIter<'_> {
145        self.inner.options()
146    }
147
148    #[cfg(feature = "downcast")]
149    fn with_static_type_annotation(
150        &self,
151    ) -> Option<coap_message::helpers::RefWithStaticType<'_, Self>> {
152        // SAFETY: It is this type's policy that its RefWithStaticType ID is the given
153        // Message<'static>.
154        Some(unsafe {
155            coap_message::helpers::RefWithStaticType::new(
156                self,
157                core::any::TypeId::of::<Message<'static>>(),
158            )
159        })
160    }
161}
162impl WithSortedOptions for Message<'_> {}
163
164/// A trait that can implemented on in-memory encoded messages; then, an [EncodedMessageView]
165/// struct can be placed around the implementer to implement [coap_message::ReadableMessage].
166pub trait EncodedMessage {
167    /// The code of the message
168    fn code(&self) -> u8;
169    /// The memory area containing the encoded options, payload marker and payload
170    fn options_and_payload(&self) -> &[u8];
171}
172
173/// A wrapper around any data structure containing a readable message
174///
175/// This is not `Sync` -- while it is currently a zero-sized wrapper, it may gain optimizations
176/// later such as memoizing where the payload begins (and this is only practical with interior
177/// mutability).
178///
179/// See [Message] for how this does not perform early validation and handles message errors.
180// FIXME test whether the _phantom does the right thing
181#[derive(Clone, Debug)]
182#[cfg_attr(feature = "defmt", derive(defmt::Format))]
183pub struct EncodedMessageView<EM: EncodedMessage> {
184    inner: EM,
185    _phantom: core::marker::PhantomData<core::cell::Cell<()>>,
186}
187
188impl<EM: EncodedMessage> EncodedMessageView<EM> {
189    pub fn new(inner: EM) -> Self {
190        Self {
191            inner,
192            _phantom: core::marker::PhantomData,
193        }
194    }
195}
196
197/// When the inner item stores more than just the code and encoded options, this can be used to
198/// gain read-only access to any additional data. (Mutating access is blocked to ensure that future
199/// optimizations like memoizing the payload position are possible; it might still be enabled if
200/// memoized data is cleared just to be on the safe side).
201// FIXME: Might even dereference into it?
202impl<EM: EncodedMessage> AsRef<EM> for EncodedMessageView<EM> {
203    fn as_ref(&self) -> &EM {
204        &self.inner
205    }
206}
207
208impl<EM: EncodedMessage> ReadableMessage for EncodedMessageView<EM> {
209    type Code = u8;
210    type MessageOption<'a>
211        = MessageOption<'a>
212    where
213        Self: 'a;
214    type OptionsIter<'a>
215        = OptionsIter<'a>
216    where
217        Self: 'a;
218
219    fn code(&self) -> u8 {
220        self.inner.code()
221    }
222
223    // This is one of the not-most-efficient things mentioned in the module introduction: It's
224    // iterating through the complete options stream on every payload call, rather than memoizing
225    // its location when the options are iterated over.
226    fn payload(&self) -> &[u8] {
227        let empty: &[u8] = &[];
228
229        // ... into which we'll index
230        let optpayload = self.inner.options_and_payload();
231
232        OptPayloadReader::new(optpayload)
233            .find(|x| !matches!(x, OptItem::Option { .. }))
234            .map(|x| {
235                if let OptItem::Payload(data) = x {
236                    // Can't return data itself because the iterator doesn't outlive this function
237                    // To be replaced when ptr_wrapping_offset_from is stabilized
238                    let offset = data.as_ptr() as usize - optpayload.as_ptr() as usize;
239                    &optpayload[offset..]
240                } else {
241                    // Silently produce empty payload -- the user will need to have checked the
242                    // options and have found the 65535 option.
243                    &[]
244                }
245            })
246            .unwrap_or(empty)
247    }
248
249    fn options(&self) -> OptionsIter<'_> {
250        OptionsIter(OptPayloadReader::new(self.inner.options_and_payload()))
251    }
252}
253
254impl<EM: EncodedMessage> WithSortedOptions for EncodedMessageView<EM> {}