1#![cfg_attr(not(feature = "std"), no_std)]
2
3use core::{
4 fmt::{self},
5 hint,
6 sync::atomic::{AtomicUsize, Ordering},
7};
8
9static STATE: AtomicUsize = AtomicUsize::new(UNINITIALIZED);
10static mut STDOUT: &dyn StdOut = &NopOut;
11
12pub trait StdOut: Send + 'static {
14 fn write_bytes(&self, bytes: &[u8]) -> fmt::Result;
16 fn write_str(&self, s: &str) -> fmt::Result;
18 fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result;
20 fn flush(&self) -> fmt::Result;
22}
23
24const UNINITIALIZED: usize = 0;
28const INITIALIZING: usize = 1;
30const INITIALIZED: usize = 2;
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq)]
34pub struct SetStdoutError(());
35
36impl SetStdoutError {
37 fn new() -> Self {
38 Self(())
39 }
40}
41
42struct NopOut;
43
44impl StdOut for NopOut {
45 fn write_str(&self, _: &str) -> fmt::Result {
46 Ok(())
47 }
48
49 fn write_bytes(&self, _bytes: &[u8]) -> fmt::Result {
50 Ok(())
51 }
52
53 fn write_fmt(&self, _args: fmt::Arguments) -> fmt::Result {
54 Ok(())
55 }
56
57 fn flush(&self) -> fmt::Result {
58 Ok(())
59 }
60}
61
62fn set_stdout_inner<F>(stdout: F) -> Result<(), SetStdoutError>
63where
64 F: FnOnce() -> &'static dyn StdOut,
65{
66 let old_state = match STATE.compare_exchange(
67 UNINITIALIZED,
68 INITIALIZING,
69 Ordering::SeqCst,
70 Ordering::SeqCst,
71 ) {
72 Ok(s) | Err(s) => s,
73 };
74
75 match old_state {
76 UNINITIALIZED => {
78 unsafe {
79 STDOUT = stdout();
80 }
81 STATE.store(INITIALIZED, Ordering::SeqCst);
82
83 Ok(())
84 }
85
86 INITIALIZING => {
88 while STATE.load(Ordering::SeqCst) == INITIALIZING {
90 hint::spin_loop();
91 }
92
93 Err(SetStdoutError::new())
94 }
95
96 _ => Err(SetStdoutError::new()),
97 }
98}
99
100pub fn init(stdout: &'static dyn StdOut) -> Result<(), SetStdoutError> {
104 set_stdout_inner(move || stdout)
105}
106
107pub fn stdout() -> &'static dyn StdOut {
111 if STATE.load(Ordering::SeqCst) != INITIALIZED {
112 static NOP: NopOut = NopOut;
113 &NOP
114 } else {
115 unsafe { STDOUT }
116 }
117}
118
119#[macro_export]
121macro_rules! uprint {
122 ($s:expr) => {{
123 $crate::stdout()
124 .write_str($s)
125 .ok();
126 }};
127 ($s:expr, $($tt:tt)*) => {{
128 $crate::stdout()
129 .write_fmt(format_args!($s, $($tt)*))
130 .ok();
131 }};
132}
133
134#[macro_export]
136macro_rules! uprintln {
137 () => {{
138 $crate::stdout()
139 .write_str(uprintln!(@newline))
140 .ok();
141 }};
142 ($s:expr) => {{
143 $crate::stdout()
144 .write_str(concat!($s, uprintln!(@newline)))
145 .ok();
146 }};
147 ($s:expr, $($tt:tt)*) => {{
148 $crate::stdout()
149 .write_fmt(format_args!(concat!($s, uprintln!(@newline)), $($tt)*))
150 .ok();
151 }};
152
153 (@newline) => { "\r\n" };
154}
155
156#[cfg(any(feature = "dprint", doc))]
161#[macro_export]
162macro_rules! dprint {
163 ($s:expr) => {{
164 $crate::stdout()
165 .write_str($s)
166 .ok();
167 }};
168 ($s:expr, $($tt:tt)*) => {{
169 $crate::stdout()
170 .write_fmt(format_args!($s, $($tt)*))
171 .ok();
172 }};
173}
174#[cfg(not(any(feature = "dprint", doc)))]
175#[macro_export]
176macro_rules! dprint {
177 ($s:expr) => {};
178 ($s:expr, $($tt:tt)*) => {};
179}
180
181#[macro_export]
186#[cfg(any(feature = "dprint", doc))]
187macro_rules! dprintln {
188 () => {{
189 $crate::stdout()
190 .write_str(dprintln!(@newline))
191 .ok();
192 }};
193 ($s:expr) => {{
194 $crate::stdout()
195 .write_str(concat!($s, dprintln!(@newline)))
196 .ok();
197 }};
198 ($s:expr, $($tt:tt)*) => {{
199 $crate::stdout()
200 .write_fmt(format_args!(concat!($s, dprintln!(@newline)), $($tt)*))
201 .ok();
202 }};
203
204 (@newline) => { "\r\n" };
205}
206#[cfg(not(any(feature = "dprint", doc)))]
207#[macro_export]
208macro_rules! dprintln {
209 () => {};
210 ($s:expr) => {};
211 ($s:expr, $($tt:tt)*) => {};
212}