Skip to main content

coap_message_implementations/inmemory/
write_impl.rs

1//! Implementation of [`MutableWritableMessage`].
2//!
3//! [`MessageMut`] is the main struct of this module. A [`MessageMut`] is constructed based on an
4//! [`MessageBufferMut`], which can encapsulate anything from slices (when `EM` is a
5//! [`SliceBufferMut`]), to owned data, or something between (like a single
6//! [`core::cell::RefMut`] that is being sliced into).
7#![cfg_attr(feature = "downcast", allow(unsafe_code))]
8
9use super::*;
10
11use crate::option_extension::option_header_len;
12
13/// A CoAP message that resides in contiguous mutable memory.
14///
15/// Exceeding the guarantees of `MutableWritableMessage`, this does allow some out-of-sequence
16/// invocations: Even after payload has been written to, options can be added, memmove'ing
17/// (right-rotating) the written payload, as long as it does not truncate it. This is needed
18/// to accommodate libOSCORE's requirements (because while libOSCORE can also do without a
19/// memmove-ing message, that'd require its use through `WritableMessage` to adhere to in-OSCORE
20/// write sequence conventions, making the whole situation no easier on the coap-message
21/// abstraction). Data will only be moved if an option is added after content has been set, so this
22/// comes at no runtime cost for those who do not need it. (It may be later turned into a feature.
23/// Then, the memmove code would be removed; carrying the latest option number in the `WriteState`
24/// should come at no extra cost due to the struct's content and alignment).
25///
26/// When viewed through the [`coap_message::ReadableMessage`] trait, this will behave as if writing
27/// had stopped (but still allows writes after reading); in particular, the payload will be shown
28/// as empty. (This may be obvious from most points of view, but when coming from a
29/// [`coap_message::MutableWritableMessage`] point of view where payloads can only ever be truncated and not
30/// made longer, this clarification is relevant).
31///
32/// The type is covariant over its lifetime. This also means that we can never add any methods
33/// where we move references into Self, but we don't do this anyway: The lifetime `'a` only serves
34/// to describe the memory backing it; data is moved in there, not stored by reference. This
35/// property is accessible through downcasting.
36///
37/// # Invariants
38///
39/// The message in the `EM` is always a well-formed CoAP message. This is upheld by construction or
40/// checked at creation time (when existing messages are consumed). As the underlying
41/// [`MessageBufferMut`] can be implemented without unsafe, this crate can only panic (but not
42/// invoke undefined behavior) when that is violated.
43#[derive(Clone)]
44pub struct MessageMut<EM: MessageBufferMut> {
45    /// Pointers to the actual message data.
46    pub(crate) encoded: EM,
47
48    /// Latest option that has been written.
49    pub(crate) latest: u16,
50    /// Index after the last written byte.
51    pub(crate) end: usize,
52    /// First byte of any written payload.
53    ///
54    /// If this has been set, the byte before it was written 0xff. This is None if the was an empty
55    /// payload, or none has been set yet.
56    pub(crate) payload_start: Option<usize>,
57}
58
59/// Helper for extracting the type ID used with an MessageBuffer out of a value with unnamed
60/// (RPIT) type.
61#[cfg(feature = "downcast")]
62fn type_id_of_emv_of_lml<T: 'static + MessageBufferMut>(
63    _val: &LifetimesMatterLittle<T>,
64) -> core::any::TypeId {
65    core::any::TypeId::of::<MessageMut<T>>()
66}
67
68impl<EM: MessageBufferMut> core::fmt::Debug for MessageMut<EM> {
69    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
70        f.debug_struct("MessageMut")
71            .field("MessageBufferMut impl", &core::any::type_name::<EM>())
72            .field("encoded.code", &self.encoded.code())
73            .field("encoded.tail", &self.encoded.tail())
74            .field("latest", &self.latest)
75            .field("end", &self.end)
76            .field("payload_start", &self.payload_start)
77            .finish()
78    }
79}
80
81#[cfg(feature = "defmt")]
82impl<EM: MessageBufferMut> defmt::Format for MessageMut<EM> {
83    fn format(&self, f: defmt::Formatter<'_>) {
84        defmt::write!(
85            f,
86            "MessageMut {{ MessageBufferMut impl {=str}, code {=u8}, tail {=[u8]}, latest {=u16}, end {}, payload_start {} }}",
87            &core::any::type_name::<EM>(),
88            self.encoded.code(),
89            self.encoded.tail(),
90            self.latest,
91            self.end,
92            self.payload_start
93        );
94    }
95}
96
97impl<'a> MessageMut<SliceBufferMut<'a>> {
98    /// Constructor of a message buffer around exclusive memory.
99    ///
100    /// This is a short-cut for creating a [`SliceBufferMut`] and passing it to
101    /// [`Self::new_empty()`].
102    pub fn new_in_slice(code: &'a mut u8, tail: &'a mut [u8]) -> Self {
103        MessageMut {
104            encoded: SliceBufferMut::new(code, tail),
105            latest: 0,
106            end: 0,
107            payload_start: None,
108        }
109    }
110
111    /// Creates a `MutableWritableMessage` on a buffer that already contains a serialized message.
112    ///
113    /// While this is generally not useful (as the message has been completed and there is no room
114    /// for anything more), it allows mutable access to the option bytes and the payload. This is
115    /// primarily useful in situations when data is processed in place, eg. decrypted (in OSCORE),
116    /// or CBOR is shifted around to get contiguous slices out of indefinite length strings.
117    ///
118    /// If the message has been parsed as a [`Message`] around a mutable back-end
119    /// before, it is preferable to use its
120    /// [`.into_mutable()`][Message::into_mutable] method, as that can avoid the
121    /// full parsing that is needed to uphold this type's guarantees.
122    ///
123    /// # Errors
124    ///
125    /// … are returned if the input slice does not contain a well-formed message.
126    pub fn new_from_encoded_slice(
127        code: &'a mut u8,
128        tail: &'a mut [u8],
129    ) -> Result<Self, ParsingError> {
130        let em = SliceBufferMut::new(code, tail);
131        let read_only = Message::new(em);
132        read_only.into_mutable()
133    }
134}
135
136impl<T: MessageBufferMut> MessageMut<T> {
137    /// Constructor of a message buffer around any [`MessageBufferMut`].
138    pub fn new_empty(encoded: T) -> Self {
139        MessageMut {
140            encoded,
141            latest: 0,
142            end: 0,
143            payload_start: None,
144        }
145    }
146
147    /// Returns a full [`Self`] from input that is expected (but not known at type level) to be one.
148    #[cfg(feature = "downcast")]
149    pub fn downcast_from<M: MinimalWritableMessage>(generic: &mut M) -> Option<&mut Self> {
150        let (reference, type_id) = generic.with_static_type_annotation()?.into_inner();
151
152        let our_static = T::static_mut_variant()?;
153        let our_id = type_id_of_emv_of_lml(&our_static);
154
155        if type_id != our_id {
156            return None;
157        }
158        // One of us, one of us.
159        let ptr = reference as *mut M as *mut Self;
160        // SAFETY: The RefWithStaticType matching our type ID will only be constructed if M is
161        // MessageMut, and whatever MessageMut<'b> it was before, it will live at least for 'a (because
162        // we got a &'a MessageMut<'b> and is covariant.
163        Some(unsafe { &mut *ptr })
164    }
165}
166
167impl<EM: MessageBufferMut> MessageMut<EM> {
168    /// Index up to which the last option was written.
169    fn options_end(&self) -> usize {
170        self.payload_start.map_or(self.end, |s| s - 1)
171    }
172
173    /// Inserts an option.
174    ///
175    /// This behaves similarly to [`add_option()`][MinimalWritableMessage::add_option()], but without the limitation of needing to
176    /// uphold option ordering manually.
177    ///
178    /// This comes at the cost of iterating over all options and re-encoding one.
179    ///
180    /// # Errors
181    ///
182    /// This function returns a [`WriteError`] if there isn't sufficient space left for the
183    /// insertion.
184    #[allow(clippy::missing_panics_doc, reason = "backed by type invariants")]
185    pub fn insert_option(&mut self, number: u16, data: &[u8]) -> Result<(), WriteError> {
186        use crate::{
187            option_extension::encode_extensions,
188            option_iteration::{OptItem, OptPayloadReader},
189        };
190
191        let Ok(data_len) = u16::try_from(data.len()) else {
192            return Err(WriteError::OutOfSpace);
193        };
194
195        let mut insertion_point = 0;
196        let mut previous_option = 0u16;
197        let mut next_option = 0u16;
198        let mut next_len = None;
199
200        let options_end = self.options_end();
201
202        let tail = self.encoded.tail_mut();
203
204        if self.latest <= number {
205            insertion_point = options_end;
206            previous_option = self.latest;
207        } else {
208            let mut opt_iter = OptPayloadReader::new(tail);
209            loop {
210                match opt_iter.next() {
211                    Some(OptItem::Option { number: num, data }) => {
212                        if num <= number {
213                            let (slice, base) = opt_iter.destruct();
214                            insertion_point = tail.len() - slice.len();
215                            previous_option = base;
216                            opt_iter = OptPayloadReader::new_from(slice, base);
217                            continue;
218                        }
219
220                        next_option = num;
221                        next_len = Some(
222                            u16::try_from(data.len()).expect("Guaranteed by OptPayloadReader"),
223                        );
224                        break;
225                    }
226                    _ => unreachable!("encoding invariant"),
227                }
228            }
229        }
230
231        // Encode the to-be-inserted option.
232        let delta = number.checked_sub(previous_option).expect("Checked above");
233        let new_encoded = encode_extensions(delta, data_len);
234        let new_encoded = new_encoded.as_ref();
235
236        let current_encoded_len = option_header_len(tail[insertion_point]);
237
238        // Except in edgecases this will be 0.
239        let encoding_length_delta;
240        let next_encoded;
241
242        // Check if option is last.
243        let added_len = if let Some(next_len) = next_len {
244            // Re-encode following option. After that encoding doesn't change due to being a delta.
245            let next_delta = next_option - number;
246            let next_encoded_tmp = encode_extensions(next_delta, next_len);
247
248            // Due to changes in option-number-delta the encoding of the option header could shrink
249            // by one or two bytes.
250            encoding_length_delta = usize::from(current_encoded_len)
251                .checked_sub(next_encoded_tmp.as_ref().len())
252                .expect("Encoding length can only shrink");
253
254            next_encoded = Some(next_encoded_tmp);
255            new_encoded.len() - encoding_length_delta + data.len()
256        } else {
257            next_encoded = None;
258
259            encoding_length_delta = 0;
260            new_encoded.len() + data.len()
261        };
262
263        if self.end + added_len > tail.len() {
264            return Err(WriteError::OutOfSpace);
265        }
266
267        // Could also rotate_right, but we don't need the shifted-out bytes preserved in the
268        // area we'll overwrite in the next instructions.
269        //
270        // This range is always less than `added_len`, as the new encoding always fits into the same space as (or less than) the
271        // old encoding.
272        let src = insertion_point + encoding_length_delta..self.end;
273        self.encoded
274            .tail_mut()
275            .copy_within(src, insertion_point + added_len);
276
277        if let Some(payload_start) = self.payload_start {
278            self.payload_start = Some(payload_start + added_len);
279        }
280
281        self.encoded
282            .tail_mut()
283            .get_mut(insertion_point..insertion_point + new_encoded.len())
284            .expect("Remaining length has been checked")
285            .copy_from_slice(new_encoded);
286        insertion_point += new_encoded.len();
287        self.encoded
288            .tail_mut()
289            .get_mut(insertion_point..insertion_point + data.len())
290            .expect("Remaining length has been checked")
291            .copy_from_slice(data);
292        insertion_point += data.len();
293
294        if let Some(next_encoded) = next_encoded {
295            self.encoded
296                .tail_mut()
297                .get_mut(insertion_point..insertion_point + next_encoded.as_ref().len())
298                .expect("Remaining length has been checked")
299                .copy_from_slice(next_encoded.as_ref());
300        } else {
301            self.latest = number;
302        }
303
304        self.end += added_len;
305
306        Ok(())
307    }
308
309    /// Removes an option from a slice located at `index`.
310    ///
311    /// Requires the current `end` of the message contained in `tail` and the previous option,
312    /// in case a following option exists and needs to be re-encoded.
313    ///
314    /// # Errors
315    ///
316    /// All errors of this function stem from either invariants of the message not being upheld, or
317    /// from the indicated arguments not aligning with removing an option from a well-formed
318    /// message.
319    ///
320    /// They are returned as an error because that's more space efficient to unwrap.
321    ///
322    /// # Space analysis
323    ///
324    /// Removing an option can never shorten a message due to the encoding properties as shown
325    /// here:
326    ///
327    /// * We call the removed option B, from a sequence of options A B C.
328    ///
329    ///   The cases where there is no option A and C are easy:
330    ///
331    ///   * If there is no option C, nothing gets re-encoded, and just B is removed.
332    ///   * If there is no option A, all is equivalent to there being an option A with the reserved
333    ///     option number zero, or a regular option (with all option numbers shifted, which has no
334    ///     effect on the delta encoding).
335    ///
336    /// * We assume without loss of generality all options are zero-length. (Length does not change
337    ///   when options are removed, and if there is any non-zero length in what is being removed,
338    ///   we just gain *more* space to use).
339    ///
340    /// * The extended delta of C can grow from 0 to 1 byte, from 1 to 2 bytes, or from 0 to 2
341    ///   bytes.
342    ///
343    ///   The cases of growing by 1 byte trivial because removing an option also removes 1 byte.
344    ///
345    /// * C's extended delta growing from 0 to 2 bytes means that B bridged some gap: B's option
346    ///   number was no more than 12 smaller than from C's.
347    ///
348    ///   The most extreme case of having no space available is when while A-C just barely requires a 2-byte
349    ///   extended delta: C-A = 269 (encoded as `E0 00 00 = 269 + 0x0000`). As it was 0 bytes
350    ///   before, B has to have been in the range 157..=268, all of which require 1 byte of
351    ///   extended delta.
352    ///
353    ///   Thus, B was encoded in at least 2 bytes, so the required room is present.
354    #[expect(
355        clippy::cast_sign_loss,
356        reason = "differences are checked by construction"
357    )]
358    #[expect(
359        clippy::cast_possible_wrap,
360        reason = "usize values stem from actual sizes"
361    )]
362    fn remove_option_at(
363        tail: &mut [u8],
364        index: usize,
365        end: usize,
366        previous_option: u16,
367    ) -> Result<isize, InvariantViolated> {
368        use crate::{
369            option_extension::encode_extensions,
370            option_iteration::{OptItem, OptPayloadReader},
371        };
372
373        let mut opt_iter = OptPayloadReader::new_from(&tail[index..], previous_option);
374
375        // Get necessary info from the to-be-removed option.
376        let Some(OptItem::Option { number, data }) = opt_iter.next() else {
377            unreachable!("caller promised that opt0on is there")
378        };
379
380        // Get next option if any.
381        let next_opt = match opt_iter.next().ok_or(InvariantViolated)? {
382            OptItem::Option { number, data } => Some((number, data)),
383            OptItem::Payload(_) => None,
384            OptItem::Error(_) => return Err(InvariantViolated),
385        };
386
387        let current_encoded_len = isize::from(option_header_len(tail[index]));
388
389        // Except in edgecases this will be 0.
390        let encoding_length_delta;
391        let next_encoded;
392
393        // Check if option is last.
394        let len_removed = if let Some((next_number, next_data)) = next_opt {
395            let next_data_len = u16::try_from(next_data.len()).map_err(|_| InvariantViolated)?;
396
397            // Get the current length of the option header encoding of the next option.
398            let current_next_delta = next_number - number;
399            let current_next_encoded = encode_extensions(current_next_delta, next_data_len);
400            let current_next_encoded_len = current_next_encoded.as_ref().len() as isize;
401
402            // Re-encode following option. After that encoding doesn't change due to being a delta.
403            let next_delta = next_number - previous_option;
404            let next_encoded_tmp = encode_extensions(next_delta, next_data_len);
405
406            // Due to changes in option-number-delta the encoding of the option header could grow
407            // by one or two bytes.
408            encoding_length_delta =
409                next_encoded_tmp.as_ref().len() as isize - current_next_encoded_len;
410
411            next_encoded = Some(next_encoded_tmp);
412            current_encoded_len - encoding_length_delta + data.len() as isize
413        } else {
414            next_encoded = None;
415
416            encoding_length_delta = 0;
417            current_encoded_len + data.len() as isize
418        };
419
420        // As per space analysis, this will not happen.
421        if tail.len() < (end as isize - len_removed) as usize {
422            return Err(InvariantViolated);
423        }
424
425        // Check if we hit the end of the message
426        if end.saturating_sub(index + current_encoded_len as usize + data.len()) != 0 {
427            // Move remaining options and payload in place.
428            let src =
429                index + current_encoded_len as usize + data.len() + encoding_length_delta as usize
430                    ..end;
431            tail.copy_within(src, index + encoding_length_delta as usize);
432        }
433
434        // Check if last.
435        if let Some(next_encoded) = next_encoded {
436            // Insert re-encoded option header.
437            tail.get_mut(index..index + next_encoded.as_ref().len())
438                .expect("checked above")
439                .copy_from_slice(next_encoded.as_ref());
440        }
441
442        Ok(len_removed)
443    }
444
445    /// Retains all options the `predicate` applies to, the rest will be removed.
446    #[expect(
447        clippy::cast_sign_loss,
448        reason = "differences are checked by construction"
449    )]
450    #[expect(
451        clippy::cast_possible_wrap,
452        reason = "usize values stem from actual sizes"
453    )]
454    pub fn retain_options<P>(&mut self, predicate: P)
455    where
456        P: Fn(u16, &[u8]) -> bool,
457    {
458        use crate::option_iteration::{OptItem, OptPayloadReader};
459
460        if self.latest == 0 {
461            return;
462        }
463
464        let tail = self.encoded.tail_mut();
465        let mut opt_iter = OptPayloadReader::new(tail);
466        let mut previous_option = 0u16;
467        let mut opt_index = 0;
468
469        loop {
470            match opt_iter.next() {
471                Some(OptItem::Option { number, data }) => {
472                    let (slice, _) = opt_iter.destruct();
473
474                    if predicate(number, data) {
475                        previous_option = number;
476                        opt_index = tail.len() - slice.len();
477
478                        opt_iter = OptPayloadReader::new_from(slice, number);
479                        continue;
480                    }
481
482                    let Ok(len_removed) =
483                        Self::remove_option_at(tail, opt_index, self.end, previous_option)
484                    else {
485                        unreachable!(
486                            "Internal invariant violated (bug in coap-message-implementations)"
487                        );
488                    };
489
490                    self.end = (self.end as isize - len_removed) as usize;
491                    let options_end = if let Some(payload_start) = self.payload_start {
492                        let new = (payload_start as isize - len_removed) as usize;
493                        self.payload_start = Some(new);
494                        new - 1
495                    } else {
496                        self.end
497                    };
498
499                    if opt_index == options_end {
500                        self.latest = previous_option;
501                        return;
502                    }
503
504                    opt_iter = OptPayloadReader::new_from(&tail[opt_index..], previous_option);
505                }
506                None | Some(OptItem::Payload(_)) => return,
507                Some(OptItem::Error(_)) => unreachable!(
508                    "Options not encoded as expected (bug in coap-message-implementations)"
509                ),
510            }
511        }
512    }
513
514    /// Discards anything that has been written in the message, and allows any operation that would
515    /// be allowed after calling [`Self::new_empty()`].
516    ///
517    /// This is a short-cut to messages that can be snapshotted and rewound, and a band-aid until
518    /// that is available in traits.
519    pub fn reset(&mut self) {
520        self.latest = 0;
521        self.end = 0;
522        self.payload_start = None;
523        // This is not strictly necessary, but at least a temporarily useful thing that will keep
524        // users from relying on the code to be persisted.
525        *self.encoded.code_mut() = 0;
526    }
527
528    /// Expands the bounds of the payload by `added_len`.
529    ///
530    /// Similar to [`payload_mut_with_len()`][MutableWritableMessage::payload_mut_with_len()],
531    /// but it has no restriction on length (beyond the length of the underlying buffer).
532    ///
533    /// # Errors
534    ///
535    /// … are raised if there is insufficient space for extending the payload.
536    pub fn untruncate(&mut self, mut added_len: usize) -> Result<(), WriteError> {
537        if self.payload_start.is_none() {
538            added_len += 1;
539        }
540
541        if self.encoded.tail().len() < self.end + added_len {
542            return Err(WriteError::OutOfSpace);
543        }
544
545        if self.payload_start.is_none() {
546            // Insert payload marker.
547            self.encoded.tail_mut()[self.end] = 0xff;
548            self.payload_start = Some(self.end + 1);
549        }
550
551        self.end += added_len;
552
553        Ok(())
554    }
555
556    /// Returns the number of bytes that were populated inside tail, along with the encoded data.
557    ///
558    /// For `EM`s that are just referenced (i.e., slices), the 2nd output can often just be
559    /// discarded, but it is essential for owned ones.
560    pub fn finish(self) -> (usize, EM) {
561        (self.end, self.encoded)
562    }
563}
564
565/// Error returned in specific places where erring is preferable over panicking when an internal
566/// invariant is violated.
567struct InvariantViolated;
568
569impl<EM: MessageBufferMut> coap_message::ReadableMessage for MessageMut<EM> {
570    type Code = u8;
571    type MessageOption<'b>
572        = MessageOption<'b>
573    where
574        Self: 'b;
575    type OptionsIter<'b>
576        = OptionsIter<'b>
577    where
578        Self: 'b;
579    fn code(&self) -> u8 {
580        self.encoded.code()
581    }
582    // Funny detail on the side: If this is called often, an inmemory_write::MessageMut might be more
583    // efficient even for plain reading than an inmemory::Message (because we don't have to iterate)
584    fn payload(&self) -> &[u8] {
585        match self.payload_start {
586            None => &[],
587            Some(start) => &self.encoded.tail()[start..self.end],
588        }
589    }
590    fn options(&self) -> <Self as coap_message::ReadableMessage>::OptionsIter<'_> {
591        OptionsIter(
592            crate::option_iteration::OptPayloadReader::new(&self.encoded.tail()[..self.end]),
593            None,
594        )
595    }
596}
597
598impl<EM: MessageBufferMut> coap_message::WithSortedOptions for MessageMut<EM> {}
599
600impl<EM: MessageBufferMut> MinimalWritableMessage for MessageMut<EM> {
601    type Code = u8;
602    type OptionNumber = u16;
603    type UnionError = WriteError;
604    type AddOptionError = WriteError;
605    type SetPayloadError = WriteError;
606
607    fn set_code(&mut self, code: u8) {
608        *self.encoded.code_mut() = code;
609    }
610
611    fn add_option(&mut self, number: u16, data: &[u8]) -> Result<(), WriteError> {
612        let delta = number
613            .checked_sub(self.latest)
614            .ok_or(WriteError::OutOfSequence)?;
615        self.latest = number;
616        let encoded = crate::option_extension::encode_extensions(delta, data.len() as u16);
617        let encoded = encoded.as_ref();
618        let added_len = encoded.len() + data.len();
619
620        // Where we encode the option
621        let option_cursor;
622        // What .end will be after this operation (but we can't set it yet as we may still err out)
623        let eventual_end = self.end + added_len;
624        if let Some(payload_start) = self.payload_start {
625            if added_len > self.encoded.tail().len() - self.end {
626                return Err(WriteError::OutOfSpace);
627            }
628            option_cursor = payload_start - 1;
629            // Could also rotate_right, but we don't need the shifted-out bytes preserved in the
630            // area we'll overwrite in the next instructions
631            let src = option_cursor..self.end;
632            self.encoded
633                .tail_mut()
634                .copy_within(src, option_cursor + added_len);
635
636            self.payload_start = Some(payload_start + added_len);
637        } else {
638            option_cursor = self.end;
639        }
640
641        self.encoded
642            .tail_mut()
643            .get_mut(option_cursor..option_cursor + encoded.len())
644            .ok_or(WriteError::OutOfSpace)?
645            .copy_from_slice(encoded);
646        let option_cursor = option_cursor + encoded.len();
647        self.encoded
648            .tail_mut()
649            .get_mut(option_cursor..option_cursor + data.len())
650            .ok_or(WriteError::OutOfSpace)?
651            .copy_from_slice(data);
652        self.end = eventual_end;
653
654        Ok(())
655    }
656
657    fn set_payload(&mut self, payload: &[u8]) -> Result<(), WriteError> {
658        if self.payload_start.is_some() {
659            // We might allow double setting the payload through later extensions, but as for this
660            // interface it's once only. We don't detect double setting of empty payloads, but it's
661            // not this implementation's purpose to act as a linter.
662            //
663            // (And whoever uses the options-and-payload-mixed properties will use payload_mut
664            // instead).
665            return Err(WriteError::OutOfSequence);
666        }
667        if !payload.is_empty() {
668            *self
669                .encoded
670                .tail_mut()
671                .get_mut(self.end)
672                .ok_or(WriteError::OutOfSpace)? = 0xff;
673            let start = self.end + 1;
674            self.end = start + payload.len();
675            self.encoded
676                .tail_mut()
677                .get_mut(start..self.end)
678                .ok_or(WriteError::OutOfSpace)?
679                .copy_from_slice(payload);
680            self.payload_start = Some(start);
681        }
682        Ok(())
683    }
684
685    #[cfg(feature = "downcast")]
686    fn with_static_type_annotation(
687        &mut self,
688    ) -> Option<coap_message::helpers::RefMutWithStaticType<'_, Self>> {
689        let em_static = EM::static_mut_variant()?;
690
691        // SAFETY: It is this type's policy that its RefMutWithStaticType ID is the given
692        // MessageMut<'static>.
693        Some(unsafe {
694            coap_message::helpers::RefMutWithStaticType::new(
695                self,
696                type_id_of_emv_of_lml(&em_static),
697            )
698        })
699    }
700
701    #[inline]
702    #[allow(refining_impl_trait_reachable)]
703    fn promote_to_mutable_writable_message(&mut self) -> Option<&mut Self> {
704        Some(self)
705    }
706}
707
708impl<EM: MessageBufferMut> MutableWritableMessage for MessageMut<EM> {
709    fn available_space(&self) -> usize {
710        self.encoded.tail().len() - self.options_end()
711    }
712
713    fn payload_mut_with_len(&mut self, len: usize) -> Result<&mut [u8], WriteError> {
714        if len == 0 {
715            // Just finish the side effect and return something good enough; this allows the easier
716            // path for the rest of the function to pick a start, end, and serve that.
717            self.truncate(0)?;
718            return Ok(&mut []);
719        }
720
721        let start = match self.payload_start {
722            None => {
723                self.encoded.tail_mut()[self.end] = 0xff;
724                self.end + 1
725            }
726            Some(payload_start) => payload_start,
727        };
728        let end = start + len;
729
730        let end = end.clamp(0, self.encoded.tail().len());
731
732        self.payload_start = Some(start);
733        self.end = end;
734        self.encoded
735            .tail_mut()
736            .get_mut(start..end)
737            .ok_or(WriteError::OutOfSpace)
738    }
739
740    fn truncate(&mut self, len: usize) -> Result<(), WriteError> {
741        match (len, self.payload_start) {
742            (0, Some(payload_start)) => {
743                self.end = payload_start - 1;
744                self.payload_start = None;
745            }
746            (0, None) => {}
747            (len, Some(payload_start)) if self.end - payload_start >= len => {
748                self.end = payload_start + len;
749            }
750            _ => return Err(WriteError::OutOfSpace),
751        }
752        Ok(())
753    }
754
755    fn mutate_options<F>(&mut self, mut f: F)
756    where
757        F: FnMut(u16, &mut [u8]),
758    {
759        // TBD this is excessively complex, and grounds for finding a better interface. ("Set
760        // option and give me a key to update it later with a mutable reference")?
761
762        let optend = self.options_end();
763
764        // May end in a payload marker or just plain end
765        let mut slice = &mut self.encoded.tail_mut()[..optend];
766
767        let mut option_base = 0;
768
769        while !slice.is_empty() {
770            // This is copied and adapted from
771            // coap_messsage_utils::option_iteration::OptPayloadReader and not used through it,
772            // because that'd be a whole separate implementation there with mut.
773            // (It's bad enough that take_extension needs the trickery)
774            let delta_len = slice[0];
775            slice = &mut slice[1..];
776
777            if delta_len == 0xff {
778                break;
779            }
780
781            let mut delta = u16::from(delta_len) >> 4;
782            let mut len = u16::from(delta_len) & 0x0f;
783
784            let new_len = {
785                // Workaround for https://github.com/rust-lang/rust-clippy/issues/10608
786                #[allow(clippy::redundant_slicing)]
787                // To get take_extension to cooperate...
788                let mut readable = &slice[..];
789
790                crate::option_extension::take_extension(&mut delta, &mut readable)
791                    .expect("Invalid encoded option in being-written message");
792                crate::option_extension::take_extension(&mut len, &mut readable)
793                    .expect("Invalid encoded option in being-written message");
794
795                readable.len()
796            };
797            // ... and get back to a mutable form
798            let trim = slice.len() - new_len;
799            slice = &mut slice[trim..];
800
801            option_base += delta;
802
803            let len = len.into();
804            f(option_base, &mut slice[..len]);
805            slice = &mut slice[len..];
806        }
807    }
808}