1use std::os::raw::c_char;
7
8const LOG_LIMIT: usize = 1024;
11
12struct StaticCString<const N: usize> {
13 buf: [std::mem::MaybeUninit<u8>; N],
14 len: usize,
15}
16
17impl<const N: usize> StaticCString<N> {
18 fn new() -> Self {
19 StaticCString {
20 buf: unsafe { std::mem::MaybeUninit::uninit().assume_init() },
21 len: 0,
22 }
23 }
24
25 fn as_cstr(&self) -> &std::ffi::CStr {
26 unsafe {
27 std::ffi::CStr::from_bytes_with_nul_unchecked(std::slice::from_raw_parts(
28 self.buf.as_ptr().cast::<u8>(),
29 self.len,
30 ))
31 }
32 }
33}
34
35impl<const N: usize> std::fmt::Write for StaticCString<N> {
36 fn write_str(&mut self, s: &str) -> std::fmt::Result {
37 use std::convert::TryInto;
38 let s = s.as_bytes();
39 let end = s.len().min(N.checked_sub(1).unwrap() - self.len);
40 debug_assert_eq!(s.len(), end, "message truncated");
41 unsafe {
42 std::ptr::copy_nonoverlapping(
43 s[..end].as_ptr(),
44 self.buf
45 .as_mut_ptr()
46 .cast::<u8>()
47 .offset(self.len.try_into().unwrap()),
48 end,
49 )
50 };
51 self.len += end;
52 self.buf[self.len].write(0);
53 Ok(())
54 }
55}
56
57pub fn cubeb_log_internal_buf_fmt(
60 log_callback: unsafe extern "C" fn(*const c_char, ...),
61 file: &str,
62 line: u32,
63 msg: std::fmt::Arguments,
64) {
65 let filename = std::path::Path::new(file)
66 .file_name()
67 .unwrap()
68 .to_str()
69 .unwrap();
70 let mut buf = StaticCString::<LOG_LIMIT>::new();
71 let _ = std::fmt::write(&mut buf, format_args!("{}:{}: {}\n", filename, line, msg));
72 unsafe {
73 log_callback(buf.as_cstr().as_ptr());
74 };
75}
76
77#[macro_export]
78macro_rules! cubeb_log_internal {
79 ($log_callback: expr, $level: expr, $fmt: expr, $($arg: expr),+) => {
80 #[allow(unused_unsafe)]
81 unsafe {
82 if $level <= $crate::ffi::cubeb_log_get_level().into() {
83 if let Some(log_callback) = $log_callback {
84 $crate::log::cubeb_log_internal_buf_fmt(log_callback, file!(), line!(), format_args!($fmt, $($arg),+));
85 }
86 }
87 }
88 };
89 ($log_callback: expr, $level: expr, $msg: expr) => {
90 cubeb_log_internal!($log_callback, $level, "{}", $msg);
91 };
92}
93
94#[macro_export]
95macro_rules! cubeb_log {
96 ($($arg: expr),+) => (cubeb_log_internal!($crate::ffi::cubeb_log_get_callback(), $crate::LogLevel::Normal, $($arg),+));
97}
98
99#[macro_export]
100macro_rules! cubeb_logv {
101 ($($arg: expr),+) => (cubeb_log_internal!($crate::ffi::cubeb_log_get_callback(), $crate::LogLevel::Verbose, $($arg),+));
102}
103
104#[macro_export]
105macro_rules! cubeb_alog {
106 ($($arg: expr),+) => (cubeb_log_internal!($crate::ffi::cubeb_async_log.into(), $crate::LogLevel::Normal, $($arg),+));
107}
108
109#[macro_export]
110macro_rules! cubeb_alogv {
111 ($($arg: expr),+) => (cubeb_log_internal!($crate::ffi::cubeb_async_log.into(), $crate::LogLevel::Verbose, $($arg),+));
112}
113
114#[cfg(test)]
115mod tests {
116 #[test]
117 fn test_normal_logging_sync() {
118 cubeb_log!("This is synchronous log output at normal level");
119 cubeb_log!("{} Formatted log", 1);
120 cubeb_log!("{} Formatted {} log {}", 1, 2, 3);
121 }
122
123 #[test]
124 fn test_verbose_logging_sync() {
125 cubeb_logv!("This is synchronous log output at verbose level");
126 cubeb_logv!("{} Formatted log", 1);
127 cubeb_logv!("{} Formatted {} log {}", 1, 2, 3);
128 }
129
130 #[test]
131 fn test_normal_logging_async() {
132 cubeb_alog!("This is asynchronous log output at normal level");
133 cubeb_alog!("{} Formatted log", 1);
134 cubeb_alog!("{} Formatted {} log {}", 1, 2, 3);
135 }
136
137 #[test]
138 fn test_verbose_logging_async() {
139 cubeb_alogv!("This is asynchronous log output at verbose level");
140 cubeb_alogv!("{} Formatted log", 1);
141 cubeb_alogv!("{} Formatted {} log {}", 1, 2, 3);
142 }
143}