Skip to content

Commit f8ae182

Browse files
sipsorcerylaanwj
authored andcommitted
Adds unicode support to Windows environment.
Currently the Windows environment uses the *A ANSI Win32 API calls for file system operations. This precludes the ability of consuming applications to pass paths with unicode characters. A detailed discussion is in google#755. This PR swtiches the *A ANSI calls to the *W wide unicode string calls along with the conversion methods from standard UTF-8 strings to the UTF-16 multi-byte strings requires by the Win32 *W API functions.
1 parent 92ae82c commit f8ae182

File tree

1 file changed

+66
-25
lines changed

1 file changed

+66
-25
lines changed

util/env_windows.cc

Lines changed: 66 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -387,8 +387,9 @@ class WindowsEnv : public Env {
387387
*result = nullptr;
388388
DWORD desired_access = GENERIC_READ;
389389
DWORD share_mode = FILE_SHARE_READ;
390-
ScopedHandle handle = ::CreateFileA(
391-
filename.c_str(), desired_access, share_mode,
390+
auto wFilename = toUtf16(filename);
391+
ScopedHandle handle = ::CreateFileW(
392+
wFilename.c_str(), desired_access, share_mode,
392393
/*lpSecurityAttributes=*/nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
393394
/*hTemplateFile=*/nullptr);
394395
if (!handle.is_valid()) {
@@ -404,8 +405,9 @@ class WindowsEnv : public Env {
404405
*result = nullptr;
405406
DWORD desired_access = GENERIC_READ;
406407
DWORD share_mode = FILE_SHARE_READ;
408+
auto wFilename = toUtf16(filename);
407409
ScopedHandle handle =
408-
::CreateFileA(filename.c_str(), desired_access, share_mode,
410+
::CreateFileW(wFilename.c_str(), desired_access, share_mode,
409411
/*lpSecurityAttributes=*/nullptr, OPEN_EXISTING,
410412
FILE_ATTRIBUTE_READONLY,
411413
/*hTemplateFile=*/nullptr);
@@ -425,7 +427,7 @@ class WindowsEnv : public Env {
425427
}
426428

427429
ScopedHandle mapping =
428-
::CreateFileMappingA(handle.get(),
430+
::CreateFileMappingW(handle.get(),
429431
/*security attributes=*/nullptr, PAGE_READONLY,
430432
/*dwMaximumSizeHigh=*/0,
431433
/*dwMaximumSizeLow=*/0,
@@ -450,8 +452,9 @@ class WindowsEnv : public Env {
450452
WritableFile** result) override {
451453
DWORD desired_access = GENERIC_WRITE;
452454
DWORD share_mode = 0; // Exclusive access.
453-
ScopedHandle handle = ::CreateFileA(
454-
filename.c_str(), desired_access, share_mode,
455+
auto wFilename = toUtf16(filename);
456+
ScopedHandle handle = ::CreateFileW(
457+
wFilename.c_str(), desired_access, share_mode,
455458
/*lpSecurityAttributes=*/nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
456459
/*hTemplateFile=*/nullptr);
457460
if (!handle.is_valid()) {
@@ -467,8 +470,9 @@ class WindowsEnv : public Env {
467470
WritableFile** result) override {
468471
DWORD desired_access = FILE_APPEND_DATA;
469472
DWORD share_mode = 0; // Exclusive access.
470-
ScopedHandle handle = ::CreateFileA(
471-
filename.c_str(), desired_access, share_mode,
473+
auto wFilename = toUtf16(filename);
474+
ScopedHandle handle = ::CreateFileW(
475+
wFilename.c_str(), desired_access, share_mode,
472476
/*lpSecurityAttributes=*/nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,
473477
/*hTemplateFile=*/nullptr);
474478
if (!handle.is_valid()) {
@@ -481,14 +485,16 @@ class WindowsEnv : public Env {
481485
}
482486

483487
bool FileExists(const std::string& filename) override {
484-
return GetFileAttributesA(filename.c_str()) != INVALID_FILE_ATTRIBUTES;
488+
auto wFilename = toUtf16(filename);
489+
return GetFileAttributesW(wFilename.c_str()) != INVALID_FILE_ATTRIBUTES;
485490
}
486491

487492
Status GetChildren(const std::string& directory_path,
488493
std::vector<std::string>* result) override {
489494
const std::string find_pattern = directory_path + "\\*";
490-
WIN32_FIND_DATAA find_data;
491-
HANDLE dir_handle = ::FindFirstFileA(find_pattern.c_str(), &find_data);
495+
WIN32_FIND_DATAW find_data;
496+
auto wFind_pattern = toUtf16(find_pattern);
497+
HANDLE dir_handle = ::FindFirstFileW(wFind_pattern.c_str(), &find_data);
492498
if (dir_handle == INVALID_HANDLE_VALUE) {
493499
DWORD last_error = ::GetLastError();
494500
if (last_error == ERROR_FILE_NOT_FOUND) {
@@ -500,11 +506,12 @@ class WindowsEnv : public Env {
500506
char base_name[_MAX_FNAME];
501507
char ext[_MAX_EXT];
502508

503-
if (!_splitpath_s(find_data.cFileName, nullptr, 0, nullptr, 0, base_name,
504-
ARRAYSIZE(base_name), ext, ARRAYSIZE(ext))) {
509+
auto find_data_filename = toUtf8(find_data.cFileName);
510+
if (!_splitpath_s(find_data_filename.c_str(), nullptr, 0, nullptr, 0,
511+
base_name, ARRAYSIZE(base_name), ext, ARRAYSIZE(ext))) {
505512
result->emplace_back(std::string(base_name) + ext);
506513
}
507-
} while (::FindNextFileA(dir_handle, &find_data));
514+
} while (::FindNextFileW(dir_handle, &find_data));
508515
DWORD last_error = ::GetLastError();
509516
::FindClose(dir_handle);
510517
if (last_error != ERROR_NO_MORE_FILES) {
@@ -514,29 +521,33 @@ class WindowsEnv : public Env {
514521
}
515522

516523
Status DeleteFile(const std::string& filename) override {
517-
if (!::DeleteFileA(filename.c_str())) {
524+
auto wFilename = toUtf16(filename);
525+
if (!::DeleteFileW(wFilename.c_str())) {
518526
return WindowsError(filename, ::GetLastError());
519527
}
520528
return Status::OK();
521529
}
522530

523531
Status CreateDir(const std::string& dirname) override {
524-
if (!::CreateDirectoryA(dirname.c_str(), nullptr)) {
532+
auto wDirname = toUtf16(dirname);
533+
if (!::CreateDirectoryW(wDirname.c_str(), nullptr)) {
525534
return WindowsError(dirname, ::GetLastError());
526535
}
527536
return Status::OK();
528537
}
529538

530539
Status DeleteDir(const std::string& dirname) override {
531-
if (!::RemoveDirectoryA(dirname.c_str())) {
540+
auto wDirname = toUtf16(dirname);
541+
if (!::RemoveDirectoryW(wDirname.c_str())) {
532542
return WindowsError(dirname, ::GetLastError());
533543
}
534544
return Status::OK();
535545
}
536546

537547
Status GetFileSize(const std::string& filename, uint64_t* size) override {
538548
WIN32_FILE_ATTRIBUTE_DATA file_attributes;
539-
if (!::GetFileAttributesExA(filename.c_str(), GetFileExInfoStandard,
549+
auto wFilename = toUtf16(filename);
550+
if (!::GetFileAttributesExW(wFilename.c_str(), GetFileExInfoStandard,
540551
&file_attributes)) {
541552
return WindowsError(filename, ::GetLastError());
542553
}
@@ -550,7 +561,9 @@ class WindowsEnv : public Env {
550561
Status RenameFile(const std::string& from, const std::string& to) override {
551562
// Try a simple move first. It will only succeed when |to| doesn't already
552563
// exist.
553-
if (::MoveFileA(from.c_str(), to.c_str())) {
564+
auto wFrom = toUtf16(from);
565+
auto wTo = toUtf16(to);
566+
if (::MoveFileW(wFrom.c_str(), wTo.c_str())) {
554567
return Status::OK();
555568
}
556569
DWORD move_error = ::GetLastError();
@@ -559,7 +572,7 @@ class WindowsEnv : public Env {
559572
// succeed when |to| does exist. When writing to a network share, we may not
560573
// be able to change the ACLs. Ignore ACL errors then
561574
// (REPLACEFILE_IGNORE_MERGE_ERRORS).
562-
if (::ReplaceFileA(to.c_str(), from.c_str(), /*lpBackupFileName=*/nullptr,
575+
if (::ReplaceFileW(wTo.c_str(), wFrom.c_str(), /*lpBackupFileName=*/nullptr,
563576
REPLACEFILE_IGNORE_MERGE_ERRORS,
564577
/*lpExclude=*/nullptr, /*lpReserved=*/nullptr)) {
565578
return Status::OK();
@@ -579,8 +592,9 @@ class WindowsEnv : public Env {
579592
Status LockFile(const std::string& filename, FileLock** lock) override {
580593
*lock = nullptr;
581594
Status result;
582-
ScopedHandle handle = ::CreateFileA(
583-
filename.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ,
595+
auto wFilename = toUtf16(filename);
596+
ScopedHandle handle = ::CreateFileW(
597+
wFilename.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ,
584598
/*lpSecurityAttributes=*/nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,
585599
nullptr);
586600
if (!handle.is_valid()) {
@@ -620,10 +634,11 @@ class WindowsEnv : public Env {
620634
return Status::OK();
621635
}
622636

623-
char tmp_path[MAX_PATH];
624-
if (!GetTempPathA(ARRAYSIZE(tmp_path), tmp_path)) {
637+
wchar_t wtmp_path[MAX_PATH];
638+
if (!GetTempPathW(ARRAYSIZE(wtmp_path), wtmp_path)) {
625639
return WindowsError("GetTempPath", ::GetLastError());
626640
}
641+
std::string tmp_path = toUtf8(std::wstring(wtmp_path));
627642
std::stringstream ss;
628643
ss << tmp_path << "leveldbtest-" << std::this_thread::get_id();
629644
*result = ss.str();
@@ -634,7 +649,8 @@ class WindowsEnv : public Env {
634649
}
635650

636651
Status NewLogger(const std::string& filename, Logger** result) override {
637-
std::FILE* fp = std::fopen(filename.c_str(), "w");
652+
auto wFilename = toUtf16(filename);
653+
std::FILE* fp = _wfopen(wFilename.c_str(), L"w");
638654
if (fp == nullptr) {
639655
*result = nullptr;
640656
return WindowsError(filename, ::GetLastError());
@@ -690,6 +706,31 @@ class WindowsEnv : public Env {
690706
GUARDED_BY(background_work_mutex_);
691707

692708
Limiter mmap_limiter_; // Thread-safe.
709+
710+
// Converts a Windows wide multi-byte UTF-16 string to a UTF-8 string.
711+
// See http://utf8everywhere.org/#windows
712+
std::string toUtf8(const std::wstring& wstr) {
713+
if (wstr.empty()) return std::string();
714+
int size_needed = WideCharToMultiByte(
715+
CP_UTF8, 0, &wstr[0], (int)wstr.size(), NULL, 0, NULL, NULL);
716+
std::string strTo(size_needed, 0);
717+
WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), &strTo[0],
718+
size_needed, NULL, NULL);
719+
return strTo;
720+
}
721+
722+
// Converts a UTF-8 string to a Windows UTF-16 multi-byte wide character
723+
// string.
724+
// See http://utf8everywhere.org/#windows
725+
std::wstring toUtf16(const std::string& str) {
726+
if (str.empty()) return std::wstring();
727+
int size_needed =
728+
MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0);
729+
std::wstring strTo(size_needed, 0);
730+
MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), &strTo[0],
731+
size_needed);
732+
return strTo;
733+
}
693734
};
694735

695736
// Return the maximum number of concurrent mmaps.

0 commit comments

Comments
 (0)