1#![no_std]
43#![warn(unsafe_op_in_unsafe_fn)]
44
45use core::convert::TryFrom;
46
47#[doc(hidden)]
50pub const __LIBC_NEWLINE: &str = "\n";
51#[doc(hidden)]
52pub const __LIBC_STDOUT: i32 = 1;
53#[doc(hidden)]
54pub const __LIBC_STDERR: i32 = 2;
55
56#[doc(hidden)]
57pub struct __LibCWriter(i32);
58
59impl core::fmt::Write for __LibCWriter {
60 #[inline]
61 fn write_str(&mut self, s: &str) -> core::fmt::Result {
62 __libc_println(self.0, s)
63 }
64}
65
66impl __LibCWriter {
67 #[inline]
68 pub fn new(handle: i32) -> __LibCWriter {
69 __LibCWriter(handle)
70 }
71
72 #[inline]
73 pub fn write_fmt(&mut self, args: core::fmt::Arguments) -> core::fmt::Result {
74 core::fmt::Write::write_fmt(self, args)
75 }
76
77 #[inline]
78 pub fn write_str(&mut self, s: &str) -> core::fmt::Result {
79 __libc_println(self.0, s)
80 }
81
82 #[inline]
83 pub fn write_nl(&mut self) -> core::fmt::Result {
84 __libc_println(self.0, __LIBC_NEWLINE)
85 }
86}
87
88#[doc(hidden)]
89#[inline]
90pub fn __libc_println(handle: i32, msg: &str) -> core::fmt::Result {
91 let msg = msg.as_bytes();
92
93 let mut written = 0;
94 while written < msg.len() {
95 match unsafe { libc_write(handle, &msg[written..]) } {
96 None | Some(0) => break,
98 Some(res) => written += res,
99 }
100 }
101
102 Ok(())
103}
104
105#[cfg(all(
106 not(all(windows, miri)),
107 not(any(all(target_family = "wasm", target_os = "unknown"), target_os = "none"))
108))]
109mod write {
110 pub(crate) use libc::write;
111}
112
113#[cfg(any(all(target_family = "wasm", target_os = "unknown"), target_os = "none"))]
114mod write {
115 #[cfg_attr(all(target_family = "wasm", target_os = "unknown"), link(wasm_import_module = "env"))]
117 unsafe extern "C" {
118 pub(crate) fn write(fd: i32, buf: *const u8, nbyte: usize) -> isize;
119 }
120}
121
122#[cfg(all(windows, miri))]
124mod write {
125 use core::ffi::c_void;
126 use core::sync::atomic::{AtomicPtr, Ordering};
127
128 #[cfg(not(miri))]
129 type BOOL = i32;
130 type DWORD = u32;
131 type ULONG = u32;
132 type HANDLE = *mut c_void;
133 type NTSTATUS = i32;
134
135 const INVALID_HANDLE_VALUE: isize = -1;
136 const STD_OUTPUT_HANDLE: DWORD = (-11i32) as DWORD;
137 const STD_ERROR_HANDLE: DWORD = (-12i32) as DWORD;
138
139 #[cfg(miri)]
142 #[cfg(target_pointer_width = "64")]
143 #[repr(C)]
144 struct IO_STATUS_BLOCK {
145 status_or_ptr: usize,
146 information: usize,
147 }
148
149 #[cfg(miri)]
150 #[cfg(target_pointer_width = "32")]
151 #[repr(C)]
152 struct IO_STATUS_BLOCK {
153 status_or_ptr: u32,
154 information: u32,
155 }
156
157 unsafe extern "system" {
158 fn GetStdHandle(nStdHandle: DWORD) -> HANDLE;
159
160 #[cfg(not(miri))]
161 fn WriteFile(
162 hFile: HANDLE,
163 lpBuffer: *const c_void,
164 nNumberOfBytesToWrite: DWORD,
165 lpNumberOfBytesWritten: *mut DWORD,
166 lpOverlapped: *mut c_void,
167 ) -> BOOL;
168
169 #[cfg(miri)]
170 fn NtWriteFile(
171 FileHandle: HANDLE,
172 Event: HANDLE,
173 ApcRoutine: *mut c_void,
174 ApcContext: *mut c_void,
175 IoStatusBlock: *mut IO_STATUS_BLOCK,
176 Buffer: *const c_void,
177 Length: ULONG,
178 ByteOffset: *mut c_void,
179 Key: *mut c_void,
180 ) -> NTSTATUS;
181 }
182
183 #[inline]
184 fn cached_std_handle(fd: i32) -> Option<(DWORD, &'static AtomicPtr<c_void>)> {
185 static STD_OUTPUT: AtomicPtr<c_void> = AtomicPtr::new(INVALID_HANDLE_VALUE as _);
186 static STD_ERROR: AtomicPtr<c_void> = AtomicPtr::new(INVALID_HANDLE_VALUE as _);
187
188 match fd {
189 1 => Some((STD_OUTPUT_HANDLE, &STD_OUTPUT)),
190 2 => Some((STD_ERROR_HANDLE, &STD_ERROR)),
191 _ => None,
192 }
193 }
194
195 #[inline]
196 fn handle_from_fd(fd: i32) -> Option<HANDLE> {
197 let (std_handle, which) = cached_std_handle(fd)?;
198
199 let mut handle = which.load(Ordering::Relaxed);
202 if handle.is_null() || handle as isize == INVALID_HANDLE_VALUE {
203 handle = unsafe { GetStdHandle(std_handle) } as *mut c_void;
204 if handle.is_null() || handle as isize == INVALID_HANDLE_VALUE {
205 return None;
206 }
207 which.store(handle, Ordering::Relaxed);
208 }
209
210 Some(handle as HANDLE)
211 }
212
213 #[cfg(miri)]
214 pub(crate) unsafe fn write(fd: i32, buf: *const u8, nbyte: usize) -> isize {
215 let h = match handle_from_fd(fd) {
216 Some(h) => h,
217 None => return -1,
218 };
219
220 let len: ULONG = match ULONG::try_from(nbyte) {
221 Ok(v) => v,
222 Err(_) => ULONG::MAX,
223 };
224
225 let mut iosb = IO_STATUS_BLOCK {
226 status_or_ptr: 0,
227 information: 0,
228 };
229
230 let status = unsafe {
231 NtWriteFile(
232 h,
233 core::ptr::null_mut(),
234 core::ptr::null_mut(),
235 core::ptr::null_mut(),
236 &mut iosb,
237 buf as *const c_void,
238 len,
239 core::ptr::null_mut(),
240 core::ptr::null_mut(),
241 )
242 };
243
244 if status < 0 {
246 -1
247 } else {
248 iosb.information as isize
249 }
250 }
251
252 #[cfg(not(miri))]
253 pub(crate) unsafe fn write(fd: i32, buf: *const u8, nbyte: usize) -> isize {
254 let h = match handle_from_fd(fd) {
255 Some(h) => h,
256 None => return -1,
257 };
258
259 let to_write: DWORD = match DWORD::try_from(nbyte) {
260 Ok(v) => v,
261 Err(_) => DWORD::MAX,
262 };
263
264 let mut written: DWORD = 0;
265 let ok = unsafe {
266 WriteFile(
267 h,
268 buf as *const c_void,
269 to_write,
270 &mut written,
271 core::ptr::null_mut(),
272 )
273 };
274
275 if ok == 0 {
276 -1
277 } else {
278 written as isize
279 }
280 }
281}
282
283unsafe fn libc_write(handle: i32, bytes: &[u8]) -> Option<usize> {
284 usize::try_from(unsafe { write::write(handle as _, bytes.as_ptr() as _, bytes.len() as _) })
285 .ok()
286}
287
288#[macro_export]
298macro_rules! libc_println {
299 () => { $crate::libc_println!("") };
300 ($($arg:tt)*) => {
301 {
302 #[allow(unused_must_use)]
303 {
304 let mut stm = $crate::__LibCWriter::new($crate::__LIBC_STDOUT);
305 stm.write_fmt(format_args!($($arg)*));
306 stm.write_nl();
307 }
308 }
309 };
310}
311
312#[macro_export]
322macro_rules! libc_print {
323 ($($arg:tt)*) => {
324 {
325 #[allow(unused_must_use)]
326 {
327 let mut stm = $crate::__LibCWriter::new($crate::__LIBC_STDOUT);
328 stm.write_fmt(format_args!($($arg)*));
329 }
330 }
331 };
332}
333
334#[macro_export]
344macro_rules! libc_eprintln {
345 () => { $crate::libc_eprintln!("") };
346 ($($arg:tt)*) => {
347 {
348 #[allow(unused_must_use)]
349 {
350 let mut stm = $crate::__LibCWriter::new($crate::__LIBC_STDERR);
351 stm.write_fmt(format_args!($($arg)*));
352 stm.write_nl();
353 }
354 }
355 };
356}
357
358#[macro_export]
368macro_rules! libc_eprint {
369 ($($arg:tt)*) => {
370 {
371 #[allow(unused_must_use)]
372 {
373 let mut stm = $crate::__LibCWriter::new($crate::__LIBC_STDERR);
374 stm.write_fmt(format_args!($($arg)*));
375 }
376 }
377 };
378}
379
380#[macro_export]
384macro_rules! libc_write {
385 ($arg:expr) => {
386 #[allow(unused_must_use)]
387 {
388 let mut stm = $crate::__LibCWriter::new($crate::__LIBC_STDOUT);
389 stm.write_str($arg);
390 }
391 };
392}
393
394#[macro_export]
398macro_rules! libc_ewrite {
399 ($arg:expr) => {{
400 #[allow(unused_must_use)]
401 {
402 let mut stm = $crate::__LibCWriter::new($crate::__LIBC_STDERR);
403 stm.write_str($arg);
404 }
405 }};
406}
407
408#[macro_export]
412macro_rules! libc_writeln {
413 ($arg:expr) => {
414 #[allow(unused_must_use)]
415 {
416 let mut stm = $crate::__LibCWriter::new($crate::__LIBC_STDOUT);
417 stm.write_str($arg);
418 stm.write_nl();
419 }
420 };
421}
422
423#[macro_export]
427macro_rules! libc_ewriteln {
428 ($arg:expr) => {{
429 #[allow(unused_must_use)]
430 {
431 let mut stm = $crate::__LibCWriter::new($crate::__LIBC_STDERR);
432 stm.write_str($arg);
433 stm.write_nl();
434 }
435 }};
436}
437
438#[macro_export]
455macro_rules! libc_dbg {
456 () => {
457 $crate::libc_eprintln!("[{}:{}]", ::core::file!(), ::core::line!())
458 };
459 ($val:expr $(,)?) => {
460 match $val {
461 tmp => {
462 $crate::libc_eprintln!("[{}:{}] {} = {:#?}", ::core::file!(), ::core::line!(), ::core::stringify!($val), &tmp);
463 tmp
464 }
465 }
466 };
467 ($($val:expr),+ $(,)?) => {
468 ($($crate::libc_dbg!($val)),+,)
469 };
470}
471
472pub mod std_name {
475 pub use super::libc_dbg as dbg;
476 pub use super::libc_eprint as eprint;
477 pub use super::libc_eprintln as eprintln;
478 pub use super::libc_print as print;
479 pub use super::libc_println as println;
480
481 #[cfg(test)]
482 mod tests_std_name {
483 use super::{eprintln, println};
484
485 #[test]
486 fn test_stdout() {
487 println!("stdout fd = {}", crate::__LIBC_STDOUT);
488 }
489
490 #[test]
491 fn test_stderr() {
492 eprintln!("stderr fd = {}", crate::__LIBC_STDERR);
493 }
494 }
495}
496
497#[cfg(test)]
498mod tests {
499 #[test]
500 fn test_stdout() {
501 super::libc_println!("stdout fd = {}", super::__LIBC_STDOUT);
502 }
503
504 #[test]
505 fn test_stderr() {
506 super::libc_eprintln!("stderr fd = {}", super::__LIBC_STDERR);
507 }
508
509 #[test]
510 fn test_stdout_write() {
511 super::libc_writeln!("stdout!");
512 }
513
514 #[test]
515 fn test_stderr_write() {
516 super::libc_ewriteln!("stderr!");
517 }
518
519 #[test]
520 fn test_dbg() {
521 let a = 2;
522 let b = libc_dbg!(a * 2) + 1;
523 assert_eq!(b, 5);
524 }
525
526 #[test]
527 fn test_in_closure_expression() {
528 use super::std_name::*;
529 let _ = Result::<(), ()>::Ok(()).unwrap_or_else(|err| eprintln!("error: {:?}", err));
531 }
532}