Skip to main content

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
200fn get_option_header_len(header: u8) -> u8 {
201    let mut res = 1;
202    match header >> 4 {
203        14 => res += 2,
204        13 => res += 1,
205        _ => (),
206    }
207    match header & 0b1111 {
208        14 => res += 2,
209        13 => res += 1,
210        _ => (),
211    }
212    res
213}
214
215impl<EM: EncodedMessage> GenericMessage<EM> {
216    /// Index up to which the last option was written.
217    fn options_end(&self) -> usize {
218        self.payload_start.map_or(self.end, |s| s - 1)
219    }
220
221    /// Insert an option.
222    ///
223    /// This behaves similarly to [`add_option()`][coap_message::MinimalWritableMessage::add_option()], but without the limitation of needing to
224    /// uphold option ordering manually.
225    ///
226    /// This comes at the cost of iterating over all options and re-encoding one.
227    ///
228    /// # Errors
229    ///
230    /// This function returns a [`WriteError`] if either the message contains malformed options, or
231    /// there isn't sufficient space left for the insertion.
232    pub fn insert_option(&mut self, number: u16, data: &[u8]) -> Result<(), WriteError> {
233        use crate::{
234            option_extension::encode_extensions,
235            option_iteration::{OptItem, OptPayloadReader},
236        };
237
238        let Ok(data_len) = u16::try_from(data.len()) else {
239            return Err(WriteError::BadEncoding);
240        };
241
242        let mut insertion_point = 0;
243        let mut previous_option = 0u16;
244        let mut next_option = 0u16;
245        let mut next_len = None;
246
247        let options_end = self.options_end();
248
249        let tail = self.encoded.tail_mut();
250
251        if self.latest <= number {
252            insertion_point = options_end;
253            previous_option = self.latest;
254        } else {
255            let mut opt_iter = OptPayloadReader::new(tail);
256            loop {
257                match opt_iter.next() {
258                    Some(OptItem::Option { number: num, data }) => {
259                        if num <= number {
260                            let (slice, base) = opt_iter.destruct();
261                            insertion_point = tail.len() - slice.len();
262                            previous_option = base;
263                            opt_iter = OptPayloadReader::new_from(slice, base);
264                            continue;
265                        }
266
267                        next_option = num;
268                        next_len = Some(
269                            u16::try_from(data.len()).expect("Guaranteed by OptPayloadReader"),
270                        );
271                        break;
272                    }
273                    _ => return Err(WriteError::BadEncoding),
274                }
275            }
276        }
277
278        // Encode the to-be-inserted option.
279        let delta = number.checked_sub(previous_option).expect("Checked above");
280        let new_encoded = encode_extensions(delta, data_len);
281        let new_encoded = new_encoded.as_ref();
282
283        let current_encoded_len = get_option_header_len(tail[insertion_point]);
284
285        // Except in edgecases this will be 0.
286        let encoding_length_delta;
287        let next_encoded;
288
289        // Check if option is last.
290        let added_len = if let Some(next_len) = next_len {
291            // Re-encode following option. After that encoding doesn't change due to being a delta.
292            let next_delta = next_option - number;
293            let next_encoded_tmp = encode_extensions(next_delta, next_len);
294
295            // Due to changes in option-number-delta the encoding of the option header could shrink
296            // by one or two bytes.
297            encoding_length_delta = usize::from(current_encoded_len)
298                .checked_sub(next_encoded_tmp.as_ref().len())
299                .expect("Encoding length can only shrink");
300
301            next_encoded = Some(next_encoded_tmp);
302            new_encoded.len() - encoding_length_delta + data.len()
303        } else {
304            next_encoded = None;
305
306            encoding_length_delta = 0;
307            new_encoded.len() + data.len()
308        };
309
310        if self.end + added_len > tail.len() {
311            return Err(WriteError::OutOfSpace);
312        }
313
314        // Could also rotate_right, but we don't need the shifted-out bytes preserved in the
315        // area we'll overwrite in the next instructions.
316        //
317        // This range is always less than `added_len`, as the new encoding always fits into the same space as (or less than) the
318        // old encoding.
319        let src = insertion_point + encoding_length_delta..self.end;
320        self.encoded
321            .tail_mut()
322            .copy_within(src, insertion_point + added_len);
323
324        if let Some(payload_start) = self.payload_start {
325            self.payload_start = Some(payload_start + added_len);
326        }
327
328        self.encoded
329            .tail_mut()
330            .get_mut(insertion_point..insertion_point + new_encoded.len())
331            .expect("Remaining length has been checked")
332            .copy_from_slice(new_encoded);
333        insertion_point += new_encoded.len();
334        self.encoded
335            .tail_mut()
336            .get_mut(insertion_point..insertion_point + data.len())
337            .expect("Remaining length has been checked")
338            .copy_from_slice(data);
339        insertion_point += data.len();
340
341        if let Some(next_encoded) = next_encoded {
342            self.encoded
343                .tail_mut()
344                .get_mut(insertion_point..insertion_point + next_encoded.as_ref().len())
345                .expect("Remaining length has been checked")
346                .copy_from_slice(next_encoded.as_ref());
347        } else {
348            self.latest = number;
349        }
350
351        self.end += added_len;
352
353        Ok(())
354    }
355
356    /// Remove an option from a slice located at `index`.
357    /// Requires the current `end` of the message contained in `tail` and the previous option,
358    /// in case a following option exists and needs to be re-encoded.
359    ///
360    /// # Errors
361    ///
362    /// This function will return an error, if either the message is badly encoded, or tail can't
363    /// fit the new encoding (it can be larger by 2 bytes).
364    fn remove_option_at(
365        tail: &mut [u8],
366        index: usize,
367        end: usize,
368        previous_option: u16,
369    ) -> Result<isize, WriteError> {
370        use crate::{
371            option_extension::encode_extensions,
372            option_iteration::{OptItem, OptPayloadReader},
373        };
374
375        let mut opt_iter = OptPayloadReader::new_from(&tail[index..], previous_option);
376
377        // Get nescessary info from the to-be-removed option.
378        let (number, data) = match opt_iter.next().ok_or(WriteError::BadEncoding)? {
379            OptItem::Option { number, data } => (number, data),
380            _ => return Err(WriteError::BadEncoding),
381        };
382
383        // Get next option if any.
384        let next_opt = match opt_iter.next().ok_or(WriteError::BadEncoding)? {
385            OptItem::Option { number, data } => Some((number, data)),
386            OptItem::Payload(_) => None,
387            _ => return Err(WriteError::BadEncoding),
388        };
389
390        let current_encoded_len = isize::from(get_option_header_len(tail[index]));
391
392        // Except in edgecases this will be 0.
393        let encoding_length_delta;
394        let next_encoded;
395
396        // Check if option is last.
397        let len_removed = if let Some((next_number, next_data)) = next_opt {
398            let next_data_len =
399                u16::try_from(next_data.len()).map_err(|_| WriteError::BadEncoding)?;
400
401            // Get the current length of the option header encoding of the next option.
402            let current_next_delta = next_number - number;
403            let current_next_encoded = encode_extensions(current_next_delta, next_data_len);
404            let current_next_encoded_len = current_next_encoded.as_ref().len() as isize;
405
406            // Re-encode following option. After that encoding doesn't change due to being a delta.
407            let next_delta = next_number - previous_option;
408            let next_encoded_tmp = encode_extensions(next_delta, next_data_len);
409
410            // Due to changes in option-number-delta the encoding of the option header could grow
411            // by one or two bytes.
412            encoding_length_delta =
413                next_encoded_tmp.as_ref().len() as isize - current_next_encoded_len;
414
415            next_encoded = Some(next_encoded_tmp);
416            current_encoded_len - encoding_length_delta + data.len() as isize
417        } else {
418            next_encoded = None;
419
420            encoding_length_delta = 0;
421            current_encoded_len + data.len() as isize
422        };
423
424        // Encoding length can grow in rare cases (possible if data_len < 3).
425        if tail.len() < (end as isize - len_removed) as usize {
426            return Err(WriteError::OutOfSpace);
427        }
428
429        // Check if we hit the end of the message
430        if end.saturating_sub(index + current_encoded_len as usize + data.len()) != 0 {
431            // Move remaining options and payload in place.
432            let src =
433                index + current_encoded_len as usize + data.len() + encoding_length_delta as usize
434                    ..end;
435            tail.copy_within(src, index + encoding_length_delta as usize);
436        }
437
438        // Check if last.
439        if let Some(next_encoded) = next_encoded {
440            // Insert re-encoded option header.
441            tail.get_mut(index..index + next_encoded.as_ref().len())
442                .expect("checked above")
443                .copy_from_slice(next_encoded.as_ref());
444        }
445
446        Ok(len_removed)
447    }
448
449    /// Retain all options the `predicate` applies to, the rest will be removed.
450    ///
451    /// # Errors
452    ///
453    /// This function will return an error, if the message is either badly encoded, or can't
454    /// fit the new encoding (it can be larger by 2 bytes per removed option).
455    pub fn retain_options<P>(&mut self, predicate: P) -> Result<(), WriteError>
456    where
457        P: Fn(u16, &[u8]) -> bool,
458    {
459        use crate::option_iteration::{OptItem, OptPayloadReader};
460
461        if self.latest == 0 {
462            return Ok(());
463        }
464
465        let tail = self.encoded.tail_mut();
466        let mut opt_iter = OptPayloadReader::new(tail);
467        let mut previous_option = 0u16;
468        let mut opt_index = 0;
469
470        loop {
471            match opt_iter.next() {
472                Some(OptItem::Option { number, data }) => {
473                    let (slice, _) = opt_iter.destruct();
474
475                    if predicate(number, data) {
476                        previous_option = number;
477                        opt_index = tail.len() - slice.len();
478
479                        opt_iter = OptPayloadReader::new_from(slice, number);
480                        continue;
481                    }
482
483                    let len_removed =
484                        Self::remove_option_at(tail, opt_index, self.end, previous_option)?;
485
486                    self.end = (self.end as isize - len_removed) as usize;
487                    let options_end = if let Some(payload_start) = self.payload_start {
488                        let new = (payload_start as isize - len_removed) as usize;
489                        self.payload_start = Some(new);
490                        new - 1
491                    } else {
492                        self.end
493                    };
494
495                    if opt_index == options_end {
496                        self.latest = previous_option;
497                        return Ok(());
498                    }
499
500                    opt_iter = OptPayloadReader::new_from(&tail[opt_index..], previous_option);
501                }
502                None | Some(OptItem::Payload(_)) => return Ok(()),
503                Some(OptItem::Error(_)) => return Err(WriteError::BadEncoding),
504            }
505        }
506    }
507
508    /// Discard anything that has been written in the message, and allow any operation that would
509    /// be allowed after calling [`Self::new()`].
510    ///
511    /// This is a short-cut to messages that can be snapshotted and rewound, and a band-aid until
512    /// that is available in traits.
513    pub fn reset(&mut self) {
514        self.latest = 0;
515        self.end = 0;
516        self.payload_start = None;
517        // This is not strictly necessary, but at least a temporarily useful thing that will keep
518        // users from relying on the code to be persisted.
519        *self.encoded.code_mut() = 0;
520    }
521
522    /// Expand the bounds of the payload by `added_len`.
523    ///
524    /// Similar to [`payload_mut_with_len()`][coap_message::MutableWritableMessage::payload_mut_with_len()],
525    /// but it has no restriction on length (beyond the length of the underlying buffer).
526    pub fn untruncate(&mut self, mut added_len: usize) -> Result<(), WriteError> {
527        if self.payload_start.is_none() {
528            added_len += 1;
529        }
530
531        if self.encoded.tail().len() < self.end + added_len {
532            return Err(WriteError::OutOfSpace);
533        }
534
535        if self.payload_start.is_none() {
536            // Insert payload marker.
537            self.encoded.tail_mut()[self.end] = 0xff;
538            self.payload_start = Some(self.end + 1);
539        }
540
541        self.end += added_len;
542
543        Ok(())
544    }
545
546    /// Return the number of bytes that were populated inside tail
547    pub fn finish(self) -> usize {
548        self.end
549    }
550
551    /// Returns the number of bytes that were populated inside tail, along with the encoded data.
552    ///
553    /// Unlike the more common [`.finish()`][Self::finish], this makes sense mostly for owned EMs,
554    /// where the caller has to move it out again and can not use the shadowed reference the caller
555    /// retained.
556    pub fn finish_and_return(self) -> (usize, EM) {
557        (self.end, self.encoded)
558    }
559}
560
561impl<EM: EncodedMessage> coap_message::ReadableMessage for GenericMessage<EM> {
562    type Code = u8;
563    type MessageOption<'b>
564        = crate::inmemory::MessageOption<'b>
565    where
566        Self: 'b;
567    type OptionsIter<'b>
568        = crate::inmemory::OptionsIter<'b>
569    where
570        Self: 'b;
571    fn code(&self) -> u8 {
572        *self.encoded.code()
573    }
574    // Funny detail on the side: If this is called often, an inmemory_write::Message might be more
575    // efficient even for plain reading than an inmemory::Message (because we don't have to iterate)
576    fn payload(&self) -> &[u8] {
577        match self.payload_start {
578            None => &[],
579            Some(start) => &self.encoded.tail()[start..self.end],
580        }
581    }
582    fn options(&self) -> <Self as coap_message::ReadableMessage>::OptionsIter<'_> {
583        crate::inmemory::OptionsIter(crate::option_iteration::OptPayloadReader::new(
584            &self.encoded.tail()[..self.end],
585        ))
586    }
587}
588
589impl<EM: EncodedMessage> coap_message::WithSortedOptions for GenericMessage<EM> {}
590
591impl<EM: EncodedMessage> coap_message::MinimalWritableMessage for GenericMessage<EM> {
592    type Code = u8;
593    type OptionNumber = u16;
594    type UnionError = WriteError;
595    type AddOptionError = WriteError;
596    type SetPayloadError = WriteError;
597
598    fn set_code(&mut self, code: u8) {
599        *self.encoded.code_mut() = code;
600    }
601
602    fn add_option(&mut self, number: u16, data: &[u8]) -> Result<(), WriteError> {
603        let delta = number
604            .checked_sub(self.latest)
605            .ok_or(WriteError::OutOfSequence)?;
606        self.latest = number;
607        let encoded = crate::option_extension::encode_extensions(delta, data.len() as u16);
608        let encoded = encoded.as_ref();
609        let added_len = encoded.len() + data.len();
610
611        // Where we encode the option
612        let option_cursor;
613        // What .end will be after this operation
614        let eventual_end;
615        if let Some(payload_start) = self.payload_start {
616            option_cursor = payload_start - 1;
617            let Some(start_dropping_at) = self.encoded.tail().len().checked_sub(added_len) else {
618                // We couldn't even add this if we're completely empty.
619                return Err(WriteError::OutOfSpace);
620            };
621            if start_dropping_at < option_cursor {
622                // Can't add it even by dropping all the payload.
623                return Err(WriteError::OutOfSpace);
624            }
625            let retained_payload_with_marker = start_dropping_at - option_cursor;
626            // > 1 because we only set the payload marker if there's also non-empty payload
627            if retained_payload_with_marker > 1 {
628                // Could also rotate_right, but we don't need the shifted-out bytes preserved in the
629                // area we'll overwrite in the next instructions
630                let src = option_cursor..start_dropping_at;
631                self.encoded
632                    .tail_mut()
633                    .copy_within(src, option_cursor + added_len);
634
635                self.payload_start = Some(payload_start + added_len);
636                eventual_end = (self.end + added_len).clamp(0, self.encoded.tail().len());
637            } else {
638                self.payload_start = None;
639                // Difference is 0 or 1
640                eventual_end = self.encoded.tail().len() - retained_payload_with_marker;
641            }
642        } else {
643            option_cursor = self.end;
644            eventual_end = self.end + added_len;
645        }
646
647        self.encoded
648            .tail_mut()
649            .get_mut(option_cursor..option_cursor + encoded.len())
650            .ok_or(WriteError::OutOfSpace)?
651            .copy_from_slice(encoded);
652        let option_cursor = option_cursor + encoded.len();
653        self.encoded
654            .tail_mut()
655            .get_mut(option_cursor..option_cursor + data.len())
656            .ok_or(WriteError::OutOfSpace)?
657            .copy_from_slice(data);
658        self.end = eventual_end;
659
660        Ok(())
661    }
662
663    fn set_payload(&mut self, payload: &[u8]) -> Result<(), WriteError> {
664        if self.payload_start.is_some() {
665            // We might allow double setting the payload through later extensions, but as for this
666            // interface it's once only. We don't detect double setting of empty payloads, but it's
667            // not this implementation's purpose to act as a linter.
668            //
669            // (And whoever uses the options-and-payload-mixed properties will use payload_mut
670            // instead).
671            return Err(WriteError::OutOfSequence);
672        }
673        if !payload.is_empty() {
674            *self
675                .encoded
676                .tail_mut()
677                .get_mut(self.end)
678                .ok_or(WriteError::OutOfSpace)? = 0xff;
679            let start = self.end + 1;
680            self.end = start + payload.len();
681            self.encoded
682                .tail_mut()
683                .get_mut(start..self.end)
684                .ok_or(WriteError::OutOfSpace)?
685                .copy_from_slice(payload);
686            self.payload_start = Some(start);
687        }
688        Ok(())
689    }
690
691    #[cfg(feature = "downcast")]
692    fn with_static_type_annotation(
693        &mut self,
694    ) -> Option<coap_message::helpers::RefMutWithStaticType<'_, Self>> {
695        // SAFETY: It is this type's policy that its RefMutWithStaticType ID is the given
696        // Message<'static>.
697        Some(unsafe {
698            coap_message::helpers::RefMutWithStaticType::new(
699                self,
700                core::any::TypeId::of::<Message<'static>>(),
701            )
702        })
703    }
704
705    #[inline]
706    #[allow(refining_impl_trait_reachable)]
707    fn promote_to_mutable_writable_message(&mut self) -> Option<&mut Self> {
708        Some(self)
709    }
710}
711
712impl<EM: EncodedMessage> coap_message::MutableWritableMessage for GenericMessage<EM> {
713    fn available_space(&self) -> usize {
714        self.encoded.tail().len() - self.options_end()
715    }
716
717    fn payload_mut_with_len(&mut self, len: usize) -> Result<&mut [u8], WriteError> {
718        if len == 0 {
719            // Just finish the side effect and return something good enough; this allows the easier
720            // path for the rest of the function to pick a start, end, and serve that.
721            self.truncate(0)?;
722            return Ok(&mut []);
723        }
724
725        let start = match self.payload_start {
726            None => {
727                self.encoded.tail_mut()[self.end] = 0xff;
728                self.end + 1
729            }
730            Some(payload_start) => payload_start,
731        };
732        let end = start + len;
733
734        let end = end.clamp(0, self.encoded.tail().len());
735
736        self.payload_start = Some(start);
737        self.end = end;
738        self.encoded
739            .tail_mut()
740            .get_mut(start..end)
741            .ok_or(WriteError::OutOfSpace)
742    }
743
744    fn truncate(&mut self, len: usize) -> Result<(), WriteError> {
745        match (len, self.payload_start) {
746            (0, Some(payload_start)) => {
747                self.end = payload_start - 1;
748                self.payload_start = None;
749            }
750            (0, None) => {}
751            (len, Some(payload_start)) if self.end - payload_start >= len => {
752                self.end = payload_start + len;
753            }
754            _ => return Err(WriteError::OutOfSpace),
755        }
756        Ok(())
757    }
758
759    fn mutate_options<F>(&mut self, mut f: F)
760    where
761        F: FnMut(u16, &mut [u8]),
762    {
763        // TBD this is excessively complex, and grounds for finding a better interface. ("Set
764        // option and give me a key to update it later with a mutable reference")?
765
766        let optend = self.options_end();
767
768        // May end in a payload marker or just plain end
769        let mut slice = &mut self.encoded.tail_mut()[..optend];
770
771        let mut option_base = 0;
772
773        while !slice.is_empty() {
774            // This is copied and adapted from
775            // coap_messsage_utils::option_iteration::OptPayloadReader and not used through it,
776            // because that'd be a whole separate implementation there with mut.
777            // (It's bad enough that take_extension needs the trickery)
778            let delta_len = slice[0];
779            slice = &mut slice[1..];
780
781            if delta_len == 0xff {
782                break;
783            }
784
785            let mut delta = u16::from(delta_len) >> 4;
786            let mut len = u16::from(delta_len) & 0x0f;
787
788            let new_len = {
789                // Workaround for https://github.com/rust-lang/rust-clippy/issues/10608
790                #[allow(clippy::redundant_slicing)]
791                // To get take_extension to cooperate...
792                let mut readable = &slice[..];
793
794                crate::option_extension::take_extension(&mut delta, &mut readable)
795                    .expect("Invalid encoded option in being-written message");
796                crate::option_extension::take_extension(&mut len, &mut readable)
797                    .expect("Invalid encoded option in being-written message");
798
799                readable.len()
800            };
801            // ... and get back to a mutable form
802            let trim = slice.len() - new_len;
803            slice = &mut slice[trim..];
804
805            option_base += delta;
806
807            let len = len.into();
808            f(option_base, &mut slice[..len]);
809            slice = &mut slice[len..];
810        }
811    }
812}