Merge pull request #34 from mandreyel/wchar-paths

Add Windows wide char path support
This commit is contained in:
mandreyel 2018-11-13 15:24:08 +01:00 committed by GitHub
commit e2fdbf6c35
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
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.
**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

View File

@ -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<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
@ -67,8 +102,8 @@ inline std::error_code last_error() noexcept
}
template<typename String>
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<char*>(::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)
{

View File

@ -26,17 +26,79 @@
namespace mio {
namespace detail {
template <typename String>
struct is_c_str
template<
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<
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<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<
typename String,
typename = decltype(std::declval<String>().data()),
typename = typename std::enable_if<!is_c_str<String>::value>::type
> const char* c_str(const String& path)
typename = typename std::enable_if<!is_c_str_or_c_wstr<String>::value>::type
> const typename char_type<String>::type* c_str(const String& path)
{
return path.data();
}
@ -56,7 +142,7 @@ template<
template<
typename String,
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)
{
return path.empty();
@ -64,15 +150,15 @@ template<
template<
typename String,
typename = typename std::enable_if<is_c_str<String>::value>::type
> const char* c_str(String path)
typename = typename std::enable_if<is_c_str_or_c_wstr<String>::value>::type
> const typename char_type<String>::type* c_str(String path)
{
return path;
}
template<
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)
{
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::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<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");