Files
alloc
core
alloc
array
char
convert
fmt
future
hash
iter
macros
mem
num
ops
prelude
ptr
slice
stdarch
str
stream
sync
task
unicode
std
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
#![unstable(issue = "none", feature = "windows_stdio")]

use crate::char::decode_utf16;
use crate::cmp;
use crate::io;
use crate::ptr;
use crate::str;
use crate::sys::c;
use crate::sys::cvt;
use crate::sys::handle::Handle;

// 不要缓存句柄,但应为每个 read/write 刷新它们。这使我们能够跟踪随时间变化的值 (例如,如果进程在运行时调用 `SetStdHandle`)。
// 请参见 #40490。
pub struct Stdin {
    surrogate: u16,
}
pub struct Stdout;
pub struct Stderr;

// 显然,Windows 不能处理 stdin 上的大量读取,也不能很好地写入 stdout/stderr (有关详细信息,请参见 #13304)。
//
// 从 MSDN (2011): ` 此缓冲区的存储是从共享堆分配的,用于 64 KB 大小的进程。
// 缓冲区的最大大小将取决于堆的使用情况。`
//
// 我们选择上限为 8 KiB,因为 libuv 也是这样做的,到目前为止,这似乎是可以接受的。
//
const MAX_BUFFER_SIZE: usize = 8192;

// Stdin 的 BufReader 的标准缓冲区大小应比 MAX_BUFFER_SIZE 中的 `u16` 字节多 3 倍。
// 这确保了读取的数据始终可以从 UTF-16 到 UTF-8 完全解码。
//
pub const STDIN_BUF_SIZE: usize = MAX_BUFFER_SIZE / 2 * 3;

pub fn get_handle(handle_id: c::DWORD) -> io::Result<c::HANDLE> {
    let handle = unsafe { c::GetStdHandle(handle_id) };
    if handle == c::INVALID_HANDLE_VALUE {
        Err(io::Error::last_os_error())
    } else if handle.is_null() {
        Err(io::Error::from_raw_os_error(c::ERROR_INVALID_HANDLE as i32))
    } else {
        Ok(handle)
    }
}

fn is_console(handle: c::HANDLE) -> bool {
    // `GetConsoleMode` 如果这是管道,将返回 false (0) (我们不在乎报告的模式)。
    // 这将仅检测 Windows 控制台,而不检测连接到 MSYS 等管道的其他终端。
    // 正是我们需要的,因为只有 Windows 控制台需要转换为 UTF-16。
    let mut mode = 0;
    unsafe { c::GetConsoleMode(handle, &mut mode) != 0 }
}

fn write(handle_id: c::DWORD, data: &[u8]) -> io::Result<usize> {
    let handle = get_handle(handle_id)?;
    if !is_console(handle) {
        let handle = Handle::new(handle);
        let ret = handle.write(data);
        handle.into_raw(); // 不要关闭句柄
        return ret;
    }

    // 由于控制台是用来显示文本的,因此我们假设 `data` 的字节来自字符串,并且编码为 UTF-8,需要将其编码为 UTF-16。
    //
    //
    // 如果数据无效 UTF-8,我们将写出尽可能多的字节。
    // 仅当没有有效字节时 (将在下一次调用中发生),才返回错误。
    let len = cmp::min(data.len(), MAX_BUFFER_SIZE / 2);
    let utf8 = match str::from_utf8(&data[..len]) {
        Ok(s) => s,
        Err(ref e) if e.valid_up_to() == 0 => {
            return Err(io::Error::new_const(
                io::ErrorKind::InvalidData,
                &"Windows stdio in console mode does not support writing non-UTF-8 byte sequences",
            ));
        }
        Err(e) => str::from_utf8(&data[..e.valid_up_to()]).unwrap(),
    };
    let mut utf16 = [0u16; MAX_BUFFER_SIZE / 2];
    let mut len_utf16 = 0;
    for (chr, dest) in utf8.encode_utf16().zip(utf16.iter_mut()) {
        *dest = chr;
        len_utf16 += 1;
    }
    let utf16 = &utf16[..len_utf16];

    let mut written = write_u16s(handle, &utf16)?;

    // 找出有多少字节的 UTF-8 被写为 UTF-16。
    if written == utf16.len() {
        Ok(utf8.len())
    } else {
        // 确保我们最终不会只写代理对的一半 (即使机会很小)。
        // 由于用户代码不可能重新生成 `data`,从而无法生成丢失的替代项 (并且也由于上面的 UTF-8 验证),因此请立即写出丢失的替代项。
        //
        // 缓冲它意味着我们必须说谎所写的字节数。
        //
        let first_char_remaining = utf16[written];
        if first_char_remaining >= 0xDCEE && first_char_remaining <= 0xDFFF {
            // 低代理我们只是希望这行得通,否则就放弃
            //
            let _ = write_u16s(handle, &utf16[written..written + 1]);
            written += 1;
        }
        // 计算实际写入的 `utf8` 字节数。
        let mut count = 0;
        for ch in utf16[..written].iter() {
            count += match ch {
                0x0000..=0x007F => 1,
                0x0080..=0x07FF => 2,
                0xDCEE..=0xDFFF => 1, // 低代理。我们已经为另一个计数了 3 个字节。
                _ => 3,
            };
        }
        debug_assert!(String::from_utf16(&utf16[..written]).unwrap() == utf8[..count]);
        Ok(count)
    }
}

fn write_u16s(handle: c::HANDLE, data: &[u16]) -> io::Result<usize> {
    let mut written = 0;
    cvt(unsafe {
        c::WriteConsoleW(
            handle,
            data.as_ptr() as c::LPCVOID,
            data.len() as u32,
            &mut written,
            ptr::null_mut(),
        )
    })?;
    Ok(written as usize)
}

impl Stdin {
    pub const fn new() -> Stdin {
        Stdin { surrogate: 0 }
    }
}

impl io::Read for Stdin {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        let handle = get_handle(c::STD_INPUT_HANDLE)?;
        if !is_console(handle) {
            let handle = Handle::new(handle);
            let ret = handle.read(buf);
            handle.into_raw(); // 不要关闭句柄
            return ret;
        }

        if buf.len() == 0 {
            return Ok(0);
        } else if buf.len() < 4 {
            return Err(io::Error::new_const(
                io::ErrorKind::InvalidInput,
                &"Windows stdin in console mode does not support a buffer too small to \
                 guarantee holding one arbitrary UTF-8 character (4 bytes)",
            ));
        }

        let mut utf16_buf = [0u16; MAX_BUFFER_SIZE / 2];
        // 在最坏的情况下,对于 UTF-16 中的每个 `u16`,UTF-8 字符串可能占用 3 个字节。
        // 因此,我们最多可以读取 `buf.len()` 字符的三分之一,并坚持保证不会丢失任何数据。
        //
        let amount = cmp::min(buf.len() / 3, utf16_buf.len());
        let read = read_u16s_fixup_surrogates(handle, &mut utf16_buf, amount, &mut self.surrogate)?;

        utf16_to_utf8(&utf16_buf[..read], buf)
    }
}

// 我们假设如果最后一个 `u16` 是不成对的代理,则它们将根据我们的缓冲区大小进行分割,并保留它以备下一次读取时使用,以将它们放在一起。
//
// 这是尽力而为的,如果我们不是 Stdin 上唯一的 reader,则可能无法正常工作。
fn read_u16s_fixup_surrogates(
    handle: c::HANDLE,
    buf: &mut [u16],
    mut amount: usize,
    surrogate: &mut u16,
) -> io::Result<usize> {
    // 插入上次读取的可能剩余的未配对代理。
    let mut start = 0;
    if *surrogate != 0 {
        buf[0] = *surrogate;
        *surrogate = 0;
        start = 1;
        if amount == 1 {
            // 特殊情况: `Stdin::read` 保证我们总是可以读取至少一个新的 `u16` 并将其与未配对的代理结合,因为 UTF-8 缓冲区至少是
            //
            // 4 字节。
            amount = 2;
        }
    }
    let mut amount = read_u16s(handle, &mut buf[start..amount])? + start;

    if amount > 0 {
        let last_char = buf[amount - 1];
        if last_char >= 0xD800 && last_char <= 0xDBFF {
            // 高代理
            *surrogate = last_char;
            amount -= 1;
        }
    }
    Ok(amount)
}

fn read_u16s(handle: c::HANDLE, buf: &mut [u16]) -> io::Result<usize> {
    // 配置 `pInputControl` 参数不仅要返回 `\r\n`,还要配置 Ctrl-Z,这是传统的 DOS 方法,用于指示字符流 / 用户输入 (SUB) 的结尾。
    //
    // 请参见 #38274 和 https://stackoverflow.com/questions/43836040/win-api-readconsole。
    const CTRL_Z: u16 = 0x1A;
    const CTRL_Z_MASK: c::ULONG = 1 << CTRL_Z;
    let mut input_control = c::CONSOLE_READCONSOLE_CONTROL {
        nLength: crate::mem::size_of::<c::CONSOLE_READCONSOLE_CONTROL>() as c::ULONG,
        nInitialChars: 0,
        dwCtrlWakeupMask: CTRL_Z_MASK,
        dwControlKeyState: 0,
    };

    let mut amount = 0;
    cvt(unsafe {
        c::ReadConsoleW(
            handle,
            buf.as_mut_ptr() as c::LPVOID,
            buf.len() as u32,
            &mut amount,
            &mut input_control as c::PCONSOLE_READCONSOLE_CONTROL,
        )
    })?;

    if amount > 0 && buf[amount as usize - 1] == CTRL_Z {
        amount -= 1;
    }
    Ok(amount as usize)
}

#[allow(unused)]
fn utf16_to_utf8(utf16: &[u16], utf8: &mut [u8]) -> io::Result<usize> {
    let mut written = 0;
    for chr in decode_utf16(utf16.iter().cloned()) {
        match chr {
            Ok(chr) => {
                chr.encode_utf8(&mut utf8[written..]);
                written += chr.len_utf8();
            }
            Err(_) => {
                // 我们真的无法做得比忘记所有数据并返回错误更好。
                return Err(io::Error::new_const(
                    io::ErrorKind::InvalidData,
                    &"Windows stdin in console mode does not support non-UTF-16 input; \
                     encountered unpaired surrogate",
                ));
            }
        }
    }
    Ok(written)
}

impl Stdout {
    pub const fn new() -> Stdout {
        Stdout
    }
}

impl io::Write for Stdout {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        write(c::STD_OUTPUT_HANDLE, 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<usize> {
        write(c::STD_ERROR_HANDLE, buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        Ok(())
    }
}

pub fn is_ebadf(err: &io::Error) -> bool {
    err.raw_os_error() == Some(c::ERROR_INVALID_HANDLE as i32)
}

pub fn panic_output() -> Option<impl io::Write> {
    Some(Stderr::new())
}