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
//! Low-level representation of CoAP messages.
//!
//! The most notable item in `toad_msg` is `Message`;
//! a CoAP message very close to the actual byte layout.
//!
//! ## Allocation
//! CoAP messages have some attributes whose size is dynamic:
//! - The message payload (in http terms: the request/response body)
//! - the number of options (in http terms: headers)
//! - the value of an option (in http terms: header value)
//!
//! `Message` does not require an allocator and has no opinions about what kind of collection
//! it uses internally to store these values.
//!
//! It solves this problem by being generic over the collections it needs and uses an `Array` trait
//! to capture its idea of what makes a collection useful.
//!
//! This means that you may use a provided implementation (for `Vec` or `tinyvec::ArrayVec`)
//! or provide your own collection (see the [custom collections example](https://github.com/clov-coffee/toad/blob/main/toad_msg/examples/custom_collections.rs))
//!
//! ```rust
//! //! Note: both of these type aliases are exported by `toad_msg` for convenience.
//!
//! use tinyvec::ArrayVec;
//! use toad_msg::{Message, Opt};
//!
//! // Message Payload byte buffer
//! // |
//! // | Array of options in the message
//! // vvvvvvv vvvvvvvvvvvvvvvvv
//! type VecMessage = Message<Vec<u8>, Vec<Opt<Vec<u8>>>>;
//!
//! // Used like: `ArrayVecMessage<1024, 256, 16>`; a message that can store a payload up to 1024 bytes, and up to 16 options each with up to a 256 byte value.
//! type ArrayVecMessage<
//! const PAYLOAD_SIZE: usize,
//! const OPT_SIZE: usize,
//! const NUM_OPTS: usize,
//! > = Message<
//! ArrayVec<[u8; PAYLOAD_SIZE]>,
//! ArrayVec<[Opt<ArrayVec<[u8; OPT_SIZE]>>; NUM_OPTS]>,
//! >;
//! ```
//!
//! It may look a little ugly, but a core goal of `toad` is to be platform- and alloc-agnostic.
//!
//! ## Performance
//! This crate uses `criterion` to measure performance of the heaped & heapless implementations in this crate as well as `coap_lite::Packet`.
//!
//! In general, `toad_msg::VecMessage` performs identically to coap_lite (+/- 5%), and both are **much** faster than `toad_msg::ArrayVecMessage`.
//!
//! Benchmarks:
//! ### Serializing to bytes
//! <details>
//! <summary>
//!
//! **Click to expand chart**
//! </summary>
//!
//! ![chart](https://raw.githubusercontent.com/clov-coffee/toad/main/toad-msg/docs/from_bytes.svg)
//! </details>
//!
//! ### Deserializing from bytes
//! <details>
//! <summary>
//!
//! **Click to expand chart**
//! </summary>
//!
//! ![chart](https://raw.githubusercontent.com/clov-coffee/toad/main/toad-msg/docs/to_bytes.svg)
//! </details>
// x-release-please-start-version
#![doc(html_root_url = "https://docs.rs/toad-msg/0.19.0")]
// x-release-please-end
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(not(test), forbid(missing_debug_implementations, unreachable_pub))]
#![cfg_attr(not(test), deny(unsafe_code, missing_copy_implementations))]
#![cfg_attr(any(docsrs, feature = "docs"), feature(doc_cfg))]
#![deny(missing_docs)]
#[cfg(feature = "alloc")]
extern crate alloc as std_alloc;
#[doc(hidden)]
pub mod from_bytes;
#[allow(missing_docs)]
pub mod cache_key;
/// Message structs
pub mod msg;
#[doc(hidden)]
pub mod to_bytes;
#[doc(inline)]
pub use cache_key::*;
#[doc(inline)]
pub use from_bytes::TryFromBytes;
#[doc(inline)]
pub use msg::*;
#[doc(inline)]
pub use to_bytes::TryIntoBytes;
use toad_array::Array;
/// Type aliases for std or alloc platforms
#[cfg(feature = "alloc")]
pub mod alloc {
use std_alloc::collections::BTreeMap;
use std_alloc::vec::Vec;
use crate::{OptNumber, OptValue};
/// [`crate::Message`] that uses Vec and BTreeMap
pub type Message = crate::Message<Vec<u8>, BTreeMap<OptNumber, Vec<OptValue<Vec<u8>>>>>;
}
#[cfg(test)]
pub(crate) fn test_msg() -> (alloc::Message, Vec<u8>) {
use std_alloc::collections::BTreeMap;
// TEST
let header: [u8; 4] = 0b0100_0001_0100_0101_0000_0000_0000_0001_u32.to_be_bytes();
let token: [u8; 1] = [254u8];
let content_format: &[u8] = b"application/json";
let options: [&[u8]; 2] = [&[0b_1100_1101u8, 0b00000011u8], content_format];
let payload: [&[u8]; 2] = [&[0b1111_1111_u8], b"hello, world!"];
let bytes = [header.as_ref(),
token.as_ref(),
options.concat().as_ref(),
payload.concat().as_ref()].concat();
let msg = alloc::Message { id: Id(1),
ty: Type::Con,
ver: Version(1),
token: Token(tinyvec::array_vec!([u8; 8] => 254)),
opts: BTreeMap::from([(OptNumber(12),
vec![OptValue(content_format.to_vec())])]),
code: Code { class: 2,
detail: 5 },
payload: Payload(b"hello, world!".to_vec()) };
(msg, bytes)
}
#[cfg(test)]
pub(crate) mod tests {
#[macro_export]
macro_rules! assert_eqb {
($actual:expr, $expected:expr) => {
if $actual != $expected {
panic!("expected {:08b} to equal {:08b}", $actual, $expected)
}
};
}
#[macro_export]
macro_rules! assert_eqb_iter {
($actual:expr, $expected:expr) => {
if $actual.iter().ne($expected.iter()) {
panic!("expected {:?} to equal {:?}",
$actual.into_iter()
.map(|b| format!("{:08b}", b))
.collect::<Vec<_>>(),
$expected.into_iter()
.map(|b| format!("{:08b}", b))
.collect::<Vec<_>>())
}
};
}
}