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}