diff --git a/CMakeLists.txt b/CMakeLists.txt index d49c31e352..feae1af4f1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -173,11 +173,19 @@ target_sources(leveldb ) # POSIX code is specified separately so we can leave it out in the future. + +if (NOT WIN32) target_sources(leveldb PRIVATE "${PROJECT_SOURCE_DIR}/util/env_posix.cc" "${PROJECT_SOURCE_DIR}/util/posix_logger.h" ) +else () +target_sources(leveldb + PRIVATE + "${PROJECT_SOURCE_DIR}/util/env_win.cc" +) +endif () # MemEnv is not part of the interface and could be pulled to a separate library. target_sources(leveldb @@ -195,9 +203,16 @@ target_compile_definitions(leveldb PRIVATE # Used by include/export.h when building shared libraries. LEVELDB_COMPILE_LIBRARY - # Used by port/port.h. - LEVELDB_PLATFORM_POSIX=1 ) + +if (NOT WIN32) + set(PLATFORM_DEFINITIONS LEVELDB_PLATFORM_POSIX=1) +else (NOT WIN32) + set(PLATFORM_DEFINITIONS LEVELDB_PLATFORM_WINDOWS=1 OS_WIN=1 _CRT_SECURE_NO_WARNINGS=1) +endif (NOT WIN32) + +target_compile_definitions(leveldb PRIVATE ${PLATFORM_DEFINITIONS}) + if (NOT HAVE_CXX17_HAS_INCLUDE) target_compile_definitions(leveldb PRIVATE @@ -228,6 +243,9 @@ endif(HAVE_SNAPPY) if(HAVE_TCMALLOC) target_link_libraries(leveldb tcmalloc) endif(HAVE_TCMALLOC) +if(WIN32) + target_link_libraries(leveldb shlwapi dbghelp) +endif() # Needed by port_stdcxx.h find_package(Threads REQUIRED) @@ -256,10 +274,7 @@ if(LEVELDB_BUILD_TESTS) "${test_file}" ) target_link_libraries("${test_target_name}" leveldb) - target_compile_definitions("${test_target_name}" - PRIVATE - LEVELDB_PLATFORM_POSIX=1 - ) + target_compile_definitions("${test_target_name}" PRIVATE ${PLATFORM_DEFINITIONS}) if (NOT HAVE_CXX17_HAS_INCLUDE) target_compile_definitions("${test_target_name}" PRIVATE @@ -307,7 +322,9 @@ if(LEVELDB_BUILD_TESTS) # TODO(costan): This test also uses # "${PROJECT_SOURCE_DIR}/util/env_posix_test_helper.h" + if (NOT WIN32) leveldb_test("${PROJECT_SOURCE_DIR}/util/env_posix_test.cc") + endif (NOT WIN32) endif(NOT BUILD_SHARED_LIBS) endif(LEVELDB_BUILD_TESTS) @@ -329,10 +346,7 @@ if(LEVELDB_BUILD_BENCHMARKS) "${bench_file}" ) target_link_libraries("${bench_target_name}" leveldb) - target_compile_definitions("${bench_target_name}" - PRIVATE - LEVELDB_PLATFORM_POSIX=1 - ) + target_compile_definitions("${bench_target_name}" PRIVATE ${PLATFORM_DEFINITIONS}) if (NOT HAVE_CXX17_HAS_INCLUDE) target_compile_definitions("${bench_target_name}" PRIVATE diff --git a/port/atomic_pointer.h b/port/atomic_pointer.h index bb4e183c08..dfd55dc3ac 100644 --- a/port/atomic_pointer.h +++ b/port/atomic_pointer.h @@ -23,7 +23,18 @@ #include #ifdef OS_WIN +#ifndef NOMINMAX +#define NOMINMAX +#endif #include + +#ifdef _MSC_VER +namespace leveldb { + typedef intptr_t ssize_t; +} +#endif +#undef DeleteFile +#undef small #endif #if defined(_M_X64) || defined(__x86_64__) diff --git a/port/port.h b/port/port.h index 0975fed0d1..b2210a7553 100644 --- a/port/port.h +++ b/port/port.h @@ -10,7 +10,7 @@ // Include the appropriate platform specific file below. If you are // porting to a new platform, see "port_example.h" for documentation // of what the new port_.h file must provide. -#if defined(LEVELDB_PLATFORM_POSIX) +#if defined(LEVELDB_PLATFORM_POSIX) || defined(LEVELDB_PLATFORM_WINDOWS) # include "port/port_stdcxx.h" #elif defined(LEVELDB_PLATFORM_CHROMIUM) # include "port/port_chromium.h" diff --git a/util/env_win.cc b/util/env_win.cc new file mode 100644 index 0000000000..085dd47205 --- /dev/null +++ b/util/env_win.cc @@ -0,0 +1,780 @@ +// This file contains source that originates from: +// http://code.google.com/p/leveldbwin/source/browse/trunk/win32_impl_src/env_win32.h +// http://code.google.com/p/leveldbwin/source/browse/trunk/win32_impl_src/port_win32.cc +// Those files don't have any explicit license headers but the +// project (http://code.google.com/p/leveldbwin/) lists the 'New BSD License' +// as the license. +#include + +#include "leveldb/env.h" + +#include "leveldb/slice.h" +#include "port/port.h" +#include "util/logging.h" + +#include +#include +#include +#include +#include +#include +#include + +#ifndef va_copy +#define va_copy(d, s) ((d) = (s)) +#endif + +#if defined DeleteFile +#undef DeleteFile +#endif + +// Declarations +namespace leveldb { + +namespace Win32 { + +std::string GetCurrentDir(); +std::wstring GetCurrentDirW(); + +static const std::string CurrentDir = GetCurrentDir(); +static const std::wstring CurrentDirW = GetCurrentDirW(); + +std::string& ModifyPath(std::string& path); +std::wstring& ModifyPath(std::wstring& path); + +std::string GetLastErrSz(); +std::wstring GetLastErrSzW(); + +size_t GetPageSize(); + +typedef void (*ScheduleProc)(void*); + +struct WorkItemWrapper { + WorkItemWrapper(ScheduleProc proc_, void* content_); + ScheduleProc proc; + void* pContent; +}; + +DWORD WINAPI WorkItemWrapperProc(LPVOID pContent); + +class Win32SequentialFile : public SequentialFile { + public: + friend class Win32Env; + virtual ~Win32SequentialFile(); + virtual Status Read(size_t n, Slice* result, char* scratch); + virtual Status Skip(uint64_t n); + BOOL isEnable(); + + private: + BOOL _Init(); + void _CleanUp(); + Win32SequentialFile(const std::string& fname); + std::string _filename; + ::HANDLE _hFile; +}; + +class Win32RandomAccessFile : public RandomAccessFile { + public: + friend class Win32Env; + virtual ~Win32RandomAccessFile(); + virtual Status Read(uint64_t offset, size_t n, Slice* result, + char* scratch) const; + BOOL isEnable(); + + private: + BOOL _Init(LPCWSTR path); + void _CleanUp(); + Win32RandomAccessFile(const std::string& fname); + HANDLE _hFile; + const std::string _filename; +}; + +class Win32WritableFile : public WritableFile { + public: + Win32WritableFile(const std::string& fname, bool append); + ~Win32WritableFile(); + + virtual Status Append(const Slice& data); + virtual Status Close(); + virtual Status Flush(); + virtual Status Sync(); + BOOL isEnable(); + + private: + std::string filename_; + ::HANDLE _hFile; +}; + +class Win32FileLock : public FileLock { + public: + friend class Win32Env; + virtual ~Win32FileLock(); + BOOL isEnable(); + + private: + BOOL _Init(LPCWSTR path); + void _CleanUp(); + Win32FileLock(const std::string& fname); + HANDLE _hFile; + std::string _filename; +}; + +class Win32Logger : public Logger { + public: + friend class Win32Env; + virtual ~Win32Logger(); + virtual void Logv(const char* format, va_list ap); + + private: + explicit Win32Logger(WritableFile* pFile); + WritableFile* _pFileProxy; +}; + +class Win32Env : public Env { + public: + Win32Env(); + virtual ~Win32Env(); + virtual Status NewSequentialFile(const std::string& fname, + SequentialFile** result); + + virtual Status NewRandomAccessFile(const std::string& fname, + RandomAccessFile** result); + virtual Status NewWritableFile(const std::string& fname, + WritableFile** result); + virtual Status NewAppendableFile(const std::string& fname, + WritableFile** result); + + virtual bool FileExists(const std::string& fname); + + virtual Status GetChildren(const std::string& dir, + std::vector* result); + + virtual Status DeleteFile(const std::string& fname); + + virtual Status CreateDir(const std::string& dirname); + + virtual Status DeleteDir(const std::string& dirname); + + virtual Status GetFileSize(const std::string& fname, uint64_t* file_size); + + virtual Status RenameFile(const std::string& src, const std::string& target); + + virtual Status LockFile(const std::string& fname, FileLock** lock); + + virtual Status UnlockFile(FileLock* lock); + + virtual void Schedule(void (*function)(void* arg), void* arg); + + virtual void StartThread(void (*function)(void* arg), void* arg); + + virtual Status GetTestDirectory(std::string* path); + + virtual Status NewLogger(const std::string& fname, Logger** result); + + virtual uint64_t NowMicros(); + + virtual void SleepForMicroseconds(int micros); +}; + +void ToWidePath(const std::string& value, std::wstring& target) { + wchar_t buffer[MAX_PATH]; + MultiByteToWideChar(CP_ACP, 0, value.c_str(), -1, buffer, MAX_PATH); + target = buffer; +} + +void ToNarrowPath(const std::wstring& value, std::string& target) { + char buffer[MAX_PATH]; + WideCharToMultiByte(CP_ACP, 0, value.c_str(), -1, buffer, MAX_PATH, NULL, + NULL); + target = buffer; +} + +std::string GetCurrentDir() { + CHAR path[MAX_PATH]; + ::GetModuleFileNameA(::GetModuleHandleA(NULL), path, MAX_PATH); + *strrchr(path, '\\') = 0; + return std::string(path); +} + +std::wstring GetCurrentDirW() { + WCHAR path[MAX_PATH]; + ::GetModuleFileNameW(::GetModuleHandleW(NULL), path, MAX_PATH); + *wcsrchr(path, L'\\') = 0; + return std::wstring(path); +} + +std::string& ModifyPath(std::string& path) { + if (path[0] == '/' || path[0] == '\\') { + path = CurrentDir + path; + } + std::replace(path.begin(), path.end(), '/', '\\'); + + return path; +} + +std::wstring& ModifyPath(std::wstring& path) { + if (path[0] == L'/' || path[0] == L'\\') { + path = CurrentDirW + path; + } + std::replace(path.begin(), path.end(), L'/', L'\\'); + return path; +} + +std::string GetLastErrSz() { + LPWSTR lpMsgBuf; + FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, GetLastError(), + 0, // Default language + (LPWSTR)&lpMsgBuf, 0, NULL); + std::string Err; + ToNarrowPath(lpMsgBuf, Err); + LocalFree(lpMsgBuf); + return Err; +} + +std::wstring GetLastErrSzW() { + LPVOID lpMsgBuf; + FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, GetLastError(), + 0, // Default language + (LPWSTR)&lpMsgBuf, 0, NULL); + std::wstring Err = (LPCWSTR)lpMsgBuf; + LocalFree(lpMsgBuf); + return Err; +} + +static Status Win32Error(const std::string& context) { + if (GetLastError() == ERROR_FILE_NOT_FOUND) { + return Status::NotFound(context, GetLastErrSz()); + } else { + return Status::IOError(context, GetLastErrSz()); + } +} + +WorkItemWrapper::WorkItemWrapper(ScheduleProc proc_, void* content_) + : proc(proc_), pContent(content_) {} + +DWORD WINAPI WorkItemWrapperProc(LPVOID pContent) { + WorkItemWrapper* item = static_cast(pContent); + ScheduleProc TempProc = item->proc; + void* arg = item->pContent; + delete item; + TempProc(arg); + return 0; +} + +size_t GetPageSize() { + SYSTEM_INFO si; + GetSystemInfo(&si); + return std::max(si.dwPageSize, si.dwAllocationGranularity); +} + +const size_t g_PageSize = GetPageSize(); + +Win32SequentialFile::Win32SequentialFile(const std::string& fname) + : _filename(fname), _hFile(NULL) { + _Init(); +} + +Win32SequentialFile::~Win32SequentialFile() { _CleanUp(); } + +Status Win32SequentialFile::Read(size_t n, Slice* result, char* scratch) { + Status sRet; + DWORD hasRead = 0; + if (_hFile && ReadFile(_hFile, scratch, n, &hasRead, NULL)) { + *result = Slice(scratch, hasRead); + } else { + sRet = Win32Error(_filename); + } + return sRet; +} + +Status Win32SequentialFile::Skip(uint64_t n) { + Status sRet; + LARGE_INTEGER Move, NowPointer; + Move.QuadPart = n; + if (!SetFilePointerEx(_hFile, Move, &NowPointer, FILE_CURRENT)) { + sRet = Win32Error(_filename); + } + return sRet; +} + +BOOL Win32SequentialFile::isEnable() { return _hFile ? TRUE : FALSE; } + +BOOL Win32SequentialFile::_Init() { + std::wstring path; + ToWidePath(_filename, path); + _hFile = CreateFileW(path.c_str(), GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); + if (_hFile == INVALID_HANDLE_VALUE) _hFile = NULL; + return _hFile ? TRUE : FALSE; +} + +void Win32SequentialFile::_CleanUp() { + if (_hFile) { + CloseHandle(_hFile); + _hFile = NULL; + } +} + +Win32RandomAccessFile::Win32RandomAccessFile(const std::string& fname) + : _filename(fname), _hFile(NULL) { + std::wstring path; + ToWidePath(fname, path); + _Init(path.c_str()); +} + +Win32RandomAccessFile::~Win32RandomAccessFile() { _CleanUp(); } + +Status Win32RandomAccessFile::Read(uint64_t offset, size_t n, Slice* result, + char* scratch) const { + Status sRet; + OVERLAPPED ol = {0}; + ZeroMemory(&ol, sizeof(ol)); + ol.Offset = (DWORD)offset; + ol.OffsetHigh = (DWORD)(offset >> 32); + DWORD hasRead = 0; + if (!ReadFile(_hFile, scratch, n, &hasRead, &ol)) + sRet = Win32Error(_filename); + else + *result = Slice(scratch, hasRead); + return sRet; +} + +BOOL Win32RandomAccessFile::_Init(LPCWSTR path) { + BOOL bRet = FALSE; + if (!_hFile) + _hFile = ::CreateFileW( + path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, NULL); + if (!_hFile || _hFile == INVALID_HANDLE_VALUE) + _hFile = NULL; + else + bRet = TRUE; + return bRet; +} + +BOOL Win32RandomAccessFile::isEnable() { return _hFile ? TRUE : FALSE; } + +void Win32RandomAccessFile::_CleanUp() { + if (_hFile) { + ::CloseHandle(_hFile); + _hFile = NULL; + } +} + +Win32WritableFile::Win32WritableFile(const std::string& fname, bool append) + : filename_(fname) { + std::wstring path; + ToWidePath(fname, path); + // NewAppendableFile: append to an existing file, or create a new one + // if none exists - this is OPEN_ALWAYS behavior, with + // FILE_APPEND_DATA to avoid having to manually position the file + // pointer at the end of the file. + // NewWritableFile: create a new file, delete if it exists - this is + // CREATE_ALWAYS behavior. This file is used for writing only so + // use GENERIC_WRITE. + _hFile = CreateFileW(path.c_str(), append ? FILE_APPEND_DATA : GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE, + NULL, append ? OPEN_ALWAYS : CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, NULL); + // CreateFileW returns INVALID_HANDLE_VALUE in case of error, always check + // isEnable() before use +} + +Win32WritableFile::~Win32WritableFile() { + if (_hFile != INVALID_HANDLE_VALUE) { + Close(); + } +} + +Status Win32WritableFile::Append(const Slice& data) { + DWORD r = 0; + if (!WriteFile(_hFile, data.data(), data.size(), &r, NULL) || + r != data.size()) { + return Win32Error("Win32WritableFile.Append::WriteFile: " + filename_); + } + return Status::OK(); +} + +Status Win32WritableFile::Close() { + if (!CloseHandle(_hFile)) { + return Win32Error("Win32WritableFile.Close::CloseHandle: " + filename_); + } + _hFile = INVALID_HANDLE_VALUE; + return Status::OK(); +} + +Status Win32WritableFile::Flush() { + // Nothing to do here, there are no application-side buffers + return Status::OK(); +} + +Status Win32WritableFile::Sync() { + if (!FlushFileBuffers(_hFile)) { + return Win32Error("Win32WritableFile.Sync::FlushFileBuffers " + filename_); + } + return Status::OK(); +} + +BOOL Win32WritableFile::isEnable() { return _hFile != INVALID_HANDLE_VALUE; } + +Win32FileLock::Win32FileLock(const std::string& fname) + : _hFile(NULL), _filename(fname) { + std::wstring path; + ToWidePath(fname, path); + _Init(path.c_str()); +} + +Win32FileLock::~Win32FileLock() { _CleanUp(); } + +BOOL Win32FileLock::_Init(LPCWSTR path) { + BOOL bRet = FALSE; + if (!_hFile) + _hFile = ::CreateFileW(path, GENERIC_READ | GENERIC_WRITE, 0, NULL, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (!_hFile || _hFile == INVALID_HANDLE_VALUE) { + _hFile = NULL; + } else + bRet = TRUE; + return bRet; +} + +void Win32FileLock::_CleanUp() { + ::CloseHandle(_hFile); + _hFile = NULL; +} + +BOOL Win32FileLock::isEnable() { return _hFile ? TRUE : FALSE; } + +Win32Logger::Win32Logger(WritableFile* pFile) : _pFileProxy(pFile) { + assert(_pFileProxy); +} + +Win32Logger::~Win32Logger() { + if (_pFileProxy) delete _pFileProxy; +} + +void Win32Logger::Logv(const char* format, va_list ap) { + uint64_t thread_id = ::GetCurrentThreadId(); + + // We try twice: the first time with a fixed-size stack allocated buffer, + // and the second time with a much larger dynamically allocated buffer. + char buffer[500]; + for (int iter = 0; iter < 2; iter++) { + char* base; + int bufsize; + if (iter == 0) { + bufsize = sizeof(buffer); + base = buffer; + } else { + bufsize = 30000; + base = new char[bufsize]; + } + char* p = base; + char* limit = base + bufsize; + + SYSTEMTIME st; + GetLocalTime(&st); + p += snprintf(p, limit - p, "%04d/%02d/%02d-%02d:%02d:%02d.%06d %llx ", + int(st.wYear), int(st.wMonth), int(st.wDay), int(st.wHour), + int(st.wMinute), int(st.wMinute), int(st.wMilliseconds), + static_cast(thread_id)); + + // Print the message + if (p < limit) { + va_list backup_ap; + va_copy(backup_ap, ap); + p += vsnprintf(p, limit - p, format, backup_ap); + va_end(backup_ap); + } + + // Truncate to available space if necessary + if (p >= limit) { + if (iter == 0) { + continue; // Try again with larger buffer + } else { + p = limit - 1; + } + } + + // Add newline if necessary + if (p == base || p[-1] != '\n') { + *p++ = '\n'; + } + + assert(p <= limit); + DWORD hasWritten = 0; + if (_pFileProxy) { + _pFileProxy->Append(Slice(base, p - base)); + _pFileProxy->Flush(); + } + if (base != buffer) { + delete[] base; + } + break; + } +} + +bool Win32Env::FileExists(const std::string& fname) { + std::string path = fname; + std::wstring wpath; + ToWidePath(ModifyPath(path), wpath); + return ::PathFileExistsW(wpath.c_str()) ? true : false; +} + +Status Win32Env::GetChildren(const std::string& dir, + std::vector* result) { + Status sRet; + ::WIN32_FIND_DATAW wfd; + std::string path = dir; + ModifyPath(path); + path += "\\*.*"; + std::wstring wpath; + ToWidePath(path, wpath); + + ::HANDLE hFind = ::FindFirstFileW(wpath.c_str(), &wfd); + if (hFind && hFind != INVALID_HANDLE_VALUE) { + BOOL hasNext = TRUE; + std::string child; + while (hasNext) { + ToNarrowPath(wfd.cFileName, child); + result->push_back(child); + hasNext = ::FindNextFileW(hFind, &wfd); + } + ::FindClose(hFind); + } else + sRet = Status::IOError(dir, "Could not get children."); + return sRet; +} + +void Win32Env::SleepForMicroseconds(int micros) { + ::Sleep((micros + 999) / 1000); +} + +Status Win32Env::DeleteFile(const std::string& fname) { + Status sRet; + std::string path = fname; + std::wstring wpath; + ToWidePath(ModifyPath(path), wpath); + + if (!::DeleteFileW(wpath.c_str())) { + sRet = Win32Error(path); + } + return sRet; +} + +Status Win32Env::GetFileSize(const std::string& fname, uint64_t* file_size) { + Status sRet; + std::string path = fname; + std::wstring wpath; + ToWidePath(ModifyPath(path), wpath); + + HANDLE file = ::CreateFileW(wpath.c_str(), GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + LARGE_INTEGER li; + if (::GetFileSizeEx(file, &li)) { + *file_size = (uint64_t)li.QuadPart; + } else + sRet = Win32Error(path + " Could not get the file size."); + CloseHandle(file); + return sRet; +} + +Status Win32Env::RenameFile(const std::string& src, const std::string& target) { + Status sRet; + std::string src_path = src; + std::wstring wsrc_path; + ToWidePath(ModifyPath(src_path), wsrc_path); + std::string target_path = target; + std::wstring wtarget_path; + ToWidePath(ModifyPath(target_path), wtarget_path); + + if (!MoveFileW(wsrc_path.c_str(), wtarget_path.c_str())) { + DWORD err = GetLastError(); + if (err == 0x000000b7) { + if (!::DeleteFileW(wtarget_path.c_str())) + sRet = Win32Error(src + "Could not rename file."); + else if (!::MoveFileW(wsrc_path.c_str(), wtarget_path.c_str())) + sRet = Win32Error(src + "Could not rename file."); + } + } + return sRet; +} + +Status Win32Env::LockFile(const std::string& fname, FileLock** lock) { + Status sRet; + std::string path = fname; + ModifyPath(path); + Win32FileLock* _lock = new Win32FileLock(path); + if (!_lock->isEnable()) { + delete _lock; + *lock = NULL; + sRet = Status::IOError(path, "Could not lock file."); + } else + *lock = _lock; + return sRet; +} + +Status Win32Env::UnlockFile(FileLock* lock) { + Status sRet; + delete lock; + return sRet; +} + +void Win32Env::Schedule(void (*function)(void* arg), void* arg) { + QueueUserWorkItem(Win32::WorkItemWrapperProc, + new Win32::WorkItemWrapper(function, arg), + WT_EXECUTEDEFAULT); +} + +void Win32Env::StartThread(void (*function)(void* arg), void* arg) { + ::_beginthread(function, 0, arg); +} + +Status Win32Env::GetTestDirectory(std::string* path) { + Status sRet; + WCHAR TempPath[MAX_PATH]; + ::GetTempPathW(MAX_PATH, TempPath); + ToNarrowPath(TempPath, *path); + path->append("leveldb\\test"); + ModifyPath(*path); + return sRet; +} + +uint64_t Win32Env::NowMicros() { +#ifndef USE_VISTA_API +#define GetTickCount64 GetTickCount +#endif + return (uint64_t)(GetTickCount64() * 1000); +} + +static Status CreateDirInner(const std::string& dirname) { + Status sRet; + DWORD attr = ::GetFileAttributes(dirname.c_str()); + if (attr == INVALID_FILE_ATTRIBUTES) { // doesn't exist: + std::size_t slash = dirname.find_last_of("\\"); + if (slash != std::string::npos) { + sRet = CreateDirInner(dirname.substr(0, slash)); + if (!sRet.ok()) return sRet; + } + BOOL result = ::CreateDirectory(dirname.c_str(), NULL); + if (result == FALSE && GetLastError() != ERROR_ALREADY_EXISTS) { + sRet = Win32Error(dirname + ":Could not create directory."); + return sRet; + } + } + return sRet; +} + +Status Win32Env::CreateDir(const std::string& dirname) { + std::string path = dirname; + if (path[path.length() - 1] != '\\') { + path += '\\'; + } + ModifyPath(path); + + return CreateDirInner(path); +} + +Status Win32Env::DeleteDir(const std::string& dirname) { + Status sRet; + std::wstring path; + ToWidePath(dirname, path); + ModifyPath(path); + if (!::RemoveDirectoryW(path.c_str())) { + sRet = Win32Error(dirname + ":Could not delete directory."); + } + return sRet; +} + +Status Win32Env::NewSequentialFile(const std::string& fname, + SequentialFile** result) { + Status sRet; + std::string path = fname; + ModifyPath(path); + Win32SequentialFile* pFile = new Win32SequentialFile(path); + if (pFile->isEnable()) { + *result = pFile; + } else { + delete pFile; + sRet = Win32Error(path); + } + return sRet; +} + +Status Win32Env::NewRandomAccessFile(const std::string& fname, + RandomAccessFile** result) { + Status sRet; + std::string path = fname; + Win32RandomAccessFile* pFile = new Win32RandomAccessFile(ModifyPath(path)); + if (!pFile->isEnable()) { + delete pFile; + *result = NULL; + sRet = Win32Error(path); + } else + *result = pFile; + return sRet; +} + +Status Win32Env::NewLogger(const std::string& fname, Logger** result) { + Status sRet; + std::string path = fname; + // Logs are opened with write semantics, not with append semantics + // (see PosixEnv::NewLogger) + Win32WritableFile* pMapFile = new Win32WritableFile(ModifyPath(path), false); + if (!pMapFile->isEnable()) { + delete pMapFile; + *result = NULL; + sRet = Status::IOError(path, "could not create a logger."); + } else + *result = new Win32Logger(pMapFile); + return sRet; +} + +Status Win32Env::NewWritableFile(const std::string& fname, + WritableFile** result) { + Status sRet; + std::string path = fname; + Win32WritableFile* pFile = new Win32WritableFile(ModifyPath(path), false); + if (!pFile->isEnable()) { + *result = NULL; + sRet = Win32Error(path); + } else + *result = pFile; + return sRet; +} + +Status Win32Env::NewAppendableFile(const std::string& fname, + WritableFile** result) { + Status sRet; + std::string path = fname; + Win32WritableFile* pFile = new Win32WritableFile(ModifyPath(path), true); + if (!pFile->isEnable()) { + *result = NULL; + sRet = Win32Error(path); + } else + *result = pFile; + return sRet; +} + +Win32Env::Win32Env() {} + +Win32Env::~Win32Env() {} + +} // namespace Win32 + +static port::OnceType once = LEVELDB_ONCE_INIT; +static Env* default_env; +static void InitDefaultEnv() { default_env = new Win32::Win32Env(); } + +Env* Env::Default() { + port::InitOnce(&once, InitDefaultEnv); + return default_env; +} + +} // namespace leveldb