1#![cfg_attr(any(feature = "example", all(doc, feature = "doccfg")), doc = " [`example`]")]
48#![cfg_attr(not(any(feature = "example", all(doc, feature = "doccfg"))), doc = " `example`")]
49#![no_std]
66#![cfg_attr(feature = "doccfg", feature(doc_cfg))]
67
68#[cfg(unix)]
70#[derive(Clone, Copy, Debug)]
73struct CompatibleSystem {}
74
75#[cfg(any(feature = "libc", feature = "example", test, all(doc, feature = "doccfg")))]
78extern crate libc;
79
80#[cfg(any(test, doc))]
81extern crate alloc;
82
83use core::ffi::{c_char, CStr};
84use core::marker::PhantomData;
85
86use crate::private::PrintfArgumentPrivate;
87use crate::validate::is_fmt_valid_for_args;
88
89pub(crate) mod private {
93 pub trait PrintfArgumentPrivate {}
95}
96
97mod larger_of;
98mod printf_arg_impls;
99mod validate;
100
101#[derive(Clone, Copy, Debug)]
107pub struct NullString {
108 s: *const c_char,
110}
111
112impl NullString {
113 #[allow(unconditional_panic)]
120 #[deny(const_err)]
121 pub const fn new(s: &'static str) -> NullString {
122 let bytes = s.as_bytes();
123 if bytes.len() == 0 || bytes[bytes.len() - 1] != b'\0' {
124 panic!("string passed to NullString::new is not null-terminated!");
125 }
126
127 NullString { s: bytes.as_ptr() as *const c_char }
128 }
129
130 #[inline]
132 pub const fn as_ptr(self) -> *const c_char {
133 self.s
134 }
135
136 #[inline]
138 pub fn as_cstr(self) -> &'static CStr {
139 unsafe { CStr::from_ptr(self.s) }
140 }
141}
142
143impl From<&'static CStr> for NullString {
144 #[inline]
145 fn from(cstr: &'static CStr) -> Self {
146 NullString { s: cstr.as_ptr() }
147 }
148}
149
150impl From<NullString> for &'static CStr {
151 #[inline]
152 fn from(nstr: NullString) -> Self {
153 nstr.as_cstr()
154 }
155}
156
157impl AsRef<CStr> for NullString {
158 #[inline]
159 fn as_ref(&self) -> &CStr {
160 self.as_cstr()
161 }
162}
163
164unsafe impl Send for NullString {}
167unsafe impl Sync for NullString {}
168
169#[macro_export]
172macro_rules! null_str {
173 ($str:expr) => {{
174 const STR: $crate::NullString = $crate::NullString::new(concat!($str, "\0"));
175 STR
176 }};
177}
178
179pub trait PrintfArgument: PrintfArgumentPrivate + Copy {
187 type CPrintfType;
190
191 fn as_c_val(self) -> Self::CPrintfType;
193
194 const IS_CHAR: bool = false;
196 const IS_SHORT: bool = false;
198 const IS_INT: bool = false;
200 const IS_LONG: bool = false;
202 const IS_LONG_LONG: bool = false;
204 #[cfg(any(feature = "libc", test, all(doc, feature = "doccfg")))]
206 #[cfg_attr(feature = "doccfg", doc(cfg(feature = "libc")))]
207 const IS_SIZE: bool = false;
208 #[cfg(any(feature = "libc", test, all(doc, feature = "doccfg")))]
210 #[cfg_attr(feature = "doccfg", doc(cfg(feature = "libc")))]
211 const IS_MAX: bool = false;
212 #[cfg(any(feature = "libc", test, all(doc, feature = "doccfg")))]
214 #[cfg_attr(feature = "doccfg", doc(cfg(feature = "libc")))]
215 const IS_PTRDIFF: bool = false;
216
217 const IS_SIGNED: bool = false;
219
220 const IS_FLOAT: bool = false;
222
223 const IS_C_STRING: bool = false;
225
226 const IS_POINTER: bool = false;
228}
229
230const fn is_compat<T: Sized, U: Sized>() -> bool {
240 use core::mem::{align_of, size_of};
241
242 size_of::<T>() == size_of::<U>() && align_of::<T>() == align_of::<U>()
243}
244
245pub trait LargerOfOp<Rhs> {
250 type Output;
253}
254
255pub type LargerOf<T, U> = <T as LargerOfOp<U>>::Output;
258
259pub trait PrintfArgs {
261 type AsList: PrintfArgsList;
263}
264
265pub trait PrintfArgsList {
267 const IS_EMPTY: bool;
269
270 type First: PrintfArgument;
272 type Rest: PrintfArgsList;
274}
275
276impl PrintfArgsList for () {
277 const IS_EMPTY: bool = true;
278
279 type First = u8;
282 type Rest = ();
283}
284
285impl<CAR: PrintfArgument, CDR: PrintfArgsList> PrintfArgsList for (CAR, CDR) {
286 const IS_EMPTY: bool = false;
287
288 type First = CAR;
289 type Rest = CDR;
290}
291
292impl<T: PrintfArgument> PrintfArgs for T {
293 type AsList = (T, ());
294}
295
296impl PrintfArgs for () {
297 type AsList = ();
298}
299
300macro_rules! nested_list_from_flat {
301 ($t:ident $(, $u:ident )*) => { ($t, nested_list_from_flat!($( $u ),*)) };
302 () => { () };
303}
304
305macro_rules! make_printf_arguments_tuple {
306 ($( $t:ident ),+) => {
307 impl<$( $t ),+> PrintfArgs for ($( $t, )+)
308 where $( $t: PrintfArgument ),+ {
309 type AsList = nested_list_from_flat!($( $t ),+);
310 }
311 };
312}
313
314make_printf_arguments_tuple!(A);
315make_printf_arguments_tuple!(A, B);
316make_printf_arguments_tuple!(A, B, C);
317make_printf_arguments_tuple!(A, B, C, D);
318make_printf_arguments_tuple!(A, B, C, D, E);
319make_printf_arguments_tuple!(A, B, C, D, E, F);
320make_printf_arguments_tuple!(A, B, C, D, E, F, G);
321make_printf_arguments_tuple!(A, B, C, D, E, F, G, H);
322make_printf_arguments_tuple!(A, B, C, D, E, F, G, H, I);
323make_printf_arguments_tuple!(A, B, C, D, E, F, G, H, I, J);
324make_printf_arguments_tuple!(A, B, C, D, E, F, G, H, I, J, K);
325make_printf_arguments_tuple!(A, B, C, D, E, F, G, H, I, J, K, L);
326make_printf_arguments_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M);
327make_printf_arguments_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
328make_printf_arguments_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
329make_printf_arguments_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
330make_printf_arguments_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q);
331make_printf_arguments_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R);
332make_printf_arguments_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S);
333make_printf_arguments_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T);
334make_printf_arguments_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U);
335make_printf_arguments_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V);
336make_printf_arguments_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W);
337make_printf_arguments_tuple!(
338 A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X
339);
340make_printf_arguments_tuple!(
341 A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y
342);
343make_printf_arguments_tuple!(
344 A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z
345);
346make_printf_arguments_tuple!(
347 A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, AA
348);
349make_printf_arguments_tuple!(
350 A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, AA, BB
351);
352make_printf_arguments_tuple!(
353 A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, AA, BB, CC
354);
355make_printf_arguments_tuple!(
356 A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, AA, BB, CC, DD
357);
358make_printf_arguments_tuple!(
359 A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, AA, BB, CC, DD,
360 EE
361);
362make_printf_arguments_tuple!(
363 A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, AA, BB, CC, DD,
364 EE, FF
365);
366
367#[derive(Debug)]
371pub struct PrintfFmt<T: PrintfArgs> {
372 fmt: *const c_char,
374 _x: CompatibleSystem,
375 _y: PhantomData<T>,
376}
377
378const fn c(x: u8) -> c_char {
380 x as c_char
381}
382
383const EMPTY_C_STRING: *const c_char = &c(b'\0') as *const c_char;
385
386impl<T: PrintfArgs> PrintfFmt<T> {
387 #[allow(unconditional_panic)]
395 #[inline]
396 pub const fn new_or_panic(fmt: &'static str) -> Self {
397 if !is_compat::<u8, c_char>() {
398 panic!("u8 and c_char have different sizes/alignments, somehow");
399 }
400
401 let fmt_as_cstr: &'static [c_char] = unsafe {
402 core::mem::transmute(fmt.as_bytes() as *const [u8] as *const [c_char])
406 };
407
408 let s = if is_fmt_valid_for_args::<T>(fmt_as_cstr, true) {
409 fmt_as_cstr.as_ptr()
410 } else {
411 EMPTY_C_STRING
412 };
413
414 PrintfFmt { fmt: s, _x: CompatibleSystem {}, _y: PhantomData }
415 }
416
417 #[inline]
425 pub const fn new(fmt: &'static str) -> Result<Self, ()> {
426 if !is_compat::<u8, c_char>() {
427 return Err(());
428 }
429
430 let fmt_as_cstr: &'static [c_char] = unsafe {
431 core::mem::transmute(fmt.as_bytes() as *const [u8] as *const [c_char])
435 };
436
437 if is_fmt_valid_for_args::<T>(fmt_as_cstr, false) {
438 Ok(PrintfFmt { fmt: fmt_as_cstr.as_ptr(), _x: CompatibleSystem {}, _y: PhantomData })
439 } else {
440 Err(())
441 }
442 }
443
444 #[inline]
446 pub const fn as_ptr(self) -> *const c_char {
447 self.fmt
448 }
449}
450
451impl<T: PrintfArgs> Clone for PrintfFmt<T> {
452 fn clone(&self) -> Self {
453 *self
454 }
455}
456
457impl<T: PrintfArgs> Copy for PrintfFmt<T> {}
458
459impl<T: PrintfArgs> AsRef<CStr> for PrintfFmt<T> {
460 fn as_ref(&self) -> &CStr {
461 unsafe { CStr::from_ptr(self.fmt) }
462 }
463}
464
465unsafe impl<T: PrintfArgs> Send for PrintfFmt<T> {}
468unsafe impl<T: PrintfArgs> Sync for PrintfFmt<T> {}
469
470#[deny(unconditional_panic)]
474#[inline]
475pub const fn is_fmt_valid<T: PrintfArgs>(fmt: &[c_char]) -> bool {
476 is_fmt_valid_for_args::<T>(fmt, false)
477}
478
479#[cfg(any(feature = "example", all(doc, feature = "doccfg"), test))]
480#[cfg_attr(feature = "doccfg", doc(cfg(feature = "example")))]
481pub mod example;
482
483#[cfg(test)]
484mod tests;