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