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::{ReadableMessage, WithSortedOptions};
13
14use crate::option_iteration::{OptItem, OptPayloadReader};
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 #[must_use]
102 pub fn new(code: u8, options_and_payload: &'a [u8]) -> Self {
103 Self {
104 inner: EncodedMessageView::new(SliceMessage {
105 code,
106 options_and_payload,
107 }),
108 }
109 }
110
111 #[cfg(feature = "downcast")]
112 pub fn downcast_from<M: ReadableMessage>(generic: &'a M) -> Option<&'a Self> {
113 let (reference, type_id) = generic.with_static_type_annotation()?.into_inner();
114 if type_id != core::any::TypeId::of::<Message<'static>>() {
115 return None;
116 }
117 // One of us, one of us.
118 let ptr = reference as *const M as *const Self;
119 // SAFETY: The RefWithStaticType matching our type ID will only be constructed if M is
120 // Message, and whatever Message<'b> it was before, it will live at least for 'a (because
121 // we got a &'a Message<'b> and is covariant.
122 Some(unsafe { &*ptr })
123 }
124}
125
126impl ReadableMessage for Message<'_> {
127 type Code = u8;
128 type MessageOption<'a>
129 = MessageOption<'a>
130 where
131 Self: 'a;
132 type OptionsIter<'a>
133 = OptionsIter<'a>
134 where
135 Self: 'a;
136
137 fn code(&self) -> u8 {
138 self.inner.code()
139 }
140
141 fn payload(&self) -> &[u8] {
142 self.inner.payload()
143 }
144
145 fn options(&self) -> OptionsIter<'_> {
146 self.inner.options()
147 }
148
149 #[cfg(feature = "downcast")]
150 fn with_static_type_annotation(
151 &self,
152 ) -> Option<coap_message::helpers::RefWithStaticType<'_, Self>> {
153 // SAFETY: It is this type's policy that its RefWithStaticType ID is the given
154 // Message<'static>.
155 Some(unsafe {
156 coap_message::helpers::RefWithStaticType::new(
157 self,
158 core::any::TypeId::of::<Message<'static>>(),
159 )
160 })
161 }
162}
163impl WithSortedOptions for Message<'_> {}
164
165/// A trait that can implemented on in-memory encoded messages; then, an [`EncodedMessageView`]
166/// struct can be placed around the implementer to implement [`coap_message::ReadableMessage`].
167///
168/// Implementations must not alter the content returned by the accessors. As this trait is safe to
169/// implement, the content of the returned slice could change between calls even when the encoded
170/// message was owned or exclusively referenced (e.g. when backed by a
171/// [`RefCell`][core::cell::RefCell]). Therefore, neither this crate nor others can rely on
172/// the content upholding any safety guarantees -- but it may (and should) panic rater than erring
173/// if the expectation of stable content is violated.
174pub trait EncodedMessage {
175 /// The code of the message
176 fn code(&self) -> u8;
177 /// The memory area containing the encoded options, payload marker and payload
178 fn options_and_payload(&self) -> &[u8];
179}
180
181/// A wrapper around any data structure containing a readable message
182///
183/// This is not `Sync` -- while it is currently a zero-sized wrapper, it may gain optimizations
184/// later such as memoizing where the payload begins (and this is only practical with interior
185/// mutability).
186///
187/// See [Message] for how this does not perform early validation and handles message errors.
188// FIXME test whether the _phantom does the right thing
189#[derive(Clone, Debug)]
190#[cfg_attr(feature = "defmt", derive(defmt::Format))]
191pub struct EncodedMessageView<EM: EncodedMessage> {
192 inner: EM,
193 _phantom: core::marker::PhantomData<core::cell::Cell<()>>,
194}
195
196impl<EM: EncodedMessage> EncodedMessageView<EM> {
197 pub fn new(inner: EM) -> Self {
198 Self {
199 inner,
200 _phantom: core::marker::PhantomData,
201 }
202 }
203}
204
205/// When the inner item stores more than just the code and encoded options, this can be used to
206/// gain read-only access to any additional data. (Mutating access is blocked to ensure that future
207/// optimizations like memoizing the payload position are possible; it might still be enabled if
208/// memoized data is cleared just to be on the safe side).
209// FIXME: Might even dereference into it?
210impl<EM: EncodedMessage> AsRef<EM> for EncodedMessageView<EM> {
211 fn as_ref(&self) -> &EM {
212 &self.inner
213 }
214}
215
216impl<EM: EncodedMessage> ReadableMessage for EncodedMessageView<EM> {
217 type Code = u8;
218 type MessageOption<'a>
219 = MessageOption<'a>
220 where
221 Self: 'a;
222 type OptionsIter<'a>
223 = OptionsIter<'a>
224 where
225 Self: 'a;
226
227 fn code(&self) -> u8 {
228 self.inner.code()
229 }
230
231 // This is one of the not-most-efficient things mentioned in the module introduction: It's
232 // iterating through the complete options stream on every payload call, rather than memoizing
233 // its location when the options are iterated over.
234 fn payload(&self) -> &[u8] {
235 let empty: &[u8] = &[];
236
237 // ... into which we'll index
238 let optpayload = self.inner.options_and_payload();
239
240 OptPayloadReader::new(optpayload)
241 .find(|x| !matches!(x, OptItem::Option { .. }))
242 .map_or(empty, |x| {
243 if let OptItem::Payload(data) = x {
244 // Can't return data itself because the iterator doesn't outlive this function
245 // To be replaced when ptr_wrapping_offset_from is stabilized
246 let offset = data.as_ptr() as usize - optpayload.as_ptr() as usize;
247 &optpayload[offset..]
248 } else {
249 // Silently produce empty payload -- the user will need to have checked the
250 // options and have found the 65535 option.
251 &[]
252 }
253 })
254 }
255
256 fn options(&self) -> OptionsIter<'_> {
257 OptionsIter(OptPayloadReader::new(self.inner.options_and_payload()))
258 }
259}
260
261impl<EM: EncodedMessage> WithSortedOptions for EncodedMessageView<EM> {}