From d4283a45ec926a72417276a25e4064c8ee792ad4 Mon Sep 17 00:00:00 2001 From: mandreyel Date: Tue, 13 Nov 2018 10:56:28 +0100 Subject: [PATCH] Add Windows wide char path support --- README.md | 4 +- include/mio/detail/mmap.ipp | 61 ++++++++++++----- include/mio/detail/string_util.hpp | 106 ++++++++++++++++++++++++++--- test/test.cpp | 12 +++- 4 files changed, 154 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 3555ce6..d915fee 100644 --- a/README.md +++ b/README.md @@ -71,8 +71,8 @@ int main() ``` However, mio does not check whether the provided file descriptor has the same access permissions as the desired mapping, so the mapping may fail. Such errors are reported via the `std::error_code` out parameter that is passed to the mapping function. -**WINDOWS USERS**: This library doesn't support the use of wide character -types for functions where character strings are expected (e.g. path parameters). +**WINDOWS USERS**: This library *does* support the use of wide character types +for functions where character strings are expected (e.g. path parameters). ### Example diff --git a/include/mio/detail/mmap.ipp b/include/mio/detail/mmap.ipp index be6ef92..c5be288 100644 --- a/include/mio/detail/mmap.ipp +++ b/include/mio/detail/mmap.ipp @@ -38,18 +38,53 @@ namespace mio { namespace detail { #ifdef _WIN32 -/** Returns the 4 upper bytes of a 8-byte integer. */ +namespace win { + +/** Returns the 4 upper bytes of an 8-byte integer. */ inline DWORD int64_high(int64_t n) noexcept { return n >> 32; } -/** Returns the 4 lower bytes of a 8-byte integer. */ +/** Returns the 4 lower bytes of an 8-byte integer. */ inline DWORD int64_low(int64_t n) noexcept { return n & 0xffffffff; } -#endif + +template< + typename String, + typename = typename std::enable_if< + std::is_same::type, char>::value + >::type +> file_handle_type open_file_helper(const String& path, const access_mode mode) +{ + return ::CreateFileA(c_str(path), + mode == access_mode::read ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + 0, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + 0); +} + +template +typename std::enable_if< + std::is_same::type, wchar_t>::value, + file_handle_type +>::type open_file_helper(const String& path, const access_mode mode) +{ + return ::CreateFileW(c_str(path), + mode == access_mode::read ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + 0, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + 0); +} + +} // win +#endif // _WIN32 /** * Returns the last platform specific system error (errno on POSIX and @@ -67,8 +102,8 @@ inline std::error_code last_error() noexcept } template -file_handle_type open_file(const String& path, - const access_mode mode, std::error_code& error) +file_handle_type open_file(const String& path, const access_mode mode, + std::error_code& error) { error.clear(); if(detail::empty(path)) @@ -77,13 +112,7 @@ file_handle_type open_file(const String& path, return invalid_handle; } #ifdef _WIN32 - const auto handle = ::CreateFileA(c_str(path), - mode == access_mode::read ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, - 0, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - 0); + const auto handle = win::open_file_helper(path, mode); #else // POSIX const auto handle = ::open(c_str(path), mode == access_mode::read ? O_RDONLY : O_RDWR); @@ -138,8 +167,8 @@ inline mmap_context memory_map(const file_handle_type file_handle, const int64_t file_handle, 0, mode == access_mode::read ? PAGE_READONLY : PAGE_READWRITE, - int64_high(max_file_size), - int64_low(max_file_size), + win::int64_high(max_file_size), + win::int64_low(max_file_size), 0); if(file_mapping_handle == invalid_handle) { @@ -149,8 +178,8 @@ inline mmap_context memory_map(const file_handle_type file_handle, const int64_t char* mapping_start = static_cast(::MapViewOfFile( file_mapping_handle, mode == access_mode::read ? FILE_MAP_READ : FILE_MAP_WRITE, - int64_high(aligned_offset), - int64_low(aligned_offset), + win::int64_high(aligned_offset), + win::int64_low(aligned_offset), length_to_map)); if(mapping_start == nullptr) { diff --git a/include/mio/detail/string_util.hpp b/include/mio/detail/string_util.hpp index c716325..2f375aa 100644 --- a/include/mio/detail/string_util.hpp +++ b/include/mio/detail/string_util.hpp @@ -26,17 +26,79 @@ namespace mio { namespace detail { -template -struct is_c_str +template< + typename S, + typename C = typename std::decay::type, + typename = decltype(std::declval().data()), + typename = typename std::enable_if< + std::is_same::value +#ifdef _WIN32 + || std::is_same::value +#endif + >::type +> struct char_type_helper { + using type = typename C::value_type; +}; + +template +struct char_type { + using type = typename char_type_helper::type; +}; + +// TODO: can we avoid this brute force approach? +template<> +struct char_type { + using type = char; +}; + +template<> +struct char_type { + using type = char; +}; + +template +struct char_type { + using type = char; +}; + +template +struct char_type { + using type = char; +}; + +#ifdef _WIN32 +template<> +struct char_type { + using type = wchar_t; +}; + +template<> +struct char_type { + using type = wchar_t; +}; + +template +struct char_type { + using type = wchar_t; +}; + +template +struct char_type { + using type = wchar_t; +}; +#endif // _WIN32 + +template +struct is_c_str_helper { static constexpr bool value = std::is_same< - char*, + CharT*, // TODO: I'm so sorry for this... Can this be made cleaner? typename std::add_pointer< typename std::remove_cv< typename std::remove_pointer< typename std::decay< - String + S >::type >::type >::type @@ -44,11 +106,35 @@ struct is_c_str >::value; }; +template +struct is_c_str +{ + static constexpr bool value = is_c_str_helper::value; +}; + +#ifdef _WIN32 +template +struct is_c_wstr +{ + static constexpr bool value = is_c_str_helper::value; +}; +#endif // _WIN32 + +template +struct is_c_str_or_c_wstr +{ + static constexpr bool value = is_c_str::value +#ifdef _WIN32 + || is_c_wstr::value +#endif + ; +}; + template< typename String, typename = decltype(std::declval().data()), - typename = typename std::enable_if::value>::type -> const char* c_str(const String& path) + typename = typename std::enable_if::value>::type +> const typename char_type::type* c_str(const String& path) { return path.data(); } @@ -56,7 +142,7 @@ template< template< typename String, typename = decltype(std::declval().empty()), - typename = typename std::enable_if::value>::type + typename = typename std::enable_if::value>::type > bool empty(const String& path) { return path.empty(); @@ -64,15 +150,15 @@ template< template< typename String, - typename = typename std::enable_if::value>::type -> const char* c_str(String path) + typename = typename std::enable_if::value>::type +> const typename char_type::type* c_str(String path) { return path; } template< typename String, - typename = typename std::enable_if::value>::type + typename = typename std::enable_if::value>::type > bool empty(String path) { return !path || (*path == 0); diff --git a/test/test.cpp b/test/test.cpp index 36d350d..169a6c2 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -98,7 +98,7 @@ int main() } { - // Make sure custom types compile. + // Make sure these compile. mio::ummap_source _1; mio::shared_ummap_source _2; // Make sure shared_mmap mapping compiles as all testing was done on @@ -106,6 +106,16 @@ int main() mio::shared_mmap_source _3(path, 0, mio::map_entire_file); auto _4 = mio::make_mmap_source(path, error); auto _5 = mio::make_mmap(path, 0, mio::map_entire_file, error); +#ifdef _WIN32 + const wchar_t* wpath1 = L"dasfsf"; + auto _6 = mio::make_mmap_source(wpath1, error); + mio::mmap_source _7; + _7.map(wpath1, error); + const std::wstring wpath2 = wpath1; + auto _8 = mio::make_mmap_source(wpath2, error); + mio::mmap_source _9; + _9.map(wpath1, error); +#endif } std::printf("all tests passed!\n");