Files
MontaukOS/programs/rust/montauk-rt/src/io.rs
T

83 lines
1.9 KiB
Rust

//! 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)*))) };
}