summaryrefslogtreecommitdiffhomepage
path: root/talpid-windows/src/string.rs
blob: 28172d88d69dae3f7f9980cd5d8955642e3fb3e4 (plain)
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
use std::ffi::CStr;
use std::io;
use std::ptr;
use windows_sys::Win32::Globalization::MultiByteToWideChar;

/// Convert `mb_string`, with the given character encoding `codepage`, to a UTF-16 string.
pub fn multibyte_to_wide(mb_string: &CStr, codepage: u32) -> Result<Vec<u16>, io::Error> {
    if mb_string.is_empty() {
        return Ok(vec![]);
    }

    // SAFETY: `mb_string` is null-terminated and valid.
    let wc_size = unsafe {
        MultiByteToWideChar(
            codepage,
            0,
            mb_string.as_ptr() as *const u8,
            -1,
            ptr::null_mut(),
            0,
        )
    };

    if wc_size == 0 {
        return Err(io::Error::last_os_error());
    }

    let mut wc_buffer = vec![0u16; usize::try_from(wc_size).unwrap()];

    // SAFETY: `wc_buffer` can contain up to `wc_size` characters, including a null
    // terminator.
    let chars_written = unsafe {
        MultiByteToWideChar(
            codepage,
            0,
            mb_string.as_ptr() as *const u8,
            -1,
            wc_buffer.as_mut_ptr(),
            wc_size,
        )
    };

    if chars_written == 0 {
        return Err(io::Error::last_os_error());
    }

    wc_buffer.truncate(usize::try_from(chars_written - 1).unwrap());

    Ok(wc_buffer)
}

#[cfg(test)]
mod test {
    use super::*;
    use windows_sys::Win32::Globalization::CP_UTF8;

    #[test]
    fn test_multibyte_to_wide() {
        // € = 0x20AC in UTF-16
        let converted = multibyte_to_wide(c"€€", CP_UTF8);
        const EXPECTED: &[u16] = &[0x20AC, 0x20AC];
        assert!(
            matches!(converted.as_deref(), Ok(EXPECTED)),
            "expected Ok({EXPECTED:?}), got {converted:?}",
        );

        // boundary case
        let converted = multibyte_to_wide(c"", CP_UTF8);
        assert!(
            matches!(converted.as_deref(), Ok([])),
            "unexpected result {converted:?}"
        );
    }
}