coap_message_implementations/inmemory.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251
//! Implementation of [coap_message::ReadableMessage] based on a serialized message
//!
//! [Message] is the main struct of this module -- if the message is available as a slice that
//! lives as long as the message, that's the type to use. Otherwise, implement [EncodedMessage] on
//! your data, and wrap it in an [EncodedMessageView]. (The [Message] is nothing else than the
//! options/payload slice plus the code in a struct).
//!
//! Note that the [`crate::inmemory_write`] has a similar mechanism but does without an equivalent
//! "view"; at the next breaking revision, those might be unified.
#![cfg_attr(feature = "downcast", allow(unsafe_code))]
use coap_message::*;
use crate::option_iteration::*;
/// An iterator producing [MessageOption]s by running along an encoded CoAP message options stream
/// by using a [OptPayloadReader] and discarding the payload.
pub struct OptionsIter<'a>(pub(crate) OptPayloadReader<'a>);
/// Option value used by this library to indicate a format error in the message that was not
/// detected by the time option / payload processing was started.
///
/// As this option is critical and proxy-unsafe, no application can expect to successfully process
/// any message that contains this option. (The number is from the experimental-use range;
/// implementations using this module will simply not support that option in experiments, as they
/// can not distinguish it from an option formatting error).
pub const OPTION_INVALID: u16 = u16::MAX;
impl<'a> Iterator for OptionsIter<'a> {
type Item = MessageOption<'a>;
fn next(&mut self) -> Option<MessageOption<'a>> {
match self.0.next() {
Some(OptItem::Option { number, data }) => Some(MessageOption {
number,
value: data,
}),
Some(OptItem::Error { .. }) => Some(MessageOption {
number: OPTION_INVALID,
value: &[],
}),
// No need to recurse or loop -- it's always the last one
Some(OptItem::Payload(_)) => None,
None => None,
}
}
}
/// A simple [coap_message::MessageOption] implementation for memory-mapped CoAP messages
pub struct MessageOption<'a> {
number: u16,
value: &'a [u8],
}
impl<'a> coap_message::MessageOption for MessageOption<'a> {
fn number(&self) -> u16 {
self.number
}
fn value(&self) -> &[u8] {
self.value
}
}
/// A CoAP message that resides in contiguous readable memory
///
/// This implementation does not attempt to do any early validation. On encoding errors discovered
/// at runtime, it simply emits the critical-and-not-safe-to-forward CoAP option 65535
/// ([OPTION_INVALID]), which to the indication indicates that something went wrong
///
/// Message is covariant over its lifetime because it only reads from there. FIXME: It should
/// express that using a Deref or something like that.
///
/// # Implementation details
///
/// When used with [coap_message::helpers::RefWithStaticType], it always uses its `'static`
/// counterpart as the corresponding type ID. Its [`Self::downcast_from`] method uses this, and
/// restores the lifetime based on the given impl Trait reference's lifetime using its covariance
/// property.
#[derive(Clone)]
pub struct Message<'a> {
inner: EncodedMessageView<SliceMessage<'a>>,
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
struct SliceMessage<'a> {
code: u8,
options_and_payload: &'a [u8],
}
impl<'a> EncodedMessage for SliceMessage<'a> {
fn code(&self) -> u8 {
self.code
}
fn options_and_payload(&self) -> &[u8] {
self.options_and_payload
}
}
impl<'a> Message<'a> {
pub fn new(code: u8, options_and_payload: &'a [u8]) -> Self {
Self {
inner: EncodedMessageView::new(SliceMessage {
code,
options_and_payload,
}),
}
}
#[cfg(feature = "downcast")]
pub fn downcast_from<M: ReadableMessage>(generic: &'a M) -> Option<&'a Self> {
let (reference, type_id) = generic.with_static_type_annotation()?.into_inner();
if type_id != core::any::TypeId::of::<Message<'static>>() {
return None;
}
// One of us, one of us.
let ptr = reference as *const M as *const Self;
// SAFETY: The RefWithStaticType matching our type ID will only be constructed if M is
// Message, and whatever Message<'b> it was before, it will live at least for 'a (because
// we got a &'a Message<'b> and is covariant.
Some(unsafe { &*ptr })
}
}
impl<'m> ReadableMessage for Message<'m> {
type Code = u8;
type MessageOption<'a> = MessageOption<'a>
where
Self: 'a,
;
type OptionsIter<'a> = OptionsIter<'a>
where
Self: 'a,
;
fn code(&self) -> u8 {
self.inner.code()
}
fn payload(&self) -> &[u8] {
self.inner.payload()
}
fn options(&self) -> OptionsIter<'_> {
self.inner.options()
}
#[cfg(feature = "downcast")]
fn with_static_type_annotation(
&self,
) -> Option<coap_message::helpers::RefWithStaticType<'_, Self>> {
// SAFETY: It is this type's policy that its RefWithStaticType ID is the given
// Message<'static>.
Some(unsafe {
coap_message::helpers::RefWithStaticType::new(
self,
core::any::TypeId::of::<Message<'static>>(),
)
})
}
}
/// A trait that can implemented on in-memory encoded messages; then, an [EncodedMessageView]
/// struct can be placed around the implementer to implement [coap_message::ReadableMessage].
pub trait EncodedMessage {
/// The code of the message
fn code(&self) -> u8;
/// The memory area containing the encoded options, payload marker and payload
fn options_and_payload(&self) -> &[u8];
}
/// A wrapper around any data structure containing a readable message
///
/// This is not `Sync` -- while it is currently a zero-sized wrapper, it may gain optimizations
/// later such as memoizing where the payload begins (and this is only practical with interior
/// mutability).
///
/// See [Message] for how this does not perform early validation and handles message errors.
// FIXME test whether the _phantom does the right thing
#[derive(Clone, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct EncodedMessageView<EM: EncodedMessage> {
inner: EM,
_phantom: core::marker::PhantomData<core::cell::Cell<()>>,
}
impl<EM: EncodedMessage> EncodedMessageView<EM> {
pub fn new(inner: EM) -> Self {
Self {
inner,
_phantom: core::marker::PhantomData,
}
}
}
/// When the inner item stores more than just the code and encoded options, this can be used to
/// gain read-only access to any additional data. (Mutating access is blocked to ensure that future
/// optimizations like memoizing the payload position are possible; it might still be enabled if
/// memoized data is cleared just to be on the safe side).
// FIXME: Might even dereference into it?
impl<EM: EncodedMessage> AsRef<EM> for EncodedMessageView<EM> {
fn as_ref(&self) -> &EM {
&self.inner
}
}
impl<EM: EncodedMessage> ReadableMessage for EncodedMessageView<EM> {
type Code = u8;
type MessageOption<'a> = MessageOption<'a>
where
Self: 'a,
;
type OptionsIter<'a> = OptionsIter<'a>
where
Self: 'a,
;
fn code(&self) -> u8 {
self.inner.code()
}
// This is one of the not-most-efficient things mentioned in the module introduction: It's
// iterating through the complete options stream on every payload call, rather than memoizing
// its location when the options are iterated over.
fn payload(&self) -> &[u8] {
let empty: &[u8] = &[];
// ... into which we'll index
let optpayload = self.inner.options_and_payload();
OptPayloadReader::new(optpayload)
.find(|x| !matches!(x, OptItem::Option { .. }))
.map(|x| {
if let OptItem::Payload(data) = x {
// Can't return data itself because the iterator doesn't outlive this function
// To be replaced when ptr_wrapping_offset_from is stabilized
let offset = data.as_ptr() as usize - optpayload.as_ptr() as usize;
&optpayload[offset..]
} else {
// Silently produce empty payload -- the user will need to have checked the
// options and have found the 65535 option.
&[]
}
})
.unwrap_or(empty)
}
fn options(&self) -> OptionsIter<'_> {
OptionsIter(OptPayloadReader::new(self.inner.options_and_payload()))
}
}