Skip to main content

coap_message_implementations/inmemory/
read_impl.rs

1//! Implementations of the [`Message`] and related types
2
3use super::*;
4
5use core::cell::Cell;
6
7impl<'a> Message<SliceBuffer<'a>> {
8    /// Creates a message that is parsed from a slice.
9    ///
10    /// This is a short-cut for creating a [`SliceBuffer`] and passing it to [`Self::new()`].
11    #[must_use]
12    pub fn new_from_slice(code: u8, options_and_payload: &'a [u8]) -> Self {
13        Message::new(SliceBuffer::new(code, options_and_payload))
14    }
15}
16
17/// A CoAP message that resides in contiguous readable memory.
18///
19/// This implementation does not attempt to do any early validation. On encoding errors discovered
20/// at runtime, it simply emits the critical-and-not-safe-to-forward CoAP option 65535
21/// ([`OPTION_INVALID`]), which to the indication indicates that something went wrong
22///
23/// Message is covariant over its lifetime because it only reads from there, effectively
24/// fulfilling the expectations of [`LifetimesMatterLittle`]. That
25/// property is currently not exposed.
26///
27/// # Implementation details
28///
29/// When used with [`coap_message::helpers::RefWithStaticType`], it always uses `Message<EMS>`
30/// where `EMS` is the type inside the `LifetimesMatterLittle` of the `EM::static_variant()`
31/// result. This is needed to run [`Self::downcast_from`], and restores the lifetime based on the
32/// given impl Trait reference's lifetime using its covariance property.
33#[derive(Clone, Debug)]
34#[cfg_attr(feature = "defmt", derive(defmt::Format))]
35pub struct Message<EM: MessageBuffer> {
36    /// The buffer message buffer that is being decoded.
37    inner: EM,
38    /// Indicator of where the payload is, which is only populated when the message is first parsed
39    /// to the payload.
40    ///
41    /// When this is set, it also indicates whether or not the full CoAP options-and-payload are
42    /// well-formed at the CoAP level.
43    payload_cache: Cell<Option<Processing>>,
44    /// Length of used part of `.inner.tail()`.
45    ///
46    /// Storing this is wasteful at first glance, as it is redundant with inner's length, but
47    ///
48    /// * This is not the case when inner is backed by a `&[u8; N]` rather than a `&[u8]` (although
49    ///   a more realistic version thereof would likely be a `*mut [u8; N]` with offsets for code
50    ///   and start-of-options).
51    ///
52    /// * When the back-end is mutable, it allows upgrading to a mutable message.
53    end: usize,
54}
55
56#[derive(Copy, Clone, Debug, PartialEq, Eq)]
57#[cfg_attr(feature = "defmt", derive(defmt::Format))]
58pub(crate) enum Processing {
59    /// The options were iterated over successfully, and payload was found and has a length of .0.
60    ///
61    /// (Implementation-wise, this is easier than storing the index, because we can't compute the
62    /// index while iterating over the options).
63    // It'd be great to have a PositiveIsize type so that the whole thing would be word-sized.
64    OkPayloadLength(usize),
65    /// The options were iterated over and ended at the data's end.
66    OkNoPayload,
67    /// The data did not contain a correctly formatted CoAP options-and-payload sequence
68    Error,
69}
70
71/// Helper for extracting the type ID used with an MessageBuffer out of a value with unnamed
72/// (RPIT) type.
73#[cfg(feature = "downcast")]
74fn type_id_of_emv_of_lml<T: 'static + MessageBuffer>(
75    _val: &LifetimesMatterLittle<T>,
76) -> core::any::TypeId {
77    core::any::TypeId::of::<Message<T>>()
78}
79
80impl<EM: MessageBuffer> Message<EM> {
81    /// Creates a new instance backed by an encoded message buffer.
82    ///
83    /// Decoding assumes that the full tail of the buffer contains options and payload.
84    pub fn new(inner: EM) -> Self {
85        Self {
86            end: inner.tail().len(),
87            payload_cache: Cell::default(),
88            inner,
89        }
90    }
91
92    /// Creates a new instance backed by an encoded message buffer.
93    ///
94    /// Decoding assumes that the first `length` bytes contain options and payload. (The buffer
95    /// having the option to be longer is mostly useful when it is also an
96    /// [`MessageBufferMut`][MessageBufferMut], and might later get
97    /// turned [`.into_mutable()`][Self::into_mutable()]).
98    pub fn new_until(inner: EM, length: usize) -> Self {
99        Self {
100            end: length,
101            payload_cache: Cell::default(),
102            inner,
103        }
104    }
105
106    #[cfg(feature = "downcast")]
107    pub fn downcast_from<M: ReadableMessage>(generic: &M) -> Option<&Self> {
108        let (reference, type_id) = generic.with_static_type_annotation()?.into_inner();
109
110        let our_static = EM::static_variant()?;
111        let our_id = type_id_of_emv_of_lml(&our_static);
112
113        if type_id != our_id {
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    /// Makes sure that the own processing state is set.
125    fn ensure_parsed(&self) -> Processing {
126        if let Some(v) = self.payload_cache.get() {
127            v
128        } else {
129            // At the point where this is done, ie. practically at the time of processing the
130            // payload, users *should* iterate over options first anyway, but let's not crash
131            // their program just because they do CoAP oddly.
132
133            // Doing it for the side effect
134            self.options().for_each(|_| ());
135
136            self.payload_cache
137                .get()
138                .expect("Side effect of options iteration is that this should have been set")
139        }
140    }
141
142    /// Converts the message into a mutable writable message.
143    ///
144    /// This can be useful to reuse a buffer that has been passed to the application, e.g. for
145    /// in-place decryption or to shove around indefinite-length CBOR encoding inside payloads.
146    ///
147    /// # Errors
148    ///
149    /// … are only raised if the message contains parsing errors (for a mutable message is stricter
150    /// in that regard).
151    pub fn into_mutable(self) -> Result<MessageMut<EM>, ParsingError>
152    where
153        EM: MessageBufferMut,
154    {
155        let payload_len = match self.ensure_parsed() {
156            Processing::OkPayloadLength(l) => l,
157            Processing::OkNoPayload => 0,
158            Processing::Error => return Err(ParsingError(())),
159        };
160
161        Ok(MessageMut {
162            encoded: self.inner,
163            end: self.end,
164            // FIXME: Do we get away with that? Adding options won't work, but inserting should.
165            latest: u16::MAX,
166            payload_start: match payload_len {
167                0 => None,
168                l => Some(self.end - l),
169            },
170        })
171    }
172}
173
174/// Error type indicating a malformed CoAP message.
175///
176/// While usually, [`Message`] does not err on parsing trouble as per its documentation (it
177/// maps the exotic error case into the more regular error case that needs to be handled by the
178/// user anyway), but in rare cases it does need to err (e.g. when converting to a
179/// [`MessageMut`] that has an internal invariant on well-formedness).
180#[derive(Debug, thiserror::Error)]
181#[error("CoAP message was not well-formed")]
182pub struct ParsingError(());
183
184/// When the inner item stores more than just the code and encoded options, this can be used to
185/// gain read-only access to any additional data.
186///
187/// Note that no matter whether access is shared or exclusive, the [`MessageBuffer`] must uphold
188/// its promise to always return the same content.
189impl<EM: MessageBuffer> AsRef<EM> for Message<EM> {
190    fn as_ref(&self) -> &EM {
191        &self.inner
192    }
193}
194
195impl<EM: MessageBuffer> ReadableMessage for Message<EM> {
196    type Code = u8;
197    type MessageOption<'a>
198        = MessageOption<'a>
199    where
200        Self: 'a;
201    type OptionsIter<'a>
202        = OptionsIter<'a>
203    where
204        Self: 'a;
205
206    fn code(&self) -> u8 {
207        self.inner.code()
208    }
209
210    fn payload(&self) -> &[u8] {
211        let len = match self.ensure_parsed() {
212            Processing::OkPayloadLength(l) => l,
213            Processing::OkNoPayload => 0,
214            // Silently produce empty payload -- the user will need to have checked the
215            // options and have found the 65535 option.
216            Processing::Error => 0,
217        };
218
219        let optpayload = &self.inner.tail()[..self.end];
220        &optpayload[optpayload.len() - len..]
221    }
222
223    fn options(&self) -> OptionsIter<'_> {
224        OptionsIter(
225            crate::option_iteration::OptPayloadReader::new(&self.inner.tail()[..self.end]),
226            Some(&self.payload_cache),
227        )
228    }
229
230    #[cfg(feature = "downcast")]
231    fn with_static_type_annotation(
232        &self,
233    ) -> Option<coap_message::helpers::RefWithStaticType<'_, Self>> {
234        // SAFETY: It is this type's policy that its RefWithStaticType ID is the given
235        // Message<'static>.
236        let em_static = EM::static_variant()?;
237
238        Some(unsafe {
239            coap_message::helpers::RefWithStaticType::new(self, type_id_of_emv_of_lml(&em_static))
240        })
241    }
242}
243
244impl<EM: MessageBuffer> WithSortedOptions for Message<EM> {}