1#[cfg(feature = "trace-bitstream")]
52use std::cell::RefCell;
53#[cfg(feature = "trace-bitstream")]
54use std::fs::File;
55#[cfg(feature = "trace-bitstream")]
56use std::io::Write;
57
58#[cfg(feature = "trace-bitstream")]
60thread_local! {
61 static TRACE_OUTPUT: RefCell<Option<File>> = const { RefCell::new(None) };
62 static SECTION_STACK: RefCell<Vec<(&'static str, usize)>> = const { RefCell::new(Vec::new()) };
63}
64
65#[cfg(feature = "trace-bitstream")]
69pub fn init_trace(path: &str) -> std::io::Result<()> {
70 let file = File::create(path)?;
71 TRACE_OUTPUT.with(|output| {
72 *output.borrow_mut() = Some(file);
73 });
74 Ok(())
75}
76
77#[cfg(feature = "trace-bitstream")]
79pub fn init_trace_stderr() {
80 }
82
83#[cfg(feature = "trace-bitstream")]
85pub fn finish_trace() {
86 TRACE_OUTPUT.with(|output| {
87 if let Some(mut f) = output.borrow_mut().take() {
88 let _ = f.flush();
89 }
90 });
91}
92
93#[cfg(feature = "trace-bitstream")]
95pub fn trace_line(line: &str) {
96 TRACE_OUTPUT.with(|output| {
97 if let Some(ref mut f) = *output.borrow_mut() {
98 let _ = writeln!(f, "{}", line);
99 } else {
100 eprintln!("{}", line);
101 }
102 });
103}
104
105#[cfg(feature = "trace-bitstream")]
107pub fn push_section(name: &'static str, bit_pos: usize) {
108 SECTION_STACK.with(|stack| {
109 stack.borrow_mut().push((name, bit_pos));
110 });
111 trace_line(&format!("[{}] >>> BEGIN {}", bit_pos, name));
112}
113
114#[cfg(feature = "trace-bitstream")]
116pub fn pop_section(name: &'static str, bit_pos: usize) {
117 SECTION_STACK.with(|stack| {
118 if let Some((popped, start_pos)) = stack.borrow_mut().pop() {
119 if popped != name {
120 trace_line(&format!(
121 "[{}] !!! SECTION MISMATCH: expected {}, got {}",
122 bit_pos, name, popped
123 ));
124 }
125 trace_line(&format!(
126 "[{}] <<< END {} ({} bits)",
127 bit_pos,
128 name,
129 bit_pos - start_pos
130 ));
131 }
132 });
133}
134
135#[cfg(feature = "trace-bitstream")]
137pub fn section_prefix() -> String {
138 SECTION_STACK.with(|stack| {
139 stack
140 .borrow()
141 .iter()
142 .map(|(name, _)| *name)
143 .collect::<Vec<_>>()
144 .join(".")
145 })
146}
147
148#[cfg(feature = "trace-bitstream")]
150pub fn format_bits(value: u64, n_bits: usize) -> String {
151 if n_bits == 0 {
152 return "0b(empty)".to_string();
153 }
154 let mut s = String::with_capacity(n_bits + 2);
155 s.push_str("0b");
156 for i in (0..n_bits).rev() {
157 if (value >> i) & 1 == 1 {
158 s.push('1');
159 } else {
160 s.push('0');
161 }
162 }
163 s
164}
165
166#[cfg(feature = "trace-bitstream")]
168#[inline]
169pub fn trace_write_impl(
170 bit_pos_before: usize,
171 n_bits: usize,
172 value: u64,
173 field: &str,
174 description: Option<&str>,
175) {
176 let bits_str = format_bits(value, n_bits);
177 let desc = match description {
178 Some(d) => format!(" // {}", d),
179 None => String::new(),
180 };
181
182 let prefix = section_prefix();
183 let full_field = if prefix.is_empty() {
184 field.to_string()
185 } else {
186 format!("{}.{}", prefix, field)
187 };
188
189 trace_line(&format!(
190 "[{:6}] {}: {} ({} bits) = {}{}",
191 bit_pos_before, full_field, value, n_bits, bits_str, desc
192 ));
193}
194
195#[macro_export]
213#[cfg(feature = "trace-bitstream")]
214macro_rules! trace_write {
215 ($writer:expr, $n_bits:expr, $value:expr, $field:expr) => {{
216 let pos = $writer.bits_written();
217 let result = $writer.write($n_bits, $value as u64);
218 $crate::trace::trace_write_impl(pos, $n_bits, $value as u64, $field, None);
219 result
220 }};
221 ($writer:expr, $n_bits:expr, $value:expr, $field:expr, $desc:expr) => {{
222 let pos = $writer.bits_written();
223 let result = $writer.write($n_bits, $value as u64);
224 $crate::trace::trace_write_impl(pos, $n_bits, $value as u64, $field, Some($desc));
225 result
226 }};
227}
228
229#[macro_export]
230#[cfg(not(feature = "trace-bitstream"))]
231macro_rules! trace_write {
232 ($writer:expr, $n_bits:expr, $value:expr, $field:expr) => {
233 $writer.write($n_bits, $value as u64)
234 };
235 ($writer:expr, $n_bits:expr, $value:expr, $field:expr, $desc:expr) => {
236 $writer.write($n_bits, $value as u64)
237 };
238}
239
240#[macro_export]
250#[cfg(feature = "trace-bitstream")]
251macro_rules! trace_section {
252 (begin $name:expr, $writer:expr) => {
253 $crate::trace::push_section($name, $writer.bits_written())
254 };
255 (end $name:expr, $writer:expr) => {
256 $crate::trace::pop_section($name, $writer.bits_written())
257 };
258}
259
260#[macro_export]
261#[cfg(not(feature = "trace-bitstream"))]
262macro_rules! trace_section {
263 (begin $name:expr, $writer:expr) => {};
264 (end $name:expr, $writer:expr) => {};
265}
266
267#[macro_export]
271#[cfg(feature = "trace-bitstream")]
272macro_rules! trace_note {
273 ($writer:expr, $($arg:tt)*) => {
274 $crate::trace::trace_line(&format!("[{:6}] NOTE: {}", $writer.bits_written(), format!($($arg)*)))
275 };
276}
277
278#[macro_export]
279#[cfg(not(feature = "trace-bitstream"))]
280macro_rules! trace_note {
281 ($writer:expr, $($arg:tt)*) => {};
282}
283
284#[macro_export]
286#[cfg(feature = "trace-bitstream")]
287macro_rules! trace_bytes {
288 ($writer:expr, $bytes:expr, $field:expr) => {{
289 let pos = $writer.bits_written();
290 let data: &[u8] = $bytes;
291 let result = $writer.append_bytes(data);
292 $crate::trace::trace_line(&format!(
293 "[{:6}] {}: [{} bytes] {:02x?}",
294 pos,
295 $field,
296 data.len(),
297 &data[..data.len().min(32)]
298 ));
299 result
300 }};
301}
302
303#[macro_export]
304#[cfg(not(feature = "trace-bitstream"))]
305macro_rules! trace_bytes {
306 ($writer:expr, $bytes:expr, $field:expr) => {
307 $writer.append_bytes($bytes)
308 };
309}
310
311#[macro_export]
314#[cfg(feature = "trace-bitstream")]
315macro_rules! debug_eprintln {
316 ($($arg:tt)*) => {
317 eprintln!($($arg)*)
318 };
319}
320
321#[macro_export]
322#[cfg(not(feature = "trace-bitstream"))]
323macro_rules! debug_eprintln {
324 ($($arg:tt)*) => {};
325}
326
327pub use debug_eprintln;
329pub use trace_bytes;
330pub use trace_note;
331pub use trace_section;
332pub use trace_write;
333
334#[cfg(not(feature = "trace-bitstream"))]
339pub fn init_trace(_path: &str) -> std::io::Result<()> {
340 Ok(())
341}
342
343#[cfg(not(feature = "trace-bitstream"))]
344pub fn init_trace_stderr() {}
345
346#[cfg(not(feature = "trace-bitstream"))]
347pub fn finish_trace() {}
348
349#[cfg(test)]
350mod tests {
351 #[cfg(feature = "trace-bitstream")]
352 use super::*;
353
354 #[test]
355 fn test_format_bits() {
356 #[cfg(feature = "trace-bitstream")]
357 {
358 assert_eq!(format_bits(0b1010, 4), "0b1010");
359 assert_eq!(format_bits(0b1, 1), "0b1");
360 assert_eq!(format_bits(0b0, 1), "0b0");
361 assert_eq!(format_bits(0xff0a, 16), "0b1111111100001010");
362 }
363 }
364}