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