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