#![cfg_attr(not(feature = "std"), no_std)]
use core::{
fmt::{self},
hint,
sync::atomic::{AtomicUsize, Ordering},
};
static STATE: AtomicUsize = AtomicUsize::new(UNINITIALIZED);
static mut STDOUT: &dyn StdOut = &NopOut;
pub trait StdOut: Send + 'static {
fn write_bytes(&self, bytes: &[u8]) -> fmt::Result;
fn write_str(&self, s: &str) -> fmt::Result;
fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result;
fn flush(&self) -> fmt::Result;
}
const UNINITIALIZED: usize = 0;
const INITIALIZING: usize = 1;
const INITIALIZED: usize = 2;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SetStdoutError(());
impl SetStdoutError {
fn new() -> Self {
Self(())
}
}
struct NopOut;
impl StdOut for NopOut {
fn write_str(&self, _: &str) -> fmt::Result {
Ok(())
}
fn write_bytes(&self, _bytes: &[u8]) -> fmt::Result {
Ok(())
}
fn write_fmt(&self, _args: fmt::Arguments) -> fmt::Result {
Ok(())
}
fn flush(&self) -> fmt::Result {
Ok(())
}
}
fn set_stdout_inner<F>(stdout: F) -> Result<(), SetStdoutError>
where
F: FnOnce() -> &'static dyn StdOut,
{
let old_state = match STATE.compare_exchange(
UNINITIALIZED,
INITIALIZING,
Ordering::SeqCst,
Ordering::SeqCst,
) {
Ok(s) | Err(s) => s,
};
match old_state {
UNINITIALIZED => {
unsafe {
STDOUT = stdout();
}
STATE.store(INITIALIZED, Ordering::SeqCst);
Ok(())
}
INITIALIZING => {
while STATE.load(Ordering::SeqCst) == INITIALIZING {
hint::spin_loop();
}
Err(SetStdoutError::new())
}
_ => Err(SetStdoutError::new()),
}
}
pub fn init(stdout: &'static dyn StdOut) -> Result<(), SetStdoutError> {
set_stdout_inner(move || stdout)
}
pub fn stdout() -> &'static dyn StdOut {
if STATE.load(Ordering::SeqCst) != INITIALIZED {
static NOP: NopOut = NopOut;
&NOP
} else {
unsafe { STDOUT }
}
}
#[macro_export]
macro_rules! uprint {
($s:expr) => {{
$crate::stdout()
.write_str($s)
.ok();
}};
($s:expr, $($tt:tt)*) => {{
$crate::stdout()
.write_fmt(format_args!($s, $($tt)*))
.ok();
}};
}
#[macro_export]
macro_rules! uprintln {
() => {{
$crate::stdout()
.write_str(uprintln!(@newline))
.ok();
}};
($s:expr) => {{
$crate::stdout()
.write_str(concat!($s, uprintln!(@newline)))
.ok();
}};
($s:expr, $($tt:tt)*) => {{
$crate::stdout()
.write_fmt(format_args!(concat!($s, uprintln!(@newline)), $($tt)*))
.ok();
}};
(@newline) => { "\r\n" };
}
#[cfg(any(feature = "dprint", doc))]
#[macro_export]
macro_rules! dprint {
($s:expr) => {{
$crate::stdout()
.write_str($s)
.ok();
}};
($s:expr, $($tt:tt)*) => {{
$crate::stdout()
.write_fmt(format_args!($s, $($tt)*))
.ok();
}};
}
#[cfg(not(any(feature = "dprint", doc)))]
#[macro_export]
macro_rules! dprint {
($s:expr) => {};
($s:expr, $($tt:tt)*) => {};
}
#[macro_export]
#[cfg(any(feature = "dprint", doc))]
macro_rules! dprintln {
() => {{
$crate::stdout()
.write_str(dprintln!(@newline))
.ok();
}};
($s:expr) => {{
$crate::stdout()
.write_str(concat!($s, dprintln!(@newline)))
.ok();
}};
($s:expr, $($tt:tt)*) => {{
$crate::stdout()
.write_fmt(format_args!(concat!($s, dprintln!(@newline)), $($tt)*))
.ok();
}};
(@newline) => { "\r\n" };
}
#[cfg(not(any(feature = "dprint", doc)))]
#[macro_export]
macro_rules! dprintln {
() => {};
($s:expr) => {};
($s:expr, $($tt:tt)*) => {};
}