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 unsafe extern "C" {
117 pub(crate) fn write(fd: i32, buf: *const u8, nbyte: usize) -> isize;
118 }
119}
120
121#[cfg(all(windows, miri))]
123mod write {
124 use core::ffi::c_void;
125 use core::sync::atomic::{AtomicPtr, Ordering};
126
127 #[cfg(not(miri))]
128 type BOOL = i32;
129 type DWORD = u32;
130 type ULONG = u32;
131 type HANDLE = *mut c_void;
132 type NTSTATUS = i32;
133
134 const INVALID_HANDLE_VALUE: isize = -1;
135 const STD_OUTPUT_HANDLE: DWORD = (-11i32) as DWORD;
136 const STD_ERROR_HANDLE: DWORD = (-12i32) as DWORD;
137
138 #[cfg(miri)]
141 #[cfg(target_pointer_width = "64")]
142 #[repr(C)]
143 struct IO_STATUS_BLOCK {
144 status_or_ptr: usize,
145 information: usize,
146 }
147
148 #[cfg(miri)]
149 #[cfg(target_pointer_width = "32")]
150 #[repr(C)]
151 struct IO_STATUS_BLOCK {
152 status_or_ptr: u32,
153 information: u32,
154 }
155
156 unsafe extern "system" {
157 fn GetStdHandle(nStdHandle: DWORD) -> HANDLE;
158
159 #[cfg(not(miri))]
160 fn WriteFile(
161 hFile: HANDLE,
162 lpBuffer: *const c_void,
163 nNumberOfBytesToWrite: DWORD,
164 lpNumberOfBytesWritten: *mut DWORD,
165 lpOverlapped: *mut c_void,
166 ) -> BOOL;
167
168 #[cfg(miri)]
169 fn NtWriteFile(
170 FileHandle: HANDLE,
171 Event: HANDLE,
172 ApcRoutine: *mut c_void,
173 ApcContext: *mut c_void,
174 IoStatusBlock: *mut IO_STATUS_BLOCK,
175 Buffer: *const c_void,
176 Length: ULONG,
177 ByteOffset: *mut c_void,
178 Key: *mut c_void,
179 ) -> NTSTATUS;
180 }
181
182 #[inline]
183 fn cached_std_handle(fd: i32) -> Option<(DWORD, &'static AtomicPtr<c_void>)> {
184 static STD_OUTPUT: AtomicPtr<c_void> = AtomicPtr::new(INVALID_HANDLE_VALUE as _);
185 static STD_ERROR: AtomicPtr<c_void> = AtomicPtr::new(INVALID_HANDLE_VALUE as _);
186
187 match fd {
188 1 => Some((STD_OUTPUT_HANDLE, &STD_OUTPUT)),
189 2 => Some((STD_ERROR_HANDLE, &STD_ERROR)),
190 _ => None,
191 }
192 }
193
194 #[inline]
195 fn handle_from_fd(fd: i32) -> Option<HANDLE> {
196 let (std_handle, which) = cached_std_handle(fd)?;
197
198 let mut handle = which.load(Ordering::Relaxed);
201 if handle.is_null() || handle as isize == INVALID_HANDLE_VALUE {
202 handle = unsafe { GetStdHandle(std_handle) } as *mut c_void;
203 if handle.is_null() || handle as isize == INVALID_HANDLE_VALUE {
204 return None;
205 }
206 which.store(handle, Ordering::Relaxed);
207 }
208
209 Some(handle as HANDLE)
210 }
211
212 #[cfg(miri)]
213 pub(crate) unsafe fn write(fd: i32, buf: *const u8, nbyte: usize) -> isize {
214 let h = match handle_from_fd(fd) {
215 Some(h) => h,
216 None => return -1,
217 };
218
219 let len: ULONG = match ULONG::try_from(nbyte) {
220 Ok(v) => v,
221 Err(_) => ULONG::MAX,
222 };
223
224 let mut iosb = IO_STATUS_BLOCK {
225 status_or_ptr: 0,
226 information: 0,
227 };
228
229 let status = unsafe {
230 NtWriteFile(
231 h,
232 core::ptr::null_mut(),
233 core::ptr::null_mut(),
234 core::ptr::null_mut(),
235 &mut iosb,
236 buf as *const c_void,
237 len,
238 core::ptr::null_mut(),
239 core::ptr::null_mut(),
240 )
241 };
242
243 if status < 0 {
245 -1
246 } else {
247 iosb.information as isize
248 }
249 }
250
251 #[cfg(not(miri))]
252 pub(crate) unsafe fn write(fd: i32, buf: *const u8, nbyte: usize) -> isize {
253 let h = match handle_from_fd(fd) {
254 Some(h) => h,
255 None => return -1,
256 };
257
258 let to_write: DWORD = match DWORD::try_from(nbyte) {
259 Ok(v) => v,
260 Err(_) => DWORD::MAX,
261 };
262
263 let mut written: DWORD = 0;
264 let ok = unsafe {
265 WriteFile(
266 h,
267 buf as *const c_void,
268 to_write,
269 &mut written,
270 core::ptr::null_mut(),
271 )
272 };
273
274 if ok == 0 {
275 -1
276 } else {
277 written as isize
278 }
279 }
280}
281
282unsafe fn libc_write(handle: i32, bytes: &[u8]) -> Option<usize> {
283 usize::try_from(unsafe { write::write(handle as _, bytes.as_ptr() as _, bytes.len() as _) })
284 .ok()
285}
286
287#[macro_export]
297macro_rules! libc_println {
298 () => { $crate::libc_println!("") };
299 ($($arg:tt)*) => {
300 {
301 #[allow(unused_must_use)]
302 {
303 let mut stm = $crate::__LibCWriter::new($crate::__LIBC_STDOUT);
304 stm.write_fmt(format_args!($($arg)*));
305 stm.write_nl();
306 }
307 }
308 };
309}
310
311#[macro_export]
321macro_rules! libc_print {
322 ($($arg:tt)*) => {
323 {
324 #[allow(unused_must_use)]
325 {
326 let mut stm = $crate::__LibCWriter::new($crate::__LIBC_STDOUT);
327 stm.write_fmt(format_args!($($arg)*));
328 }
329 }
330 };
331}
332
333#[macro_export]
343macro_rules! libc_eprintln {
344 () => { $crate::libc_eprintln!("") };
345 ($($arg:tt)*) => {
346 {
347 #[allow(unused_must_use)]
348 {
349 let mut stm = $crate::__LibCWriter::new($crate::__LIBC_STDERR);
350 stm.write_fmt(format_args!($($arg)*));
351 stm.write_nl();
352 }
353 }
354 };
355}
356
357#[macro_export]
367macro_rules! libc_eprint {
368 ($($arg:tt)*) => {
369 {
370 #[allow(unused_must_use)]
371 {
372 let mut stm = $crate::__LibCWriter::new($crate::__LIBC_STDERR);
373 stm.write_fmt(format_args!($($arg)*));
374 }
375 }
376 };
377}
378
379#[macro_export]
383macro_rules! libc_write {
384 ($arg:expr) => {
385 #[allow(unused_must_use)]
386 {
387 let mut stm = $crate::__LibCWriter::new($crate::__LIBC_STDOUT);
388 stm.write_str($arg);
389 }
390 };
391}
392
393#[macro_export]
397macro_rules! libc_ewrite {
398 ($arg:expr) => {{
399 #[allow(unused_must_use)]
400 {
401 let mut stm = $crate::__LibCWriter::new($crate::__LIBC_STDERR);
402 stm.write_str($arg);
403 }
404 }};
405}
406
407#[macro_export]
411macro_rules! libc_writeln {
412 ($arg:expr) => {
413 #[allow(unused_must_use)]
414 {
415 let mut stm = $crate::__LibCWriter::new($crate::__LIBC_STDOUT);
416 stm.write_str($arg);
417 stm.write_nl();
418 }
419 };
420}
421
422#[macro_export]
426macro_rules! libc_ewriteln {
427 ($arg:expr) => {{
428 #[allow(unused_must_use)]
429 {
430 let mut stm = $crate::__LibCWriter::new($crate::__LIBC_STDERR);
431 stm.write_str($arg);
432 stm.write_nl();
433 }
434 }};
435}
436
437#[macro_export]
454macro_rules! libc_dbg {
455 () => {
456 $crate::libc_eprintln!("[{}:{}]", ::core::file!(), ::core::line!())
457 };
458 ($val:expr $(,)?) => {
459 match $val {
460 tmp => {
461 $crate::libc_eprintln!("[{}:{}] {} = {:#?}", ::core::file!(), ::core::line!(), ::core::stringify!($val), &tmp);
462 tmp
463 }
464 }
465 };
466 ($($val:expr),+ $(,)?) => {
467 ($($crate::libc_dbg!($val)),+,)
468 };
469}
470
471pub mod std_name {
474 pub use super::libc_dbg as dbg;
475 pub use super::libc_eprint as eprint;
476 pub use super::libc_eprintln as eprintln;
477 pub use super::libc_print as print;
478 pub use super::libc_println as println;
479
480 #[cfg(test)]
481 mod tests_std_name {
482 use super::{eprintln, println};
483
484 #[test]
485 fn test_stdout() {
486 println!("stdout fd = {}", crate::__LIBC_STDOUT);
487 }
488
489 #[test]
490 fn test_stderr() {
491 eprintln!("stderr fd = {}", crate::__LIBC_STDERR);
492 }
493 }
494}
495
496#[cfg(test)]
497mod tests {
498 #[test]
499 fn test_stdout() {
500 super::libc_println!("stdout fd = {}", super::__LIBC_STDOUT);
501 }
502
503 #[test]
504 fn test_stderr() {
505 super::libc_eprintln!("stderr fd = {}", super::__LIBC_STDERR);
506 }
507
508 #[test]
509 fn test_stdout_write() {
510 super::libc_writeln!("stdout!");
511 }
512
513 #[test]
514 fn test_stderr_write() {
515 super::libc_ewriteln!("stderr!");
516 }
517
518 #[test]
519 fn test_dbg() {
520 let a = 2;
521 let b = libc_dbg!(a * 2) + 1;
522 assert_eq!(b, 5);
523 }
524
525 #[test]
526 fn test_in_closure_expression() {
527 use super::std_name::*;
528 let _ = Result::<(), ()>::Ok(()).unwrap_or_else(|err| eprintln!("error: {:?}", err));
530 }
531}