1 // Copyright 2023 The Pigweed Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not 4 // use this file except in compliance with the License. You may obtain a copy of 5 // the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 // License for the specific language governing permissions and limitations under 13 // the License. 14 15 //! # Infrastructure for building calls to `printf`. 16 //! 17 //! The `varargs` modules solves a particularly tricky problem: Some arguments 18 //! passed to `pw_log` result in a single argument passed to printf (a `u32` 19 //! is passed directly) while some may result in multiple arguments (a `&str` 20 //! is passed as a length argument and a data argument). Since function like 21 //! proc macros don't know the types of the arguments we rely on chained generic 22 //! types. 23 //! 24 //! ## VarArgs trait 25 //! The [`VarArgs`] both encapsulates the structure of the arguments as well 26 //! as provides a method for calling `printf`. It accomplishes this through a 27 //! recursive, chained/wrapped type through it's `OneMore<T>` associated type. 28 //! We then provide generic implementations for [`VarArgs`] for tuples 29 //! ov values that implement [`Clone`] such that: 30 //! 31 //! ``` 32 //! # use pw_log_backend_printf::varargs::VarArgs; 33 //! # use core::any::TypeId; 34 //! # use core::ffi::{c_int, c_uchar}; 35 //! type VarArgsType = 36 //! <<<<() as VarArgs> 37 //! ::OneMore<u32> as VarArgs> 38 //! ::OneMore<i32> as VarArgs> 39 //! ::OneMore<c_int> as VarArgs> 40 //! ::OneMore<*const c_uchar>; 41 //! type TupleType = (u32, i32, c_int, *const c_uchar); 42 //! assert_eq!(TypeId::of::<VarArgsType>(), TypeId::of::<TupleType>()); 43 //! ``` 44 //! 45 //! [`VarArgs`] provides an `append()` method that allows building up these 46 //! typed tuple values: 47 //! 48 //! ``` 49 //! # use pw_log_backend_printf::varargs::VarArgs; 50 //! # use core::ffi::{c_int, c_uchar}; 51 //! let string = "test"; 52 //! let args = () 53 //! .append(0u32) 54 //! .append(-1i32) 55 //! .append(string.len() as c_int) 56 //! .append(string.as_ptr().cast::<*const c_uchar>()); 57 //! assert_eq!(args, ( 58 //! 0u32, 59 //! -1i32, 60 //! string.len() as c_int, 61 //! string.as_ptr().cast::<*const c_uchar>())); 62 //! ``` 63 //! 64 //! Lastly [`VarArgs`] exposes an unsafe `call_printf()` method that calls 65 //! printf and passes the build up arguments to `printf()`: 66 //! 67 //! ``` 68 //! # use pw_log_backend_printf::varargs::VarArgs; 69 //! # use core::ffi::{c_int, c_uchar}; 70 //! let string = "test"; 71 //! unsafe { 72 //! // This generates a call to: 73 //! // `printf("[%s] %u %d %.*s\0".as_ptr(), "INF".as_ptr(), 0u32, -1i32, 4, string.as_ptr())`. 74 //! () 75 //! .append(0u32) 76 //! .append(-1i32) 77 //! .append(string.len() as c_int) 78 //! .append(string.as_ptr().cast::<*const c_uchar>()) 79 //! // Note that we always pass the null terminated log level string here 80 //! // as an argument to `call_printf()`. 81 //! .call_printf("[%s] %u %d %.*s\0".as_ptr().cast(), "INF\0".as_ptr().cast()); 82 //! } 83 //! ``` 84 //! 85 //! ## Arguments Trait 86 //! The [`Arguments`] trait is the final piece of the puzzle. It is used to 87 //! generate one or more calls to [`VarArgs::append()`] for each argument to 88 //! `pw_log`. Most simple cases that push a single argument look like: 89 //! 90 //! ```ignore 91 //! # // Ignored as a test as the test is neither the crate that defines 92 //! # // `Arguments` nor `i32`. 93 //! impl Arguments<i32> for i32 { 94 //! type PushArg<Head: VarArgs> = Head::OneMore<i32>; 95 //! fn push_arg<Head: VarArgs>(head: Head, arg: &i32) -> Self::PushArg<Head> { 96 //! head.append(*arg) 97 //! } 98 //! } 99 //! ``` 100 //! 101 //! A more complex case like `&str` can push multiple arguments: 102 //! ```ignore 103 //! # // Ignored as a test as the test is neither the crate that defines 104 //! # // `Arguments` nor `&str`. 105 //! impl Arguments<&str> for &str { 106 //! // Arguments are a chain of two `OneMore` types. One for the string length 107 //! // and one for the pointer to the string data. 108 //! type PushArg<Head: VarArgs> = 109 //! <Head::OneMore<c_int> as VarArgs>::OneMore<*const c_uchar>; 110 //! 111 //! // `push_arg` pushes both the length and pointer to the string into the args tuple. 112 //! fn push_arg<Head: VarArgs>(head: Head, arg: &&str) -> Self::PushArg<Head> { 113 //! let arg = *arg; 114 //! head.append(arg.len() as c_int).append(arg.as_ptr().cast::<*const c_uchar>()) 115 //! } 116 //! } 117 //! ``` 118 //! 119 //! ## Putting it all together 120 //! With all of these building blocks, the backend proc macro emits the following 121 //! code: 122 //! 123 //! ``` 124 //! # use pw_log_backend_printf::varargs::{Arguments, VarArgs}; 125 //! // Code emitted for a call to `info!("Hello {}. It is {}:00", "Pigweed" as &str, 2 as u32) 126 //! let args = (); 127 //! let args = <&str as Arguments<&str>>::push_arg(args, &("Pigweed" as &str)); 128 //! let args = <u32 as Arguments<u32>>::push_arg(args, &(2 as u32)); 129 //! unsafe { 130 //! args.call_printf("[%s] Hello %.*s. It is %d:00".as_ptr().cast(), "INF\0".as_ptr().cast()); 131 //! } 132 //! ``` 133 use core::convert::Infallible; 134 use core::ffi::{c_int, c_uchar}; 135 136 /// Implements a list of arguments to a vararg call. 137 /// 138 /// See [module level docs](crate::varargs) for a detailed description on how 139 /// [`Arguments`] works and is used. 140 pub trait Arguments<T: ?Sized> { 141 /// Type produced by calling [`Self::push_arg()`]. 142 type PushArg<Head: VarArgs>: VarArgs; 143 144 /// Push an argument onto the list of varargs. 145 /// 146 /// This may actually push zero, one, or more arguments onto the list 147 /// depending on implementation. push_arg<Head: VarArgs>(head: Head, arg: &T) -> Self::PushArg<Head>148 fn push_arg<Head: VarArgs>(head: Head, arg: &T) -> Self::PushArg<Head>; 149 } 150 151 /// Represents a variable length list of arguments to printf. 152 /// 153 /// See [module level docs](crate::varargs) for a detailed description on how 154 /// how [`VarArgs`] works and is used. 155 pub trait VarArgs: Clone { 156 /// The type that is produced by a call to `append()` 157 type OneMore<T: Clone>: VarArgs; 158 159 /// Used to check if there is space left in the argument list. 160 /// 161 /// If the there is no space left in the argument list an [`Arguments<T>`]'s 162 /// PushArg type will expand to a type where `CHECK` us unable to be 163 /// compiled. 164 const CHECK: () = (); 165 166 /// Append an additional argument to this argument list. append<T: Clone>(self, val: T) -> Self::OneMore<T>167 fn append<T: Clone>(self, val: T) -> Self::OneMore<T>; 168 169 /// Calls `printf` with the arguments in `self` and the given format and log level string. 170 /// 171 /// # Safety 172 /// 173 /// Calls into `libc` printf without any input validation. The code further 174 /// up the stack is responsible for initializing valid [`VarArgs`] that 175 /// will cause printf to execute in a sound manner. call_printf(self, format_str: *const c_uchar, log_level_str: *const c_uchar) -> c_int176 unsafe fn call_printf(self, format_str: *const c_uchar, log_level_str: *const c_uchar) 177 -> c_int; 178 } 179 180 #[derive(Clone)] 181 /// A sentinel type for trying to append too many (>12) arguments to a 182 /// [`VarArgs`] tuple. 183 pub struct TooMany(Infallible); 184 impl TooMany { panic() -> !185 const fn panic() -> ! { 186 panic!("Too many arguments to logging call") 187 } 188 } 189 190 #[doc(hidden)] 191 /// Implementation VarArgs for TooMany. Usages of TooMany::CHECK will cause a 192 /// compile-time error. 193 impl VarArgs for TooMany { 194 type OneMore<T: Clone> = TooMany; 195 const CHECK: () = Self::panic(); 196 append<T: Clone>(self, _: T) -> Self::OneMore<T>197 fn append<T: Clone>(self, _: T) -> Self::OneMore<T> { 198 Self::panic() 199 } 200 call_printf(self, _: *const c_uchar, _: *const c_uchar) -> c_int201 unsafe fn call_printf(self, _: *const c_uchar, _: *const c_uchar) -> c_int { 202 Self::panic() 203 } 204 } 205 206 /// Used to implement [`VarArgs`] on tuples. 207 /// 208 /// This recursive macro divides it's arguments into a set of arguments for use 209 /// in the recursive case in `[]`s and arguments used for the current 210 /// implementation of [`VarArgs`] following the `[]`'s. 211 macro_rules! impl_args_list { 212 // Entry point into the macro that directly invokes `@impl` with its 213 // arguments. 214 // 215 // Take a list of arguments of the form `ident => position`. `ident` 216 // is used to name the generic type argument for the implementation 217 // of `[VarArgs]` (i.e. `impl<ident: Clone, ...> VarArgs for (ident, ...)`). 218 // `position` is used to index into the argument tuple when calling 219 // printf (i.e. `printf(format_str, ..., args.position, ...)`). 220 ($($arg:ident => $arg_ident:tt),* $(,)?) => { 221 impl_args_list!(@impl [$($arg => $arg_ident),*]); 222 }; 223 224 // Recursive case for [`VarArgs`] implementation. 225 // 226 // Implements [`VarArgs`] for a tuple with length equal to the number 227 // of arguments listed after the `[]`. It then recurses with taking 228 // the first argument between the `[]`s and appending it to the list 229 // after the `[]`s. 230 (@impl [$next:ident => $next_num:tt 231 $(, $remaining:ident => $next_remaining:tt)*] 232 $($current:ident => $current_num:tt),*) => { 233 impl<$($current: Clone),*> VarArgs for ($($current,)*) { 234 type OneMore<$next: Clone> = ($($current,)* $next,); 235 fn append<$next>(self, val: $next) -> ($($current,)* $next,) { 236 ($(self. $current_num,)* val,) 237 } 238 239 unsafe fn call_printf( 240 self, 241 format_str: *const c_uchar, 242 log_level_str: *const c_uchar, 243 ) -> c_int { 244 extern "C" { 245 fn printf(fmt: *const c_uchar, ...) -> c_int; 246 } 247 printf(format_str, log_level_str, $(self. $current_num),*) 248 } 249 } 250 251 impl_args_list!(@impl 252 [$($remaining => $next_remaining),*] 253 $($current => $current_num,)* $next => $next_num); 254 }; 255 256 // Base for [`VarArgs`] implementation. 257 // 258 // Implements [`VarArgs`] for the full list of arguments and sets 259 // its `OneMore` type to `TooMany` to cause a compilation error 260 // if code tries to instantiate an argument list longer that this. 261 (@impl [] $($current:ident => $current_num:tt),*) => { 262 impl<$($current: Clone),*> VarArgs for ($($current),*) { 263 type OneMore<T: Clone> = TooMany; 264 fn append<T: Clone>(self, _: T) -> TooMany { 265 panic!("Too many arguments to logging call") 266 } 267 268 unsafe fn call_printf( 269 self, 270 format_str: *const c_uchar, 271 log_level_str: *const c_uchar, 272 ) -> c_int { 273 extern "C" { 274 fn printf(fmt: *const c_uchar, ...) -> c_int; 275 } 276 printf(format_str, log_level_str, $(self. $current_num),*) 277 } 278 } 279 }; 280 } 281 282 // Expands to implementations of [`VarArgs`] for tuples of length 0-12. 283 impl_args_list!( 284 ARGS0 => 0, 285 ARGS1 => 1, 286 ARGS2 => 2, 287 ARGS3 => 3, 288 ARGS4 => 4, 289 ARGS5 => 5, 290 ARGS6 => 6, 291 ARGS7 => 7, 292 ARGS8 => 8, 293 ARGS9 => 9, 294 ARGS10 => 10, 295 ARGS11 => 11, 296 ARGS12 => 12, 297 ); 298 299 #[cfg(test)] 300 mod tests { 301 use super::*; 302 303 #[test] appended_args_yields_correct_tuple()304 fn appended_args_yields_correct_tuple() { 305 let string = "test"; 306 let args = 307 ().append(0u32) 308 .append(-1i32) 309 .append(string.len() as c_int) 310 .append(string.as_ptr().cast::<*const c_uchar>()); 311 312 assert_eq!( 313 args, 314 ( 315 0u32, 316 -1i32, 317 string.len() as c_int, 318 string.as_ptr().cast::<*const c_uchar>() 319 ) 320 ); 321 } 322 323 #[test] twelve_argument_long_tuples_are_supported()324 fn twelve_argument_long_tuples_are_supported() { 325 let args = 326 ().append(0u32) 327 .append(1u32) 328 .append(2u32) 329 .append(3u32) 330 .append(4u32) 331 .append(5u32) 332 .append(6u32) 333 .append(7u32) 334 .append(8u32) 335 .append(9u32) 336 .append(10u32) 337 .append(11u32); 338 339 assert_eq!(args, (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)); 340 } 341 } 342