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