axstd/io/
stdio.rs

1use crate::io::{self, BufReader, prelude::*};
2use crate::sync::{Mutex, MutexGuard};
3
4#[cfg(feature = "alloc")]
5use alloc::{string::String, vec::Vec};
6
7struct StdinRaw;
8struct StdoutRaw;
9
10impl Read for StdinRaw {
11    // Non-blocking read, returns number of bytes read.
12    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
13        let mut read_len = 0;
14        while read_len < buf.len() {
15            let len = arceos_api::stdio::ax_console_read_bytes(buf[read_len..].as_mut())?;
16            if len == 0 {
17                break;
18            }
19            read_len += len;
20        }
21        Ok(read_len)
22    }
23}
24
25impl Write for StdoutRaw {
26    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
27        arceos_api::stdio::ax_console_write_bytes(buf)
28    }
29    fn flush(&mut self) -> io::Result<()> {
30        Ok(())
31    }
32}
33
34/// A handle to the standard input stream of a process.
35pub struct Stdin {
36    inner: &'static Mutex<BufReader<StdinRaw>>,
37}
38
39/// A locked reference to the [`Stdin`] handle.
40pub struct StdinLock<'a> {
41    inner: MutexGuard<'a, BufReader<StdinRaw>>,
42}
43
44impl Stdin {
45    /// Locks this handle to the standard input stream, returning a readable
46    /// guard.
47    ///
48    /// The lock is released when the returned lock goes out of scope. The
49    /// returned guard also implements the [`Read`] and [`BufRead`] traits for
50    /// accessing the underlying data.
51    pub fn lock(&self) -> StdinLock<'static> {
52        // Locks this handle with 'static lifetime. This depends on the
53        // implementation detail that the underlying `Mutex` is static.
54        StdinLock {
55            inner: self.inner.lock(),
56        }
57    }
58
59    /// Locks this handle and reads a line of input, appending it to the specified buffer.
60    #[cfg(feature = "alloc")]
61    pub fn read_line(&self, buf: &mut String) -> io::Result<usize> {
62        self.inner.lock().read_line(buf)
63    }
64}
65
66impl Read for Stdin {
67    // Block until at least one byte is read.
68    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
69        let read_len = self.inner.lock().read(buf)?;
70        if buf.is_empty() || read_len > 0 {
71            return Ok(read_len);
72        }
73        // try again until we got something
74        loop {
75            let read_len = self.inner.lock().read(buf)?;
76            if read_len > 0 {
77                return Ok(read_len);
78            }
79            crate::thread::yield_now();
80        }
81    }
82}
83
84impl Read for StdinLock<'_> {
85    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
86        self.inner.read(buf)
87    }
88}
89
90impl BufRead for StdinLock<'_> {
91    fn fill_buf(&mut self) -> io::Result<&[u8]> {
92        self.inner.fill_buf()
93    }
94
95    fn consume(&mut self, n: usize) {
96        self.inner.consume(n)
97    }
98
99    #[cfg(feature = "alloc")]
100    fn read_until(&mut self, byte: u8, buf: &mut Vec<u8>) -> io::Result<usize> {
101        self.inner.read_until(byte, buf)
102    }
103
104    #[cfg(feature = "alloc")]
105    fn read_line(&mut self, buf: &mut String) -> io::Result<usize> {
106        self.inner.read_line(buf)
107    }
108}
109
110/// A handle to the global standard output stream of the current process.
111pub struct Stdout {
112    inner: &'static Mutex<StdoutRaw>,
113}
114
115/// A locked reference to the [`Stdout`] handle.
116pub struct StdoutLock<'a> {
117    inner: MutexGuard<'a, StdoutRaw>,
118}
119
120impl Stdout {
121    /// Locks this handle to the standard output stream, returning a writable
122    /// guard.
123    ///
124    /// The lock is released when the returned lock goes out of scope. The
125    /// returned guard also implements the `Write` trait for writing data.
126    pub fn lock(&self) -> StdoutLock<'static> {
127        StdoutLock {
128            inner: self.inner.lock(),
129        }
130    }
131}
132
133impl Write for Stdout {
134    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
135        self.inner.lock().write(buf)
136    }
137    fn flush(&mut self) -> io::Result<()> {
138        self.inner.lock().flush()
139    }
140}
141
142impl Write for StdoutLock<'_> {
143    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
144        self.inner.write(buf)
145    }
146    fn flush(&mut self) -> io::Result<()> {
147        self.inner.flush()
148    }
149}
150
151/// Constructs a new handle to the standard input of the current process.
152pub fn stdin() -> Stdin {
153    static INSTANCE: Mutex<BufReader<StdinRaw>> = Mutex::new(BufReader::new(StdinRaw));
154    Stdin { inner: &INSTANCE }
155}
156
157/// Constructs a new handle to the standard output of the current process.
158pub fn stdout() -> Stdout {
159    static INSTANCE: Mutex<StdoutRaw> = Mutex::new(StdoutRaw);
160    Stdout { inner: &INSTANCE }
161}
162
163#[doc(hidden)]
164pub fn __print_impl(args: core::fmt::Arguments) {
165    if cfg!(feature = "smp") {
166        // synchronize using the lock in axlog, to avoid interleaving
167        // with kernel logs
168        arceos_api::stdio::ax_console_write_fmt(args).unwrap();
169    } else {
170        stdout().lock().write_fmt(args).unwrap();
171    }
172}