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. call_printf(self, format_str: *const c_uchar, log_level_str: *const c_uchar) -> c_int170 unsafe fn call_printf(self, format_str: *const c_uchar, log_level_str: *const c_uchar) 171 -> c_int; 172 } 173 174 #[derive(Clone)] 175 /// A sentinel type for trying to append too many (>12) arguments to a 176 /// [`VarArgs`] tuple. 177 pub struct TooMany(Infallible); 178 impl TooMany { panic() -> !179 const fn panic() -> ! { 180 panic!("Too many arguments to logging call") 181 } 182 } 183 184 #[doc(hidden)] 185 /// Implementation VarArgs for TooMany. Usages of TooMany::CHECK will cause a 186 /// compile-time error. 187 impl VarArgs for TooMany { 188 type OneMore<T: Clone> = TooMany; 189 const CHECK: () = Self::panic(); 190 append<T: Clone>(self, _: T) -> Self::OneMore<T>191 fn append<T: Clone>(self, _: T) -> Self::OneMore<T> { 192 Self::panic() 193 } 194 call_printf(self, _: *const c_uchar, _: *const c_uchar) -> c_int195 unsafe fn call_printf(self, _: *const c_uchar, _: *const c_uchar) -> c_int { 196 Self::panic() 197 } 198 } 199 200 /// Used to implement [`VarArgs`] on tuples. 201 /// 202 /// This recursive macro divides it's arguments into a set of arguments for use 203 /// in the recursive case in `[]`s and arguments used for the current 204 /// implementation of [`VarArgs`] following the `[]`'s. 205 macro_rules! impl_args_list { 206 // Entry point into the macro that directly invokes `@impl` with its 207 // arguments. 208 // 209 // Take a list of arguments of the form `ident => position`. `ident` 210 // is used to name the generic type argument for the implementation 211 // of `[VarArgs]` (i.e. `impl<ident: Clone, ...> VarArgs for (ident, ...)`). 212 // `position` is used to index into the argument tuple when calling 213 // printf (i.e. `printf(format_str, ..., args.position, ...)`). 214 ($($arg:ident => $arg_ident:tt),* $(,)?) => { 215 impl_args_list!(@impl [$($arg => $arg_ident),*]); 216 }; 217 218 // Recursive case for [`VarArgs`] implementation. 219 // 220 // Implements [`VarArgs`] for a tuple with length equal to the number 221 // of arguments listed after the `[]`. It then recurses with taking 222 // the first argument between the `[]`s and appending it to the list 223 // after the `[]`s. 224 (@impl [$next:ident => $next_num:tt 225 $(, $remaining:ident => $next_remaining:tt)*] 226 $($current:ident => $current_num:tt),*) => { 227 impl<$($current: Clone),*> VarArgs for ($($current,)*) { 228 type OneMore<$next: Clone> = ($($current,)* $next,); 229 fn append<$next>(self, val: $next) -> ($($current,)* $next,) { 230 ($(self. $current_num,)* val,) 231 } 232 233 unsafe fn call_printf( 234 self, 235 format_str: *const c_uchar, 236 log_level_str: *const c_uchar, 237 ) -> c_int { 238 extern "C" { 239 fn printf(fmt: *const c_uchar, ...) -> c_int; 240 } 241 printf(format_str, log_level_str, $(self. $current_num),*) 242 } 243 } 244 245 impl_args_list!(@impl 246 [$($remaining => $next_remaining),*] 247 $($current => $current_num,)* $next => $next_num); 248 }; 249 250 // Base for [`VarArgs`] implementation. 251 // 252 // Implements [`VarArgs`] for the full list of arguments and sets 253 // its `OneMore` type to `TooMany` to cause a compilation error 254 // if code tries to instantiate an argument list longer that this. 255 (@impl [] $($current:ident => $current_num:tt),*) => { 256 impl<$($current: Clone),*> VarArgs for ($($current),*) { 257 type OneMore<T: Clone> = TooMany; 258 fn append<T: Clone>(self, _: T) -> TooMany { 259 panic!("Too many arguments to logging call") 260 } 261 262 unsafe fn call_printf( 263 self, 264 format_str: *const c_uchar, 265 log_level_str: *const c_uchar, 266 ) -> c_int { 267 extern "C" { 268 fn printf(fmt: *const c_uchar, ...) -> c_int; 269 } 270 printf(format_str, log_level_str, $(self. $current_num),*) 271 } 272 } 273 }; 274 } 275 276 // Expands to implementations of [`VarArgs`] for tuples of length 0-12. 277 impl_args_list!( 278 ARGS0 => 0, 279 ARGS1 => 1, 280 ARGS2 => 2, 281 ARGS3 => 3, 282 ARGS4 => 4, 283 ARGS5 => 5, 284 ARGS6 => 6, 285 ARGS7 => 7, 286 ARGS8 => 8, 287 ARGS9 => 9, 288 ARGS10 => 10, 289 ARGS11 => 11, 290 ARGS12 => 12, 291 ); 292 293 #[cfg(test)] 294 mod tests { 295 use super::*; 296 297 #[test] appended_args_yields_correct_tuple()298 fn appended_args_yields_correct_tuple() { 299 let string = "test"; 300 let args = 301 ().append(0u32) 302 .append(-1i32) 303 .append(string.len() as c_int) 304 .append(string.as_ptr().cast::<*const c_uchar>()); 305 306 assert_eq!( 307 args, 308 ( 309 0u32, 310 -1i32, 311 string.len() as c_int, 312 string.as_ptr().cast::<*const c_uchar>() 313 ) 314 ); 315 } 316 317 #[test] twelve_argument_long_tuples_are_supported()318 fn twelve_argument_long_tuples_are_supported() { 319 let args = 320 ().append(0u32) 321 .append(1u32) 322 .append(2u32) 323 .append(3u32) 324 .append(4u32) 325 .append(5u32) 326 .append(6u32) 327 .append(7u32) 328 .append(8u32) 329 .append(9u32) 330 .append(10u32) 331 .append(11u32); 332 333 assert_eq!(args, (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)); 334 } 335 } 336