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> {}