Add Windows wide char path support

This commit is contained in:
mandreyel 2018-11-13 10:56:28 +01:00
parent d77add92da
commit d4283a45ec
4 changed files with 154 additions and 29 deletions

View File

@ -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. 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 **WINDOWS USERS**: This library *does* support the use of wide character types
types for functions where character strings are expected (e.g. path parameters). for functions where character strings are expected (e.g. path parameters).
### Example ### Example

View File

@ -38,18 +38,53 @@ namespace mio {
namespace detail { namespace detail {
#ifdef _WIN32 #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 inline DWORD int64_high(int64_t n) noexcept
{ {
return n >> 32; 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 inline DWORD int64_low(int64_t n) noexcept
{ {
return n & 0xffffffff; return n & 0xffffffff;
} }
#endif
template<
typename String,
typename = typename std::enable_if<
std::is_same<typename char_type<String>::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 String>
typename std::enable_if<
std::is_same<typename char_type<String>::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 * Returns the last platform specific system error (errno on POSIX and
@ -67,8 +102,8 @@ inline std::error_code last_error() noexcept
} }
template<typename String> template<typename String>
file_handle_type open_file(const String& path, file_handle_type open_file(const String& path, const access_mode mode,
const access_mode mode, std::error_code& error) std::error_code& error)
{ {
error.clear(); error.clear();
if(detail::empty(path)) if(detail::empty(path))
@ -77,13 +112,7 @@ file_handle_type open_file(const String& path,
return invalid_handle; return invalid_handle;
} }
#ifdef _WIN32 #ifdef _WIN32
const auto handle = ::CreateFileA(c_str(path), const auto handle = win::open_file_helper(path, mode);
mode == access_mode::read ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
0,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
0);
#else // POSIX #else // POSIX
const auto handle = ::open(c_str(path), const auto handle = ::open(c_str(path),
mode == access_mode::read ? O_RDONLY : O_RDWR); 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, file_handle,
0, 0,
mode == access_mode::read ? PAGE_READONLY : PAGE_READWRITE, mode == access_mode::read ? PAGE_READONLY : PAGE_READWRITE,
int64_high(max_file_size), win::int64_high(max_file_size),
int64_low(max_file_size), win::int64_low(max_file_size),
0); 0);
if(file_mapping_handle == invalid_handle) 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<char*>(::MapViewOfFile( char* mapping_start = static_cast<char*>(::MapViewOfFile(
file_mapping_handle, file_mapping_handle,
mode == access_mode::read ? FILE_MAP_READ : FILE_MAP_WRITE, mode == access_mode::read ? FILE_MAP_READ : FILE_MAP_WRITE,
int64_high(aligned_offset), win::int64_high(aligned_offset),
int64_low(aligned_offset), win::int64_low(aligned_offset),
length_to_map)); length_to_map));
if(mapping_start == nullptr) if(mapping_start == nullptr)
{ {

View File

@ -26,17 +26,79 @@
namespace mio { namespace mio {
namespace detail { namespace detail {
template <typename String> template<
struct is_c_str typename S,
typename C = typename std::decay<S>::type,
typename = decltype(std::declval<C>().data()),
typename = typename std::enable_if<
std::is_same<typename C::value_type, char>::value
#ifdef _WIN32
|| std::is_same<typename C::value_type, wchar_t>::value
#endif
>::type
> struct char_type_helper {
using type = typename C::value_type;
};
template<class T>
struct char_type {
using type = typename char_type_helper<T>::type;
};
// TODO: can we avoid this brute force approach?
template<>
struct char_type<char*> {
using type = char;
};
template<>
struct char_type<const char*> {
using type = char;
};
template<size_t N>
struct char_type<char[N]> {
using type = char;
};
template<size_t N>
struct char_type<const char[N]> {
using type = char;
};
#ifdef _WIN32
template<>
struct char_type<wchar_t*> {
using type = wchar_t;
};
template<>
struct char_type<const wchar_t*> {
using type = wchar_t;
};
template<size_t N>
struct char_type<wchar_t[N]> {
using type = wchar_t;
};
template<size_t N>
struct char_type<const wchar_t[N]> {
using type = wchar_t;
};
#endif // _WIN32
template<typename CharT, typename S>
struct is_c_str_helper
{ {
static constexpr bool value = std::is_same< static constexpr bool value = std::is_same<
char*, CharT*,
// TODO: I'm so sorry for this... Can this be made cleaner? // TODO: I'm so sorry for this... Can this be made cleaner?
typename std::add_pointer< typename std::add_pointer<
typename std::remove_cv< typename std::remove_cv<
typename std::remove_pointer< typename std::remove_pointer<
typename std::decay< typename std::decay<
String S
>::type >::type
>::type >::type
>::type >::type
@ -44,11 +106,35 @@ struct is_c_str
>::value; >::value;
}; };
template<typename S>
struct is_c_str
{
static constexpr bool value = is_c_str_helper<char, S>::value;
};
#ifdef _WIN32
template<typename S>
struct is_c_wstr
{
static constexpr bool value = is_c_str_helper<wchar_t, S>::value;
};
#endif // _WIN32
template<typename S>
struct is_c_str_or_c_wstr
{
static constexpr bool value = is_c_str<S>::value
#ifdef _WIN32
|| is_c_wstr<S>::value
#endif
;
};
template< template<
typename String, typename String,
typename = decltype(std::declval<String>().data()), typename = decltype(std::declval<String>().data()),
typename = typename std::enable_if<!is_c_str<String>::value>::type typename = typename std::enable_if<!is_c_str_or_c_wstr<String>::value>::type
> const char* c_str(const String& path) > const typename char_type<String>::type* c_str(const String& path)
{ {
return path.data(); return path.data();
} }
@ -56,7 +142,7 @@ template<
template< template<
typename String, typename String,
typename = decltype(std::declval<String>().empty()), typename = decltype(std::declval<String>().empty()),
typename = typename std::enable_if<!is_c_str<String>::value>::type typename = typename std::enable_if<!is_c_str_or_c_wstr<String>::value>::type
> bool empty(const String& path) > bool empty(const String& path)
{ {
return path.empty(); return path.empty();
@ -64,15 +150,15 @@ template<
template< template<
typename String, typename String,
typename = typename std::enable_if<is_c_str<String>::value>::type typename = typename std::enable_if<is_c_str_or_c_wstr<String>::value>::type
> const char* c_str(String path) > const typename char_type<String>::type* c_str(String path)
{ {
return path; return path;
} }
template< template<
typename String, typename String,
typename = typename std::enable_if<is_c_str<String>::value>::type typename = typename std::enable_if<is_c_str_or_c_wstr<String>::value>::type
> bool empty(String path) > bool empty(String path)
{ {
return !path || (*path == 0); return !path || (*path == 0);

View File

@ -98,7 +98,7 @@ int main()
} }
{ {
// Make sure custom types compile. // Make sure these compile.
mio::ummap_source _1; mio::ummap_source _1;
mio::shared_ummap_source _2; mio::shared_ummap_source _2;
// Make sure shared_mmap mapping compiles as all testing was done on // 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); mio::shared_mmap_source _3(path, 0, mio::map_entire_file);
auto _4 = mio::make_mmap_source(path, error); auto _4 = mio::make_mmap_source(path, error);
auto _5 = mio::make_mmap<mio::shared_mmap_source>(path, 0, mio::map_entire_file, error); auto _5 = mio::make_mmap<mio::shared_mmap_source>(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"); std::printf("all tests passed!\n");