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