toad_msg/lib.rs
1//! Low-level representation of CoAP messages.
2//!
3//! The most notable item in `toad_msg` is `Message`;
4//! a CoAP message very close to the actual byte layout.
5//!
6//! ## Allocation
7//! CoAP messages have some attributes whose size is dynamic:
8//! - The message payload (in http terms: the request/response body)
9//! - the number of options (in http terms: headers)
10//! - the value of an option (in http terms: header value)
11//!
12//! `Message` does not require an allocator and has no opinions about what kind of collection
13//! it uses internally to store these values.
14//!
15//! It solves this problem by being generic over the collections it needs and uses an `Array` trait
16//! to capture its idea of what makes a collection useful.
17//!
18//! This means that you may use a provided implementation (for `Vec` or `tinyvec::ArrayVec`)
19//! or provide your own collection (see the [custom collections example](https://github.com/clov-coffee/toad/blob/main/toad_msg/examples/custom_collections.rs))
20//!
21//! ```rust
22//! //! Note: both of these type aliases are exported by `toad_msg` for convenience.
23//!
24//! use tinyvec::ArrayVec;
25//! use toad_msg::{Message, Opt};
26//!
27//! // Message Payload byte buffer
28//! // |
29//! // | Array of options in the message
30//! // vvvvvvv vvvvvvvvvvvvvvvvv
31//! type VecMessage = Message<Vec<u8>, Vec<Opt<Vec<u8>>>>;
32//!
33//! // 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.
34//! type ArrayVecMessage<
35//! const PAYLOAD_SIZE: usize,
36//! const OPT_SIZE: usize,
37//! const NUM_OPTS: usize,
38//! > = Message<
39//! ArrayVec<[u8; PAYLOAD_SIZE]>,
40//! ArrayVec<[Opt<ArrayVec<[u8; OPT_SIZE]>>; NUM_OPTS]>,
41//! >;
42//! ```
43//!
44//! It may look a little ugly, but a core goal of `toad` is to be platform- and alloc-agnostic.
45//!
46//! ## Performance
47//! This crate uses `criterion` to measure performance of the heaped & heapless implementations in this crate as well as `coap_lite::Packet`.
48//!
49//! In general, `toad_msg::VecMessage` performs identically to coap_lite (+/- 5%), and both are **much** faster than `toad_msg::ArrayVecMessage`.
50//!
51//! Benchmarks:
52//! ### Serializing to bytes
53//! <details>
54//! <summary>
55//!
56//! **Click to expand chart**
57//! </summary>
58//!
59//! 
60//! </details>
61//!
62//! ### Deserializing from bytes
63//! <details>
64//! <summary>
65//!
66//! **Click to expand chart**
67//! </summary>
68//!
69//! 
70//! </details>
71
72// x-release-please-start-version
73#![doc(html_root_url = "https://docs.rs/toad-msg/0.19.0")]
74// x-release-please-end
75#![cfg_attr(not(feature = "std"), no_std)]
76#![cfg_attr(not(test), forbid(missing_debug_implementations, unreachable_pub))]
77#![cfg_attr(not(test), deny(unsafe_code, missing_copy_implementations))]
78#![cfg_attr(any(docsrs, feature = "docs"), feature(doc_cfg))]
79#![deny(missing_docs)]
80
81#[cfg(feature = "alloc")]
82extern crate alloc as std_alloc;
83
84#[doc(hidden)]
85pub mod from_bytes;
86
87#[allow(missing_docs)]
88pub mod cache_key;
89
90/// Message structs
91pub mod msg;
92
93#[doc(hidden)]
94pub mod to_bytes;
95
96#[doc(inline)]
97pub use cache_key::*;
98#[doc(inline)]
99pub use from_bytes::TryFromBytes;
100#[doc(inline)]
101pub use msg::*;
102#[doc(inline)]
103pub use to_bytes::TryIntoBytes;
104use toad_array::Array;
105
106/// Type aliases for std or alloc platforms
107#[cfg(feature = "alloc")]
108pub mod alloc {
109 use std_alloc::collections::BTreeMap;
110 use std_alloc::vec::Vec;
111
112 use crate::{OptNumber, OptValue};
113
114 /// [`crate::Message`] that uses Vec and BTreeMap
115 pub type Message = crate::Message<Vec<u8>, BTreeMap<OptNumber, Vec<OptValue<Vec<u8>>>>>;
116}
117
118#[cfg(test)]
119pub(crate) fn test_msg() -> (alloc::Message, Vec<u8>) {
120 use std_alloc::collections::BTreeMap;
121 // TEST
122
123 let header: [u8; 4] = 0b0100_0001_0100_0101_0000_0000_0000_0001_u32.to_be_bytes();
124 let token: [u8; 1] = [254u8];
125 let content_format: &[u8] = b"application/json";
126 let options: [&[u8]; 2] = [&[0b_1100_1101u8, 0b00000011u8], content_format];
127 let payload: [&[u8]; 2] = [&[0b1111_1111_u8], b"hello, world!"];
128 let bytes = [header.as_ref(),
129 token.as_ref(),
130 options.concat().as_ref(),
131 payload.concat().as_ref()].concat();
132
133 let msg = alloc::Message { id: Id(1),
134 ty: Type::Con,
135 ver: Version(1),
136 token: Token(tinyvec::array_vec!([u8; 8] => 254)),
137 opts: BTreeMap::from([(OptNumber(12),
138 vec![OptValue(content_format.to_vec())])]),
139 code: Code { class: 2,
140 detail: 5 },
141 payload: Payload(b"hello, world!".to_vec()) };
142 (msg, bytes)
143}
144
145#[cfg(test)]
146pub(crate) mod tests {
147 #[macro_export]
148 macro_rules! assert_eqb {
149 ($actual:expr, $expected:expr) => {
150 if $actual != $expected {
151 panic!("expected {:08b} to equal {:08b}", $actual, $expected)
152 }
153 };
154 }
155
156 #[macro_export]
157 macro_rules! assert_eqb_iter {
158 ($actual:expr, $expected:expr) => {
159 if $actual.iter().ne($expected.iter()) {
160 panic!("expected {:?} to equal {:?}",
161 $actual.into_iter()
162 .map(|b| format!("{:08b}", b))
163 .collect::<Vec<_>>(),
164 $expected.into_iter()
165 .map(|b| format!("{:08b}", b))
166 .collect::<Vec<_>>())
167 }
168 };
169 }
170}