xref: /aosp_15_r20/external/pigweed/pw_log/rust/pw_log_backend_printf/varargs.rs (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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