Skip to content

Commit 026b3b8

Browse files
committed
Auto merge of rust-lang#117174 - Ayush1325:uefi-stdio-improve, r=workingjubilee
Improve UEFI stdio Fixed some things suggested in last PR: rust-lang#116207 cc `@dvdhrm` cc `@nicholasbishop`
2 parents c1b478e + 1fbb00b commit 026b3b8

File tree

1 file changed

+94
-37
lines changed

1 file changed

+94
-37
lines changed

library/std/src/sys/pal/uefi/stdio.rs

+94-37
Original file line numberDiff line numberDiff line change
@@ -4,51 +4,100 @@ use crate::mem::MaybeUninit;
44
use crate::os::uefi;
55
use crate::ptr::NonNull;
66

7-
const MAX_BUFFER_SIZE: usize = 8192;
7+
pub struct Stdin {
8+
surrogate: Option<u16>,
9+
incomplete_utf8: IncompleteUtf8,
10+
}
11+
12+
struct IncompleteUtf8 {
13+
bytes: [u8; 4],
14+
len: u8,
15+
}
16+
17+
impl IncompleteUtf8 {
18+
pub const fn new() -> IncompleteUtf8 {
19+
IncompleteUtf8 { bytes: [0; 4], len: 0 }
20+
}
21+
22+
// Implemented for use in Stdin::read.
23+
fn read(&mut self, buf: &mut [u8]) -> usize {
24+
// Write to buffer until the buffer is full or we run out of bytes.
25+
let to_write = crate::cmp::min(buf.len(), self.len as usize);
26+
buf[..to_write].copy_from_slice(&self.bytes[..to_write]);
27+
28+
// Rotate the remaining bytes if not enough remaining space in buffer.
29+
if usize::from(self.len) > buf.len() {
30+
self.bytes.copy_within(to_write.., 0);
31+
self.len -= to_write as u8;
32+
} else {
33+
self.len = 0;
34+
}
35+
36+
to_write
37+
}
38+
}
839

9-
pub struct Stdin;
1040
pub struct Stdout;
1141
pub struct Stderr;
1242

1343
impl Stdin {
1444
pub const fn new() -> Stdin {
15-
Stdin
45+
Stdin { surrogate: None, incomplete_utf8: IncompleteUtf8::new() }
1646
}
1747
}
1848

1949
impl io::Read for Stdin {
2050
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
21-
let st: NonNull<r_efi::efi::SystemTable> = uefi::env::system_table().cast();
22-
let stdin = unsafe { (*st.as_ptr()).con_in };
23-
24-
// Try reading any pending data
25-
let inp = match read_key_stroke(stdin) {
26-
Ok(x) => x,
27-
Err(e) if e == r_efi::efi::Status::NOT_READY => {
28-
// Wait for keypress for new data
29-
wait_stdin(stdin)?;
30-
read_key_stroke(stdin).map_err(|x| io::Error::from_raw_os_error(x.as_usize()))?
31-
}
32-
Err(e) => {
33-
return Err(io::Error::from_raw_os_error(e.as_usize()));
34-
}
51+
// If there are bytes in the incomplete utf-8, start with those.
52+
// (No-op if there is nothing in the buffer.)
53+
let mut bytes_copied = self.incomplete_utf8.read(buf);
54+
55+
let stdin: *mut r_efi::protocols::simple_text_input::Protocol = unsafe {
56+
let st: NonNull<r_efi::efi::SystemTable> = uefi::env::system_table().cast();
57+
(*st.as_ptr()).con_in
3558
};
3659

37-
// Check if the key is printiable character
38-
if inp.scan_code != 0x00 {
39-
return Err(io::const_io_error!(io::ErrorKind::Interrupted, "Special Key Press"));
60+
if bytes_copied == buf.len() {
61+
return Ok(bytes_copied);
4062
}
4163

42-
// SAFETY: Iterator will have only 1 character since we are reading only 1 Key
43-
// SAFETY: This character will always be UCS-2 and thus no surrogates.
44-
let ch: char = char::decode_utf16([inp.unicode_char]).next().unwrap().unwrap();
45-
if ch.len_utf8() > buf.len() {
46-
return Ok(0);
64+
let ch = simple_text_input_read(stdin)?;
65+
// Only 1 character should be returned.
66+
let mut ch: Vec<Result<char, crate::char::DecodeUtf16Error>> =
67+
if let Some(x) = self.surrogate.take() {
68+
char::decode_utf16([x, ch]).collect()
69+
} else {
70+
char::decode_utf16([ch]).collect()
71+
};
72+
73+
if ch.len() > 1 {
74+
return Err(io::Error::new(io::ErrorKind::InvalidData, "invalid utf-16 sequence"));
4775
}
4876

49-
ch.encode_utf8(buf);
77+
match ch.pop().unwrap() {
78+
Err(e) => {
79+
self.surrogate = Some(e.unpaired_surrogate());
80+
}
81+
Ok(x) => {
82+
// This will always be > 0
83+
let buf_free_count = buf.len() - bytes_copied;
84+
assert!(buf_free_count > 0);
85+
86+
if buf_free_count >= x.len_utf8() {
87+
// There is enough space in the buffer for the character.
88+
bytes_copied += x.encode_utf8(&mut buf[bytes_copied..]).len();
89+
} else {
90+
// There is not enough space in the buffer for the character.
91+
// Store the character in the incomplete buffer.
92+
self.incomplete_utf8.len =
93+
x.encode_utf8(&mut self.incomplete_utf8.bytes).len() as u8;
94+
// write partial character to buffer.
95+
bytes_copied += self.incomplete_utf8.read(buf);
96+
}
97+
}
98+
}
5099

51-
Ok(ch.len_utf8())
100+
Ok(bytes_copied)
52101
}
53102
}
54103

@@ -90,11 +139,11 @@ impl io::Write for Stderr {
90139
}
91140
}
92141

93-
// UCS-2 character should occupy 3 bytes at most in UTF-8
94-
pub const STDIN_BUF_SIZE: usize = 3;
142+
// UTF-16 character should occupy 4 bytes at most in UTF-8
143+
pub const STDIN_BUF_SIZE: usize = 4;
95144

96145
pub fn is_ebadf(_err: &io::Error) -> bool {
97-
true
146+
false
98147
}
99148

100149
pub fn panic_output() -> Option<impl io::Write> {
@@ -105,19 +154,15 @@ fn write(
105154
protocol: *mut r_efi::protocols::simple_text_output::Protocol,
106155
buf: &[u8],
107156
) -> io::Result<usize> {
108-
let mut utf16 = [0; MAX_BUFFER_SIZE / 2];
109-
110157
// Get valid UTF-8 buffer
111158
let utf8 = match crate::str::from_utf8(buf) {
112159
Ok(x) => x,
113160
Err(e) => unsafe { crate::str::from_utf8_unchecked(&buf[..e.valid_up_to()]) },
114161
};
115-
// Clip UTF-8 buffer to max UTF-16 buffer we support
116-
let utf8 = &utf8[..utf8.floor_char_boundary(utf16.len() - 1)];
117162

118-
for (i, ch) in utf8.encode_utf16().enumerate() {
119-
utf16[i] = ch;
120-
}
163+
let mut utf16: Vec<u16> = utf8.encode_utf16().collect();
164+
// NULL terminate the string
165+
utf16.push(0);
121166

122167
unsafe { simple_text_output(protocol, &mut utf16) }?;
123168

@@ -132,6 +177,18 @@ unsafe fn simple_text_output(
132177
if res.is_error() { Err(io::Error::from_raw_os_error(res.as_usize())) } else { Ok(()) }
133178
}
134179

180+
fn simple_text_input_read(
181+
stdin: *mut r_efi::protocols::simple_text_input::Protocol,
182+
) -> io::Result<u16> {
183+
loop {
184+
match read_key_stroke(stdin) {
185+
Ok(x) => return Ok(x.unicode_char),
186+
Err(e) if e == r_efi::efi::Status::NOT_READY => wait_stdin(stdin)?,
187+
Err(e) => return Err(io::Error::from_raw_os_error(e.as_usize())),
188+
}
189+
}
190+
}
191+
135192
fn wait_stdin(stdin: *mut r_efi::protocols::simple_text_input::Protocol) -> io::Result<()> {
136193
let boot_services: NonNull<r_efi::efi::BootServices> =
137194
uefi::env::boot_services().unwrap().cast();

0 commit comments

Comments
 (0)