1use thiserror::Error;
5use zerocopy::IntoBytes;
6
7#[derive(Clone)]
8pub struct FxtBuilder<H> {
9 header: H,
10 buf: Vec<u64>,
11}
12
13#[derive(Error, Debug)]
14pub enum SerializeError {
15 #[error("Encountered Empty StringRefs when serializing argument's name")]
16 MissingArgName,
17}
18
19impl<H: crate::header::TraceHeader> FxtBuilder<H> {
20 pub fn new(mut header: H) -> Self {
23 let buf = vec![0];
25
26 header.set_size_words(1);
28
29 Self { header, buf }
30 }
31
32 pub fn atom(mut self, atom: impl AsRef<[u8]>) -> Self {
33 let atom = atom.as_ref();
34 let start = self.buf.len() * 8;
35 let size_words = self.buf.len() + atom.len().div_ceil(8);
36 assert!(
37 size_words <= crate::MAX_RECORD_SIZE_WORDS,
38 "maximum record size is {} words",
39 crate::MAX_RECORD_SIZE_WORDS
40 );
41 self.buf.resize(size_words, 0);
42 self.buf.as_mut_slice().as_mut_bytes()[start..start + atom.len()].copy_from_slice(atom);
43 self.header.set_size_words(
44 size_words.try_into().expect("trace records size in words must fit in a u16"),
45 );
46 self
47 }
48
49 pub fn atom_words(self, words: impl AsRef<[u64]>) -> Self {
51 self.atom(words.as_ref().as_bytes())
52 }
53
54 pub fn build(mut self) -> Vec<u64> {
56 self.buf[0] = u64::from_ne_bytes(self.header.to_le_bytes());
57 self.buf
58 }
59}
60
61impl<H: std::fmt::Debug> std::fmt::Debug for FxtBuilder<H> {
62 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63 let chunks = self.buf.iter().skip(1).collect::<Vec<_>>();
65 f.debug_struct("FxtBuilder").field("header", &self.header).field("buf", &chunks).finish()
66 }
67}
68
69#[cfg(test)]
70mod tests {
71 use super::*;
72
73 #[derive(Clone, Copy)]
74 struct TestHeader(u64);
75
76 impl crate::header::TraceHeader for TestHeader {
77 fn set_size_words(&mut self, n: u16) {
78 self.0 = n.into();
79 }
80
81 fn to_le_bytes(&self) -> [u8; 8] {
82 self.0.to_le_bytes()
83 }
84 }
85
86 #[test]
87 fn builder_preserves_record_bytes_and_allocation() {
88 let first_atom = [1, 2, 3];
89 let second_atom = [4, 5, 6, 7, 8, 9, 10, 11, 12];
90 let builder = FxtBuilder::new(TestHeader(0)).atom(first_atom).atom(second_atom);
91 let backing = builder.buf.as_ptr();
92 let words: Vec<u64> = builder.build();
93 assert_eq!(words.as_ptr(), backing);
95 assert_eq!((words.as_ptr() as usize) % std::mem::align_of::<u64>(), 0);
96 let bytes = words.as_slice().as_bytes();
97 let expected = [
98 4, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 0, 0, 0, 0, 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 0, 0, 0, 0, 0, ];
102 assert_eq!(bytes, expected);
103 }
104
105 #[test]
106 fn serialized_words_can_be_parsed_as_session_bytes() {
107 let magic = [0x10, 0x00, 0x04, 0x46, 0x78, 0x54, 0x16, 0x00];
108 let mut header = crate::event::EventHeader::empty();
109 header.set_thread_ref(1);
110 let event = FxtBuilder::new(header).atom(42u64.to_le_bytes()).build();
111 let mut session = magic.to_vec();
112 session.extend_from_slice(event.as_bytes());
113
114 let mut parser = crate::SessionParser::new(std::io::Cursor::new(session));
115 assert!(matches!(parser.next(), Some(Ok(crate::TraceRecord::Event(_)))));
116 }
117}