use crate::io; use crate::iter::Iterator; use crate::mem::MaybeUninit; use crate::os::uefi; use crate::ptr::NonNull; pub struct Stdin { surrogate: Option, incomplete_utf8: IncompleteUtf8, } struct IncompleteUtf8 { bytes: [u8; 4], len: u8, } impl IncompleteUtf8 { pub const fn new() -> IncompleteUtf8 { IncompleteUtf8 { bytes: [0; 4], len: 0 } } // Implemented for use in Stdin::read. fn read(&mut self, buf: &mut [u8]) -> usize { // Write to buffer until the buffer is full or we run out of bytes. let to_write = crate::cmp::min(buf.len(), self.len as usize); buf[..to_write].copy_from_slice(&self.bytes[..to_write]); // Rotate the remaining bytes if not enough remaining space in buffer. if usize::from(self.len) > buf.len() { self.bytes.copy_within(to_write.., 0); self.len -= to_write as u8; } else { self.len = 0; } to_write } } pub struct Stdout; pub struct Stderr; impl Stdin { pub const fn new() -> Stdin { Stdin { surrogate: None, incomplete_utf8: IncompleteUtf8::new() } } } impl io::Read for Stdin { fn read(&mut self, buf: &mut [u8]) -> io::Result { // If there are bytes in the incomplete utf-8, start with those. // (No-op if there is nothing in the buffer.) let mut bytes_copied = self.incomplete_utf8.read(buf); let stdin: *mut r_efi::protocols::simple_text_input::Protocol = unsafe { let st: NonNull = uefi::env::system_table().cast(); (*st.as_ptr()).con_in }; if bytes_copied == buf.len() { return Ok(bytes_copied); } let ch = simple_text_input_read(stdin)?; // Only 1 character should be returned. let mut ch: Vec> = if let Some(x) = self.surrogate.take() { char::decode_utf16([x, ch]).collect() } else { char::decode_utf16([ch]).collect() }; if ch.len() > 1 { return Err(io::const_error!(io::ErrorKind::InvalidData, "invalid UTF-16 sequence")); } match ch.pop().unwrap() { Err(e) => { self.surrogate = Some(e.unpaired_surrogate()); } Ok(x) => { // This will always be > 0 let buf_free_count = buf.len() - bytes_copied; assert!(buf_free_count > 0); if buf_free_count >= x.len_utf8() { // There is enough space in the buffer for the character. bytes_copied += x.encode_utf8(&mut buf[bytes_copied..]).len(); } else { // There is not enough space in the buffer for the character. // Store the character in the incomplete buffer. self.incomplete_utf8.len = x.encode_utf8(&mut self.incomplete_utf8.bytes).len() as u8; // write partial character to buffer. bytes_copied += self.incomplete_utf8.read(buf); } } } Ok(bytes_copied) } } impl Stdout { pub const fn new() -> Stdout { Stdout } } impl io::Write for Stdout { fn write(&mut self, buf: &[u8]) -> io::Result { let st: NonNull = uefi::env::system_table().cast(); let stdout = unsafe { (*st.as_ptr()).con_out }; write(stdout, buf) } fn flush(&mut self) -> io::Result<()> { Ok(()) } } impl Stderr { pub const fn new() -> Stderr { Stderr } } impl io::Write for Stderr { fn write(&mut self, buf: &[u8]) -> io::Result { let st: NonNull = uefi::env::system_table().cast(); let stderr = unsafe { (*st.as_ptr()).std_err }; write(stderr, buf) } fn flush(&mut self) -> io::Result<()> { Ok(()) } } // UTF-16 character should occupy 4 bytes at most in UTF-8 pub const STDIN_BUF_SIZE: usize = 4; pub fn is_ebadf(err: &io::Error) -> bool { if let Some(x) = err.raw_os_error() { r_efi::efi::Status::UNSUPPORTED.as_usize() == x } else { false } } pub fn panic_output() -> Option { uefi::env::try_system_table().map(|_| Stderr::new()) } fn write( protocol: *mut r_efi::protocols::simple_text_output::Protocol, buf: &[u8], ) -> io::Result { // Get valid UTF-8 buffer let utf8 = match crate::str::from_utf8(buf) { Ok(x) => x, Err(e) => unsafe { crate::str::from_utf8_unchecked(&buf[..e.valid_up_to()]) }, }; let mut utf16: Vec = utf8.encode_utf16().collect(); // NULL terminate the string utf16.push(0); unsafe { simple_text_output(protocol, &mut utf16) }?; Ok(utf8.len()) } unsafe fn simple_text_output( protocol: *mut r_efi::protocols::simple_text_output::Protocol, buf: &mut [u16], ) -> io::Result<()> { let res = unsafe { ((*protocol).output_string)(protocol, buf.as_mut_ptr()) }; if res.is_error() { Err(io::Error::from_raw_os_error(res.as_usize())) } else { Ok(()) } } fn simple_text_input_read( stdin: *mut r_efi::protocols::simple_text_input::Protocol, ) -> io::Result { loop { match read_key_stroke(stdin) { Ok(x) => return Ok(x.unicode_char), Err(e) if e == r_efi::efi::Status::NOT_READY => wait_stdin(stdin)?, Err(e) => return Err(io::Error::from_raw_os_error(e.as_usize())), } } } fn wait_stdin(stdin: *mut r_efi::protocols::simple_text_input::Protocol) -> io::Result<()> { let boot_services: NonNull = uefi::env::boot_services().unwrap().cast(); let wait_for_event = unsafe { (*boot_services.as_ptr()).wait_for_event }; let wait_for_key_event = unsafe { (*stdin).wait_for_key }; let r = { let mut x: usize = 0; (wait_for_event)(1, [wait_for_key_event].as_mut_ptr(), &mut x) }; if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } } fn read_key_stroke( stdin: *mut r_efi::protocols::simple_text_input::Protocol, ) -> Result { let mut input_key: MaybeUninit = MaybeUninit::uninit(); let r = unsafe { ((*stdin).read_key_stroke)(stdin, input_key.as_mut_ptr()) }; if r.is_error() { Err(r) } else { let input_key = unsafe { input_key.assume_init() }; Ok(input_key) } }