//! Terminal output. `print!`/`println!` format into a stack buffer and hand //! it to SYS_PRINT in NUL-terminated chunks, so one call is one (or a few) //! syscalls rather than libc write()'s one SYS_PUTCHAR per byte. use core::fmt::{self, Write}; use crate::sys; const CHUNK: usize = 256; /// Buffered writer to the process's terminal (stdout). pub struct Stdout { buf: [u8; CHUNK], len: usize, } impl Stdout { pub const fn new() -> Self { Stdout { buf: [0; CHUNK], len: 0 } } pub fn write_bytes(&mut self, bytes: &[u8]) { for &b in bytes { if b == 0 { // SYS_PRINT stops at NUL; send it on its own. self.flush(); unsafe { sys::syscall1(sys::SYS_PUTCHAR, 0) }; continue; } if self.len == CHUNK - 1 { self.flush(); } self.buf[self.len] = b; self.len += 1; } } pub fn flush(&mut self) { if self.len == 0 { return; } self.buf[self.len] = 0; unsafe { sys::syscall1(sys::SYS_PRINT, self.buf.as_ptr() as u64) }; self.len = 0; } } impl Default for Stdout { fn default() -> Self { Self::new() } } impl Write for Stdout { fn write_str(&mut self, s: &str) -> fmt::Result { self.write_bytes(s.as_bytes()); Ok(()) } } impl Drop for Stdout { fn drop(&mut self) { self.flush(); } } #[doc(hidden)] pub fn _print(args: fmt::Arguments) { let mut out = Stdout::new(); let _ = out.write_fmt(args); } #[macro_export] macro_rules! print { ($($arg:tt)*) => { $crate::io::_print(format_args!($($arg)*)) }; } #[macro_export] macro_rules! println { () => { $crate::print!("\n") }; ($($arg:tt)*) => { $crate::io::_print(format_args!("{}\n", format_args!($($arg)*))) }; }