diff options
author | Egor Tensin <Egor.Tensin@gmail.com> | 2020-10-13 16:47:56 +0300 |
---|---|---|
committer | Egor Tensin <Egor.Tensin@gmail.com> | 2020-10-13 16:59:21 +0300 |
commit | a53523162f6f09d1297780d04679790f35509361 (patch) | |
tree | 6a1e85873d1682aa04bb95a2c094cc0796fb5d05 | |
parent | README: add Travis badge (diff) | |
download | winapi-utf8-a53523162f6f09d1297780d04679790f35509361.tar.gz winapi-utf8-a53523162f6f09d1297780d04679790f35509361.zip |
use SafeInt
-rw-r--r-- | .clang-format | 2 | ||||
-rw-r--r-- | .gitmodules | 3 | ||||
m--------- | 3rdparty/microsoft/SafeInt | 0 | ||||
-rw-r--r-- | CMakeLists.txt | 3 | ||||
-rw-r--r-- | src/convert.cpp | 87 |
5 files changed, 76 insertions, 19 deletions
diff --git a/.clang-format b/.clang-format index bf44ecc..12345ea 100644 --- a/.clang-format +++ b/.clang-format @@ -13,7 +13,7 @@ IncludeCategories: Priority: 1 - Regex: '^<(winapi)/' Priority: 2 - - Regex: '^<boost/' + - Regex: '^<boost\/|^<SafeInt\.' Priority: 3 - Regex: '^<.*\.h>$' Priority: 4 diff --git a/.gitmodules b/.gitmodules index d65ecb9..486e870 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "cmake"] path = cmake url = https://github.com/egor-tensin/cmake-common.git +[submodule "3rdparty/microsoft/SafeInt"] + path = 3rdparty/microsoft/SafeInt + url = https://github.com/egor-tensin/SafeInt.git diff --git a/3rdparty/microsoft/SafeInt b/3rdparty/microsoft/SafeInt new file mode 160000 +Subproject dd78d4336b87d0d741446697a32618fa8e4dbc9 diff --git a/CMakeLists.txt b/CMakeLists.txt index 5588dd6..c868a3b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,10 +4,13 @@ project(winapi_utf8 CXX) include(cmake/common.cmake) +add_subdirectory(3rdparty/microsoft/SafeInt) + file(GLOB_RECURSE winapi_utf8_include "include/*.hpp") file(GLOB winapi_utf8_src "src/*.cpp") add_library(winapi_utf8 ${winapi_utf8_include} ${winapi_utf8_src}) target_include_directories(winapi_utf8 PUBLIC include/) +target_link_libraries(winapi_utf8 PRIVATE SafeInt) # Vista is the lower bound (due to WC_ERR_INVALID_CHARS): if(MINGW) diff --git a/src/convert.cpp b/src/convert.cpp index 303ead2..c30acb9 100644 --- a/src/convert.cpp +++ b/src/convert.cpp @@ -5,6 +5,8 @@ #include <winapi/utf8.hpp> +#include <SafeInt.hpp> + #include <windows.h> #include <cstddef> @@ -22,6 +24,62 @@ std::runtime_error error(const char* function, DWORD code) { return std::runtime_error{oss.str()}; } +int convert_input_bytes_to_bytes(std::size_t nb) { + int real_nb = 0; + + if (!SafeCast(nb, real_nb)) { + std::ostringstream oss; + oss << "input buffer is too large at " << nb << " bytes"; + throw std::runtime_error{oss.str()}; + } + + return real_nb; +} + +int convert_input_bytes_to_chars(std::size_t nb) { + if (nb % sizeof(WCHAR) != 0) { + std::ostringstream oss; + oss << "invalid buffer size: " << nb; + throw std::runtime_error{oss.str()}; + } + + const std::size_t nch = nb / sizeof(WCHAR); + + int real_nch = 0; + + if (!SafeCast(nch, real_nch)) { + std::ostringstream oss; + oss << "input buffer is too large at " << nch << " characters"; + throw std::runtime_error{oss.str()}; + } + + return real_nch; +} + +template <typename CharT> +std::vector<CharT> output_buffer(int size) { + std::size_t real_size = 0; + + if (!SafeCast(size, real_size)) { + std::ostringstream oss; + oss << "invalid buffer size " << size; + throw std::runtime_error{oss.str()}; + } + + std::vector<CharT> buffer; + buffer.resize(real_size); + return buffer; +} + +template <typename CharT> +void verify_output(const std::vector<CharT>& expected, int actual_size) { + if (!SafeEquals(expected.size(), actual_size)) { + std::ostringstream oss; + oss << "expected output length " << expected.size() << ", got " << actual_size; + throw std::runtime_error{oss.str()}; + } +} + } // namespace std::wstring widen(const std::string& src) { @@ -35,24 +93,25 @@ std::wstring widen(const std::vector<unsigned char>& src) { std::wstring widen(const void* src, std::size_t in_nb) { const DWORD flags = MB_ERR_INVALID_CHARS; - const char* in_data = reinterpret_cast<const char*>(src); + const auto in_data = reinterpret_cast<const char*>(src); + const auto real_in_nb = convert_input_bytes_to_bytes(in_nb); - auto out_nch = ::MultiByteToWideChar(CP_UTF8, flags, in_data, in_nb, NULL, 0); + auto out_nch = ::MultiByteToWideChar(CP_UTF8, flags, in_data, real_in_nb, NULL, 0); if (out_nch == 0) { throw error("MultiByteToWideChar", GetLastError()); } static_assert(sizeof(wchar_t) == sizeof(WCHAR), "wchar_t != WCHAR"); - std::vector<wchar_t> out; - out.resize(out_nch); + auto out = output_buffer<wchar_t>(out_nch); - out_nch = ::MultiByteToWideChar(CP_UTF8, flags, in_data, in_nb, out.data(), out.size()); + out_nch = ::MultiByteToWideChar(CP_UTF8, flags, in_data, real_in_nb, out.data(), out_nch); if (out_nch == 0) { throw error("MultiByteToWideChar", GetLastError()); } + verify_output(out, out_nch); return {out.data(), out.size()}; } @@ -66,17 +125,10 @@ std::string narrow(const std::vector<unsigned char>& src) { } std::string narrow(const void* src, std::size_t in_nb) { - if (in_nb % sizeof(WCHAR) != 0) { - std::ostringstream err_msg; - err_msg << "narrow: invalid buffer size: " << in_nb; - throw std::runtime_error{err_msg.str()}; - } - - const std::size_t in_nch = in_nb / sizeof(WCHAR); - const DWORD flags = WC_ERR_INVALID_CHARS; - const wchar_t* in_data = reinterpret_cast<const wchar_t*>(src); + const auto in_data = reinterpret_cast<const wchar_t*>(src); + const auto in_nch = convert_input_bytes_to_chars(in_nb); auto out_nb = ::WideCharToMultiByte(CP_UTF8, flags, in_data, in_nch, NULL, 0, NULL, NULL); @@ -84,16 +136,15 @@ std::string narrow(const void* src, std::size_t in_nb) { throw error("WideCharToMultiByte", GetLastError()); } - std::vector<char> out; - out.resize(out_nb); + auto out = output_buffer<char>(out_nb); - out_nb = - ::WideCharToMultiByte(CP_UTF8, flags, in_data, in_nch, out.data(), out.size(), NULL, NULL); + out_nb = ::WideCharToMultiByte(CP_UTF8, flags, in_data, in_nch, out.data(), out_nb, NULL, NULL); if (out_nb == 0) { throw error("WideCharToMultiByte", GetLastError()); } + verify_output(out, out_nb); return {out.data(), out.size()}; } |