1#![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#[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;
28use serde::{Deserialize, Serialize};
29
30use crate::{decoder::Decoder, elf2table::parse_impl};
31
32pub use crate::{
33 elf2table::{Location, Locations},
34 frame::Frame,
35 stream::StreamDecoder,
36};
37
38#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)]
40pub enum Tag {
41 Prim,
43 Derived,
45 Bitflags,
47 Write,
49 Str,
51 Timestamp,
53
54 BitflagsValue,
56 Println,
58
59 Trace,
60 Debug,
61 Info,
62 Warn,
63 Error,
64}
65
66impl Tag {
67 fn to_level(&self) -> Option<Level> {
68 match self {
69 Tag::Trace => Some(Level::Trace),
70 Tag::Debug => Some(Level::Debug),
71 Tag::Info => Some(Level::Info),
72 Tag::Warn => Some(Level::Warn),
73 Tag::Error => Some(Level::Error),
74 _ => None,
75 }
76 }
77}
78
79#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
81pub struct TableEntry {
82 string: StringEntry,
83 raw_symbol: String,
84}
85
86impl TableEntry {
87 pub fn new(string: StringEntry, raw_symbol: String) -> Self {
88 Self { string, raw_symbol }
89 }
90
91 #[cfg(test)]
92 fn new_without_symbol(tag: Tag, string: String) -> Self {
93 Self {
94 string: StringEntry::new(tag, string),
95 raw_symbol: "<unknown>".to_string(),
96 }
97 }
98}
99
100#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
102pub struct StringEntry {
103 tag: Tag,
104 string: String,
105}
106
107impl StringEntry {
108 pub fn new(tag: Tag, string: String) -> Self {
109 Self { tag, string }
110 }
111}
112
113#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
115struct BitflagsKey {
116 ident: String,
118 package: String,
119 disambig: String,
120 crate_name: Option<String>,
121}
122
123#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
125#[non_exhaustive]
126pub enum Encoding {
127 Raw,
129 Rzcobs,
131}
132
133impl FromStr for Encoding {
134 type Err = anyhow::Error;
135
136 fn from_str(s: &str) -> Result<Self, Self::Err> {
137 match s {
138 "raw" => Ok(Encoding::Raw),
139 "rzcobs" => Ok(Encoding::Rzcobs),
140 _ => anyhow::bail!("Unknown defmt encoding '{}' specified. This is a bug.", s),
141 }
142 }
143}
144
145impl Encoding {
146 pub const fn can_recover(&self) -> bool {
148 match self {
149 Encoding::Raw => false,
150 Encoding::Rzcobs => true,
151 }
152 }
153}
154
155#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
157pub struct Table {
158 timestamp: Option<TableEntry>,
159 entries: BTreeMap<usize, TableEntry>,
160 bitflags: HashMap<BitflagsKey, Vec<(String, u128)>>,
161 encoding: Encoding,
162}
163
164impl Table {
165 pub fn parse(elf: &[u8]) -> Result<Option<Table>, anyhow::Error> {
169 parse_impl(elf, true)
170 }
171
172 pub fn parse_ignore_version(elf: &[u8]) -> Result<Option<Table>, anyhow::Error> {
176 parse_impl(elf, false)
177 }
178
179 pub fn set_timestamp_entry(&mut self, timestamp: TableEntry) {
180 self.timestamp = Some(timestamp);
181 }
182
183 fn _get(&self, index: usize) -> Result<(Option<Level>, &str), ()> {
184 let entry = self.entries.get(&index).ok_or(())?;
185 Ok((entry.string.tag.to_level(), &entry.string.string))
186 }
187
188 fn get_with_level(&self, index: usize) -> Result<(Option<Level>, &str), ()> {
189 self._get(index)
190 }
191
192 fn get_without_level(&self, index: usize) -> Result<&str, ()> {
193 let (lvl, format) = self._get(index)?;
194 if lvl.is_none() {
195 Ok(format)
196 } else {
197 Err(())
198 }
199 }
200
201 pub fn indices(&self) -> impl Iterator<Item = usize> + '_ {
202 self.entries.iter().filter_map(move |(idx, entry)| {
203 if entry.string.tag.to_level().is_some() || entry.string.tag == Tag::Println {
204 Some(*idx)
205 } else {
206 None
207 }
208 })
209 }
210
211 pub fn is_empty(&self) -> bool {
212 self.entries.is_empty()
213 }
214
215 pub fn raw_symbols(&self) -> impl Iterator<Item = &str> + '_ {
217 self.entries.values().map(|s| &*s.raw_symbol)
218 }
219
220 pub fn get_locations(&self, elf: &[u8]) -> Result<Locations, anyhow::Error> {
221 elf2table::get_locations(elf, self)
222 }
223
224 pub fn decode<'t>(
230 &'t self,
231 mut bytes: &[u8],
232 ) -> Result<(Frame<'t>, usize), DecodeError> {
233 let len = bytes.len();
234 let index = bytes.read_u16::<LE>()? as u64;
235
236 let mut decoder = Decoder::new(self, bytes);
237
238 let mut timestamp_format = None;
239 let mut timestamp_args = Vec::new();
240 if let Some(entry) = self.timestamp.as_ref() {
241 let format = &entry.string.string;
242 timestamp_format = Some(&**format);
243 timestamp_args = decoder.decode_format(format)?;
244 }
245
246 let (level, format) = self
247 .get_with_level(index as usize)
248 .map_err(|_| DecodeError::Malformed)?;
249
250 let args = decoder.decode_format(format)?;
251
252 let frame = Frame::new(
253 self,
254 level,
255 index,
256 timestamp_format,
257 timestamp_args,
258 format,
259 args,
260 );
261
262 let consumed = len - decoder.bytes.len();
263 Ok((frame, consumed))
264 }
265
266 pub fn new_stream_decoder(&self) -> Box<dyn StreamDecoder + Send + Sync + '_> {
267 match self.encoding {
268 Encoding::Raw => Box::new(stream::Raw::new(self)),
269 Encoding::Rzcobs => Box::new(stream::Rzcobs::new(self)),
270 }
271 }
272
273 pub fn encoding(&self) -> Encoding {
274 self.encoding
275 }
276
277 pub fn has_timestamp(&self) -> bool {
278 self.timestamp.is_some()
279 }
280}
281
282#[derive(Debug, Clone, PartialEq)]
284enum Arg<'t> {
285 Bool(bool),
287 F32(f32),
288 F64(f64),
289 Uxx(u128),
291 Ixx(i128),
293 Str(String),
295 IStr(&'t str),
297 Format {
299 format: &'t str,
300 args: Vec<Arg<'t>>,
301 },
302 FormatSlice {
303 elements: Vec<FormatSliceElement<'t>>,
304 },
305 FormatSequence {
306 args: Vec<Arg<'t>>,
307 },
308 Slice(Vec<u8>),
310 Char(char),
312
313 Preformatted(String),
315}
316
317#[derive(Debug, Clone, PartialEq)]
318struct FormatSliceElement<'t> {
319 format: &'t str,
322 args: Vec<Arg<'t>>,
323}
324
325#[derive(Debug, Eq, PartialEq)]
327pub enum DecodeError {
328 UnexpectedEof,
330 Malformed,
332}
333
334impl From<io::Error> for DecodeError {
335 fn from(e: io::Error) -> Self {
336 if e.kind() == io::ErrorKind::UnexpectedEof {
337 DecodeError::UnexpectedEof
338 } else {
339 DecodeError::Malformed
340 }
341 }
342}
343
344impl fmt::Display for DecodeError {
345 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
346 match self {
347 DecodeError::UnexpectedEof => f.write_str("unexpected end of stream"),
348 DecodeError::Malformed => f.write_str("malformed data"),
349 }
350 }
351}
352
353impl Error for DecodeError {}
354
355#[cfg(test)]
356mod tests {
357 use super::*;
358
359 fn test_table(entries: impl IntoIterator<Item = TableEntry>) -> Table {
360 Table {
361 timestamp: None,
362 entries: entries.into_iter().enumerate().collect(),
363 bitflags: Default::default(),
364 encoding: Encoding::Raw,
365 }
366 }
367
368 fn test_table_with_timestamp(
369 entries: impl IntoIterator<Item = TableEntry>,
370 timestamp: &str,
371 ) -> Table {
372 Table {
373 timestamp: Some(TableEntry::new_without_symbol(
374 Tag::Timestamp,
375 timestamp.into(),
376 )),
377 entries: entries.into_iter().enumerate().collect(),
378 bitflags: Default::default(),
379 encoding: Encoding::Raw,
380 }
381 }
382
383 fn decode_and_expect(format: &str, bytes: &[u8], expectation: &str) {
389 let mut entries = BTreeMap::new();
390 entries.insert(
391 bytes[0] as usize,
392 TableEntry::new_without_symbol(Tag::Info, format.to_string()),
393 );
394
395 let table = Table {
396 entries,
397 timestamp: Some(TableEntry::new_without_symbol(
398 Tag::Timestamp,
399 "{=u8:us}".to_owned(),
400 )),
401 bitflags: Default::default(),
402 encoding: Encoding::Raw,
403 };
404
405 let frame = table.decode(bytes).unwrap().0;
406 assert_eq!(frame.display(false).to_string(), expectation.to_owned());
407 }
408
409 #[test]
410 fn decode() {
411 let entries = vec![
412 TableEntry::new_without_symbol(Tag::Info, "Hello, world!".to_owned()),
413 TableEntry::new_without_symbol(Tag::Debug, "The answer is {=u8}!".to_owned()),
414 ];
415
416 let table = test_table(entries);
417
418 let bytes = [0, 0];
419 assert_eq!(
422 table.decode(&bytes),
423 Ok((
424 Frame::new(
425 &table,
426 Some(Level::Info),
427 0,
428 None,
429 vec![],
430 "Hello, world!",
431 vec![],
432 ),
433 bytes.len(),
434 ))
435 );
436
437 let bytes = [
438 1, 0, 42, ];
441
442 assert_eq!(
443 table.decode(&bytes),
444 Ok((
445 Frame::new(
446 &table,
447 Some(Level::Debug),
448 1,
449 None,
450 vec![],
451 "The answer is {=u8}!",
452 vec![Arg::Uxx(42)],
453 ),
454 bytes.len(),
455 ))
456 );
457
458 }
460
461 #[test]
462 fn all_integers() {
463 const FMT: &str =
464 "Hello, {=u8} {=u16} {=u32} {=u64} {=u128} {=i8} {=i16} {=i32} {=i64} {=i128}!";
465
466 let entries = vec![TableEntry::new_without_symbol(Tag::Info, FMT.to_owned())];
467
468 let table = test_table(entries);
469
470 let bytes = [
471 0, 0, 42, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
477 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
483 0xff, 0xff, ];
485
486 assert_eq!(
487 table.decode(&bytes),
488 Ok((
489 Frame::new(
490 &table,
491 Some(Level::Info),
492 0,
493 None,
494 vec![],
495 FMT,
496 vec![
497 Arg::Uxx(42), Arg::Uxx(u16::MAX.into()), Arg::Uxx(u32::MAX.into()), Arg::Uxx(u64::MAX.into()), Arg::Uxx(u128::MAX), Arg::Ixx(-1), Arg::Ixx(-1), Arg::Ixx(-1), Arg::Ixx(-1), Arg::Ixx(-1), ],
508 ),
509 bytes.len(),
510 ))
511 );
512 }
513
514 #[test]
515 fn indices() {
516 let entries = vec![
517 TableEntry::new_without_symbol(Tag::Info, "The answer is {0=u8} {0=u8}!".to_owned()),
518 TableEntry::new_without_symbol(
519 Tag::Info,
520 "The answer is {1=u16} {0=u8} {1=u16}!".to_owned(),
521 ),
522 ];
523
524 let table = test_table(entries);
525 let bytes = [
526 0, 0, 42, ];
529
530 assert_eq!(
531 table.decode(&bytes),
532 Ok((
533 Frame::new(
534 &table,
535 Some(Level::Info),
536 0,
537 None,
538 vec![],
539 "The answer is {0=u8} {0=u8}!",
540 vec![Arg::Uxx(42)],
541 ),
542 bytes.len(),
543 ))
544 );
545
546 let bytes = [
547 1, 0, 42, 0xff, 0xff, ];
551
552 assert_eq!(
553 table.decode(&bytes),
554 Ok((
555 Frame::new(
556 &table,
557 Some(Level::Info),
558 1,
559 None,
560 vec![],
561 "The answer is {1=u16} {0=u8} {1=u16}!",
562 vec![Arg::Uxx(42), Arg::Uxx(0xffff)],
563 ),
564 bytes.len(),
565 ))
566 );
567 }
568
569 #[test]
570 fn format() {
571 let entries = vec![
572 TableEntry::new_without_symbol(Tag::Info, "x={=?}".to_owned()),
573 TableEntry::new_without_symbol(Tag::Derived, "Foo {{ x: {=u8} }}".to_owned()),
574 ];
575
576 let table = test_table(entries);
577
578 let bytes = [
579 0, 0, 1, 0, 42, ];
583
584 assert_eq!(
585 table.decode(&bytes),
586 Ok((
587 Frame::new(
588 &table,
589 Some(Level::Info),
590 0,
591 None,
592 vec![],
593 "x={=?}",
594 vec![Arg::Format {
595 format: "Foo {{ x: {=u8} }}",
596 args: vec![Arg::Uxx(42)]
597 }],
598 ),
599 bytes.len(),
600 ))
601 );
602 }
603
604 #[test]
605 fn format_sequence() {
606 let entries = vec![
607 TableEntry::new_without_symbol(Tag::Info, "{=__internal_FormatSequence}".to_owned()),
608 TableEntry::new_without_symbol(Tag::Derived, "Foo".to_owned()),
609 TableEntry::new_without_symbol(Tag::Derived, "Bar({=u8})".to_owned()),
610 TableEntry::new_without_symbol(Tag::Derived, "State {=u8}|".to_owned()),
611 ];
612
613 let table = test_table(entries);
614
615 let bytes = [
616 0, 0, 1, 0, 2, 0, 42, 3, 0, 23, 0, 0, ];
624
625 assert_eq!(
626 table.decode(&bytes),
627 Ok((
628 Frame::new(
629 &table,
630 Some(Level::Info),
631 0,
632 None,
633 vec![],
634 "{=__internal_FormatSequence}",
635 vec![Arg::FormatSequence {
636 args: vec![
637 Arg::Format {
638 format: "Foo",
639 args: vec![]
640 },
641 Arg::Format {
642 format: "Bar({=u8})",
643 args: vec![Arg::Uxx(42)]
644 },
645 Arg::Format {
646 format: "State {=u8}|",
647 args: vec![Arg::Uxx(23)]
648 }
649 ]
650 }],
651 ),
652 bytes.len(),
653 ))
654 );
655 }
656
657 #[test]
658 fn display() {
659 let entries = vec![
660 TableEntry::new_without_symbol(Tag::Info, "x={=?}".to_owned()),
661 TableEntry::new_without_symbol(Tag::Derived, "Foo {{ x: {=u8} }}".to_owned()),
662 ];
663
664 let table = test_table_with_timestamp(entries, "{=u8:us}");
665
666 let bytes = [
667 0, 0, 2, 1, 0, 42, ];
672
673 let frame = table.decode(&bytes).unwrap().0;
674 assert_eq!(
675 frame.display(false).to_string(),
676 "0.000002 INFO x=Foo { x: 42 }"
677 );
678 }
679
680 #[test]
681 fn display_message() {
682 let entries = vec![
683 TableEntry::new_without_symbol(Tag::Info, "x={=?}".to_owned()),
684 TableEntry::new_without_symbol(Tag::Derived, "Foo {{ x: {=u8} }}".to_owned()),
685 ];
686
687 let table = test_table(entries);
688
689 let bytes = [
690 0, 0, 1, 0, 42, ];
694
695 let frame = table.decode(&bytes).unwrap().0;
696 assert_eq!(frame.display_message().to_string(), "x=Foo { x: 42 }");
697 }
698
699 #[test]
700 fn display_fragments() {
701 let entries = vec![
702 TableEntry::new_without_symbol(Tag::Info, "x={=?}".to_owned()),
703 TableEntry::new_without_symbol(Tag::Derived, "Foo {{ x: {=u8} }}".to_owned()),
704 ];
705
706 let table = test_table(entries);
707
708 let bytes = [
709 0, 0, 1, 0, 42, ];
713
714 let frame = table.decode(&bytes).unwrap().0;
715 assert_eq!(
716 frame.display_fragments().collect::<Vec<String>>(),
717 ["x=", "Foo { x: 42 }"],
718 );
719 }
720
721 #[test]
722 fn display_i16_with_hex_hint() {
723 let bytes = [
725 0,
726 0, 2, 0b1111_1111, 0b1111_1111,
730 ];
731
732 decode_and_expect(
733 "i16 as hex {=i16:#x}",
734 &bytes,
735 "0.000002 INFO i16 as hex 0xffff",
736 );
737 }
738
739 #[test]
740 fn display_use_inner_type_hint() {
741 let entries = vec![
742 TableEntry::new_without_symbol(Tag::Info, "x={:b}".to_owned()),
743 TableEntry::new_without_symbol(Tag::Derived, "S {{ x: {=u8:x} }}".to_owned()),
744 ];
745
746 let table = test_table_with_timestamp(entries, "{=u8:us}");
747
748 let bytes = [
749 0, 0, 2, 1, 0, 42, ];
754
755 let frame = table.decode(&bytes).unwrap().0;
756 assert_eq!(
757 frame.display(false).to_string(),
758 "0.000002 INFO x=S { x: 2a }",
759 );
760 assert_eq!(
761 frame.display_fragments().collect::<Vec<String>>(),
762 ["x=", "S { x: 2a }"],
763 );
764 }
765
766 #[test]
767 fn display_use_outer_type_hint() {
768 let entries = vec![
769 TableEntry::new_without_symbol(Tag::Info, "x={:b}".to_owned()),
770 TableEntry::new_without_symbol(Tag::Derived, "S {{ x: {=u8:?} }}".to_owned()),
771 ];
772
773 let table = test_table_with_timestamp(entries, "{=u8:us}");
774
775 let bytes = [
776 0, 0, 2, 1, 0, 42, ];
781
782 let frame = table.decode(&bytes).unwrap().0;
783 assert_eq!(
784 frame.display(false).to_string(),
785 "0.000002 INFO x=S { x: 101010 }",
786 );
787 assert_eq!(
788 frame.display_fragments().collect::<Vec<String>>(),
789 ["x=", "S { x: 101010 }"],
790 );
791 }
792
793 #[test]
794 fn display_inner_str_in_struct() {
795 let entries = vec![
796 TableEntry::new_without_symbol(Tag::Info, "{}".to_owned()),
797 TableEntry::new_without_symbol(Tag::Derived, "S {{ x: {=str:?} }}".to_owned()),
798 ];
799
800 let table = test_table_with_timestamp(entries, "{=u8:us}");
801
802 let bytes = [
803 0, 0, 2, 1, 0, 5, 0, 0, 0, b'H', b'e', b'l', b'l', b'o', ];
809 let frame = table.decode(&bytes).unwrap().0;
810 assert_eq!(
811 frame.display(false).to_string(),
812 "0.000002 INFO S { x: \"Hello\" }",
813 );
814 assert_eq!(
815 frame.display_fragments().collect::<Vec<String>>(),
816 ["S { x: \"Hello\" }"],
817 );
818 }
819
820 #[test]
821 fn display_u8_vec() {
822 let entries = vec![
823 TableEntry::new_without_symbol(Tag::Prim, "{=u8}".to_owned()),
824 TableEntry::new_without_symbol(Tag::Prim, "{=[?]}".to_owned()),
825 TableEntry::new_without_symbol(Tag::Derived, "Data {{ name: {=?:?} }}".to_owned()),
826 TableEntry::new_without_symbol(Tag::Info, "{=[?]:a}".to_owned()),
827 ];
828
829 let table = test_table_with_timestamp(entries, "{=u8:us}");
830
831 let bytes = [
832 3, 0, 2, 1, 0, 0, 0, 2, 0, 1, 0, 2, 0, 0, 0, 0, 0, 72, 105, ];
842 let frame = table.decode(&bytes).unwrap().0;
843 assert_eq!(
844 frame.display(false).to_string(),
845 "0.000002 INFO [Data { name: b\"Hi\" }]",
846 );
847 assert_eq!(
848 frame.display_fragments().collect::<Vec<String>>(),
849 ["[Data { name: b\"Hi\" }]"],
850 );
851 }
852
853 #[test]
854 fn display_iso8601_timestamp() {
855 let bytes = [
856 0, 0, 2, 36, 188, 151, 238, 120, 1, 0, 0, ];
860
861 decode_and_expect(
862 "{=u64:iso8601ms}",
863 &bytes,
864 "0.000002 INFO 2021-04-20T09:23:44.804Z",
865 );
866 }
867
868 #[test]
869 fn bools_simple() {
870 let bytes = [
871 0, 0, 2, true as u8, ];
875
876 decode_and_expect("my bool={=bool}", &bytes, "0.000002 INFO my bool=true");
877 }
878
879 #[test]
880 fn bitfields() {
881 let bytes = [
882 0,
883 0, 2, 0b1110_0101, ];
887 decode_and_expect(
888 "x: {0=0..4:b}, y: {0=3..8:#b}",
889 &bytes,
890 "0.000002 INFO x: 101, y: 0b11100",
891 );
892 }
893
894 #[test]
895 fn bitfields_reverse_order() {
896 let bytes = [
897 0,
898 0, 2, 0b1101_0010, ];
902 decode_and_expect(
903 "x: {0=0..7:b}, y: {0=3..5:b}",
904 &bytes,
905 "0.000002 INFO x: 1010010, y: 10",
906 );
907 }
908
909 #[test]
910 fn bitfields_different_indices() {
911 let bytes = [
912 0,
913 0, 2, 0b1111_0000, 0b1110_0101, ];
918 decode_and_expect(
919 "#0: {0=0..5:b}, #1: {1=3..8:b}",
920 &bytes,
921 "0.000002 INFO #0: 10000, #1: 11100",
922 );
923 }
924
925 #[test]
926 fn bitfields_u16() {
927 let bytes = [
928 0,
929 0, 2, 0b1111_0000,
932 0b1110_0101, ];
934 decode_and_expect("x: {0=7..12:b}", &bytes, "0.000002 INFO x: 1011");
935 }
936
937 #[test]
938 fn bitfields_mixed_types() {
939 let bytes = [
940 0,
941 0, 2, 0b1111_0000,
944 0b1110_0101, 0b1111_0001, ];
947 decode_and_expect(
948 "#0: {0=7..12:b}, #1: {1=0..5:b}",
949 &bytes,
950 "0.000002 INFO #0: 1011, #1: 10001",
951 );
952 }
953
954 #[test]
955 fn bitfields_mixed() {
956 let bytes = [
957 0,
958 0, 2, 0b1111_0000,
961 0b1110_0101, 42, 0b1111_0001, ];
965 decode_and_expect(
966 "#0: {0=7..12:b}, #1: {1=u8}, #2: {2=0..5:b}",
967 &bytes,
968 "0.000002 INFO #0: 1011, #1: 42, #2: 10001",
969 );
970 }
971
972 #[test]
973 fn bitfields_across_boundaries() {
974 let bytes = [
975 0,
976 0, 2, 0b1101_0010,
979 0b0110_0011, ];
981 decode_and_expect(
982 "bitfields {0=0..7:b} {0=9..14:b}",
983 &bytes,
984 "0.000002 INFO bitfields 1010010 10001",
985 );
986 }
987
988 #[test]
989 fn bitfields_across_boundaries_diff_indices() {
990 let bytes = [
991 0,
992 0, 2, 0b1101_0010,
995 0b0110_0011, 0b1111_1111, ];
998 decode_and_expect(
999 "bitfields {0=0..7:b} {0=9..14:b} {1=8..10:b}",
1000 &bytes,
1001 "0.000002 INFO bitfields 1010010 10001 11",
1002 );
1003 }
1004
1005 #[test]
1006 fn bitfields_truncated_front() {
1007 let bytes = [
1008 0,
1009 0, 2, 0b0110_0011, ];
1013 decode_and_expect(
1014 "bitfields {0=9..14:b}",
1015 &bytes,
1016 "0.000002 INFO bitfields 10001",
1017 );
1018 }
1019
1020 #[test]
1021 fn bitfields_non_truncated_u32() {
1022 let bytes = [
1023 0,
1024 0, 2, 0b0110_0011, 0b0000_1111, 0b0101_1010, 0b1100_0011, ];
1031 decode_and_expect(
1032 "bitfields {0=0..2:b} {0=28..31:b}",
1033 &bytes,
1034 "0.000002 INFO bitfields 11 100",
1035 );
1036 }
1037
1038 #[test]
1039 fn bitfields_u128() {
1040 let bytes = [
1041 0,
1042 0, 2, 0b1110_0101, 0b1110_0101, 0b0000_0000, 0b0000_0000, 0b0000_0000, 0b0000_0000, 0b0000_0000, 0b0000_0000, 0b0000_0000, 0b0000_0000, 0b0000_0000, 0b0000_0000, 0b0000_0000, 0b0000_0000, 0b0000_0000, 0b0000_0000, ];
1061 decode_and_expect("x: {0=119..124:b}", &bytes, "0.000002 INFO x: 1011");
1062 }
1063
1064 #[test]
1065 fn slice() {
1066 let bytes = [
1067 0, 0, 2, 2, 0, 0, 0, 23, 42, ];
1072 decode_and_expect("x={=[u8]}", &bytes, "0.000002 INFO x=[23, 42]");
1073 }
1074
1075 #[test]
1076 fn slice_with_trailing_args() {
1077 let bytes = [
1078 0, 0, 2, 2, 0, 0, 0, 23, 42, 1, ];
1084
1085 decode_and_expect(
1086 "x={=[u8]} trailing arg={=u8}",
1087 &bytes,
1088 "0.000002 INFO x=[23, 42] trailing arg=1",
1089 );
1090 }
1091
1092 #[test]
1093 fn string_hello_world() {
1094 let bytes = [
1095 0, 0, 2, 5, 0, 0, 0, b'W', b'o', b'r', b'l', b'd',
1099 ];
1100
1101 decode_and_expect("Hello {=str}", &bytes, "0.000002 INFO Hello World");
1102 }
1103
1104 #[test]
1105 fn string_with_trailing_data() {
1106 let bytes = [
1107 0, 0, 2, 5, 0, 0, 0, b'W', b'o', b'r', b'l', b'd', 125, ];
1112
1113 decode_and_expect(
1114 "Hello {=str} {=u8}",
1115 &bytes,
1116 "0.000002 INFO Hello World 125",
1117 );
1118 }
1119
1120 #[test]
1121 fn char_data() {
1122 let bytes = [
1123 0, 0, 2, 0x61, 0x00, 0x00, 0x00, 0x9C, 0xF4, 0x01, 0x00, ];
1128
1129 decode_and_expect(
1130 "Supports ASCII {=char} and Unicode {=char}",
1131 &bytes,
1132 "0.000002 INFO Supports ASCII a and Unicode 💜",
1133 );
1134 }
1135
1136 #[test]
1137 fn option() {
1138 let mut entries = BTreeMap::new();
1139 entries.insert(
1140 4,
1141 TableEntry::new_without_symbol(Tag::Info, "x={=?}".to_owned()),
1142 );
1143 entries.insert(
1144 3,
1145 TableEntry::new_without_symbol(Tag::Derived, "None|Some({=?})".to_owned()),
1146 );
1147 entries.insert(
1148 2,
1149 TableEntry::new_without_symbol(Tag::Derived, "{=u8}".to_owned()),
1150 );
1151
1152 let table = Table {
1153 entries,
1154 timestamp: Some(TableEntry::new_without_symbol(
1155 Tag::Timestamp,
1156 "{=u8:us}".to_owned(),
1157 )),
1158 bitflags: Default::default(),
1159 encoding: Encoding::Raw,
1160 };
1161
1162 let bytes = [
1163 4, 0, 0, 3, 0, 1, 2, 0, 42, ];
1170
1171 let frame = table.decode(&bytes).unwrap().0;
1172 assert_eq!(frame.display(false).to_string(), "0.000000 INFO x=Some(42)");
1173 assert_eq!(frame.display_message().to_string(), "x=Some(42)");
1174 assert_eq!(
1175 frame.display_fragments().collect::<Vec<String>>(),
1176 ["x=", "Some(42)"],
1177 );
1178
1179 let bytes = [
1180 4, 0, 1, 3, 0, 0, ];
1185
1186 let frame = table.decode(&bytes).unwrap().0;
1187 assert_eq!(frame.display(false).to_string(), "0.000001 INFO x=None");
1188 assert_eq!(frame.display_message().to_string(), "x=None");
1189 assert_eq!(
1190 frame.display_fragments().collect::<Vec<String>>(),
1191 ["x=", "None"],
1192 );
1193 }
1194}