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
200impl<EM: EncodedMessage> GenericMessage<EM> {
201 /// Discard anything that has been written in the message, and allow any operation that would
202 /// be allowed after calling [`Self::new()`].
203 ///
204 /// This is a short-cut to messages that can be snapshotted and rewound, and a band-aid until
205 /// that is available in traits.
206 pub fn reset(&mut self) {
207 self.latest = 0;
208 self.end = 0;
209 self.payload_start = None;
210 // This is not strictly necessary, but at least a temporarily useful thing that will keep
211 // users from relying on the code to be persisted.
212 *self.encoded.code_mut() = 0;
213 }
214
215 /// Return the number of bytes that were populated inside tail
216 pub fn finish(self) -> usize {
217 self.end
218 }
219
220 /// Returns the number of bytes that were populated inside tail, along with the encoded data.
221 ///
222 /// Unlike the more common [`.finish()`][Self::finish], this makes sense mostly for owned EMs,
223 /// where the caller has to move it out again and can not use the shadowed reference the caller
224 /// retained.
225 pub fn finish_and_return(self) -> (usize, EM) {
226 (self.end, self.encoded)
227 }
228}
229
230impl<EM: EncodedMessage> coap_message::ReadableMessage for GenericMessage<EM> {
231 type Code = u8;
232 type MessageOption<'b>
233 = crate::inmemory::MessageOption<'b>
234 where
235 Self: 'b;
236 type OptionsIter<'b>
237 = crate::inmemory::OptionsIter<'b>
238 where
239 Self: 'b;
240 fn code(&self) -> u8 {
241 *self.encoded.code()
242 }
243 // Funny detail on the side: If this is called often, an inmemory_write::Message might be more
244 // efficient even for plain reading than an inmemory::Message (because we don't have to iterate)
245 fn payload(&self) -> &[u8] {
246 match self.payload_start {
247 None => &[],
248 Some(start) => &self.encoded.tail()[start..self.end],
249 }
250 }
251 fn options(&self) -> <Self as coap_message::ReadableMessage>::OptionsIter<'_> {
252 crate::inmemory::OptionsIter(crate::option_iteration::OptPayloadReader::new(
253 &self.encoded.tail()[..self.end],
254 ))
255 }
256}
257
258impl<EM: EncodedMessage> coap_message::WithSortedOptions for GenericMessage<EM> {}
259
260impl<EM: EncodedMessage> coap_message::MinimalWritableMessage for GenericMessage<EM> {
261 type Code = u8;
262 type OptionNumber = u16;
263 type UnionError = WriteError;
264 type AddOptionError = WriteError;
265 type SetPayloadError = WriteError;
266
267 fn set_code(&mut self, code: u8) {
268 *self.encoded.code_mut() = code;
269 }
270
271 fn add_option(&mut self, number: u16, data: &[u8]) -> Result<(), WriteError> {
272 let delta = number
273 .checked_sub(self.latest)
274 .ok_or(WriteError::OutOfSequence)?;
275 self.latest = number;
276 let encoded = crate::option_extension::encode_extensions(delta, data.len() as u16);
277 let encoded = encoded.as_ref();
278 let added_len = encoded.len() + data.len();
279
280 let option_cursor;
281 if let Some(payload_start) = self.payload_start {
282 option_cursor = payload_start - 1;
283 // Could also rotate_right, but we don't need the shifted-out bytes preserved in the
284 // area we'll overwrite in the next instructions
285 let src = option_cursor..self.encoded.tail().len() - added_len;
286 self.encoded
287 .tail_mut()
288 .copy_within(src, option_cursor + added_len);
289 self.payload_start = Some(payload_start + added_len);
290 } else {
291 option_cursor = self.end;
292 }
293
294 self.encoded
295 .tail_mut()
296 .get_mut(option_cursor..option_cursor + encoded.len())
297 .ok_or(WriteError::OutOfSpace)?
298 .copy_from_slice(encoded);
299 let option_cursor = option_cursor + encoded.len();
300 self.encoded
301 .tail_mut()
302 .get_mut(option_cursor..option_cursor + data.len())
303 .ok_or(WriteError::OutOfSpace)?
304 .copy_from_slice(data);
305 self.end += added_len;
306
307 Ok(())
308 }
309
310 fn set_payload(&mut self, payload: &[u8]) -> Result<(), WriteError> {
311 if self.payload_start.is_some() {
312 // We might allow double setting the payload through later extensions, but as for this
313 // interface it's once only. We don't detect double setting of empty payloads, but it's
314 // not this implementation's purpose to act as a linter.
315 //
316 // (And whoever uses the options-and-payload-mixed properties will use payload_mut
317 // instead).
318 return Err(WriteError::OutOfSequence);
319 };
320 if !payload.is_empty() {
321 *self
322 .encoded
323 .tail_mut()
324 .get_mut(self.end)
325 .ok_or(WriteError::OutOfSpace)? = 0xff;
326 let start = self.end + 1;
327 self.end = start + payload.len();
328 self.encoded
329 .tail_mut()
330 .get_mut(start..self.end)
331 .ok_or(WriteError::OutOfSpace)?
332 .copy_from_slice(payload);
333 self.payload_start = Some(start);
334 }
335 Ok(())
336 }
337
338 #[cfg(feature = "downcast")]
339 fn with_static_type_annotation(
340 &mut self,
341 ) -> Option<coap_message::helpers::RefMutWithStaticType<'_, Self>> {
342 // SAFETY: It is this type's policy that its RefMutWithStaticType ID is the given
343 // Message<'static>.
344 Some(unsafe {
345 coap_message::helpers::RefMutWithStaticType::new(
346 self,
347 core::any::TypeId::of::<Message<'static>>(),
348 )
349 })
350 }
351
352 #[inline]
353 #[allow(refining_impl_trait_reachable)]
354 fn promote_to_mutable_writable_message(&mut self) -> Option<&mut Self> {
355 Some(self)
356 }
357}
358
359impl<EM: EncodedMessage> coap_message::MutableWritableMessage for GenericMessage<EM> {
360 fn available_space(&self) -> usize {
361 self.encoded.tail().len()
362 - self
363 .payload_start
364 .map(|s| s - 1) // available_space includes the payload indicator
365 .unwrap_or(self.end)
366 }
367
368 fn payload_mut_with_len(&mut self, len: usize) -> Result<&mut [u8], WriteError> {
369 if len == 0 {
370 // Just finish the side effect and return something good enough; this allows the easier
371 // path for the rest of the function to pick a start, end, and serve that.
372 self.truncate(0)?;
373 return Ok(&mut []);
374 }
375
376 let start = match self.payload_start {
377 None => {
378 self.encoded.tail_mut()[self.end] = 0xff;
379 self.end + 1
380 }
381 Some(payload_start) => payload_start,
382 };
383 let end = start + len;
384
385 let end = end.clamp(0, self.encoded.tail().len());
386
387 self.payload_start = Some(start);
388 self.end = end;
389 self.encoded
390 .tail_mut()
391 .get_mut(start..end)
392 .ok_or(WriteError::OutOfSpace)
393 }
394
395 fn truncate(&mut self, len: usize) -> Result<(), WriteError> {
396 match (len, self.payload_start) {
397 (0, Some(payload_start)) => {
398 self.end = payload_start - 1;
399 }
400 (0, None) => {}
401 (len, Some(payload_start)) if self.end - payload_start >= len => {
402 self.end = payload_start + len;
403 }
404 _ => return Err(WriteError::OutOfSpace),
405 }
406 Ok(())
407 }
408
409 fn mutate_options<F>(&mut self, mut f: F)
410 where
411 F: FnMut(u16, &mut [u8]),
412 {
413 // TBD this is excessively complex, and grounds for finding a better interface. ("Set
414 // option and give me a key to update it later with a mutable reference")?
415
416 let optend = self.payload_start.map(|s| s - 1).unwrap_or(self.end);
417
418 // May end in a payload marker or just plain end
419 let mut slice = &mut self.encoded.tail_mut()[..optend];
420
421 let mut option_base = 0;
422
423 while !slice.is_empty() {
424 // This is copied and adapted from
425 // coap_messsage_utils::option_iteration::OptPayloadReader and not used through it,
426 // because that'd be a whole separate implementation there with mut.
427 // (It's bad enough that take_extension needs the trickery)
428 let delta_len = slice[0];
429 slice = &mut slice[1..];
430
431 if delta_len == 0xff {
432 break;
433 }
434
435 let mut delta = (delta_len as u16) >> 4;
436 let mut len = (delta_len as u16) & 0x0f;
437
438 let new_len = {
439 // Workaround for https://github.com/rust-lang/rust-clippy/issues/10608
440 #[allow(clippy::redundant_slicing)]
441 // To get take_extension to cooperate...
442 let mut readable = &slice[..];
443
444 crate::option_extension::take_extension(&mut delta, &mut readable)
445 .expect("Invalid encoded option in being-written message");
446 crate::option_extension::take_extension(&mut len, &mut readable)
447 .expect("Invalid encoded option in being-written message");
448
449 readable.len()
450 };
451 // ... and get back to a mutable form
452 let trim = slice.len() - new_len;
453 slice = &mut slice[trim..];
454
455 option_base += delta;
456
457 let len = len.into();
458 f(option_base, &mut slice[..len]);
459 slice = &mut slice[len..];
460 }
461 }
462}