1#![unstable(issue = "none", feature = "windows_stdio")]
2
3use core::char::MAX_LEN_UTF8;
4use core::str::utf8_char_width;
5
6use crate::mem::MaybeUninit;
7use crate::os::windows::io::{FromRawHandle, IntoRawHandle};
8use crate::sys::handle::Handle;
9use crate::sys::pal::api::{self, WinError};
10use crate::sys::{c, cvt};
11use crate::{cmp, io, ptr, str};
12
13#[cfg(test)]
14mod tests;
15
16pub struct Stdin {
19 surrogate: u16,
20 incomplete_utf8: IncompleteUtf8,
21}
22
23pub struct Stdout {
24 incomplete_utf8: IncompleteUtf8,
25}
26
27pub struct Stderr {
28 incomplete_utf8: IncompleteUtf8,
29}
30
31struct IncompleteUtf8 {
32 bytes: [u8; 4],
33 len: u8,
34}
35
36impl IncompleteUtf8 {
37 fn read(&mut self, buf: &mut [u8]) -> usize {
39 let to_write = cmp::min(buf.len(), self.len as usize);
41 buf[..to_write].copy_from_slice(&self.bytes[..to_write]);
42
43 if usize::from(self.len) > buf.len() {
45 self.bytes.copy_within(to_write.., 0);
46 self.len -= to_write as u8;
47 } else {
48 self.len = 0;
49 }
50
51 to_write
52 }
53}
54
55const MAX_BUFFER_SIZE: usize = 8192;
63
64pub const STDIN_BUF_SIZE: usize = MAX_BUFFER_SIZE / 2 * 3;
68
69pub fn get_handle(handle_id: u32) -> io::Result<c::HANDLE> {
70 let handle = unsafe { c::GetStdHandle(handle_id) };
71 if handle == c::INVALID_HANDLE_VALUE {
72 Err(io::Error::last_os_error())
73 } else if handle.is_null() {
74 Err(io::Error::from_raw_os_error(c::ERROR_INVALID_HANDLE as i32))
75 } else {
76 Ok(handle)
77 }
78}
79
80fn is_console(handle: c::HANDLE) -> bool {
81 let mut mode = 0;
85 unsafe { c::GetConsoleMode(handle, &mut mode) != 0 }
86}
87
88#[cfg(not(target_vendor = "win7"))]
90fn is_utf8_console() -> bool {
91 unsafe { c::GetConsoleOutputCP() == c::CP_UTF8 }
92}
93
94#[cfg(target_vendor = "win7")]
95fn is_utf8_console() -> bool {
96 false
100}
101
102fn write(handle_id: u32, data: &[u8], incomplete_utf8: &mut IncompleteUtf8) -> io::Result<usize> {
103 if data.is_empty() {
104 return Ok(0);
105 }
106
107 let handle = get_handle(handle_id)?;
108 if !is_console(handle) || is_utf8_console() {
109 unsafe {
110 let handle = Handle::from_raw_handle(handle);
111 let ret = handle.write(data);
112 let _ = handle.into_raw_handle(); return ret;
114 }
115 } else {
116 write_console_utf16(data, incomplete_utf8, handle)
117 }
118}
119
120fn write_console_utf16(
121 data: &[u8],
122 incomplete_utf8: &mut IncompleteUtf8,
123 handle: c::HANDLE,
124) -> io::Result<usize> {
125 if incomplete_utf8.len > 0 {
126 assert!(
127 incomplete_utf8.len < 4,
128 "Unexpected number of bytes for incomplete UTF-8 codepoint."
129 );
130 if data[0] >> 6 != 0b10 {
131 incomplete_utf8.len = 0;
133 return Err(io::const_error!(
134 io::ErrorKind::InvalidData,
135 "Windows stdio in console mode does not support writing non-UTF-8 byte sequences",
136 ));
137 }
138 incomplete_utf8.bytes[incomplete_utf8.len as usize] = data[0];
139 incomplete_utf8.len += 1;
140 let char_width = utf8_char_width(incomplete_utf8.bytes[0]);
141 if (incomplete_utf8.len as usize) < char_width {
142 return Ok(1);
144 }
145 let s = str::from_utf8(&incomplete_utf8.bytes[0..incomplete_utf8.len as usize]);
146 incomplete_utf8.len = 0;
147 match s {
148 Ok(s) => {
149 assert_eq!(char_width, s.len());
150 let written = write_valid_utf8_to_console(handle, s)?;
151 assert_eq!(written, s.len()); return Ok(1);
153 }
154 Err(_) => {
155 return Err(io::const_error!(
156 io::ErrorKind::InvalidData,
157 "Windows stdio in console mode does not support writing non-UTF-8 byte sequences",
158 ));
159 }
160 }
161 }
162
163 let len = cmp::min(data.len(), MAX_BUFFER_SIZE / 2);
170 let utf8 = match str::from_utf8(&data[..len]) {
171 Ok(s) => s,
172 Err(ref e) if e.valid_up_to() == 0 => {
173 let first_byte_char_width = utf8_char_width(data[0]);
174 if first_byte_char_width > 1 && data.len() < first_byte_char_width {
175 incomplete_utf8.bytes[0] = data[0];
176 incomplete_utf8.len = 1;
177 return Ok(1);
178 } else {
179 return Err(io::const_error!(
180 io::ErrorKind::InvalidData,
181 "Windows stdio in console mode does not support writing non-UTF-8 byte sequences",
182 ));
183 }
184 }
185 Err(e) => str::from_utf8(&data[..e.valid_up_to()]).unwrap(),
186 };
187
188 write_valid_utf8_to_console(handle, utf8)
189}
190
191fn write_valid_utf8_to_console(handle: c::HANDLE, utf8: &str) -> io::Result<usize> {
192 debug_assert!(!utf8.is_empty());
193
194 let mut utf16 = [MaybeUninit::<u16>::uninit(); MAX_BUFFER_SIZE / 2];
195 let utf8 = &utf8[..utf8.floor_char_boundary(utf16.len())];
196
197 let utf16: &[u16] = unsafe {
198 let result = c::MultiByteToWideChar(
201 c::CP_UTF8, c::MB_ERR_INVALID_CHARS, utf8.as_ptr(), utf8.len() as i32, utf16.as_mut_ptr() as *mut c::WCHAR, utf16.len() as i32, );
208 assert!(result != 0, "Unexpected error in MultiByteToWideChar");
209
210 utf16[..result as usize].assume_init_ref()
212 };
213
214 let mut written = write_u16s(handle, utf16)?;
215
216 if written == utf16.len() {
218 Ok(utf8.len())
219 } else {
220 let first_code_unit_remaining = utf16[written];
226 if matches!(first_code_unit_remaining, 0xDCEE..=0xDFFF) {
227 let _ = write_u16s(handle, &utf16[written..written + 1]);
230 written += 1;
231 }
232 let mut count = 0;
234 for ch in utf16[..written].iter() {
235 count += match ch {
236 0x0000..=0x007F => 1,
237 0x0080..=0x07FF => 2,
238 0xDCEE..=0xDFFF => 1, _ => 3,
240 };
241 }
242 debug_assert!(String::from_utf16(&utf16[..written]).unwrap() == utf8[..count]);
243 Ok(count)
244 }
245}
246
247fn write_u16s(handle: c::HANDLE, data: &[u16]) -> io::Result<usize> {
248 debug_assert!(data.len() < u32::MAX as usize);
249 let mut written = 0;
250 cvt(unsafe {
251 c::WriteConsoleW(handle, data.as_ptr(), data.len() as u32, &mut written, ptr::null_mut())
252 })?;
253 Ok(written as usize)
254}
255
256impl Stdin {
257 pub const fn new() -> Stdin {
258 Stdin { surrogate: 0, incomplete_utf8: IncompleteUtf8::new() }
259 }
260}
261
262impl io::Read for Stdin {
263 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
264 let handle = get_handle(c::STD_INPUT_HANDLE)?;
265 if !is_console(handle) {
266 unsafe {
267 let handle = Handle::from_raw_handle(handle);
268 let ret = handle.read(buf);
269 let _ = handle.into_raw_handle(); return ret;
271 }
272 }
273
274 let mut bytes_copied = self.incomplete_utf8.read(buf);
277
278 if bytes_copied == buf.len() {
279 Ok(bytes_copied)
280 } else if buf.len() - bytes_copied < 4 {
281 let mut utf16_buf = [MaybeUninit::new(0); 1];
283 let read = read_u16s_fixup_surrogates(handle, &mut utf16_buf, 1, &mut self.surrogate)?;
285 let read_bytes = utf16_to_utf8(
287 unsafe { utf16_buf[..read].assume_init_ref() },
288 &mut self.incomplete_utf8.bytes,
289 )?;
290
291 self.incomplete_utf8.len = read_bytes as u8;
293 bytes_copied += self.incomplete_utf8.read(&mut buf[bytes_copied..]);
295 Ok(bytes_copied)
296 } else {
297 let mut utf16_buf = [MaybeUninit::<u16>::uninit(); MAX_BUFFER_SIZE / 2];
298
299 let amount = cmp::min(buf.len() / 3, utf16_buf.len());
303 let read =
304 read_u16s_fixup_surrogates(handle, &mut utf16_buf, amount, &mut self.surrogate)?;
305 let utf16s = unsafe { utf16_buf[..read].assume_init_ref() };
308 match utf16_to_utf8(utf16s, buf) {
309 Ok(value) => return Ok(bytes_copied + value),
310 Err(e) => return Err(e),
311 }
312 }
313 }
314}
315
316fn read_u16s_fixup_surrogates(
320 handle: c::HANDLE,
321 buf: &mut [MaybeUninit<u16>],
322 mut amount: usize,
323 surrogate: &mut u16,
324) -> io::Result<usize> {
325 let mut start = 0;
327 if *surrogate != 0 {
328 buf[0] = MaybeUninit::new(*surrogate);
329 *surrogate = 0;
330 start = 1;
331 if amount == 1 {
332 amount = 2;
336 }
337 }
338 let mut amount = read_u16s(handle, &mut buf[start..amount])? + start;
339
340 if amount > 0 {
341 let last_char = unsafe { buf[amount - 1].assume_init() };
345 if matches!(last_char, 0xD800..=0xDBFF) {
346 *surrogate = last_char;
348 amount -= 1;
349 }
350 }
351 Ok(amount)
352}
353
354fn read_u16s(handle: c::HANDLE, buf: &mut [MaybeUninit<u16>]) -> io::Result<usize> {
356 const CTRL_Z: u16 = 0x1A;
360 const CTRL_Z_MASK: u32 = 1 << CTRL_Z;
361 let input_control = c::CONSOLE_READCONSOLE_CONTROL {
362 nLength: size_of::<c::CONSOLE_READCONSOLE_CONTROL>() as u32,
363 nInitialChars: 0,
364 dwCtrlWakeupMask: CTRL_Z_MASK,
365 dwControlKeyState: 0,
366 };
367
368 let mut amount = 0;
369 loop {
370 cvt(unsafe {
371 c::SetLastError(0);
372 c::ReadConsoleW(
373 handle,
374 buf.as_mut_ptr() as *mut core::ffi::c_void,
375 buf.len() as u32,
376 &mut amount,
377 &input_control,
378 )
379 })?;
380
381 if amount == 0 && api::get_last_error() == WinError::OPERATION_ABORTED {
384 continue;
385 }
386 break;
387 }
388 if amount > 0 && unsafe { buf[amount as usize - 1].assume_init() } == CTRL_Z {
391 amount -= 1;
392 }
393 Ok(amount as usize)
394}
395
396fn utf16_to_utf8(utf16: &[u16], utf8: &mut [u8]) -> io::Result<usize> {
397 debug_assert!(utf16.len() <= i32::MAX as usize);
398 debug_assert!(utf8.len() <= i32::MAX as usize);
399
400 if utf16.is_empty() {
401 return Ok(0);
402 }
403
404 let result = unsafe {
405 c::WideCharToMultiByte(
406 c::CP_UTF8, c::WC_ERR_INVALID_CHARS, utf16.as_ptr(), utf16.len() as i32, utf8.as_mut_ptr(), utf8.len() as i32, ptr::null(), ptr::null_mut(), )
415 };
416 if result == 0 {
417 Err(io::const_error!(
419 io::ErrorKind::InvalidData,
420 "Windows stdin in console mode does not support non-UTF-16 input; \
421 encountered unpaired surrogate",
422 ))
423 } else {
424 Ok(result as usize)
425 }
426}
427
428impl IncompleteUtf8 {
429 pub const fn new() -> IncompleteUtf8 {
430 IncompleteUtf8 { bytes: [0; MAX_LEN_UTF8], len: 0 }
431 }
432}
433
434impl Stdout {
435 pub const fn new() -> Stdout {
436 Stdout { incomplete_utf8: IncompleteUtf8::new() }
437 }
438}
439
440impl io::Write for Stdout {
441 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
442 write(c::STD_OUTPUT_HANDLE, buf, &mut self.incomplete_utf8)
443 }
444
445 fn flush(&mut self) -> io::Result<()> {
446 Ok(())
447 }
448}
449
450impl Stderr {
451 pub const fn new() -> Stderr {
452 Stderr { incomplete_utf8: IncompleteUtf8::new() }
453 }
454}
455
456impl io::Write for Stderr {
457 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
458 write(c::STD_ERROR_HANDLE, buf, &mut self.incomplete_utf8)
459 }
460
461 fn flush(&mut self) -> io::Result<()> {
462 Ok(())
463 }
464}
465
466pub fn is_ebadf(err: &io::Error) -> bool {
467 err.raw_os_error() == Some(c::ERROR_INVALID_HANDLE as i32)
468}
469
470pub fn panic_output() -> Option<impl io::Write> {
471 Some(Stderr::new())
472}