coap_message_implementations/
inmemory_write.rs

1//! Implementation of [coap_message::MutableWritableMessage] on a slice of memory
2//!
3//! [`GenericMessage`] is the main struct of this module, with [`Message`] being the legacy version
4//! thereof that only supports slices (it is a type alias). A [`GenericMessage`] is constructed
5//! based on an [`EncodedMessage`], which can encapsulate anything from slices (when `EM` is a
6//! [`SliceMessage`]), to owned data, or something inbetween (like a single [`core::cell::RefMut`]
7//! that is being sliced into).
8//!
9//! Note that the [`crate::inmemory`] has a similar mechanism but uses a
10//! [`crate::inmemory::EncodedMessageView`] struct between the message and its encoded type; at the
11//! next breaking revision, those might be unified.
12#![cfg_attr(feature = "downcast", allow(unsafe_code))]
13
14pub use crate::error::WriteError;
15
16/// A message writing into a preallocated buffer consisting of slices
17pub type Message<'a> = GenericMessage<SliceMessage<'a>>;
18
19/// A message writing into a preallocated buffer
20#[derive(Clone)]
21pub struct GenericMessage<EM: EncodedMessage> {
22    /// Pointers to the actuall message data.
23    encoded: EM,
24
25    /// Latest option that has been written
26    latest: u16,
27    /// Index after the last written byte
28    end: usize,
29    /// First byte of any written payload
30    ///
31    /// If this has been set, the byte before it was written 0xff. This is None if the was an empty
32    /// payload.
33    payload_start: Option<usize>,
34}
35
36impl<EM: EncodedMessage> core::fmt::Debug for GenericMessage<EM> {
37    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
38        f.debug_struct("GenericMessage")
39            .field("EncodedMessage impl", &core::any::type_name::<EM>())
40            .field("encoded.code", &self.encoded.code())
41            .field("encoded.tail", &self.encoded.tail())
42            .field("latest", &self.latest)
43            .field("end", &self.end)
44            .field("payload_start", &self.payload_start)
45            .finish()
46    }
47}
48
49#[cfg(feature = "defmt")]
50impl<EM: EncodedMessage> defmt::Format for GenericMessage<EM> {
51    fn format(&self, f: defmt::Formatter<'_>) {
52        defmt::write!(
53            f,
54            "GenericMessage {{ EncodedMessage impl {=str}, code {=u8}, tail {=[u8]}, latest {=u16}, end {}, payload_start {} }}",
55            &core::any::type_name::<EM>(),
56            self.encoded.code(),
57            self.encoded.tail(),
58            self.latest,
59            self.end,
60            self.payload_start
61        );
62    }
63}
64
65/// Data backing a [`GenericMessage`].
66///
67/// Think of this as [`core::convert::AsMut`] but with differentiated access to two areas (the code
68/// and the options-and-payload).
69pub trait EncodedMessage {
70    fn code(&self) -> &u8;
71    fn code_mut(&mut self) -> &mut u8;
72    fn tail(&self) -> &[u8];
73    fn tail_mut(&mut self) -> &mut [u8];
74}
75
76/// The easiest implementation of [`EncodedMessage`] that is backed by exclusive references.
77pub struct SliceMessage<'a> {
78    code: &'a mut u8,
79    tail: &'a mut [u8],
80}
81
82impl EncodedMessage for SliceMessage<'_> {
83    fn code_mut(&mut self) -> &mut u8 {
84        self.code
85    }
86
87    fn tail_mut(&mut self) -> &mut [u8] {
88        self.tail
89    }
90
91    fn code(&self) -> &u8 {
92        self.code
93    }
94
95    fn tail(&self) -> &[u8] {
96        self.tail
97    }
98}
99
100/// A CoAP messag that resides in contiguous mutable memory
101///
102/// Exceeding the guarantees of MutableWritableMessage, this does allow some out-of-sequence
103/// invocations: Even after payload has been written to, options can be added, memmove'ing
104/// (right-rotating) the written payload, possibly truncating it out of the buffer. This is needed
105/// to accommodate libOSCORE's requirements (because while libOSCORE can also do without a
106/// memmove-ing message, that'd require its use through WritableMessage to adhere to in-OSCORE
107/// write sequence conventions, making the whole situation no easier on the coap-message
108/// abstraction). Data will only be moved if an option is added after content has been set, so this
109/// comes at no runtime cost for those who do not need it. (It may be later turned into a feature.
110/// Then, the memmove code would be removed; carrying the latest option number in the WriteState
111/// should come at no extra cost due to the struct's content and alignment).
112///
113/// When viewed through the [coap_message::ReadableMessage] trait, this will behave as if writing
114/// had stopped (but still allows writes after reading); in particular, the payload will be shown
115/// as empty. (This may be obvious from most points of view, but when coming from a
116/// [coap_message::MutableWritableMessage] point of view where payloads can only ever be truncated and not
117/// made longer, this clarification is relevant).
118///
119/// The type is covariant over its lifetime. This also means that we can never add any methods
120/// where we move references into Self, but we don't do this: The lifetime `'a` only serves to
121/// describe the memory backing it; data is moved in there, not stored by reference.
122///
123/// FIXME: The covaraint property is not available through ... how would it even be made available?
124impl<'a> Message<'a> {
125    pub fn new(code: &'a mut u8, tail: &'a mut [u8]) -> Self {
126        Message {
127            encoded: SliceMessage { code, tail },
128            latest: 0,
129            end: 0,
130            payload_start: None,
131        }
132    }
133
134    /// Create a MutableWritableMessage on a buffer that already contains a serialized message
135    ///
136    /// While this is generally not useful (a parsed message has its paylod already set, and if
137    /// there's no payload, there's no space to add options or any payload), it allows mutable
138    /// access to the option bytes and the payload. This is primarily useful in situations when
139    /// data is processed in place, eg. decrypted (in OSCORE), or CBOR is shifted around to get
140    /// contiguous slices out of indefinite length strings.
141    ///
142    /// This uses the same strategy for errors as [crate::inmemory::Message]: Validation happens
143    /// incrementally.
144    pub fn new_from_existing(code: &'a mut u8, tail: &'a mut [u8]) -> Self {
145        // We'll use a read-only message to get the cursor parameters. This is not terribly great
146        // performance-wise (this will iterate over the options, and then the user will do that
147        // again); can still be improved to keep the cursor in an indefinite state until the
148        // options have been iterated over, preferably when the inmemory message gains the
149        // corresponding memoization.
150        let read_only = crate::inmemory::Message::new(*code, tail);
151        // We don't do thorough parsing, just enough to get an easy index. Either the message is
152        // valid, then this will be OK, or it's not and the user will get garbage at read time.
153        use coap_message::ReadableMessage;
154        let payload_len = read_only.payload().len();
155
156        let payload_start = match payload_len {
157            0 => None,
158            n => Some(tail.len() - n),
159        };
160        let end = tail.len();
161
162        Message {
163            encoded: SliceMessage { code, tail },
164            payload_start,
165            end,
166            // No point in determining what the actual last option is: This is for mutating
167            // options, not adding new ones
168            latest: u16::MAX,
169        }
170    }
171
172    #[cfg(feature = "downcast")]
173    pub fn downcast_from<M: coap_message::MinimalWritableMessage>(
174        generic: &'a mut M,
175    ) -> Option<&'a mut Self> {
176        let (reference, type_id) = generic.with_static_type_annotation()?.into_inner();
177        if type_id != core::any::TypeId::of::<Message<'static>>() {
178            return None;
179        }
180        // One of us, one of us.
181        let ptr = reference as *mut M as *mut Self;
182        // SAFETY: The RefWithStaticType matching our type ID will only be constructed if M is
183        // Message, and whatever Message<'b> it was before, it will live at least for 'a (because
184        // we got a &'a Message<'b> and is covariant.
185        Some(unsafe { &mut *ptr })
186    }
187}
188
189impl<T: EncodedMessage> GenericMessage<T> {
190    pub fn new_from_empty_encoded(encoded: T) -> Self {
191        GenericMessage {
192            encoded,
193            latest: 0,
194            end: 0,
195            payload_start: None,
196        }
197    }
198}
199
200impl<EM: EncodedMessage> GenericMessage<EM> {
201    /// Discard anything that has been written in the message, and allow any operation that would
202    /// be allowed after calling [`Self::new()`].
203    ///
204    /// This is a short-cut to messages that can be snapshotted and rewound, and a band-aid until
205    /// that is available in traits.
206    pub fn reset(&mut self) {
207        self.latest = 0;
208        self.end = 0;
209        self.payload_start = None;
210        // This is not strictly necessary, but at least a temporarily useful thing that will keep
211        // users from relying on the code to be persisted.
212        *self.encoded.code_mut() = 0;
213    }
214
215    /// Return the number of bytes that were populated inside tail
216    pub fn finish(self) -> usize {
217        self.end
218    }
219
220    /// Returns the number of bytes that were populated inside tail, along with the encoded data.
221    ///
222    /// Unlike the more common [`.finish()`][Self::finish], this makes sense mostly for owned EMs,
223    /// where the caller has to move it out again and can not use the shadowed reference the caller
224    /// retained.
225    pub fn finish_and_return(self) -> (usize, EM) {
226        (self.end, self.encoded)
227    }
228}
229
230impl<EM: EncodedMessage> coap_message::ReadableMessage for GenericMessage<EM> {
231    type Code = u8;
232    type MessageOption<'b>
233        = crate::inmemory::MessageOption<'b>
234    where
235        Self: 'b;
236    type OptionsIter<'b>
237        = crate::inmemory::OptionsIter<'b>
238    where
239        Self: 'b;
240    fn code(&self) -> u8 {
241        *self.encoded.code()
242    }
243    // Funny detail on the side: If this is called often, an inmemory_write::Message might be more
244    // efficient even for plain reading than an inmemory::Message (because we don't have to iterate)
245    fn payload(&self) -> &[u8] {
246        match self.payload_start {
247            None => &[],
248            Some(start) => &self.encoded.tail()[start..self.end],
249        }
250    }
251    fn options(&self) -> <Self as coap_message::ReadableMessage>::OptionsIter<'_> {
252        crate::inmemory::OptionsIter(crate::option_iteration::OptPayloadReader::new(
253            &self.encoded.tail()[..self.end],
254        ))
255    }
256}
257
258impl<EM: EncodedMessage> coap_message::WithSortedOptions for GenericMessage<EM> {}
259
260impl<EM: EncodedMessage> coap_message::MinimalWritableMessage for GenericMessage<EM> {
261    type Code = u8;
262    type OptionNumber = u16;
263    type UnionError = WriteError;
264    type AddOptionError = WriteError;
265    type SetPayloadError = WriteError;
266
267    fn set_code(&mut self, code: u8) {
268        *self.encoded.code_mut() = code;
269    }
270
271    fn add_option(&mut self, number: u16, data: &[u8]) -> Result<(), WriteError> {
272        let delta = number
273            .checked_sub(self.latest)
274            .ok_or(WriteError::OutOfSequence)?;
275        self.latest = number;
276        let encoded = crate::option_extension::encode_extensions(delta, data.len() as u16);
277        let encoded = encoded.as_ref();
278        let added_len = encoded.len() + data.len();
279
280        let option_cursor;
281        if let Some(payload_start) = self.payload_start {
282            option_cursor = payload_start - 1;
283            // Could also rotate_right, but we don't need the shifted-out bytes preserved in the
284            // area we'll overwrite in the next instructions
285            let src = option_cursor..self.encoded.tail().len() - added_len;
286            self.encoded
287                .tail_mut()
288                .copy_within(src, option_cursor + added_len);
289            self.payload_start = Some(payload_start + added_len);
290        } else {
291            option_cursor = self.end;
292        }
293
294        self.encoded
295            .tail_mut()
296            .get_mut(option_cursor..option_cursor + encoded.len())
297            .ok_or(WriteError::OutOfSpace)?
298            .copy_from_slice(encoded);
299        let option_cursor = option_cursor + encoded.len();
300        self.encoded
301            .tail_mut()
302            .get_mut(option_cursor..option_cursor + data.len())
303            .ok_or(WriteError::OutOfSpace)?
304            .copy_from_slice(data);
305        self.end += added_len;
306
307        Ok(())
308    }
309
310    fn set_payload(&mut self, payload: &[u8]) -> Result<(), WriteError> {
311        if self.payload_start.is_some() {
312            // We might allow double setting the payload through later extensions, but as for this
313            // interface it's once only. We don't detect double setting of empty payloads, but it's
314            // not this implementation's purpose to act as a linter.
315            //
316            // (And whoever uses the options-and-payload-mixed properties will use payload_mut
317            // instead).
318            return Err(WriteError::OutOfSequence);
319        };
320        if !payload.is_empty() {
321            *self
322                .encoded
323                .tail_mut()
324                .get_mut(self.end)
325                .ok_or(WriteError::OutOfSpace)? = 0xff;
326            let start = self.end + 1;
327            self.end = start + payload.len();
328            self.encoded
329                .tail_mut()
330                .get_mut(start..self.end)
331                .ok_or(WriteError::OutOfSpace)?
332                .copy_from_slice(payload);
333            self.payload_start = Some(start);
334        }
335        Ok(())
336    }
337
338    #[cfg(feature = "downcast")]
339    fn with_static_type_annotation(
340        &mut self,
341    ) -> Option<coap_message::helpers::RefMutWithStaticType<'_, Self>> {
342        // SAFETY: It is this type's policy that its RefMutWithStaticType ID is the given
343        // Message<'static>.
344        Some(unsafe {
345            coap_message::helpers::RefMutWithStaticType::new(
346                self,
347                core::any::TypeId::of::<Message<'static>>(),
348            )
349        })
350    }
351
352    #[inline]
353    #[allow(refining_impl_trait_reachable)]
354    fn promote_to_mutable_writable_message(&mut self) -> Option<&mut Self> {
355        Some(self)
356    }
357}
358
359impl<EM: EncodedMessage> coap_message::MutableWritableMessage for GenericMessage<EM> {
360    fn available_space(&self) -> usize {
361        self.encoded.tail().len()
362            - self
363                .payload_start
364                .map(|s| s - 1) // available_space includes the payload indicator
365                .unwrap_or(self.end)
366    }
367
368    fn payload_mut_with_len(&mut self, len: usize) -> Result<&mut [u8], WriteError> {
369        if len == 0 {
370            // Just finish the side effect and return something good enough; this allows the easier
371            // path for the rest of the function to pick a start, end, and serve that.
372            self.truncate(0)?;
373            return Ok(&mut []);
374        }
375
376        let start = match self.payload_start {
377            None => {
378                self.encoded.tail_mut()[self.end] = 0xff;
379                self.end + 1
380            }
381            Some(payload_start) => payload_start,
382        };
383        let end = start + len;
384
385        let end = end.clamp(0, self.encoded.tail().len());
386
387        self.payload_start = Some(start);
388        self.end = end;
389        self.encoded
390            .tail_mut()
391            .get_mut(start..end)
392            .ok_or(WriteError::OutOfSpace)
393    }
394
395    fn truncate(&mut self, len: usize) -> Result<(), WriteError> {
396        match (len, self.payload_start) {
397            (0, Some(payload_start)) => {
398                self.end = payload_start - 1;
399            }
400            (0, None) => {}
401            (len, Some(payload_start)) if self.end - payload_start >= len => {
402                self.end = payload_start + len;
403            }
404            _ => return Err(WriteError::OutOfSpace),
405        }
406        Ok(())
407    }
408
409    fn mutate_options<F>(&mut self, mut f: F)
410    where
411        F: FnMut(u16, &mut [u8]),
412    {
413        // TBD this is excessively complex, and grounds for finding a better interface. ("Set
414        // option and give me a key to update it later with a mutable reference")?
415
416        let optend = self.payload_start.map(|s| s - 1).unwrap_or(self.end);
417
418        // May end in a payload marker or just plain end
419        let mut slice = &mut self.encoded.tail_mut()[..optend];
420
421        let mut option_base = 0;
422
423        while !slice.is_empty() {
424            // This is copied and adapted from
425            // coap_messsage_utils::option_iteration::OptPayloadReader and not used through it,
426            // because that'd be a whole separate implementation there with mut.
427            // (It's bad enough that take_extension needs the trickery)
428            let delta_len = slice[0];
429            slice = &mut slice[1..];
430
431            if delta_len == 0xff {
432                break;
433            }
434
435            let mut delta = (delta_len as u16) >> 4;
436            let mut len = (delta_len as u16) & 0x0f;
437
438            let new_len = {
439                // Workaround for https://github.com/rust-lang/rust-clippy/issues/10608
440                #[allow(clippy::redundant_slicing)]
441                // To get take_extension to cooperate...
442                let mut readable = &slice[..];
443
444                crate::option_extension::take_extension(&mut delta, &mut readable)
445                    .expect("Invalid encoded option in being-written message");
446                crate::option_extension::take_extension(&mut len, &mut readable)
447                    .expect("Invalid encoded option in being-written message");
448
449                readable.len()
450            };
451            // ... and get back to a mutable form
452            let trim = slice.len() - new_len;
453            slice = &mut slice[trim..];
454
455            option_base += delta;
456
457            let len = len.into();
458            f(option_base, &mut slice[..len]);
459            slice = &mut slice[len..];
460        }
461    }
462}