Echo Writes Code

windows_unicode.cpp

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
#include "crucible/core/windows_unicode.hpp"

#include <limits>

#include <Windows.h>

namespace crucible
{
  auto bulk_transcode_utf8_to_wide(String_View const &utf8) -> WindowsUnicodeResult<FFIString<wchar_t>>
  {
    constexpr ::UINT CODEPAGE { CP_UTF8 };
    constexpr ::DWORD FLAGS { MB_ERR_INVALID_CHARS };

    if (utf8.size() > std::numeric_limits<int>::max()) {
      return make_failure<WindowsUnicodeError>(MultiByteToWideCharInputTooLarge {});
    }

    auto const utf8_size { static_cast<int>(utf8_chars.size()) };

    // 1st call: find out how big of a string we need to allocate
    ::SetLastError(::ERROR_SUCCESS);
    int const required_size {
      ::MultiByteToWideChar(
        CODEPAGE,
        FLAGS,
        utf8.data(),
        utf8_size,
        nullptr,
        0)
    };

    if (required_size <= 0) {
      return make_failure<WindowsUnicodeError>(MultiByteToWideCharFailed {});
    }

    auto wide { make_heap_buffer<wchar_t>(required_size) };

    // 2nd call: actually do the transcoding
    ::SetLastError(::ERROR_SUCCESS);
    int const characters_written {
      ::MultiByteToWideChar(
        CODEPAGE,
        FLAGS,
        utf8.data(),
        utf8_size,
        wide.data(),
        required_size)
    };

    if (characters_written <= 0) {
      return make_failure<WindowsUnicodeError>(MultiByteToWideCharFailed {});
    }

    if (characters_written != required_size) {
      return make_failure<WindowsUnicodeError>(MultiByteToWideCharOutputTruncated {});
    }

    return make_success(std::move(wide));
  }

  auto bulk_transcode_wide_to_utf8(FFIString<wchar_t> const &wide) -> WindowsUnicodeResult<String>
  {
    constexpr ::UINT CODEPAGE { CP_UTF8 };
    constexpr ::DWORD FLAGS { WC_ERR_INVALID_CHARS };

    ::LPCCH default_character { nullptr };
    ::LPBOOL used_default_character { nullptr };

    if (wide.size() > std::numeric_limits<int>::max()) {
      return make_failure<WindowsUnicodeError>(WideCharToMultiByteInputTooLarge {});
    }

    auto const wide_size { static_cast<int>(wide.size()) };

    // 1st call: find out how big of a string we need to allocate
    ::SetLastError(::ERROR_SUCCESS);
    int const required_size {
      ::WideCharToMultiByte(
        CODEPAGE,
        FLAGS,
        wide.data(),
        wide_size,
        nullptr,
        0,
        default_character,
        used_default_character)
    };

    if (required_size <= 0) {
      return make_failure<WindowsUnicodeError>(WideCharToMultiByteFailed {});
    }

    auto utf8 { make_heap_buffer<char>(required_size) };

    // 2nd call: actually do the transcoding
    ::SetLastError(::ERROR_SUCCESS);
    int const bytes_written {
      ::WideCharToMultiByte(
        CODEPAGE,
        FLAGS,
        wide.data(),
        wide_size,
        utf8.data(),
        required_size,
        default_character,
        used_default_character)
    };

    if (bytes_written <= 0) {
      return make_failure<WindowsUnicodeError>(WideCharToMultiByteFailed {});
    }

    if (bytes_written != required_size) {
      return make_failure<WindowsUnicodeError>(WideCharToMultiByteOutputTruncated {});
    }

    return make_success(std::move(utf8));
  }
}