Vendor dependencies

This commit is contained in:
2026-08-01 16:11:49 +03:00
parent 7f139a0241
commit 6b5e7f0f8b
29706 changed files with 9575646 additions and 0 deletions
+87
View File
@@ -0,0 +1,87 @@
#[cfg(all(feature = "encoding-raw", feature = "encoding-rzcobs"))]
compile_error!("Multiple `encoding-*` features are enabled. You may only enable one.");
#[cfg_attr(feature = "encoding-raw", path = "raw.rs")]
#[cfg_attr(not(feature = "encoding-raw"), path = "rzcobs.rs")]
mod inner;
// This wrapper struct is to avoid copypasting the public docs in all the impls.
/// Encode raw defmt frames for sending over the wire.
///
/// defmt emits "log frames", which are sequences of bytes. The raw log frame data
/// is then *encoded* prior to sending over the wire.
///
/// `Encoder` will encode the frames according to the currently selected
/// `encoding-*` Cargo feature. See `Cargo.toml` for the supported encodings
/// and their tradeoffs.
///
/// Encodings may perform two functions:
///
/// - Framing: Adds extra data to allow the encoder to know when each frame starts
/// and ends in the stream. Unframed log frames already contain enough information for
/// the decoder to know when they end, so framing is optional. However, without framing
/// the decoder must receive all bytes intact or it may "lose sync". With framing, it can
/// recover from missing/corrupted data, and can start decoding from the "middle" of an
/// already-running stream.
/// - Compression: The frame data has rather low entropy (for example, it contains many
/// zero bytes due to encoding all integers in fixed with, and will likely contain many
/// repetitions). Compression can decrease the on-the-wire required bandwidth.
///
/// defmt provides the `Encoder` separately instead of feeding already-encoded bytes
/// to the `Logger` because `Logger` implementations may decide to allow
/// concurrent logging from multiple "contexts" such as threads or interrupt
/// priority levels. In this case, the Logger implementation needs to create one
/// Encoder for each such context.
pub struct Encoder {
inner: inner::Encoder,
}
impl Encoder {
/// Create a new `Encoder`.
#[allow(clippy::new_without_default)]
pub const fn new() -> Self {
Self {
inner: inner::Encoder::new(),
}
}
/// Start encoding a log frame.
///
/// `Logger` impls will typically call this from `acquire()`.
///
/// You may only call `start_frame` when no frame is currently being encoded.
/// Failure to do so may result in corrupted data on the wire.
///
/// The `write` closure will be called with the encoded data that must
/// be sent on the wire. It may be called zero, one, or multiple times.
pub fn start_frame(&mut self, write: impl FnMut(&[u8])) {
self.inner.start_frame(write)
}
/// Finish encoding a log frame.
///
/// `Logger` impls will typically call this from `release()`.
///
/// You may only call `end_frame` when a frame is currently being encoded.
/// Failure to do so may result in corrupted data on the wire.
///
/// The `write` closure will be called with the encoded data that must
/// be sent on the wire. It may be called zero, one, or multiple times.
pub fn end_frame(&mut self, write: impl FnMut(&[u8])) {
self.inner.end_frame(write)
}
/// Write part of data for a log frame.
///
/// `Logger` impls will typically call this from `write()`.
///
/// You may only call `write` when a frame is currently being encoded.
/// Failure to do so may result in corrupted data on the wire.
///
/// The `write` closure will be called with the encoded data that must
/// be sent on the wire. It may be called zero, one, or multiple times.
pub fn write(&mut self, data: &[u8], write: impl FnMut(&[u8])) {
self.inner.write(data, write)
}
}
+17
View File
@@ -0,0 +1,17 @@
pub(crate) struct Encoder {
_private: (),
}
impl Encoder {
pub(crate) const fn new() -> Self {
Self { _private: () }
}
pub(crate) fn start_frame(&mut self, _write: impl FnMut(&[u8])) {}
pub(crate) fn end_frame(&mut self, _write: impl FnMut(&[u8])) {}
pub(crate) fn write(&mut self, data: &[u8], mut write: impl FnMut(&[u8])) {
write(data)
}
}
+267
View File
@@ -0,0 +1,267 @@
// Standard COBS/rCOBS:
// 00000000 => end of frame
// nnnnnnnn => output n-1 bytes from stream, output 0x00
// 11111111 => output 254 bytes from stream
//
// zCOBS/rzCOBS:
// 00000000 => end of frame
// 0xxxxxxx => foreach x from LSB to MSB: if x=0 output 1 byte from stream, if x=1 output 0x00
// 1nnnnnnn => output n+7 bytes from stream, output 0x00
// 11111111 => output 134 bytes from stream
pub(crate) struct Encoder {
run: u8,
zeros: u8,
started: bool,
}
impl Encoder {
pub const fn new() -> Self {
Self {
run: 0,
zeros: 0,
started: false,
}
}
pub fn start_frame(&mut self, mut write: impl FnMut(&[u8])) {
let mut write_byte = move |b: u8| write(&[b]);
if !self.started {
self.started = true;
// Write a frame-separator at the very beginning. This allows the
// decoder to correctly decode the first frame if a previous boot had left a
// partly-written frame.
write_byte(0x00);
}
}
pub fn end_frame(&mut self, mut write: impl FnMut(&[u8])) {
let mut write_byte = move |b: u8| write(&[b]);
// Finish writing the previous symbol if needed.
match self.run {
0 => {}
1..=6 => write_byte((self.zeros | (0xFF << self.run)) & 0x7F),
_ => write_byte((self.run - 7) | 0x80),
}
// Write frame-separator.
write_byte(0x00);
self.run = 0;
self.zeros = 0;
}
pub fn write(&mut self, data: &[u8], mut write: impl FnMut(&[u8])) {
let mut write_byte = move |b: u8| write(&[b]);
for &byte in data {
if self.run < 7 {
if byte == 0 {
self.zeros |= 1 << self.run;
} else {
write_byte(byte);
}
self.run += 1;
if self.run == 7 && self.zeros != 0x00 {
write_byte(self.zeros);
self.run = 0;
self.zeros = 0;
}
} else if byte == 0 {
write_byte((self.run - 7) | 0x80);
self.run = 0;
self.zeros = 0;
} else {
write_byte(byte);
self.run += 1;
if self.run == 134 {
write_byte(0xFF);
self.run = 0;
self.zeros = 0;
}
}
}
}
}
#[cfg(feature = "unstable-test")]
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
let tests: &[(&[u8], &[u8])] = &[
(&[], &[0x00]),
(&[0x00], &[0x7f, 0x00]),
(&[0x00, 0x00], &[0x7f, 0x00]),
(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], &[0x7f, 0x00]),
(
&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
&[0x7f, 0x7f, 0x00],
),
(&[0x01], &[0x01, 0x7e, 0x00]),
(&[0x01, 0x00], &[0x01, 0x7e, 0x00]),
(&[0x00, 0x01], &[0x01, 0x7d, 0x00]),
(&[0x01, 0x02], &[0x01, 0x02, 0x7c, 0x00]),
(
&[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x00],
&[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x40, 0x00],
),
(
&[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77],
&[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x80, 0x00],
),
(
&[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x00],
&[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x80, 0x00],
),
(
&[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88],
&[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x81, 0x00],
),
(
&[
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff,
],
&[0x7f, 0xff, 0x3f, 0x00],
),
(
&[
0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff,
],
&[0x44, 0x5f, 0xff, 0x3f, 0x00],
),
(
&[
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a,
0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34,
0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41,
0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e,
0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b,
0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75,
0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82,
0x83, 0x84, 0x85,
],
&[
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a,
0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34,
0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41,
0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e,
0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b,
0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75,
0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82,
0x83, 0x84, 0x85, 0xfe, 0x00,
],
),
(
&[
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a,
0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34,
0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41,
0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e,
0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b,
0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75,
0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82,
0x83, 0x84, 0x85, 0x00,
],
&[
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a,
0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34,
0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41,
0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e,
0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b,
0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75,
0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82,
0x83, 0x84, 0x85, 0xfe, 0x00,
],
),
(
&[
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a,
0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34,
0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41,
0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e,
0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b,
0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75,
0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82,
0x83, 0x84, 0x85, 0x86,
],
&[
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a,
0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34,
0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41,
0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e,
0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b,
0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75,
0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82,
0x83, 0x84, 0x85, 0x86, 0xff, 0x00,
],
),
(
&[
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a,
0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34,
0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41,
0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e,
0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b,
0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75,
0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82,
0x83, 0x84, 0x85, 0x86, 0x00,
],
&[
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a,
0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34,
0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41,
0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e,
0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b,
0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75,
0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82,
0x83, 0x84, 0x85, 0x86, 0xff, 0x7f, 0x00,
],
),
];
for (dec, enc) in tests {
let mut res: Vec<u8> = Vec::new();
let mut e = Encoder::new();
e.started = true; // simulate that this is not the first frame.
e.start_frame(|data| res.extend(data));
e.write(dec, |data| res.extend(data));
e.end_frame(|data| res.extend(data));
assert_eq!(enc, &res);
}
}
}
+22
View File
@@ -0,0 +1,22 @@
use super::*;
macro_rules! write_to_le_bytes {
($($s:ident),*) => {
$(/// Implementation detail
pub fn $s(b: &$s) {
write(&b.to_le_bytes())
})*
};
}
write_to_le_bytes!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128);
/// Implementation detail
pub fn usize(b: &usize) {
write(&(*b as u32).to_le_bytes())
}
/// Implementation detail
pub fn isize(b: &isize) {
write(&(*b as i32).to_le_bytes())
}
+235
View File
@@ -0,0 +1,235 @@
mod integers;
mod traits;
use core::fmt::Write as _;
use crate::{Format, Formatter, Str};
pub use self::integers::*;
pub use bitflags::bitflags;
pub trait UnsignedInt {}
impl UnsignedInt for u8 {}
impl UnsignedInt for u16 {}
impl UnsignedInt for u32 {}
impl UnsignedInt for u64 {}
impl UnsignedInt for u128 {}
#[cfg(feature = "unstable-test")]
thread_local! {
static I: core::sync::atomic::AtomicU16 = const { core::sync::atomic::AtomicU16::new(0) };
static BYTES: core::cell::RefCell<Vec<u8>> = const { core::cell::RefCell::new(Vec::new()) };
}
/// For testing purposes
#[cfg(feature = "unstable-test")]
pub fn fetch_string_index() -> u16 {
I.with(|i| i.load(core::sync::atomic::Ordering::Relaxed))
}
/// For testing purposes
#[cfg(feature = "unstable-test")]
pub fn fetch_add_string_index() -> u16 {
I.with(|i| i.fetch_add(1, core::sync::atomic::Ordering::Relaxed))
}
/// Get and clear the logged bytes
#[cfg(feature = "unstable-test")]
pub fn fetch_bytes() -> Vec<u8> {
BYTES.with(|b| core::mem::take(&mut *b.borrow_mut()))
}
/// Only to be used by the defmt macros
/// Safety: must be paired with a later call to release()
#[cfg(feature = "unstable-test")]
pub unsafe fn acquire() {}
/// Only to be used by the defmt macros
/// Safety: must be paired with a later call to release()
#[cfg(not(feature = "unstable-test"))]
#[inline(always)]
pub unsafe fn acquire() {
extern "Rust" {
fn _defmt_acquire();
}
_defmt_acquire()
}
/// Only to be used by the defmt macros
/// Safety: must follow an earlier call to acquire()
#[cfg(feature = "unstable-test")]
pub unsafe fn release() {}
/// Only to be used by the defmt macros
/// Safety: must follow an earlier call to acquire()
#[cfg(not(feature = "unstable-test"))]
#[inline(always)]
pub unsafe fn release() {
extern "Rust" {
fn _defmt_release();
}
_defmt_release()
}
#[cfg(feature = "unstable-test")]
pub fn write(bytes: &[u8]) {
BYTES.with(|b| b.borrow_mut().extend(bytes))
}
#[cfg(not(feature = "unstable-test"))]
#[inline(always)]
pub fn write(bytes: &[u8]) {
extern "Rust" {
fn _defmt_write(bytes: &[u8]);
}
unsafe { _defmt_write(bytes) }
}
/// For testing purposes
#[cfg(feature = "unstable-test")]
pub fn timestamp(_fmt: crate::Formatter<'_>) {}
#[cfg(not(feature = "unstable-test"))]
#[inline(always)]
pub fn timestamp(fmt: crate::Formatter<'_>) {
extern "Rust" {
fn _defmt_timestamp(_: crate::Formatter<'_>);
}
unsafe { _defmt_timestamp(fmt) }
}
/// Returns the interned string at `address`.
pub fn make_istr(address: u16) -> Str {
Str { address }
}
/// Create a Formatter.
pub fn make_formatter<'a>() -> Formatter<'a> {
Formatter {
_phantom: core::marker::PhantomData,
}
}
pub fn truncate<T>(x: impl traits::Truncate<T>) -> T {
x.truncate()
}
pub fn into_result<T: traits::IntoResult>(x: T) -> Result<T::Ok, T::Error> {
x.into_result()
}
/// For testing purposes
#[cfg(feature = "unstable-test")]
pub fn panic() -> ! {
panic!()
}
#[cfg(not(feature = "unstable-test"))]
#[inline(always)]
pub fn panic() -> ! {
extern "Rust" {
fn _defmt_panic() -> !;
}
unsafe { _defmt_panic() }
}
/// Implementation detail
pub fn fmt<T: Format + ?Sized>(f: &T) {
istr(&T::_format_tag());
f._format_data();
}
/// Implementation detail
pub fn fmt_slice<T: Format>(values: &[T]) {
usize(&values.len());
istr(&T::_format_tag());
for value in values {
value._format_data();
}
}
/// Implementation detail
pub fn f32(b: &f32) {
write(&f32::to_bits(*b).to_le_bytes())
}
/// Implementation detail
pub fn f64(b: &f64) {
write(&f64::to_bits(*b).to_le_bytes())
}
/// Implementation detail
pub fn char(b: &char) {
write(&(*b as u32).to_le_bytes())
}
pub fn str(s: &str) {
usize(&s.len());
write(s.as_bytes());
}
pub fn slice(s: &[u8]) {
usize(&s.len());
write(s);
}
// NOTE: This is passed `&[u8; N]` – it's just coerced to a slice.
pub fn u8_array(a: &[u8]) {
write(a);
}
// NOTE: This is passed `&[u8; N]` – it's just coerced to a slice.
pub fn fmt_array<T: Format>(a: &[T]) {
istr(&T::_format_tag());
for value in a {
value._format_data();
}
}
/// Implementation detail
pub fn istr(s: &Str) {
write(&s.address.to_le_bytes())
}
/// Implementation detail
pub fn bool(b: &bool) {
u8(&(*b as u8));
}
/// Implementation detail
pub fn debug(val: &dyn core::fmt::Debug) {
core::write!(FmtWrite, "{val:?}").ok();
write(&[0xff]);
}
/// Implementation detail
pub fn display(val: &dyn core::fmt::Display) {
core::write!(FmtWrite, "{val}").ok();
write(&[0xff]);
}
#[inline(never)]
pub unsafe fn acquire_and_header(s: &Str) {
acquire();
istr(s);
timestamp(make_formatter());
}
#[inline(never)]
pub fn acquire_header_and_release(s: &Str) {
// safety: will be released a few lines further down
unsafe { acquire() };
istr(s);
timestamp(make_formatter());
// safety: acquire() was called a few lines above
unsafe { release() };
}
struct FmtWrite;
impl core::fmt::Write for FmtWrite {
fn write_str(&mut self, s: &str) -> core::fmt::Result {
write(s.as_bytes());
Ok(())
}
}
+82
View File
@@ -0,0 +1,82 @@
#[allow(unused_imports)]
use crate as defmt;
use crate::{Format, Formatter, Str};
pub trait Truncate<U> {
fn truncate(self) -> U;
}
macro_rules! impl_truncate {
($($from:ty => $into:ty),*) => {
$(impl Truncate<$into> for $from {
fn truncate(self) -> $into {
self as $into
}
})*
};
}
// We implement `Truncate<X> for X` so that the macro can unconditionally use it,
// even if no truncation is performed.
impl_truncate!(
u8 => u8,
u16 => u8,
u32 => u8,
u64 => u8,
u128 => u8,
u16 => u16,
u32 => u16,
u64 => u16,
u128 => u16,
u32 => u32,
u64 => u32,
u128 => u32,
u64 => u64,
u128 => u64,
u128 => u128
);
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct NoneError;
impl Format for NoneError {
fn format(&self, _fmt: Formatter) {
unreachable!();
}
fn _format_tag() -> Str {
defmt_macros::internp!("Unwrap of a None option value")
}
fn _format_data(&self) {}
}
/// Transform `self` into a `Result`
///
/// # Call sites
/// * [`defmt::unwrap!`]
pub trait IntoResult {
type Ok;
type Error;
fn into_result(self) -> Result<Self::Ok, Self::Error>;
}
impl<T> IntoResult for Option<T> {
type Ok = T;
type Error = NoneError;
#[inline]
fn into_result(self) -> Result<T, NoneError> {
self.ok_or(NoneError)
}
}
impl<T, E> IntoResult for Result<T, E> {
type Ok = T;
type Error = E;
#[inline]
fn into_result(self) -> Self {
self
}
}
+16
View File
@@ -0,0 +1,16 @@
use core::marker::PhantomData;
/// Handle to a defmt logger.
#[derive(Copy, Clone)]
pub struct Formatter<'a> {
pub(crate) _phantom: PhantomData<&'a ()>,
}
/// An interned string created via [`intern!`].
///
/// [`intern!`]: macro.intern.html
#[derive(Clone, Copy)]
pub struct Str {
/// 16-bit address
pub(crate) address: u16,
}
+87
View File
@@ -0,0 +1,87 @@
use core::fmt;
use crate as defmt;
use crate::{export, Format, Formatter, Str};
/// An "adapter" type to feed `Debug` values into defmt macros, which expect `defmt::Format` values.
///
/// This adapter disables compression and uses the `core::fmt` code on-device! You should prefer
/// `defmt::Format` over `Debug` whenever possible.
///
/// # Examples
///
/// ```rust
/// # #[derive(Debug)]
/// # struct ExpensiveThing();
/// # let expensive_thing = ExpensiveThing();
/// #
/// defmt::info!("{:?}", defmt::Debug2Format(&expensive_thing));
/// // ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ
/// // must `#[derive(Debug)]`
/// ```
///
/// Note that any provided defmt display hints will be ignored
/// because this always uses `{:?}` to format the contained value.
pub struct Debug2Format<'a, T: fmt::Debug + ?Sized>(pub &'a T);
impl<T: fmt::Debug + ?Sized> fmt::Debug for Debug2Format<'_, T> {
fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
self.0.fmt(fmt)
}
}
impl<T: fmt::Debug + ?Sized> Format for Debug2Format<'_, T> {
default_format!();
fn _format_tag() -> Str {
defmt_macros::internp!("{=__internal_Debug}")
}
fn _format_data(&self) {
export::debug(&self.0);
}
}
/// An "adapter" type to feed `Display` values into defmt macros, which expect `defmt::Format` values.
///
/// This adapter disables compression and uses the `core::fmt` code on-device! You should prefer
/// `defmt::Format` over `Display` whenever possible.
///
/// # Examples
///
/// ```rust
/// # struct ExpensiveThing();
/// #
/// # impl core::fmt::Display for ExpensiveThing {
/// # fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
/// # write!(f, "{}", "expensive")
/// # }
/// # }
/// # let expensive_thing = ExpensiveThing();
/// #
/// defmt::info!("{}", defmt::Display2Format(&expensive_thing));
/// // ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ
/// // must implement `fmt::Display`
/// ```
///
/// Note that any provided defmt display hints will be ignored
/// because this always uses `{}` to format the contained value.
pub struct Display2Format<'a, T: fmt::Display + ?Sized>(pub &'a T);
impl<T: fmt::Display + ?Sized> fmt::Display for Display2Format<'_, T> {
fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
self.0.fmt(fmt)
}
}
impl<T: fmt::Display + ?Sized> Format for Display2Format<'_, T> {
default_format!();
fn _format_tag() -> Str {
defmt_macros::internp!("{=__internal_Display}")
}
fn _format_data(&self) {
export::display(&self.0);
}
}
+46
View File
@@ -0,0 +1,46 @@
use super::*;
impl<T> Format for alloc::boxed::Box<T>
where
T: ?Sized + Format,
{
delegate_format!(T, self, &*self);
}
impl<T> Format for alloc::rc::Rc<T>
where
T: ?Sized + Format,
{
delegate_format!(T, self, &*self);
}
#[cfg(not(no_cas))]
impl<T> Format for alloc::sync::Arc<T>
where
T: ?Sized + Format,
{
delegate_format!(T, self, &*self);
}
impl<T> Format for alloc::vec::Vec<T>
where
T: Format,
{
delegate_format!([T], self, self.as_slice());
}
impl Format for alloc::string::String {
delegate_format!(str, self, self.as_str());
}
impl<'a, T> Format for alloc::borrow::Cow<'a, [T]>
where
T: 'a + Format,
[T]: alloc::borrow::ToOwned<Owned = alloc::vec::Vec<T>>,
{
delegate_format!([T], self, self.as_ref());
}
impl<'a> Format for alloc::borrow::Cow<'a, str> {
delegate_format!(str, self, self.as_ref());
}
+100
View File
@@ -0,0 +1,100 @@
use super::*;
use crate::export;
macro_rules! arrays {
( $($len:literal $fmt:literal,)+ ) => {
impl<T, const N: usize> Format for [T; N]
where
T: Format
{
default_format!();
#[inline]
fn _format_tag() -> Str {
match N {
$(
$len => internp!($fmt),
)+
_ => internp!("{=[?]}"),
}
}
#[inline]
fn _format_data(&self) {
match N {
$( $len )|+ => export::fmt_array(self),
_ => export::fmt_slice(self),
}
}
}
};
}
arrays! {
0 "{=[?;0]}",
1 "{=[?;1]}",
2 "{=[?;2]}",
3 "{=[?;3]}",
4 "{=[?;4]}",
5 "{=[?;5]}",
6 "{=[?;6]}",
7 "{=[?;7]}",
8 "{=[?;8]}",
9 "{=[?;9]}",
10 "{=[?;10]}",
11 "{=[?;11]}",
12 "{=[?;12]}",
13 "{=[?;13]}",
14 "{=[?;14]}",
15 "{=[?;15]}",
16 "{=[?;16]}",
17 "{=[?;17]}",
18 "{=[?;18]}",
19 "{=[?;19]}",
20 "{=[?;20]}",
21 "{=[?;21]}",
22 "{=[?;22]}",
23 "{=[?;23]}",
24 "{=[?;24]}",
25 "{=[?;25]}",
26 "{=[?;26]}",
27 "{=[?;27]}",
28 "{=[?;28]}",
29 "{=[?;29]}",
30 "{=[?;30]}",
31 "{=[?;31]}",
32 "{=[?;32]}",
64 "{=[?;64]}",
128 "{=[?;128]}",
256 "{=[?;256]}",
512 "{=[?;512]}",
1024 "{=[?;1024]}",
2048 "{=[?;2048]}",
4096 "{=[?;4096]}",
8192 "{=[?;8192]}",
16384 "{=[?;16384]}",
32768 "{=[?;32768]}",
65536 "{=[?;65536]}",
131072 "{=[?;131072]}",
262144 "{=[?;262144]}",
524288 "{=[?;524288]}",
1048576 "{=[?;1048576]}",
2097152 "{=[?;2097152]}",
4194304 "{=[?;4194304]}",
8388608 "{=[?;8388608]}",
16777216 "{=[?;16777216]}",
33554432 "{=[?;33554432]}",
67108864 "{=[?;67108864]}",
134217728 "{=[?;134217728]}",
268435456 "{=[?;268435456]}",
536870912 "{=[?;536870912]}",
1073741824 "{=[?;1073741824]}",
100 "{=[?;100]}",
1000 "{=[?;1000]}",
10000 "{=[?;10000]}",
100000 "{=[?;100000]}",
1000000 "{=[?;1000000]}",
10000000 "{=[?;10000000]}",
100000000 "{=[?;100000000]}",
1000000000 "{=[?;1000000000]}",
}
+14
View File
@@ -0,0 +1,14 @@
use core::alloc;
use super::*;
impl Format for alloc::Layout {
fn format(&self, fmt: Formatter) {
crate::write!(
fmt,
"Layout {{ size: {}, align: {} }}",
self.size(),
self.align()
);
}
}
+9
View File
@@ -0,0 +1,9 @@
use core::array;
use super::*;
impl Format for array::TryFromSliceError {
fn format(&self, fmt: Formatter) {
crate::write!(fmt, "TryFromSliceError(())");
}
}
+46
View File
@@ -0,0 +1,46 @@
use super::*;
impl<T> Format for core::cell::Cell<T>
where
T: Format + Copy,
{
fn format(&self, fmt: Formatter) {
crate::write!(fmt, "Cell {{ value: {=?} }})", self.get())
}
}
impl<T> Format for core::cell::RefCell<T>
where
T: Format,
{
default_format!();
#[inline]
fn _format_tag() -> Str {
internp!("RefCell {{ value: <borrowed> }}|RefCell {{ value: {=?} }}")
}
#[inline]
fn _format_data(&self) {
match self.try_borrow() {
Err(_) => export::u8(&0),
Ok(x) => {
export::u8(&1);
export::istr(&T::_format_tag());
x._format_data()
}
}
}
}
impl Format for core::cell::BorrowError {
fn format(&self, fmt: Formatter) {
crate::write!(fmt, "BorrowError")
}
}
impl Format for core::cell::BorrowMutError {
fn format(&self, fmt: Formatter) {
crate::write!(fmt, "BorrowMutError")
}
}
+7
View File
@@ -0,0 +1,7 @@
use super::*;
impl Format for core::fmt::Error {
fn format(&self, fmt: Formatter) {
crate::write!(fmt, "fmt::Error")
}
}
+121
View File
@@ -0,0 +1,121 @@
//! Some of these objects don't expose enough to accurately report their debug state. In this case
//! we show as much state as we can. Users can always use `Debug2Format` to get more information,
//! at the cost of bringing core::fmt into the firmware and doing the layout work on device.
//!
//! We generally keep the type parameter trait bounds in case it becomes possible to use this
//! later, without making a backwards-incompatible change.
mod alloc_;
mod array;
mod cell;
mod fmt;
#[cfg(feature = "ip_in_core")]
mod net;
mod num;
mod ops;
mod panic;
mod ptr;
mod slice;
use super::*;
use crate::export;
impl<T> Format for Option<T>
where
T: Format,
{
default_format!();
#[inline]
fn _format_tag() -> Str {
internp!("None|Some({=?})")
}
#[inline]
fn _format_data(&self) {
match self {
None => export::u8(&0),
Some(x) => {
export::u8(&1);
export::istr(&T::_format_tag());
x._format_data()
}
}
}
}
impl<T, E> Format for Result<T, E>
where
T: Format,
E: Format,
{
default_format!();
#[inline]
fn _format_tag() -> Str {
internp!("Err({=?})|Ok({=?})")
}
#[inline]
fn _format_data(&self) {
match self {
Err(e) => {
export::u8(&0);
export::istr(&E::_format_tag());
e._format_data()
}
Ok(x) => {
export::u8(&1);
export::istr(&T::_format_tag());
x._format_data()
}
}
}
}
impl<T> Format for core::marker::PhantomData<T> {
default_format!();
#[inline]
fn _format_tag() -> Str {
internp!("PhantomData")
}
#[inline]
fn _format_data(&self) {}
}
impl Format for core::convert::Infallible {
default_format!();
#[inline]
fn _format_tag() -> Str {
unreachable!();
}
#[inline]
fn _format_data(&self) {
unreachable!();
}
}
impl Format for core::time::Duration {
fn format(&self, fmt: Formatter) {
crate::write!(
fmt,
"Duration {{ secs: {=u64}, nanos: {=u32} }}",
self.as_secs(),
self.subsec_nanos(),
)
}
}
impl<A, B> Format for core::iter::Zip<A, B>
where
A: Format,
B: Format,
{
fn format(&self, fmt: Formatter) {
crate::write!(fmt, "Zip(..)")
}
}
+72
View File
@@ -0,0 +1,72 @@
use core::net;
use super::*;
impl Format for net::AddrParseError {
fn format(&self, fmt: Formatter) {
crate::write!(fmt, "AddrParseError(_)");
}
}
impl Format for net::Ipv4Addr {
fn format(&self, fmt: Formatter) {
let [a, b, c, d] = self.octets();
crate::write!(fmt, "{}.{}.{}.{}", a, b, c, d);
}
}
impl Format for net::SocketAddrV4 {
fn format(&self, fmt: Formatter) {
crate::write!(fmt, "{}:{}", self.ip(), self.port());
}
}
impl Format for net::Ipv6Addr {
fn format(&self, fmt: Formatter) {
let octets: [u8; 16] = self.octets();
crate::write!(
fmt,
"{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}",
octets[0],
octets[1],
octets[2],
octets[3],
octets[4],
octets[5],
octets[6],
octets[7],
octets[8],
octets[9],
octets[10],
octets[11],
octets[12],
octets[13],
octets[14],
octets[15]
);
}
}
impl Format for net::SocketAddrV6 {
fn format(&self, fmt: Formatter) {
crate::write!(fmt, "[{}]:{}", self.ip(), self.port());
}
}
impl Format for net::IpAddr {
fn format(&self, fmt: Formatter) {
match self {
net::IpAddr::V4(a) => crate::write!(fmt, "{}", a),
net::IpAddr::V6(a) => crate::write!(fmt, "{}", a),
}
}
}
impl Format for net::SocketAddr {
fn format(&self, fmt: Formatter) {
match self {
net::SocketAddr::V4(a) => crate::write!(fmt, "{}", a),
net::SocketAddr::V6(a) => crate::write!(fmt, "{}", a),
}
}
}
+38
View File
@@ -0,0 +1,38 @@
use core::num::{self, Wrapping};
use super::*;
macro_rules! non_zero {
($type:ty, $hint:literal) => {
impl Format for $type {
fn format(&self, fmt: Formatter) {
crate::write!(fmt, $hint, self.get());
}
}
};
}
non_zero! {num::NonZeroI8, "{=i8}"}
non_zero! {num::NonZeroI16, "{=i16}"}
non_zero! {num::NonZeroI32, "{=i32}"}
non_zero! {num::NonZeroI64, "{=i64}"}
non_zero! {num::NonZeroI128, "{=i128}"}
non_zero! {num::NonZeroIsize, "{=isize}"}
non_zero! {num::NonZeroU8, "{=u8}"}
non_zero! {num::NonZeroU16, "{=u16}"}
non_zero! {num::NonZeroU32, "{=u32}"}
non_zero! {num::NonZeroU64, "{=u64}"}
non_zero! {num::NonZeroU128, "{=u128}"}
non_zero! {num::NonZeroUsize, "{=usize}"}
impl Format for num::TryFromIntError {
fn format(&self, fmt: Formatter) {
crate::write!(fmt, "TryFromIntError(())");
}
}
impl<T: Format> Format for Wrapping<T> {
fn format(&self, fmt: Formatter) {
self.0.format(fmt);
}
}
+52
View File
@@ -0,0 +1,52 @@
use super::*;
impl<Idx> Format for core::ops::Range<Idx>
where
Idx: Format,
{
fn format(&self, fmt: Formatter) {
crate::write!(fmt, "{}..{}", self.start, self.end)
}
}
impl<Idx> Format for core::ops::RangeFrom<Idx>
where
Idx: Format,
{
fn format(&self, fmt: Formatter) {
crate::write!(fmt, "{}..", self.start)
}
}
impl Format for core::ops::RangeFull {
fn format(&self, fmt: Formatter) {
crate::write!(fmt, "..",)
}
}
impl<Idx> Format for core::ops::RangeInclusive<Idx>
where
Idx: Format,
{
fn format(&self, fmt: Formatter) {
crate::write!(fmt, "{}..={}", self.start(), self.end())
}
}
impl<Idx> Format for core::ops::RangeTo<Idx>
where
Idx: Format,
{
fn format(&self, fmt: Formatter) {
crate::write!(fmt, "..{}", self.end)
}
}
impl<Idx> Format for core::ops::RangeToInclusive<Idx>
where
Idx: Format,
{
fn format(&self, fmt: Formatter) {
crate::write!(fmt, "..={}", self.end)
}
}
+27
View File
@@ -0,0 +1,27 @@
use core::panic;
use super::*;
impl Format for panic::PanicInfo<'_> {
fn format(&self, f: Formatter) {
if let Some(location) = self.location() {
crate::write!(f, "panicked at {}", location);
} else {
crate::write!(f, "panicked");
}
// TODO: consider supporting self.message() once stabilized, or add a crate feature for
// conditional support
}
}
impl Format for panic::Location<'_> {
fn format(&self, f: Formatter) {
crate::write!(
f,
"{=str}:{=u32}:{=u32}",
self.file(),
self.line(),
self.column()
);
}
}
+69
View File
@@ -0,0 +1,69 @@
use super::*;
impl<T> Format for core::ptr::NonNull<T> {
fn format(&self, fmt: Formatter) {
crate::write!(fmt, "{}", self.as_ptr())
}
}
#[cfg(c_variadic)]
macro_rules! fnptr_format_cvariadic {
($($Arg:ident),+) => {
impl<Ret, $($Arg),*> Format for extern "C" fn($($Arg),* , ...) -> Ret {
fn format(&self, fmt: Formatter) {
crate::write!(fmt, "{}", (*self as usize) as *const ())
}
}
impl<Ret, $($Arg),*> Format for unsafe extern "C" fn($($Arg),* , ...) -> Ret {
fn format(&self, fmt: Formatter) {
crate::write!(fmt, "{}", (*self as usize) as *const ())
}
}
};
() => {
// C variadics require at least one other argument
};
}
macro_rules! fnptr_format_args {
($($Arg:ident),*) => {
impl<Ret, $($Arg),*> Format for extern "Rust" fn($($Arg),*) -> Ret {
fn format(&self, fmt: Formatter) {
crate::write!(fmt, "{}", (*self as usize) as *const ())
}
}
impl<Ret, $($Arg),*> Format for extern "C" fn($($Arg),*) -> Ret {
fn format(&self, fmt: Formatter) {
crate::write!(fmt, "{}", (*self as usize) as *const ())
}
}
impl<Ret, $($Arg),*> Format for unsafe extern "Rust" fn($($Arg),*) -> Ret {
fn format(&self, fmt: Formatter) {
crate::write!(fmt, "{}", (*self as usize) as *const ())
}
}
impl<Ret, $($Arg),*> Format for unsafe extern "C" fn($($Arg),*) -> Ret {
fn format(&self, fmt: Formatter) {
crate::write!(fmt, "{}", (*self as usize) as *const ())
}
}
#[cfg(c_variadic)]
fnptr_format_cvariadic!{ $($Arg),* }
};
}
// core::ptr has fnptr impls up to 12 arguments
// https://doc.rust-lang.org/src/core/ptr/mod.rs.html#1994
fnptr_format_args! {}
fnptr_format_args! { A }
fnptr_format_args! { A, B }
fnptr_format_args! { A, B, C }
fnptr_format_args! { A, B, C, D }
fnptr_format_args! { A, B, C, D, E }
fnptr_format_args! { A, B, C, D, E, F }
fnptr_format_args! { A, B, C, D, E, F, G }
fnptr_format_args! { A, B, C, D, E, F, G, H }
fnptr_format_args! { A, B, C, D, E, F, G, H, I }
fnptr_format_args! { A, B, C, D, E, F, G, H, I, J }
fnptr_format_args! { A, B, C, D, E, F, G, H, I, J, K }
fnptr_format_args! { A, B, C, D, E, F, G, H, I, J, K, L }
+32
View File
@@ -0,0 +1,32 @@
use super::*;
impl<'a, T: 'a> Format for core::slice::ChunksExact<'a, T>
where
T: Format,
{
fn format(&self, fmt: Formatter) {
crate::write!(fmt, "ChunksExact(..)")
}
}
impl<'a, T: 'a> Format for core::slice::Iter<'a, T>
where
T: Format,
{
fn format(&self, fmt: Formatter) {
crate::write!(
fmt,
"Iter {{ slice: {=[?]}, position: ? }}",
self.as_slice()
)
}
}
impl<'a, T: 'a> Format for core::slice::Windows<'a, T>
where
T: Format,
{
fn format(&self, fmt: Formatter) {
crate::write!(fmt, "Windows(..)")
}
}
+40
View File
@@ -0,0 +1,40 @@
macro_rules! default_format {
() => {
#[inline]
fn format(&self, _fmt: Formatter) {
crate::export::istr(&Self::_format_tag());
self._format_data();
}
};
}
macro_rules! delegate_format {
($ty:ty, $self_:ident, $val:expr) => {
#[inline]
fn format(&$self_, fmt: Formatter) {
<$ty as Format>::format($val, fmt)
}
#[inline]
fn _format_tag() -> Str {
<$ty as Format>::_format_tag()
}
#[inline]
fn _format_data(&$self_) {
<$ty as Format>::_format_data($val)
}
};
}
pub mod adapter;
#[cfg(feature = "alloc")]
mod alloc_;
mod arrays;
mod core_;
mod primitives;
mod tuples;
use defmt_macros::internp;
use crate::{self as defmt, Format, Formatter, Str};
+92
View File
@@ -0,0 +1,92 @@
use crate::export;
use super::*;
macro_rules! prim {
($ty:ty, $fmt: literal, $self_:ident, $write:expr) => {
impl Format for $ty {
default_format!();
#[inline]
fn _format_tag() -> Str {
internp!($fmt)
}
#[inline]
fn _format_data(&$self_) {
$write
}
}
};
}
prim!(i8, "{=i8}", self, export::i8(self));
prim!(i16, "{=i16}", self, export::i16(self));
prim!(i32, "{=i32}", self, export::i32(self));
prim!(i64, "{=i64}", self, export::i64(self));
prim!(i128, "{=i128}", self, export::i128(self));
prim!(isize, "{=isize}", self, export::isize(self));
prim!(u8, "{=u8}", self, export::u8(self));
prim!(u16, "{=u16}", self, export::u16(self));
prim!(u32, "{=u32}", self, export::u32(self));
prim!(u64, "{=u64}", self, export::u64(self));
prim!(u128, "{=u128}", self, export::u128(self));
prim!(usize, "{=usize}", self, export::usize(self));
prim!(f32, "{=f32}", self, export::f32(self));
prim!(f64, "{=f64}", self, export::f64(self));
prim!(str, "{=str}", self, export::str(self));
prim!(bool, "{=bool}", self, export::bool(self));
prim!(Str, "{=istr}", self, export::istr(self));
prim!(char, "{=char}", self, export::char(self));
impl<T> Format for [T]
where
T: Format,
{
default_format!();
#[inline]
fn _format_tag() -> Str {
internp!("{=[?]}")
}
#[inline]
fn _format_data(&self) {
export::fmt_slice(self);
}
}
impl<T> Format for &'_ T
where
T: Format + ?Sized,
{
delegate_format!(T, self, self);
}
impl<T> Format for &'_ mut T
where
T: Format + ?Sized,
{
delegate_format!(T, self, self);
}
// Format raw pointer as hexadecimal
//
// First cast raw pointer to thin pointer, then to usize and finally format as hexadecimal.
impl<T> Format for *const T
where
T: ?Sized,
{
fn format(&self, fmt: Formatter) {
crate::write!(fmt, "0x{:x}", *self as *const () as usize);
}
}
impl<T> Format for *mut T
where
T: ?Sized,
{
fn format(&self, fmt: Formatter) {
Format::format(&(*self as *const T), fmt)
}
}
+55
View File
@@ -0,0 +1,55 @@
use super::*;
use crate::export;
impl Format for () {
default_format!();
#[inline]
fn _format_tag() -> Str {
internp!("()")
}
#[inline]
fn _format_data(&self) {}
}
macro_rules! tuple {
( $format:expr, ($($name:ident),+) ) => (
impl<$($name:Format),+> Format for ($($name,)+) where last_type!($($name,)+): ?Sized {
default_format!();
#[inline]
fn _format_tag() -> Str {
internp!($format)
}
#[inline]
#[allow(non_snake_case, unused_assignments)]
fn _format_data(&self) {
let ($(ref $name,)+) = *self;
$(
export::istr(&$name::_format_tag());
$name._format_data();
)+
}
}
)
}
macro_rules! last_type {
($a:ident,) => { $a };
($a:ident, $($rest_a:ident,)+) => { last_type!($($rest_a,)+) };
}
tuple! { "({=?})", (T0) }
tuple! { "({=?}, {=?})", (T0, T1) }
tuple! { "({=?}, {=?}, {=?})", (T0, T1, T2) }
tuple! { "({=?}, {=?}, {=?}, {=?})", (T0, T1, T2, T3) }
tuple! { "({=?}, {=?}, {=?}, {=?}, {=?})", (T0, T1, T2, T3, T4) }
tuple! { "({=?}, {=?}, {=?}, {=?}, {=?}, {=?})", (T0, T1, T2, T3, T4, T5) }
tuple! { "({=?}, {=?}, {=?}, {=?}, {=?}, {=?}, {=?})", (T0, T1, T2, T3, T4, T5, T6) }
tuple! { "({=?}, {=?}, {=?}, {=?}, {=?}, {=?}, {=?}, {=?})", (T0, T1, T2, T3, T4, T5, T6, T7) }
tuple! { "({=?}, {=?}, {=?}, {=?}, {=?}, {=?}, {=?}, {=?}, {=?})", (T0, T1, T2, T3, T4, T5, T6, T7, T8) }
tuple! { "({=?}, {=?}, {=?}, {=?}, {=?}, {=?}, {=?}, {=?}, {=?}, {=?})", (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) }
tuple! { "({=?}, {=?}, {=?}, {=?}, {=?}, {=?}, {=?}, {=?}, {=?}, {=?}, {=?})", (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) }
tuple! { "({=?}, {=?}, {=?}, {=?}, {=?}, {=?}, {=?}, {=?}, {=?}, {=?}, {=?}, {=?})", (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11) }
+467
View File
@@ -0,0 +1,467 @@
//! A highly efficient logging framework that targets resource-constrained
//! devices, like microcontrollers.
//!
//! Check out the defmt book at <https://defmt.ferrous-systems.com> for more
//! information about how to use it.
//!
//! # Compatibility
//!
//! The `defmt` wire format might change between minor versions. Attempting to
//! read a defmt stream with an incompatible version will result in an error,
//! and any tool used to process that stream should first check for a symbol
//! named like `_defmt_version_ = X`, where X indicates the wire format version
//! in use.
//!
//! Updating your version of defmt might mean you also have to update your
//! version of `defmt-print` or `defmt-decoder`.
#![cfg_attr(not(feature = "unstable-test"), no_std)]
// NOTE if you change this URL you'll also need to update all other crates in this repo
#![doc(html_logo_url = "https://knurling.ferrous-systems.com/knurling_logo_light_text.svg")]
#![warn(missing_docs)]
#[cfg(feature = "alloc")]
extern crate alloc;
// This must be in the root lib.rs, otherwise it doesn't appear in the final binary.
/// The defmt ABI and wire format version.
///
/// This number has to be updated every time there is a backwards-incompatible change to
/// - the symbol naming scheme
/// - the symbol and section layout
/// - the data encoding / wire format
#[used]
#[cfg_attr(target_os = "macos", link_section = ".defmt,end.VERSION")]
#[cfg_attr(not(target_os = "macos"), link_section = ".defmt.end")]
#[export_name = "_defmt_version_ = 4"]
static DEFMT_VERSION: u8 = 0;
#[used]
#[cfg_attr(target_os = "macos", link_section = ".defmt,end.ENCODING")]
#[cfg_attr(not(target_os = "macos"), link_section = ".defmt.end")]
#[cfg_attr(feature = "encoding-raw", export_name = "_defmt_encoding_ = raw")]
#[cfg_attr(
not(feature = "encoding-raw"),
export_name = "_defmt_encoding_ = rzcobs"
)]
#[allow(missing_docs)]
#[doc(hidden)]
pub static DEFMT_ENCODING: u8 = 0;
mod encoding;
#[doc(hidden)]
pub mod export;
mod formatter;
mod impls;
#[cfg(all(test, feature = "unstable-test"))]
mod tests;
mod traits;
pub use crate::{
encoding::Encoder,
formatter::{Formatter, Str},
impls::adapter::{Debug2Format, Display2Format},
traits::{Format, Logger},
};
#[cfg(all(test, not(feature = "unstable-test")))]
compile_error!(
"to run unit tests enable the `unstable-test` feature, e.g. `cargo t --features unstable-test`"
);
/// Just like the [`core::assert!`] macro but `defmt` is used to log the panic message
///
/// [`core::assert!`]: https://doc.rust-lang.org/core/macro.assert.html
///
/// If used, the format string must follow the defmt syntax (documented in [the manual])
///
/// [the manual]: https://defmt.ferrous-systems.com/macros.html
pub use defmt_macros::assert_ as assert;
/// Just like the [`core::assert_eq!`] macro but `defmt` is used to log the panic message
///
/// [`core::assert_eq!`]: https://doc.rust-lang.org/core/macro.assert_eq.html
///
/// If used, the format string must follow the defmt syntax (documented in [the manual])
///
/// [the manual]: https://defmt.ferrous-systems.com/macros.html
pub use defmt_macros::assert_eq_ as assert_eq;
/// Just like the [`core::assert_ne!`] macro but `defmt` is used to log the panic message
///
/// [`core::assert_ne!`]: https://doc.rust-lang.org/core/macro.assert_ne.html
///
/// If used, the format string must follow the defmt syntax (documented in [the manual])
///
/// [the manual]: https://defmt.ferrous-systems.com/macros.html
pub use defmt_macros::assert_ne_ as assert_ne;
/// Just like the [`core::debug_assert!`] macro but `defmt` is used to log the panic message
///
/// [`core::debug_assert!`]: https://doc.rust-lang.org/core/macro.debug_assert.html
///
/// If used, the format string must follow the defmt syntax (documented in [the manual])
///
/// [the manual]: https://defmt.ferrous-systems.com/macros.html
pub use defmt_macros::debug_assert_ as debug_assert;
/// Just like the [`core::debug_assert_eq!`] macro but `defmt` is used to log the panic message
///
/// [`core::debug_assert_eq!`]: https://doc.rust-lang.org/core/macro.debug_assert_eq.html
///
/// If used, the format string must follow the defmt syntax (documented in [the manual])
///
/// [the manual]: https://defmt.ferrous-systems.com/macros.html
pub use defmt_macros::debug_assert_eq_ as debug_assert_eq;
/// Just like the [`core::debug_assert_ne!`] macro but `defmt` is used to log the panic message
///
/// [`core::debug_assert_ne!`]: https://doc.rust-lang.org/core/macro.debug_assert_ne.html
///
/// If used, the format string must follow the defmt syntax (documented in [the manual])
///
/// [the manual]: https://defmt.ferrous-systems.com/macros.html
pub use defmt_macros::debug_assert_ne_ as debug_assert_ne;
/// Just like the [`core::unreachable!`] macro but `defmt` is used to log the panic message
///
/// [`core::unreachable!`]: https://doc.rust-lang.org/core/macro.unreachable.html
///
/// If used, the format string must follow the defmt syntax (documented in [the manual])
///
/// [the manual]: https://defmt.ferrous-systems.com/macros.html
pub use defmt_macros::unreachable_ as unreachable;
/// Just like the [`core::todo!`] macro but `defmt` is used to log the panic message
///
/// [`core::todo!`]: https://doc.rust-lang.org/core/macro.todo.html
///
/// If used, the format string must follow the defmt syntax (documented in [the manual])
///
/// [the manual]: https://defmt.ferrous-systems.com/macros.html
pub use defmt_macros::todo_ as todo;
/// Just like the [`core::unimplemented!`] macro but `defmt` is used to log the panic message
///
/// [`core::unimplemented!`]: https://doc.rust-lang.org/core/macro.unimplemented.html
///
/// If used, the format string must follow the defmt syntax (documented in [the manual])
///
/// [the manual]: https://defmt.ferrous-systems.com/macros.html
pub use defmt_macros::todo_ as unimplemented;
/// Just like the [`core::panic!`] macro but `defmt` is used to log the panic message
///
/// [`core::panic!`]: https://doc.rust-lang.org/core/macro.panic.html
///
/// If used, the format string must follow the defmt syntax (documented in [the manual])
///
/// [the manual]: https://defmt.ferrous-systems.com/macros.html
pub use defmt_macros::panic_ as panic;
/// Unwraps an `Option` or `Result`, panicking if it is `None` or `Err`.
///
/// This macro is roughly equivalent to `{Option,Result}::{expect,unwrap}` but invocation looks
/// a bit different because this is a macro and not a method. The other difference is that
/// `unwrap!`-ing a `Result<T, E>` value requires that the error type `E` implements the `Format`
/// trait
///
/// The following snippet shows the differences between core's unwrap method and defmt's unwrap
/// macro:
///
/// ```
/// use defmt::unwrap;
///
/// # let option = Some(());
/// let x = option.unwrap();
/// let x = unwrap!(option);
///
/// # let result = Ok::<(), ()>(());
/// let x = result.unwrap();
/// let x = unwrap!(result);
///
/// let x = result.expect("text");
/// let x = unwrap!(result, "text");
///
/// # let arg = ();
/// let x = result.expect(&format!("text {:?}", arg));
/// let x = unwrap!(result, "text {:?}", arg); // arg must be implement `Format`
/// ```
///
/// If used, the format string must follow the defmt syntax (documented in [the manual])
///
/// [the manual]: https://defmt.ferrous-systems.com/macros.html
pub use defmt_macros::unwrap;
/// This is an alias for defmt's [`unwrap`] macro which supports messages like std's except.
/// ```
/// use defmt::expect;
///
/// # let result = Ok::<(), ()>(());
/// # let arg = ();
/// let x = result.expect(&format!("text {:?}", arg));
/// let x = expect!(result, "text {:?}", arg); // arg must be implement `Format`
/// ```
///
/// For the complete documentation see that of defmt's *unwrap* macro.
// note: Linking to unwrap is broken as of 2024-10-09, it links back to expect
pub use defmt_macros::unwrap as expect;
/// Overrides the panicking behavior of `defmt::panic!`
///
/// By default, `defmt::panic!` calls `core::panic!` after logging the panic message using `defmt`.
/// This can result in the panic message being printed twice in some cases. To avoid that issue use
/// this macro. See [the manual] for details.
///
/// [the manual]: https://defmt.ferrous-systems.com/panic.html
///
/// # Inter-operation with built-in attributes
///
/// This attribute cannot be used together with the `export_name` or `no_mangle` attributes
pub use defmt_macros::panic_handler;
/// Creates an interned string ([`Str`]) from a string literal.
///
/// This must be called on a string literal, and will allocate the literal in the object file. At
/// runtime, only a small string index is required to refer to the string, represented as the
/// [`Str`] type.
///
/// # Example
///
/// ```
/// let interned = defmt::intern!("long string literal taking up little space");
/// ```
///
/// [`Str`]: struct.Str.html
pub use defmt_macros::intern;
/// Always logs data irrespective of log level.
///
/// Please refer to [the manual] for documentation on the syntax.
///
/// [the manual]: https://defmt.ferrous-systems.com/macros.html
pub use defmt_macros::println;
/// Logs data at *debug* level.
///
/// Please refer to [the manual] for documentation on the syntax.
///
/// [the manual]: https://defmt.ferrous-systems.com/macros.html
pub use defmt_macros::debug;
/// Logs data at *error* level.
///
/// Please refer to [the manual] for documentation on the syntax.
///
/// [the manual]: https://defmt.ferrous-systems.com/macros.html
pub use defmt_macros::error;
/// Logs data at *info* level.
///
/// Please refer to [the manual] for documentation on the syntax.
///
/// [the manual]: https://defmt.ferrous-systems.com/macros.html
pub use defmt_macros::info;
/// Logs data at *trace* level.
///
/// Please refer to [the manual] for documentation on the syntax.
///
/// [the manual]: https://defmt.ferrous-systems.com/macros.html
pub use defmt_macros::trace;
/// Logs data at *warn* level.
///
/// Please refer to [the manual] for documentation on the syntax.
///
/// [the manual]: https://defmt.ferrous-systems.com/macros.html
pub use defmt_macros::warn;
/// Just like the [`std::dbg!`] macro but `defmt` is used to log the message at `TRACE` level.
///
/// [`std::dbg!`]: https://doc.rust-lang.org/std/macro.dbg.html
pub use defmt_macros::dbg;
/// Writes formatted data to a [`Formatter`].
///
/// [`Formatter`]: struct.Formatter.html
pub use defmt_macros::write;
/// Defines the global defmt logger.
///
/// `#[global_logger]` needs to be put on a unit struct type declaration. This struct has to
/// implement the [`Logger`] trait.
///
/// # Example
///
/// ```
/// use defmt::{Logger, global_logger};
///
/// #[global_logger]
/// struct MyLogger;
///
/// unsafe impl Logger for MyLogger {
/// fn acquire() {
/// # todo!()
/// // ...
/// }
/// unsafe fn flush() {
/// # todo!()
/// // ...
/// }
/// unsafe fn release() {
/// # todo!()
/// // ...
/// }
/// unsafe fn write(bytes: &[u8]) {
/// # todo!()
/// // ...
/// }
/// }
/// ```
///
/// [`Logger`]: trait.Logger.html
pub use defmt_macros::global_logger;
/// Defines the global timestamp provider for defmt.
///
/// This macro can be used to attach a timestamp or other data to every defmt message. Its syntax
/// works exactly like the logging macros, except that no local variables can be accessed and the
/// macro should be placed in a module instead of a function.
///
/// `timestamp!` must only be used once across the crate graph.
///
/// If no crate defines a timestamp, no timestamp will be included in the logged messages.
///
/// # Examples
///
/// ```
/// # use core::sync::atomic::{AtomicU32, Ordering};
///
/// static COUNT: AtomicU32 = AtomicU32::new(0);
/// defmt::timestamp!("{=u32:us}", COUNT.fetch_add(1, Ordering::Relaxed));
/// ```
pub use defmt_macros::timestamp;
/// Generates a bitflags structure that can be formatted with defmt.
///
/// This macro is a wrapper around the [`bitflags!`] crate, and provides an (almost) identical
/// interface. Refer to [its documentation] for an explanation of the syntax.
///
/// [its documentation]: https://docs.rs/bitflags/1/bitflags/
///
/// # Limitations
///
/// This macro only supports bitflags structs represented as one of Rust's built-in unsigned integer
/// types (`u8`, `u16`, `u32`, `u64`, or `u128`). Custom types are not supported. This restriction
/// is necessary to support defmt's efficient encoding.
///
/// # Examples
///
/// The example from the bitflags crate works as-is:
///
/// ```
/// defmt::bitflags! {
/// struct Flags: u32 {
/// const A = 0b00000001;
/// const B = 0b00000010;
/// const C = 0b00000100;
/// const ABC = Self::A.bits | Self::B.bits | Self::C.bits;
/// }
/// }
///
/// defmt::info!("Flags::ABC: {}", Flags::ABC);
/// defmt::info!("Flags::empty(): {}", Flags::empty());
/// ```
pub use defmt_macros::bitflags;
#[doc(hidden)] // documented as the `Format` trait instead
pub use defmt_macros::Format;
// There is no default timestamp format. Instead, the decoder looks for a matching ELF symbol. If
// absent, timestamps are turned off.
#[export_name = "__defmt_default_timestamp"]
fn default_timestamp(_f: Formatter<'_>) {}
#[export_name = "__defmt_default_panic"]
fn default_panic() -> ! {
core::panic!()
}
/// Block until host has read all pending data.
///
/// The flush operation will not fail, but might not succeed in flushing _all_ pending data. It is
/// implemented as a "best effort" operation.
///
/// This calls the method `flush` of the used "global [`Logger`]". The logger is likely provided by
/// [`defmt-rtt`](https://crates.io/crates/defmt-rtt) or [`defmt-itm`](https://crates.io/crates/defmt-itm).
pub fn flush() {
match () {
#[cfg(feature = "unstable-test")]
() => {
// no-op when run on host
}
#[cfg(not(feature = "unstable-test"))]
() => {
extern "Rust" {
fn _defmt_acquire();
fn _defmt_flush();
fn _defmt_release();
}
// SAFETY:
// * we call these function in the correct order: first acquire the lock, then flush and
// finally release the lock
// * these function should be provided by the macro `#[global_logger]` and therefore
// trustworthy to call through FFI-bounds
unsafe {
_defmt_acquire();
_defmt_flush();
_defmt_release()
}
}
}
}
#[cfg(not(feature = "unstable-test"))]
#[doc(hidden)]
pub struct IdRanges {
pub trace: core::ops::Range<u16>,
pub debug: core::ops::Range<u16>,
pub info: core::ops::Range<u16>,
pub warn: core::ops::Range<u16>,
pub error: core::ops::Range<u16>,
}
#[cfg(not(feature = "unstable-test"))]
impl IdRanges {
pub fn get() -> Self {
extern "C" {
static __DEFMT_MARKER_TRACE_START: u8;
static __DEFMT_MARKER_TRACE_END: u8;
static __DEFMT_MARKER_DEBUG_START: u8;
static __DEFMT_MARKER_DEBUG_END: u8;
static __DEFMT_MARKER_INFO_START: u8;
static __DEFMT_MARKER_INFO_END: u8;
static __DEFMT_MARKER_WARN_START: u8;
static __DEFMT_MARKER_WARN_END: u8;
static __DEFMT_MARKER_ERROR_START: u8;
static __DEFMT_MARKER_ERROR_END: u8;
}
let trace_start = unsafe { &__DEFMT_MARKER_TRACE_START as *const u8 as u16 };
let trace_end = unsafe { &__DEFMT_MARKER_TRACE_END as *const u8 as u16 };
let debug_start = unsafe { &__DEFMT_MARKER_DEBUG_START as *const u8 as u16 };
let debug_end = unsafe { &__DEFMT_MARKER_DEBUG_END as *const u8 as u16 };
let info_start = unsafe { &__DEFMT_MARKER_INFO_START as *const u8 as u16 };
let info_end = unsafe { &__DEFMT_MARKER_INFO_END as *const u8 as u16 };
let warn_start = unsafe { &__DEFMT_MARKER_WARN_START as *const u8 as u16 };
let warn_end = unsafe { &__DEFMT_MARKER_WARN_END as *const u8 as u16 };
let error_start = unsafe { &__DEFMT_MARKER_ERROR_START as *const u8 as u16 };
let error_end = unsafe { &__DEFMT_MARKER_ERROR_END as *const u8 as u16 };
Self {
trace: trace_start..trace_end,
debug: debug_start..debug_end,
info: info_start..info_end,
warn: warn_start..warn_end,
error: error_start..error_end,
}
}
}
+34
View File
@@ -0,0 +1,34 @@
use crate as defmt;
#[test]
fn log_levels() {
// just make sure they build OK for now
defmt::trace!("test trace");
defmt::debug!("test debug");
defmt::info!("test info");
defmt::warn!("test warn");
defmt::error!("test error");
}
#[test]
fn str() {
defmt::info!("Hello, {=str}", "world");
let world = defmt::intern!("world");
defmt::info!("Hello, {=istr}", world);
}
#[test]
fn trailing_comma() {
defmt::trace!("test trace",);
defmt::debug!("test debug",);
defmt::info!("test info",);
defmt::warn!("test warn",);
defmt::error!("test error",);
defmt::trace!("test trace {=?}", 0,);
defmt::debug!("test debug {=?}", 0,);
defmt::info!("test info {=?}", 0,);
defmt::warn!("test warn {=?}", 0,);
defmt::error!("test error {=?}", 0,);
}
+131
View File
@@ -0,0 +1,131 @@
use defmt_macros::internp;
#[allow(unused_imports)]
use crate as defmt;
use crate::{export, Formatter, Str};
/// Trait for types that can be formatted via defmt.
///
/// This trait is used by the `{:?}` format specifier and can format a wide range of types.
/// User-defined types can `#[derive(Format)]` to get an auto-generated implementation of this
/// trait.
///
/// **Note**: The implementation of `#[derive(Format)]` assumes that no builtin types are shadowed
/// (for example by defining a `struct u8;`). This allows it to represent them more compactly.
///
/// # Example
///
/// Usually, an implementation of this trait can be `#[derive]`d automatically:
///
/// ```
/// use defmt::Format;
///
/// #[derive(Format)]
/// struct Header {
/// source: u8,
/// destination: u8,
/// sequence: u16,
/// }
/// ```
///
/// Manual implementations can make use of the [`write!`] macro:
///
/// ```
/// use defmt::{Format, Formatter, write};
///
/// struct Id(u32);
///
/// impl Format for Id {
/// fn format(&self, fmt: Formatter) {
/// // Format as hexadecimal.
/// write!(fmt, "Id({:x})", self.0);
/// }
/// }
/// ```
/// **Note** Some implementations of standard types like `Vec<T>` are hidden behind the `alloc` feature flag.
pub trait Format {
/// Writes the defmt representation of `self` to `fmt`.
fn format(&self, fmt: Formatter);
#[doc(hidden)]
fn _format_tag() -> Str {
internp!("{=__internal_FormatSequence}")
}
#[doc(hidden)]
fn _format_data(&self) {
self.format(export::make_formatter());
export::u16(&0); // terminator
}
}
/// Global logger acquire-release mechanism
///
/// This trait's methods will be called by the defmt logging macros to transmit the
/// encoded log data over the wire. The call order is:
/// - One `acquire()` call to start the log frame.
/// - Multiple `write()` calls, with fragments of the log frame data each.
/// - One `release()` call.
///
/// The data passed to `write()` is *unencoded*. Implementations MUST encode it with `Encoder`
/// prior to sending it over the wire. The simplest way is for `acquire()` to call `Encoder::start_frame()`,
/// `write()` to call `Encoder::write()`, and `release()` to call `Encoder::end_frame()`.
///
/// The global logger can be acquired once for each "execution context". The definition
/// of execution context is up to the implementation. For example, it can be:
///
/// - the entire process.
/// - one thread in std environments.
/// - one interrupt priority level in embedded devices.
///
/// # Safety
///
/// - `acquire` logically acquires the global logger in the current execution context.
/// The acquiring is tracked internally, no Rust object is returned representing ownership.
/// - `acquire` is a safe function, therefore it must be thread-safe and interrupt-safe
///
/// And, not safety related, the methods should never be invoked from user code. The easiest way to
/// ensure this is to implement `Logger` on a *private* `struct` and mark that `struct` as the
/// `#[global_logger]`.
pub unsafe trait Logger {
/// Acquire the global logger in the current execution context.
///
/// This will be called by the defmt logging macros before writing each log frame.
///
/// Panics if already acquired in the current execution context. Otherwise it must never fail.
fn acquire();
/// Block until host has read all pending data.
///
/// The flush operation must not fail. This is a "best effort" operation, I/O errors should be discarded.
///
/// # Safety
/// Must only be called when the global logger is acquired in the current execution context.
/// (i.e. between `acquire()` and `release()`).
unsafe fn flush();
/// Releases the global logger in the current execution context.
///
/// This will be called by the defmt logging macros after writing each log frame.
///
/// # Safety
/// Must be called exactly once for each acquire(), in the same execution context.
unsafe fn release();
/// Writes `bytes` to the destination.
///
/// This will be called by the defmt logging macros to transmit frame data. One log frame may cause multiple `write` calls.
///
/// The write operation must not fail. This is a "best effort" operation, I/O errors should be discarded.
///
/// The `bytes` are unencoded log frame data, they MUST be encoded with `Encoder` prior to
/// sending over the wire.
///
/// Note that a call to `write` does *not* correspond to a defmt logging macro invocation. A
/// single `defmt::info!` call can result in an arbitrary number of `write` calls.
///
/// # Safety
/// Must only be called when the global logger is acquired in the current execution context.
/// (i.e. between `acquire()` and `release()`).
unsafe fn write(bytes: &[u8]);
}