#![cfg_attr(any(feature = "example", all(doc, feature = "doccfg")), doc = " [`example`]")]
#![cfg_attr(not(any(feature = "example", all(doc, feature = "doccfg"))), doc = " `example`")]
#![no_std]
#![cfg_attr(feature = "doccfg", feature(doc_cfg))]
#[cfg(unix)]
#[derive(Clone, Copy, Debug)]
struct CompatibleSystem {}
#[cfg(any(feature = "libc", feature = "example", test, all(doc, feature = "doccfg")))]
extern crate libc;
#[cfg(any(test, doc))]
extern crate alloc;
use core::ffi::{c_char, CStr};
use core::marker::PhantomData;
use crate::private::PrintfArgumentPrivate;
use crate::validate::is_fmt_valid_for_args;
pub(crate) mod private {
pub trait PrintfArgumentPrivate {}
}
mod larger_of;
mod printf_arg_impls;
mod validate;
#[derive(Clone, Copy, Debug)]
pub struct NullString {
s: *const c_char,
}
impl NullString {
#[allow(unconditional_panic)]
#[deny(const_err)]
pub const fn new(s: &'static str) -> NullString {
let bytes = s.as_bytes();
if bytes.len() == 0 || bytes[bytes.len() - 1] != b'\0' {
panic!("string passed to NullString::new is not null-terminated!");
}
NullString { s: bytes.as_ptr() as *const c_char }
}
#[inline]
pub const fn as_ptr(self) -> *const c_char {
self.s
}
#[inline]
pub fn as_cstr(self) -> &'static CStr {
unsafe { CStr::from_ptr(self.s) }
}
}
impl From<&'static CStr> for NullString {
#[inline]
fn from(cstr: &'static CStr) -> Self {
NullString { s: cstr.as_ptr() }
}
}
impl From<NullString> for &'static CStr {
#[inline]
fn from(nstr: NullString) -> Self {
nstr.as_cstr()
}
}
impl AsRef<CStr> for NullString {
#[inline]
fn as_ref(&self) -> &CStr {
self.as_cstr()
}
}
unsafe impl Send for NullString {}
unsafe impl Sync for NullString {}
#[macro_export]
macro_rules! null_str {
($str:expr) => {{
const STR: $crate::NullString = $crate::NullString::new(concat!($str, "\0"));
STR
}};
}
pub trait PrintfArgument: PrintfArgumentPrivate + Copy {
type CPrintfType;
fn as_c_val(self) -> Self::CPrintfType;
const IS_CHAR: bool = false;
const IS_SHORT: bool = false;
const IS_INT: bool = false;
const IS_LONG: bool = false;
const IS_LONG_LONG: bool = false;
#[cfg(any(feature = "libc", test, all(doc, feature = "doccfg")))]
#[cfg_attr(feature = "doccfg", doc(cfg(feature = "libc")))]
const IS_SIZE: bool = false;
#[cfg(any(feature = "libc", test, all(doc, feature = "doccfg")))]
#[cfg_attr(feature = "doccfg", doc(cfg(feature = "libc")))]
const IS_MAX: bool = false;
#[cfg(any(feature = "libc", test, all(doc, feature = "doccfg")))]
#[cfg_attr(feature = "doccfg", doc(cfg(feature = "libc")))]
const IS_PTRDIFF: bool = false;
const IS_SIGNED: bool = false;
const IS_FLOAT: bool = false;
const IS_C_STRING: bool = false;
const IS_POINTER: bool = false;
}
const fn is_compat<T: Sized, U: Sized>() -> bool {
use core::mem::{align_of, size_of};
size_of::<T>() == size_of::<U>() && align_of::<T>() == align_of::<U>()
}
pub trait LargerOfOp<Rhs> {
type Output;
}
pub type LargerOf<T, U> = <T as LargerOfOp<U>>::Output;
pub trait PrintfArgs {
type AsList: PrintfArgsList;
}
pub trait PrintfArgsList {
const IS_EMPTY: bool;
type First: PrintfArgument;
type Rest: PrintfArgsList;
}
impl PrintfArgsList for () {
const IS_EMPTY: bool = true;
type First = u8;
type Rest = ();
}
impl<CAR: PrintfArgument, CDR: PrintfArgsList> PrintfArgsList for (CAR, CDR) {
const IS_EMPTY: bool = false;
type First = CAR;
type Rest = CDR;
}
impl<T: PrintfArgument> PrintfArgs for T {
type AsList = (T, ());
}
impl PrintfArgs for () {
type AsList = ();
}
macro_rules! nested_list_from_flat {
($t:ident $(, $u:ident )*) => { ($t, nested_list_from_flat!($( $u ),*)) };
() => { () };
}
macro_rules! make_printf_arguments_tuple {
($( $t:ident ),+) => {
impl<$( $t ),+> PrintfArgs for ($( $t, )+)
where $( $t: PrintfArgument ),+ {
type AsList = nested_list_from_flat!($( $t ),+);
}
};
}
make_printf_arguments_tuple!(A);
make_printf_arguments_tuple!(A, B);
make_printf_arguments_tuple!(A, B, C);
make_printf_arguments_tuple!(A, B, C, D);
make_printf_arguments_tuple!(A, B, C, D, E);
make_printf_arguments_tuple!(A, B, C, D, E, F);
make_printf_arguments_tuple!(A, B, C, D, E, F, G);
make_printf_arguments_tuple!(A, B, C, D, E, F, G, H);
make_printf_arguments_tuple!(A, B, C, D, E, F, G, H, I);
make_printf_arguments_tuple!(A, B, C, D, E, F, G, H, I, J);
make_printf_arguments_tuple!(A, B, C, D, E, F, G, H, I, J, K);
make_printf_arguments_tuple!(A, B, C, D, E, F, G, H, I, J, K, L);
make_printf_arguments_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M);
make_printf_arguments_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
make_printf_arguments_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
make_printf_arguments_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
make_printf_arguments_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q);
make_printf_arguments_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R);
make_printf_arguments_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S);
make_printf_arguments_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T);
make_printf_arguments_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U);
make_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);
make_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);
make_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, X
);
make_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, X, Y
);
make_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, X, Y, Z
);
make_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, X, Y, Z, AA
);
make_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, X, Y, Z, AA, BB
);
make_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, X, Y, Z, AA, BB, CC
);
make_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, X, Y, Z, AA, BB, CC, DD
);
make_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, X, Y, Z, AA, BB, CC, DD,
EE
);
make_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, X, Y, Z, AA, BB, CC, DD,
EE, FF
);
#[derive(Debug)]
pub struct PrintfFmt<T: PrintfArgs> {
fmt: *const c_char,
_x: CompatibleSystem,
_y: PhantomData<T>,
}
const fn c(x: u8) -> c_char {
x as c_char
}
const EMPTY_C_STRING: *const c_char = &c(b'\0') as *const c_char;
impl<T: PrintfArgs> PrintfFmt<T> {
#[allow(unconditional_panic)]
#[inline]
pub const fn new_or_panic(fmt: &'static str) -> Self {
if !is_compat::<u8, c_char>() {
panic!("u8 and c_char have different sizes/alignments, somehow");
}
let fmt_as_cstr: &'static [c_char] = unsafe {
core::mem::transmute(fmt.as_bytes() as *const [u8] as *const [c_char])
};
let s = if is_fmt_valid_for_args::<T>(fmt_as_cstr, true) {
fmt_as_cstr.as_ptr()
} else {
EMPTY_C_STRING
};
PrintfFmt { fmt: s, _x: CompatibleSystem {}, _y: PhantomData }
}
#[inline]
pub const fn new(fmt: &'static str) -> Result<Self, ()> {
if !is_compat::<u8, c_char>() {
return Err(());
}
let fmt_as_cstr: &'static [c_char] = unsafe {
core::mem::transmute(fmt.as_bytes() as *const [u8] as *const [c_char])
};
if is_fmt_valid_for_args::<T>(fmt_as_cstr, false) {
Ok(PrintfFmt { fmt: fmt_as_cstr.as_ptr(), _x: CompatibleSystem {}, _y: PhantomData })
} else {
Err(())
}
}
#[inline]
pub const fn as_ptr(self) -> *const c_char {
self.fmt
}
}
impl<T: PrintfArgs> Clone for PrintfFmt<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: PrintfArgs> Copy for PrintfFmt<T> {}
impl<T: PrintfArgs> AsRef<CStr> for PrintfFmt<T> {
fn as_ref(&self) -> &CStr {
unsafe { CStr::from_ptr(self.fmt) }
}
}
unsafe impl<T: PrintfArgs> Send for PrintfFmt<T> {}
unsafe impl<T: PrintfArgs> Sync for PrintfFmt<T> {}
#[deny(unconditional_panic)]
#[inline]
pub const fn is_fmt_valid<T: PrintfArgs>(fmt: &[c_char]) -> bool {
is_fmt_valid_for_args::<T>(fmt, false)
}
#[cfg(any(feature = "example", all(doc, feature = "doccfg"), test))]
#[cfg_attr(feature = "doccfg", doc(cfg(feature = "example")))]
pub mod example;
#[cfg(test)]
mod tests;