defmt_decoder/
lib.rs

1//! Decodes [`defmt`](https://github.com/knurling-rs/defmt) log frames
2//!
3//! NOTE: The decoder always runs on the host!
4
5#![cfg_attr(docsrs, feature(doc_cfg))]
6#![doc(html_logo_url = "https://knurling.ferrous-systems.com/knurling_logo_light_text.svg")]
7
8pub const DEFMT_VERSIONS: &[&str] = &["3", "4"];
9// To avoid a breaking change, still provide `DEFMT_VERSION`.
10#[deprecated = "Please use DEFMT_VERSIONS instead"]
11pub const DEFMT_VERSION: &str = DEFMT_VERSIONS[1];
12
13mod decoder;
14mod elf2table;
15mod frame;
16pub mod log;
17mod stream;
18
19use std::{
20    collections::{BTreeMap, HashMap},
21    error::Error,
22    fmt, io,
23    str::FromStr,
24};
25
26use byteorder::{ReadBytesExt, LE};
27use defmt_parser::Level;
28
29use crate::{decoder::Decoder, elf2table::parse_impl};
30
31pub use crate::{
32    elf2table::{Location, Locations},
33    frame::Frame,
34    stream::StreamDecoder,
35};
36
37/// Specifies the origin of a format string
38#[derive(PartialEq, Eq, Debug)]
39pub enum Tag {
40    /// Defmt-controlled format string for primitive types.
41    Prim,
42    /// Format string created by `#[derive(Format)]`.
43    Derived,
44    /// Format string created by `defmt::bitflags!`.
45    Bitflags,
46    /// A user-defined format string from a `write!` invocation.
47    Write,
48    /// An interned string, for use with `{=istr}`.
49    Str,
50    /// Defines the global timestamp format.
51    Timestamp,
52
53    /// `static` containing a possible value of a bitflags type.
54    BitflagsValue,
55    /// Format string created by `defmt::println!`.
56    Println,
57
58    Trace,
59    Debug,
60    Info,
61    Warn,
62    Error,
63}
64
65impl Tag {
66    fn to_level(&self) -> Option<Level> {
67        match self {
68            Tag::Trace => Some(Level::Trace),
69            Tag::Debug => Some(Level::Debug),
70            Tag::Info => Some(Level::Info),
71            Tag::Warn => Some(Level::Warn),
72            Tag::Error => Some(Level::Error),
73            _ => None,
74        }
75    }
76}
77
78/// Entry in [`Table`] combining a format string with its raw symbol
79#[derive(Debug, Eq, PartialEq)]
80pub struct TableEntry {
81    string: StringEntry,
82    raw_symbol: String,
83}
84
85impl TableEntry {
86    pub fn new(string: StringEntry, raw_symbol: String) -> Self {
87        Self { string, raw_symbol }
88    }
89
90    #[cfg(test)]
91    fn new_without_symbol(tag: Tag, string: String) -> Self {
92        Self {
93            string: StringEntry::new(tag, string),
94            raw_symbol: "<unknown>".to_string(),
95        }
96    }
97}
98
99/// A format string and it's [`Tag`]
100#[derive(Debug, Eq, PartialEq)]
101pub struct StringEntry {
102    tag: Tag,
103    string: String,
104}
105
106impl StringEntry {
107    pub fn new(tag: Tag, string: String) -> Self {
108        Self { tag, string }
109    }
110}
111
112/// Data that uniquely identifies a `defmt::bitflags!` invocation.
113#[derive(Debug, PartialEq, Eq, Hash)]
114struct BitflagsKey {
115    /// Name of the bitflags struct (this is really redundant with `disambig`).
116    ident: String,
117    package: String,
118    disambig: String,
119    crate_name: Option<String>,
120}
121
122/// How a defmt frame is encoded
123#[derive(Copy, Clone, Debug, Eq, PartialEq)]
124#[non_exhaustive]
125pub enum Encoding {
126    /// raw data, that is no encoding.
127    Raw,
128    /// [Reverse Zero-compressing COBS encoding](https://github.com/Dirbaio/rzcobs)
129    Rzcobs,
130}
131
132impl FromStr for Encoding {
133    type Err = anyhow::Error;
134
135    fn from_str(s: &str) -> Result<Self, Self::Err> {
136        match s {
137            "raw" => Ok(Encoding::Raw),
138            "rzcobs" => Ok(Encoding::Rzcobs),
139            _ => anyhow::bail!("Unknown defmt encoding '{}' specified. This is a bug.", s),
140        }
141    }
142}
143
144impl Encoding {
145    /// Can this encoding recover from missed bytes?
146    pub const fn can_recover(&self) -> bool {
147        match self {
148            Encoding::Raw => false,
149            Encoding::Rzcobs => true,
150        }
151    }
152}
153
154/// Internal table that holds log levels and maps format strings to indices
155#[derive(Debug, Eq, PartialEq)]
156pub struct Table {
157    timestamp: Option<TableEntry>,
158    entries: BTreeMap<usize, TableEntry>,
159    bitflags: HashMap<BitflagsKey, Vec<(String, u128)>>,
160    encoding: Encoding,
161}
162
163impl Table {
164    /// Parses an ELF file and returns the decoded `defmt` table.
165    ///
166    /// This function returns `None` if the ELF file contains no `.defmt` section.
167    pub fn parse(elf: &[u8]) -> Result<Option<Table>, anyhow::Error> {
168        parse_impl(elf, true)
169    }
170
171    /// Like `parse`, but does not verify that the defmt version in the firmware matches the host.
172    ///
173    /// CAUTION: This is meant for defmt/probe-run development only and can result in reading garbage data.
174    pub fn parse_ignore_version(elf: &[u8]) -> Result<Option<Table>, anyhow::Error> {
175        parse_impl(elf, false)
176    }
177
178    pub fn set_timestamp_entry(&mut self, timestamp: TableEntry) {
179        self.timestamp = Some(timestamp);
180    }
181
182    fn _get(&self, index: usize) -> Result<(Option<Level>, &str), ()> {
183        let entry = self.entries.get(&index).ok_or(())?;
184        Ok((entry.string.tag.to_level(), &entry.string.string))
185    }
186
187    fn get_with_level(&self, index: usize) -> Result<(Option<Level>, &str), ()> {
188        self._get(index)
189    }
190
191    fn get_without_level(&self, index: usize) -> Result<&str, ()> {
192        let (lvl, format) = self._get(index)?;
193        if lvl.is_none() {
194            Ok(format)
195        } else {
196            Err(())
197        }
198    }
199
200    pub fn indices(&self) -> impl Iterator<Item = usize> + '_ {
201        self.entries.iter().filter_map(move |(idx, entry)| {
202            if entry.string.tag.to_level().is_some() || entry.string.tag == Tag::Println {
203                Some(*idx)
204            } else {
205                None
206            }
207        })
208    }
209
210    pub fn is_empty(&self) -> bool {
211        self.entries.is_empty()
212    }
213
214    /// Iterates over the raw symbols of the table entries
215    pub fn raw_symbols(&self) -> impl Iterator<Item = &str> + '_ {
216        self.entries.values().map(|s| &*s.raw_symbol)
217    }
218
219    pub fn get_locations(&self, elf: &[u8]) -> Result<Locations, anyhow::Error> {
220        elf2table::get_locations(elf, self)
221    }
222
223    /// Decode the data sent by the device using the previously stored metadata.
224    ///
225    /// * `bytes`
226    ///   * contains the data sent by the device that logs.
227    ///   * contains the [log string index, timestamp, optional fmt string args]
228    pub fn decode<'t>(
229        &'t self,
230        mut bytes: &[u8],
231    ) -> Result<(Frame<'t>, /* consumed: */ usize), DecodeError> {
232        let len = bytes.len();
233        let index = bytes.read_u16::<LE>()? as u64;
234
235        let mut decoder = Decoder::new(self, bytes);
236
237        let mut timestamp_format = None;
238        let mut timestamp_args = Vec::new();
239        if let Some(entry) = self.timestamp.as_ref() {
240            let format = &entry.string.string;
241            timestamp_format = Some(&**format);
242            timestamp_args = decoder.decode_format(format)?;
243        }
244
245        let (level, format) = self
246            .get_with_level(index as usize)
247            .map_err(|_| DecodeError::Malformed)?;
248
249        let args = decoder.decode_format(format)?;
250
251        let frame = Frame::new(
252            self,
253            level,
254            index,
255            timestamp_format,
256            timestamp_args,
257            format,
258            args,
259        );
260
261        let consumed = len - decoder.bytes.len();
262        Ok((frame, consumed))
263    }
264
265    pub fn new_stream_decoder(&self) -> Box<dyn StreamDecoder + '_> {
266        match self.encoding {
267            Encoding::Raw => Box::new(stream::Raw::new(self)),
268            Encoding::Rzcobs => Box::new(stream::Rzcobs::new(self)),
269        }
270    }
271
272    pub fn encoding(&self) -> Encoding {
273        self.encoding
274    }
275
276    pub fn has_timestamp(&self) -> bool {
277        self.timestamp.is_some()
278    }
279}
280
281// NOTE follows `parser::Type`
282#[derive(Debug, Clone, PartialEq)]
283enum Arg<'t> {
284    /// Bool
285    Bool(bool),
286    F32(f32),
287    F64(f64),
288    /// U8, U16, U32, U64, U128
289    Uxx(u128),
290    /// I8, I16, I32, I64, I128
291    Ixx(i128),
292    /// Str
293    Str(String),
294    /// Interned string
295    IStr(&'t str),
296    /// Format
297    Format {
298        format: &'t str,
299        args: Vec<Arg<'t>>,
300    },
301    FormatSlice {
302        elements: Vec<FormatSliceElement<'t>>,
303    },
304    FormatSequence {
305        args: Vec<Arg<'t>>,
306    },
307    /// Slice or Array of bytes.
308    Slice(Vec<u8>),
309    /// Char
310    Char(char),
311
312    /// `fmt::Debug` / `fmt::Display` formatted on-target.
313    Preformatted(String),
314}
315
316#[derive(Debug, Clone, PartialEq)]
317struct FormatSliceElement<'t> {
318    // this will usually be the same format string for all elements; except when the format string
319    // is an enum -- in that case `format` will be the variant
320    format: &'t str,
321    args: Vec<Arg<'t>>,
322}
323
324/// Ways in which decoding a defmt frame can fail.
325#[derive(Debug, Eq, PartialEq)]
326pub enum DecodeError {
327    /// More data is needed to decode the next frame.
328    UnexpectedEof,
329    /// The frame was not in the expected format.
330    Malformed,
331}
332
333impl From<io::Error> for DecodeError {
334    fn from(e: io::Error) -> Self {
335        if e.kind() == io::ErrorKind::UnexpectedEof {
336            DecodeError::UnexpectedEof
337        } else {
338            DecodeError::Malformed
339        }
340    }
341}
342
343impl fmt::Display for DecodeError {
344    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
345        match self {
346            DecodeError::UnexpectedEof => f.write_str("unexpected end of stream"),
347            DecodeError::Malformed => f.write_str("malformed data"),
348        }
349    }
350}
351
352impl Error for DecodeError {}
353
354#[cfg(test)]
355mod tests {
356    use super::*;
357
358    fn test_table(entries: impl IntoIterator<Item = TableEntry>) -> Table {
359        Table {
360            timestamp: None,
361            entries: entries.into_iter().enumerate().collect(),
362            bitflags: Default::default(),
363            encoding: Encoding::Raw,
364        }
365    }
366
367    fn test_table_with_timestamp(
368        entries: impl IntoIterator<Item = TableEntry>,
369        timestamp: &str,
370    ) -> Table {
371        Table {
372            timestamp: Some(TableEntry::new_without_symbol(
373                Tag::Timestamp,
374                timestamp.into(),
375            )),
376            entries: entries.into_iter().enumerate().collect(),
377            bitflags: Default::default(),
378            encoding: Encoding::Raw,
379        }
380    }
381
382    // helper function to initiate decoding and assert that the result is as expected.
383    //
384    // format:       format string to be expanded
385    // bytes:        arguments + metadata
386    // expectation:  the expected result
387    fn decode_and_expect(format: &str, bytes: &[u8], expectation: &str) {
388        let mut entries = BTreeMap::new();
389        entries.insert(
390            bytes[0] as usize,
391            TableEntry::new_without_symbol(Tag::Info, format.to_string()),
392        );
393
394        let table = Table {
395            entries,
396            timestamp: Some(TableEntry::new_without_symbol(
397                Tag::Timestamp,
398                "{=u8:us}".to_owned(),
399            )),
400            bitflags: Default::default(),
401            encoding: Encoding::Raw,
402        };
403
404        let frame = table.decode(bytes).unwrap().0;
405        assert_eq!(frame.display(false).to_string(), expectation.to_owned());
406    }
407
408    #[test]
409    fn decode() {
410        let entries = vec![
411            TableEntry::new_without_symbol(Tag::Info, "Hello, world!".to_owned()),
412            TableEntry::new_without_symbol(Tag::Debug, "The answer is {=u8}!".to_owned()),
413        ];
414
415        let table = test_table(entries);
416
417        let bytes = [0, 0];
418        //     index ^
419
420        assert_eq!(
421            table.decode(&bytes),
422            Ok((
423                Frame::new(
424                    &table,
425                    Some(Level::Info),
426                    0,
427                    None,
428                    vec![],
429                    "Hello, world!",
430                    vec![],
431                ),
432                bytes.len(),
433            ))
434        );
435
436        let bytes = [
437            1, 0,  // index
438            42, // argument
439        ];
440
441        assert_eq!(
442            table.decode(&bytes),
443            Ok((
444                Frame::new(
445                    &table,
446                    Some(Level::Debug),
447                    1,
448                    None,
449                    vec![],
450                    "The answer is {=u8}!",
451                    vec![Arg::Uxx(42)],
452                ),
453                bytes.len(),
454            ))
455        );
456
457        // TODO Format ({:?})
458    }
459
460    #[test]
461    fn all_integers() {
462        const FMT: &str =
463            "Hello, {=u8} {=u16} {=u32} {=u64} {=u128} {=i8} {=i16} {=i32} {=i64} {=i128}!";
464
465        let entries = vec![TableEntry::new_without_symbol(Tag::Info, FMT.to_owned())];
466
467        let table = test_table(entries);
468
469        let bytes = [
470            0, 0,  // index
471            42, // u8
472            0xff, 0xff, // u16
473            0xff, 0xff, 0xff, 0xff, // u32
474            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // u64
475            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
476            0xff, 0xff, // u128
477            0xff, // i8
478            0xff, 0xff, // i16
479            0xff, 0xff, 0xff, 0xff, // i32
480            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // i64
481            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
482            0xff, 0xff, // i128
483        ];
484
485        assert_eq!(
486            table.decode(&bytes),
487            Ok((
488                Frame::new(
489                    &table,
490                    Some(Level::Info),
491                    0,
492                    None,
493                    vec![],
494                    FMT,
495                    vec![
496                        Arg::Uxx(42),                      // u8
497                        Arg::Uxx(u16::max_value().into()), // u16
498                        Arg::Uxx(u32::max_value().into()), // u32
499                        Arg::Uxx(u64::max_value().into()), // u64
500                        Arg::Uxx(u128::max_value()),       // u128
501                        Arg::Ixx(-1),                      // i8
502                        Arg::Ixx(-1),                      // i16
503                        Arg::Ixx(-1),                      // i32
504                        Arg::Ixx(-1),                      // i64
505                        Arg::Ixx(-1),                      // i128
506                    ],
507                ),
508                bytes.len(),
509            ))
510        );
511    }
512
513    #[test]
514    fn indices() {
515        let entries = vec![
516            TableEntry::new_without_symbol(Tag::Info, "The answer is {0=u8} {0=u8}!".to_owned()),
517            TableEntry::new_without_symbol(
518                Tag::Info,
519                "The answer is {1=u16} {0=u8} {1=u16}!".to_owned(),
520            ),
521        ];
522
523        let table = test_table(entries);
524        let bytes = [
525            0, 0,  // index
526            42, // argument
527        ];
528
529        assert_eq!(
530            table.decode(&bytes),
531            Ok((
532                Frame::new(
533                    &table,
534                    Some(Level::Info),
535                    0,
536                    None,
537                    vec![],
538                    "The answer is {0=u8} {0=u8}!",
539                    vec![Arg::Uxx(42)],
540                ),
541                bytes.len(),
542            ))
543        );
544
545        let bytes = [
546            1, 0,  // index
547            42, // u8
548            0xff, 0xff, // u16
549        ];
550
551        assert_eq!(
552            table.decode(&bytes),
553            Ok((
554                Frame::new(
555                    &table,
556                    Some(Level::Info),
557                    1,
558                    None,
559                    vec![],
560                    "The answer is {1=u16} {0=u8} {1=u16}!",
561                    vec![Arg::Uxx(42), Arg::Uxx(0xffff)],
562                ),
563                bytes.len(),
564            ))
565        );
566    }
567
568    #[test]
569    fn format() {
570        let entries = vec![
571            TableEntry::new_without_symbol(Tag::Info, "x={=?}".to_owned()),
572            TableEntry::new_without_symbol(Tag::Derived, "Foo {{ x: {=u8} }}".to_owned()),
573        ];
574
575        let table = test_table(entries);
576
577        let bytes = [
578            0, 0, // index
579            1, 0,  // index of the struct
580            42, // Foo.x
581        ];
582
583        assert_eq!(
584            table.decode(&bytes),
585            Ok((
586                Frame::new(
587                    &table,
588                    Some(Level::Info),
589                    0,
590                    None,
591                    vec![],
592                    "x={=?}",
593                    vec![Arg::Format {
594                        format: "Foo {{ x: {=u8} }}",
595                        args: vec![Arg::Uxx(42)]
596                    }],
597                ),
598                bytes.len(),
599            ))
600        );
601    }
602
603    #[test]
604    fn format_sequence() {
605        let entries = vec![
606            TableEntry::new_without_symbol(Tag::Info, "{=__internal_FormatSequence}".to_owned()),
607            TableEntry::new_without_symbol(Tag::Derived, "Foo".to_owned()),
608            TableEntry::new_without_symbol(Tag::Derived, "Bar({=u8})".to_owned()),
609            TableEntry::new_without_symbol(Tag::Derived, "State {=u8}|".to_owned()),
610        ];
611
612        let table = test_table(entries);
613
614        let bytes = [
615            0, 0, // index
616            1, 0, // index of Foo
617            2, 0,  // index of Bar
618            42, // bar.x
619            3, 0,  // index of State
620            23, // State variable
621            0, 0, // terminator
622        ];
623
624        assert_eq!(
625            table.decode(&bytes),
626            Ok((
627                Frame::new(
628                    &table,
629                    Some(Level::Info),
630                    0,
631                    None,
632                    vec![],
633                    "{=__internal_FormatSequence}",
634                    vec![Arg::FormatSequence {
635                        args: vec![
636                            Arg::Format {
637                                format: "Foo",
638                                args: vec![]
639                            },
640                            Arg::Format {
641                                format: "Bar({=u8})",
642                                args: vec![Arg::Uxx(42)]
643                            },
644                            Arg::Format {
645                                format: "State {=u8}|",
646                                args: vec![Arg::Uxx(23)]
647                            }
648                        ]
649                    }],
650                ),
651                bytes.len(),
652            ))
653        );
654    }
655
656    #[test]
657    fn display() {
658        let entries = vec![
659            TableEntry::new_without_symbol(Tag::Info, "x={=?}".to_owned()),
660            TableEntry::new_without_symbol(Tag::Derived, "Foo {{ x: {=u8} }}".to_owned()),
661        ];
662
663        let table = test_table_with_timestamp(entries, "{=u8:us}");
664
665        let bytes = [
666            0, 0, // index
667            2, // timestamp
668            1, 0,  // index of the struct
669            42, // Foo.x
670        ];
671
672        let frame = table.decode(&bytes).unwrap().0;
673        assert_eq!(
674            frame.display(false).to_string(),
675            "0.000002 INFO x=Foo { x: 42 }"
676        );
677    }
678
679    #[test]
680    fn display_i16_with_hex_hint() {
681        // defmt::info!("x: {=i16:#x},y: {=i16:#x},z: {=i16:#x}", -1_i16, -100_i16, -1000_i16);
682        let bytes = [
683            0,
684            0,           // index
685            2,           // timestamp
686            0b1111_1111, // the logged i16 value -1
687            0b1111_1111,
688        ];
689
690        decode_and_expect(
691            "i16 as hex {=i16:#x}",
692            &bytes,
693            "0.000002 INFO i16 as hex 0xffff",
694        );
695    }
696
697    #[test]
698    fn display_use_inner_type_hint() {
699        let entries = vec![
700            TableEntry::new_without_symbol(Tag::Info, "x={:b}".to_owned()),
701            TableEntry::new_without_symbol(Tag::Derived, "S {{ x: {=u8:x} }}".to_owned()),
702        ];
703
704        let table = test_table_with_timestamp(entries, "{=u8:us}");
705
706        let bytes = [
707            0, 0, // index
708            2, // timestamp
709            1, 0,  // index of the struct
710            42, // value
711        ];
712
713        let frame = table.decode(&bytes).unwrap().0;
714        assert_eq!(
715            frame.display(false).to_string(),
716            "0.000002 INFO x=S { x: 2a }",
717        );
718    }
719
720    #[test]
721    fn display_use_outer_type_hint() {
722        let entries = vec![
723            TableEntry::new_without_symbol(Tag::Info, "x={:b}".to_owned()),
724            TableEntry::new_without_symbol(Tag::Derived, "S {{ x: {=u8:?} }}".to_owned()),
725        ];
726
727        let table = test_table_with_timestamp(entries, "{=u8:us}");
728
729        let bytes = [
730            0, 0, // index
731            2, // timestamp
732            1, 0,  // index of the struct
733            42, // value
734        ];
735
736        let frame = table.decode(&bytes).unwrap().0;
737        assert_eq!(
738            frame.display(false).to_string(),
739            "0.000002 INFO x=S { x: 101010 }",
740        );
741    }
742
743    #[test]
744    fn display_inner_str_in_struct() {
745        let entries = vec![
746            TableEntry::new_without_symbol(Tag::Info, "{}".to_owned()),
747            TableEntry::new_without_symbol(Tag::Derived, "S {{ x: {=str:?} }}".to_owned()),
748        ];
749
750        let table = test_table_with_timestamp(entries, "{=u8:us}");
751
752        let bytes = [
753            0, 0, // index
754            2, // timestamp
755            1, 0, // index into the struct
756            5, 0, 0, 0, // length of the string
757            b'H', b'e', b'l', b'l', b'o', // string "Hello"
758        ];
759        let frame = table.decode(&bytes).unwrap().0;
760        assert_eq!(
761            frame.display(false).to_string(),
762            "0.000002 INFO S { x: \"Hello\" }",
763        );
764    }
765
766    #[test]
767    fn display_u8_vec() {
768        let entries = vec![
769            TableEntry::new_without_symbol(Tag::Prim, "{=u8}".to_owned()),
770            TableEntry::new_without_symbol(Tag::Prim, "{=[?]}".to_owned()),
771            TableEntry::new_without_symbol(Tag::Derived, "Data {{ name: {=?:?} }}".to_owned()),
772            TableEntry::new_without_symbol(Tag::Info, "{=[?]:a}".to_owned()),
773        ];
774
775        let table = test_table_with_timestamp(entries, "{=u8:us}");
776
777        let bytes = [
778            3, 0, // frame index
779            2, // timestamp value of type `u8`
780            1, 0, 0, 0, // number of elements in `FormatSlice`
781            2, 0, // index to `Data` struct
782            1, 0, // Format index to table entry: `{=[?]}`
783            2, 0, 0, 0, // inner FormatSlice, number of elements in `name` field
784            0, 0,   // Format index to table entry: `{=u8}`
785            72,  // "H"
786            105, // "i"
787        ];
788        let frame = table.decode(&bytes).unwrap().0;
789        assert_eq!(
790            frame.display(false).to_string(),
791            "0.000002 INFO [Data { name: b\"Hi\" }]",
792        );
793    }
794
795    #[test]
796    fn display_iso8601_timestamp() {
797        let bytes = [
798            0, 0, // index
799            2, // timestamp
800            36, 188, 151, 238, 120, 1, 0, 0, // unix timestamp in bytes: 1618910624804
801        ];
802
803        decode_and_expect(
804            "{=u64:iso8601ms}",
805            &bytes,
806            "0.000002 INFO 2021-04-20T09:23:44.804Z",
807        );
808    }
809
810    #[test]
811    fn bools_simple() {
812        let bytes = [
813            0, 0,          // index
814            2,          // timestamp
815            true as u8, // the logged bool value
816        ];
817
818        decode_and_expect("my bool={=bool}", &bytes, "0.000002 INFO my bool=true");
819    }
820
821    #[test]
822    fn bitfields() {
823        let bytes = [
824            0,
825            0,           // index
826            2,           // timestamp
827            0b1110_0101, // u8
828        ];
829        decode_and_expect(
830            "x: {0=0..4:b}, y: {0=3..8:#b}",
831            &bytes,
832            "0.000002 INFO x: 101, y: 0b11100",
833        );
834    }
835
836    #[test]
837    fn bitfields_reverse_order() {
838        let bytes = [
839            0,
840            0,           // index
841            2,           // timestamp
842            0b1101_0010, // u8
843        ];
844        decode_and_expect(
845            "x: {0=0..7:b}, y: {0=3..5:b}",
846            &bytes,
847            "0.000002 INFO x: 1010010, y: 10",
848        );
849    }
850
851    #[test]
852    fn bitfields_different_indices() {
853        let bytes = [
854            0,
855            0,           // index
856            2,           // timestamp
857            0b1111_0000, // u8
858            0b1110_0101, // u8
859        ];
860        decode_and_expect(
861            "#0: {0=0..5:b}, #1: {1=3..8:b}",
862            &bytes,
863            "0.000002 INFO #0: 10000, #1: 11100",
864        );
865    }
866
867    #[test]
868    fn bitfields_u16() {
869        let bytes = [
870            0,
871            0, // index
872            2, // timestamp
873            0b1111_0000,
874            0b1110_0101, // u16
875        ];
876        decode_and_expect("x: {0=7..12:b}", &bytes, "0.000002 INFO x: 1011");
877    }
878
879    #[test]
880    fn bitfields_mixed_types() {
881        let bytes = [
882            0,
883            0, // index
884            2, // timestamp
885            0b1111_0000,
886            0b1110_0101, // u16
887            0b1111_0001, // u8
888        ];
889        decode_and_expect(
890            "#0: {0=7..12:b}, #1: {1=0..5:b}",
891            &bytes,
892            "0.000002 INFO #0: 1011, #1: 10001",
893        );
894    }
895
896    #[test]
897    fn bitfields_mixed() {
898        let bytes = [
899            0,
900            0, // index
901            2, // timestamp
902            0b1111_0000,
903            0b1110_0101, // u16 bitfields
904            42,          // u8
905            0b1111_0001, // u8 bitfields
906        ];
907        decode_and_expect(
908            "#0: {0=7..12:b}, #1: {1=u8}, #2: {2=0..5:b}",
909            &bytes,
910            "0.000002 INFO #0: 1011, #1: 42, #2: 10001",
911        );
912    }
913
914    #[test]
915    fn bitfields_across_boundaries() {
916        let bytes = [
917            0,
918            0, // index
919            2, // timestamp
920            0b1101_0010,
921            0b0110_0011, // u16
922        ];
923        decode_and_expect(
924            "bitfields {0=0..7:b} {0=9..14:b}",
925            &bytes,
926            "0.000002 INFO bitfields 1010010 10001",
927        );
928    }
929
930    #[test]
931    fn bitfields_across_boundaries_diff_indices() {
932        let bytes = [
933            0,
934            0, // index
935            2, // timestamp
936            0b1101_0010,
937            0b0110_0011, // u16
938            0b1111_1111, // truncated u16
939        ];
940        decode_and_expect(
941            "bitfields {0=0..7:b} {0=9..14:b} {1=8..10:b}",
942            &bytes,
943            "0.000002 INFO bitfields 1010010 10001 11",
944        );
945    }
946
947    #[test]
948    fn bitfields_truncated_front() {
949        let bytes = [
950            0,
951            0,           // index
952            2,           // timestamp
953            0b0110_0011, // truncated(!) u16
954        ];
955        decode_and_expect(
956            "bitfields {0=9..14:b}",
957            &bytes,
958            "0.000002 INFO bitfields 10001",
959        );
960    }
961
962    #[test]
963    fn bitfields_non_truncated_u32() {
964        let bytes = [
965            0,
966            0,           // index
967            2,           // timestamp
968            0b0110_0011, // -
969            0b0000_1111, //  |
970            0b0101_1010, //  | u32
971            0b1100_0011, // -
972        ];
973        decode_and_expect(
974            "bitfields {0=0..2:b} {0=28..31:b}",
975            &bytes,
976            "0.000002 INFO bitfields 11 100",
977        );
978    }
979
980    #[test]
981    fn bitfields_u128() {
982        let bytes = [
983            0,
984            0,           // index
985            2,           // timestamp
986            0b1110_0101, // 120..127
987            0b1110_0101, // 112..119
988            0b0000_0000, // 104..111
989            0b0000_0000, // 96..103
990            0b0000_0000, // 88..95
991            0b0000_0000, // 80..87
992            0b0000_0000, // 72..79
993            0b0000_0000, // 64..71
994            0b0000_0000, // 56..63
995            0b0000_0000, // 48..55
996            0b0000_0000, // 40..47
997            0b0000_0000, // 32..39
998            0b0000_0000, // 24..31
999            0b0000_0000, // 16..23
1000            0b0000_0000, // 8..15
1001            0b0000_0000, // 0..7
1002        ];
1003        decode_and_expect("x: {0=119..124:b}", &bytes, "0.000002 INFO x: 1011");
1004    }
1005
1006    #[test]
1007    fn slice() {
1008        let bytes = [
1009            0, 0, // index
1010            2, // timestamp
1011            2, 0, 0, 0, // length of the slice
1012            23, 42, // slice content
1013        ];
1014        decode_and_expect("x={=[u8]}", &bytes, "0.000002 INFO x=[23, 42]");
1015    }
1016
1017    #[test]
1018    fn slice_with_trailing_args() {
1019        let bytes = [
1020            0, 0, // index
1021            2, // timestamp
1022            2, 0, 0, 0, // length of the slice
1023            23, 42, // slice content
1024            1,  // trailing arg
1025        ];
1026
1027        decode_and_expect(
1028            "x={=[u8]} trailing arg={=u8}",
1029            &bytes,
1030            "0.000002 INFO x=[23, 42] trailing arg=1",
1031        );
1032    }
1033
1034    #[test]
1035    fn string_hello_world() {
1036        let bytes = [
1037            0, 0, // index
1038            2, // timestamp
1039            5, 0, 0, 0, // length of the string
1040            b'W', b'o', b'r', b'l', b'd',
1041        ];
1042
1043        decode_and_expect("Hello {=str}", &bytes, "0.000002 INFO Hello World");
1044    }
1045
1046    #[test]
1047    fn string_with_trailing_data() {
1048        let bytes = [
1049            0, 0, // index
1050            2, // timestamp
1051            5, 0, 0, 0, // length of the string
1052            b'W', b'o', b'r', b'l', b'd', 125, // trailing data
1053        ];
1054
1055        decode_and_expect(
1056            "Hello {=str} {=u8}",
1057            &bytes,
1058            "0.000002 INFO Hello World 125",
1059        );
1060    }
1061
1062    #[test]
1063    fn char_data() {
1064        let bytes = [
1065            0, 0, // index
1066            2, // timestamp
1067            0x61, 0x00, 0x00, 0x00, // char 'a'
1068            0x9C, 0xF4, 0x01, 0x00, // Purple heart emoji
1069        ];
1070
1071        decode_and_expect(
1072            "Supports ASCII {=char} and Unicode {=char}",
1073            &bytes,
1074            "0.000002 INFO Supports ASCII a and Unicode 💜",
1075        );
1076    }
1077
1078    #[test]
1079    fn option() {
1080        let mut entries = BTreeMap::new();
1081        entries.insert(
1082            4,
1083            TableEntry::new_without_symbol(Tag::Info, "x={=?}".to_owned()),
1084        );
1085        entries.insert(
1086            3,
1087            TableEntry::new_without_symbol(Tag::Derived, "None|Some({=?})".to_owned()),
1088        );
1089        entries.insert(
1090            2,
1091            TableEntry::new_without_symbol(Tag::Derived, "{=u8}".to_owned()),
1092        );
1093
1094        let table = Table {
1095            entries,
1096            timestamp: Some(TableEntry::new_without_symbol(
1097                Tag::Timestamp,
1098                "{=u8:us}".to_owned(),
1099            )),
1100            bitflags: Default::default(),
1101            encoding: Encoding::Raw,
1102        };
1103
1104        let bytes = [
1105            4, 0, // string index (INFO)
1106            0, // timestamp
1107            3, 0, // string index (enum)
1108            1, // Some discriminant
1109            2, 0,  // string index (u8)
1110            42, // Some.0
1111        ];
1112
1113        let frame = table.decode(&bytes).unwrap().0;
1114        assert_eq!(frame.display(false).to_string(), "0.000000 INFO x=Some(42)");
1115
1116        let bytes = [
1117            4, 0, // string index (INFO)
1118            1, // timestamp
1119            3, 0, // string index (enum)
1120            0, // None discriminant
1121        ];
1122
1123        let frame = table.decode(&bytes).unwrap().0;
1124        assert_eq!(frame.display(false).to_string(), "0.000001 INFO x=None");
1125    }
1126}