1 // This file contains source that originates from:
2 // http://code.google.com/p/leveldbwin/source/browse/trunk/win32_impl_src/env_win32.h
3 // http://code.google.com/p/leveldbwin/source/browse/trunk/win32_impl_src/port_win32.cc
4 // Those files dont' have any explict license headers but the
5 // project (http://code.google.com/p/leveldbwin/) lists the 'New BSD License'
7 #if defined(LEVELDB_PLATFORM_WINDOWS)
11 #include "leveldb/env.h"
13 #include "port/port.h"
14 #include "leveldb/slice.h"
15 #include "util/logging.h"
30 #define va_copy(d,s) ((d) = (s))
33 #if defined DeleteFile
44 #define DISALLOW_COPY_AND_ASSIGN(TypeName) \
45 TypeName(const TypeName&); \
46 void operator=(const TypeName&)
48 std::string GetCurrentDir();
49 std::wstring GetCurrentDirW();
51 static const std::string CurrentDir = GetCurrentDir();
52 static const std::wstring CurrentDirW = GetCurrentDirW();
54 std::string& ModifyPath(std::string& path);
55 std::wstring& ModifyPath(std::wstring& path);
57 std::string GetLastErrSz();
58 std::wstring GetLastErrSzW();
62 typedef void (*ScheduleProc)(void*) ;
64 struct WorkItemWrapper
66 WorkItemWrapper(ScheduleProc proc_,void* content_);
71 DWORD WINAPI WorkItemWrapperProc(LPVOID pContent);
73 class Win32SequentialFile : public SequentialFile
76 friend class Win32Env;
77 virtual ~Win32SequentialFile();
78 virtual Status Read(size_t n, Slice* result, char* scratch);
79 virtual Status Skip(uint64_t n);
84 Win32SequentialFile(const std::string& fname);
85 std::string _filename;
87 DISALLOW_COPY_AND_ASSIGN(Win32SequentialFile);
90 class Win32RandomAccessFile : public RandomAccessFile
93 friend class Win32Env;
94 virtual ~Win32RandomAccessFile();
95 virtual Status Read(uint64_t offset, size_t n, Slice* result,char* scratch) const;
98 BOOL _Init(LPCWSTR path);
100 Win32RandomAccessFile(const std::string& fname);
102 const std::string _filename;
103 DISALLOW_COPY_AND_ASSIGN(Win32RandomAccessFile);
106 class Win32MapFile : public WritableFile
109 Win32MapFile(const std::string& fname);
112 virtual Status Append(const Slice& data);
113 virtual Status Close();
114 virtual Status Flush();
115 virtual Status Sync();
118 std::string _filename;
121 size_t _map_size; // How much extra memory to map at a time
122 char* _base; // The mapped region
124 char* _limit; // Limit of the mapped region
125 char* _dst; // Where to write next (in range [base_,limit_])
126 char* _last_sync; // Where have we synced up to
127 uint64_t _file_offset; // Offset of base_ in file
128 //LARGE_INTEGER file_offset_;
129 // Have we done an munmap of unsynced data?
132 // Roundup x to a multiple of y
133 static size_t _Roundup(size_t x, size_t y);
134 size_t _TruncateToPageBoundary(size_t s);
135 bool _UnmapCurrentRegion();
136 bool _MapNewRegion();
137 DISALLOW_COPY_AND_ASSIGN(Win32MapFile);
138 BOOL _Init(LPCWSTR Path);
141 class Win32FileLock : public FileLock
144 friend class Win32Env;
145 virtual ~Win32FileLock();
148 BOOL _Init(LPCWSTR path);
150 Win32FileLock(const std::string& fname);
152 std::string _filename;
153 DISALLOW_COPY_AND_ASSIGN(Win32FileLock);
156 class Win32Logger : public Logger
159 friend class Win32Env;
160 virtual ~Win32Logger();
161 virtual void Logv(const char* format, va_list ap);
163 explicit Win32Logger(WritableFile* pFile);
164 WritableFile* _pFileProxy;
165 DISALLOW_COPY_AND_ASSIGN(Win32Logger);
168 class Win32Env : public Env
173 virtual Status NewSequentialFile(const std::string& fname,
174 SequentialFile** result);
176 virtual Status NewRandomAccessFile(const std::string& fname,
177 RandomAccessFile** result);
178 virtual Status NewWritableFile(const std::string& fname,
179 WritableFile** result);
181 virtual bool FileExists(const std::string& fname);
183 virtual Status GetChildren(const std::string& dir,
184 std::vector<std::string>* result);
186 virtual Status DeleteFile(const std::string& fname);
188 virtual Status CreateDir(const std::string& dirname);
190 virtual Status DeleteDir(const std::string& dirname);
192 virtual Status GetFileSize(const std::string& fname, uint64_t* file_size);
194 virtual Status RenameFile(const std::string& src,
195 const std::string& target);
197 virtual Status LockFile(const std::string& fname, FileLock** lock);
199 virtual Status UnlockFile(FileLock* lock);
201 virtual void Schedule(
202 void (*function)(void* arg),
205 virtual void StartThread(void (*function)(void* arg), void* arg);
207 virtual Status GetTestDirectory(std::string* path);
209 //virtual void Logv(WritableFile* log, const char* format, va_list ap);
211 virtual Status NewLogger(const std::string& fname, Logger** result);
213 virtual uint64_t NowMicros();
215 virtual void SleepForMicroseconds(int micros);
218 void ToWidePath(const std::string& value, std::wstring& target) {
219 wchar_t buffer[MAX_PATH];
220 MultiByteToWideChar(CP_ACP, 0, value.c_str(), -1, buffer, MAX_PATH);
224 void ToNarrowPath(const std::wstring& value, std::string& target) {
225 char buffer[MAX_PATH];
226 WideCharToMultiByte(CP_ACP, 0, value.c_str(), -1, buffer, MAX_PATH, NULL, NULL);
230 std::string GetCurrentDir()
233 ::GetModuleFileNameA(::GetModuleHandleA(NULL),path,MAX_PATH);
234 *strrchr(path,'\\') = 0;
235 return std::string(path);
238 std::wstring GetCurrentDirW()
240 WCHAR path[MAX_PATH];
241 ::GetModuleFileNameW(::GetModuleHandleW(NULL),path,MAX_PATH);
242 *wcsrchr(path,L'\\') = 0;
243 return std::wstring(path);
246 std::string& ModifyPath(std::string& path)
248 if(path[0] == '/' || path[0] == '\\'){
249 path = CurrentDir + path;
251 std::replace(path.begin(),path.end(),'/','\\');
256 std::wstring& ModifyPath(std::wstring& path)
258 if(path[0] == L'/' || path[0] == L'\\'){
259 path = CurrentDirW + path;
261 std::replace(path.begin(),path.end(),L'/',L'\\');
265 std::string GetLastErrSz()
269 FORMAT_MESSAGE_ALLOCATE_BUFFER |
270 FORMAT_MESSAGE_FROM_SYSTEM |
271 FORMAT_MESSAGE_IGNORE_INSERTS,
274 0, // Default language
280 ToNarrowPath(lpMsgBuf, Err);
281 LocalFree( lpMsgBuf );
285 std::wstring GetLastErrSzW()
289 FORMAT_MESSAGE_ALLOCATE_BUFFER |
290 FORMAT_MESSAGE_FROM_SYSTEM |
291 FORMAT_MESSAGE_IGNORE_INSERTS,
294 0, // Default language
299 std::wstring Err = (LPCWSTR)lpMsgBuf;
304 WorkItemWrapper::WorkItemWrapper( ScheduleProc proc_,void* content_ ) :
305 proc(proc_),pContent(content_)
310 DWORD WINAPI WorkItemWrapperProc(LPVOID pContent)
312 WorkItemWrapper* item = static_cast<WorkItemWrapper*>(pContent);
313 ScheduleProc TempProc = item->proc;
314 void* arg = item->pContent;
324 return std::max(si.dwPageSize,si.dwAllocationGranularity);
327 const size_t g_PageSize = GetPageSize();
330 Win32SequentialFile::Win32SequentialFile( const std::string& fname ) :
331 _filename(fname),_hFile(NULL)
336 Win32SequentialFile::~Win32SequentialFile()
341 Status Win32SequentialFile::Read( size_t n, Slice* result, char* scratch )
345 if(_hFile && ReadFile(_hFile,scratch,n,&hasRead,NULL) ){
346 *result = Slice(scratch,hasRead);
348 sRet = Status::IOError(_filename, Win32::GetLastErrSz() );
353 Status Win32SequentialFile::Skip( uint64_t n )
356 LARGE_INTEGER Move,NowPointer;
358 if(!SetFilePointerEx(_hFile,Move,&NowPointer,FILE_CURRENT)){
359 sRet = Status::IOError(_filename,Win32::GetLastErrSz());
364 BOOL Win32SequentialFile::isEnable()
366 return _hFile ? TRUE : FALSE;
369 BOOL Win32SequentialFile::_Init()
372 ToWidePath(_filename, path);
373 _hFile = CreateFileW(path.c_str(),
378 FILE_ATTRIBUTE_NORMAL,
380 return _hFile ? TRUE : FALSE;
383 void Win32SequentialFile::_CleanUp()
391 Win32RandomAccessFile::Win32RandomAccessFile( const std::string& fname ) :
392 _filename(fname),_hFile(NULL)
395 ToWidePath(fname, path);
396 _Init( path.c_str() );
399 Win32RandomAccessFile::~Win32RandomAccessFile()
404 Status Win32RandomAccessFile::Read(uint64_t offset,size_t n,Slice* result,char* scratch) const
408 ZeroMemory(&ol,sizeof(ol));
409 ol.Offset = (DWORD)offset;
410 ol.OffsetHigh = (DWORD)(offset >> 32);
412 if(!ReadFile(_hFile,scratch,n,&hasRead,&ol))
413 sRet = Status::IOError(_filename,Win32::GetLastErrSz());
415 *result = Slice(scratch,hasRead);
419 BOOL Win32RandomAccessFile::_Init( LPCWSTR path )
423 _hFile = ::CreateFileW(path,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,
424 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,NULL);
425 if(!_hFile || _hFile == INVALID_HANDLE_VALUE )
432 BOOL Win32RandomAccessFile::isEnable()
434 return _hFile ? TRUE : FALSE;
437 void Win32RandomAccessFile::_CleanUp()
440 ::CloseHandle(_hFile);
445 size_t Win32MapFile::_Roundup( size_t x, size_t y )
447 return ((x + y - 1) / y) * y;
450 size_t Win32MapFile::_TruncateToPageBoundary( size_t s )
452 s -= (s & (_page_size - 1));
453 assert((s % _page_size) == 0);
457 bool Win32MapFile::_UnmapCurrentRegion()
461 if (_last_sync < _limit) {
462 // Defer syncing this data until next Sync() call, if any
463 _pending_sync = true;
465 if (!UnmapViewOfFile(_base) || !CloseHandle(_base_handle))
467 _file_offset += _limit - _base;
473 // Increase the amount we map the next time, but capped at 1MB
474 if (_map_size < (1<<20)) {
481 bool Win32MapFile::_MapNewRegion()
483 assert(_base == NULL);
484 //LONG newSizeHigh = (LONG)((file_offset_ + map_size_) >> 32);
485 //LONG newSizeLow = (LONG)((file_offset_ + map_size_) & 0xFFFFFFFF);
486 DWORD off_hi = (DWORD)(_file_offset >> 32);
487 DWORD off_lo = (DWORD)(_file_offset & 0xFFFFFFFF);
488 LARGE_INTEGER newSize;
489 newSize.QuadPart = _file_offset + _map_size;
490 SetFilePointerEx(_hFile, newSize, NULL, FILE_BEGIN);
491 SetEndOfFile(_hFile);
493 _base_handle = CreateFileMappingA(
500 if (_base_handle != NULL) {
501 _base = (char*) MapViewOfFile(_base_handle,
507 _limit = _base + _map_size;
516 Win32MapFile::Win32MapFile( const std::string& fname) :
519 _page_size(Win32::g_PageSize),
520 _map_size(_Roundup(65536, Win32::g_PageSize)),
530 ToWidePath(fname, path);
532 assert((Win32::g_PageSize & (Win32::g_PageSize - 1)) == 0);
535 Status Win32MapFile::Append( const Slice& data )
537 const char* src = data.data();
538 size_t left = data.size();
541 assert(_base <= _dst);
542 assert(_dst <= _limit);
543 size_t avail = _limit - _dst;
545 if (!_UnmapCurrentRegion() ||
547 return Status::IOError("WinMmapFile.Append::UnmapCurrentRegion or MapNewRegion: ", Win32::GetLastErrSz());
550 size_t n = (left <= avail) ? left : avail;
551 memcpy(_dst, src, n);
559 Status Win32MapFile::Close()
562 size_t unused = _limit - _dst;
563 if (!_UnmapCurrentRegion()) {
564 s = Status::IOError("WinMmapFile.Close::UnmapCurrentRegion: ",Win32::GetLastErrSz());
565 } else if (unused > 0) {
566 // Trim the extra space at the end of the file
567 LARGE_INTEGER newSize;
568 newSize.QuadPart = _file_offset - unused;
569 if (!SetFilePointerEx(_hFile, newSize, NULL, FILE_BEGIN)) {
570 s = Status::IOError("WinMmapFile.Close::SetFilePointer: ",Win32::GetLastErrSz());
572 SetEndOfFile(_hFile);
574 if (!CloseHandle(_hFile)) {
576 s = Status::IOError("WinMmapFile.Close::CloseHandle: ", Win32::GetLastErrSz());
579 _hFile = INVALID_HANDLE_VALUE;
587 Status Win32MapFile::Sync()
591 // Some unmapped data was not synced
592 _pending_sync = false;
593 if (!FlushFileBuffers(_hFile)) {
594 s = Status::IOError("WinMmapFile.Sync::FlushFileBuffers: ",Win32::GetLastErrSz());
597 if (_dst > _last_sync) {
598 // Find the beginnings of the pages that contain the first and last
599 // bytes to be synced.
600 size_t p1 = _TruncateToPageBoundary(_last_sync - _base);
601 size_t p2 = _TruncateToPageBoundary(_dst - _base - 1);
603 if (!FlushViewOfFile(_base + p1, p2 - p1 + _page_size)) {
604 s = Status::IOError("WinMmapFile.Sync::FlushViewOfFile: ",Win32::GetLastErrSz());
610 Status Win32MapFile::Flush()
615 Win32MapFile::~Win32MapFile()
617 if (_hFile != INVALID_HANDLE_VALUE) {
618 Win32MapFile::Close();
622 BOOL Win32MapFile::_Init( LPCWSTR Path )
624 DWORD Flag = PathFileExistsW(Path) ? OPEN_EXISTING : CREATE_ALWAYS;
625 _hFile = CreateFileW(Path,
626 GENERIC_READ | GENERIC_WRITE,
627 FILE_SHARE_READ|FILE_SHARE_DELETE|FILE_SHARE_WRITE,
630 FILE_ATTRIBUTE_NORMAL,
632 if(!_hFile || _hFile == INVALID_HANDLE_VALUE)
638 BOOL Win32MapFile::isEnable()
640 return _hFile ? TRUE : FALSE;
643 Win32FileLock::Win32FileLock( const std::string& fname ) :
644 _hFile(NULL),_filename(fname)
647 ToWidePath(fname, path);
651 Win32FileLock::~Win32FileLock()
656 BOOL Win32FileLock::_Init( LPCWSTR path )
660 _hFile = ::CreateFileW(path,0,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
661 if(!_hFile || _hFile == INVALID_HANDLE_VALUE ){
669 void Win32FileLock::_CleanUp()
671 ::CloseHandle(_hFile);
675 BOOL Win32FileLock::isEnable()
677 return _hFile ? TRUE : FALSE;
680 Win32Logger::Win32Logger(WritableFile* pFile) : _pFileProxy(pFile)
685 Win32Logger::~Win32Logger()
691 void Win32Logger::Logv( const char* format, va_list ap )
693 uint64_t thread_id = ::GetCurrentThreadId();
695 // We try twice: the first time with a fixed-size stack allocated buffer,
696 // and the second time with a much larger dynamically allocated buffer.
698 for (int iter = 0; iter < 2; iter++) {
702 bufsize = sizeof(buffer);
706 base = new char[bufsize];
709 char* limit = base + bufsize;
713 p += snprintf(p, limit - p,
714 "%04d/%02d/%02d-%02d:%02d:%02d.%06d %llx ",
721 int(st.wMilliseconds),
722 static_cast<long long unsigned int>(thread_id));
727 va_copy(backup_ap, ap);
728 p += vsnprintf(p, limit - p, format, backup_ap);
732 // Truncate to available space if necessary
735 continue; // Try again with larger buffer
741 // Add newline if necessary
742 if (p == base || p[-1] != '\n') {
747 DWORD hasWritten = 0;
749 _pFileProxy->Append(Slice(base, p - base));
750 _pFileProxy->Flush();
752 if (base != buffer) {
759 bool Win32Env::FileExists(const std::string& fname)
761 std::string path = fname;
763 ToWidePath(ModifyPath(path), wpath);
764 return ::PathFileExistsW(wpath.c_str()) ? true : false;
767 Status Win32Env::GetChildren(const std::string& dir, std::vector<std::string>* result)
770 ::WIN32_FIND_DATAW wfd;
771 std::string path = dir;
775 ToWidePath(path, wpath);
777 ::HANDLE hFind = ::FindFirstFileW(wpath.c_str() ,&wfd);
778 if(hFind && hFind != INVALID_HANDLE_VALUE){
782 ToNarrowPath(wfd.cFileName, child);
783 if(child != ".." && child != ".") {
784 result->push_back(child);
786 hasNext = ::FindNextFileW(hFind,&wfd);
791 sRet = Status::IOError(dir,"Could not get children.");
795 void Win32Env::SleepForMicroseconds( int micros )
797 ::Sleep((micros + 999) /1000);
801 Status Win32Env::DeleteFile( const std::string& fname )
804 std::string path = fname;
806 ToWidePath(ModifyPath(path), wpath);
808 if(!::DeleteFileW(wpath.c_str())) {
809 sRet = Status::IOError(path, "Could not delete file.");
814 Status Win32Env::GetFileSize( const std::string& fname, uint64_t* file_size )
817 std::string path = fname;
819 ToWidePath(ModifyPath(path), wpath);
821 HANDLE file = ::CreateFileW(wpath.c_str(),
822 GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
824 if(::GetFileSizeEx(file,&li)){
825 *file_size = (uint64_t)li.QuadPart;
827 sRet = Status::IOError(path,"Could not get the file size.");
832 Status Win32Env::RenameFile( const std::string& src, const std::string& target )
835 std::string src_path = src;
836 std::wstring wsrc_path;
837 ToWidePath(ModifyPath(src_path), wsrc_path);
838 std::string target_path = target;
839 std::wstring wtarget_path;
840 ToWidePath(ModifyPath(target_path), wtarget_path);
842 if(!MoveFileW(wsrc_path.c_str(), wtarget_path.c_str() ) ){
843 DWORD err = GetLastError();
844 if(err == 0x000000b7){
845 if(!::DeleteFileW(wtarget_path.c_str() ) )
846 sRet = Status::IOError(src, "Could not rename file.");
847 else if(!::MoveFileW(wsrc_path.c_str(),
848 wtarget_path.c_str() ) )
849 sRet = Status::IOError(src, "Could not rename file.");
855 Status Win32Env::LockFile( const std::string& fname, FileLock** lock )
858 std::string path = fname;
860 Win32FileLock* _lock = new Win32FileLock(path);
861 if(!_lock->isEnable()){
864 sRet = Status::IOError(path, "Could not lock file.");
871 Status Win32Env::UnlockFile( FileLock* lock )
878 void Win32Env::Schedule( void (*function)(void* arg), void* arg )
880 QueueUserWorkItem(Win32::WorkItemWrapperProc,
881 new Win32::WorkItemWrapper(function,arg),
885 void Win32Env::StartThread( void (*function)(void* arg), void* arg )
887 ::_beginthread(function,0,arg);
890 Status Win32Env::GetTestDirectory( std::string* path )
893 WCHAR TempPath[MAX_PATH];
894 ::GetTempPathW(MAX_PATH,TempPath);
895 ToNarrowPath(TempPath, *path);
896 path->append("leveldb\\test\\");
901 uint64_t Win32Env::NowMicros()
903 #ifndef USE_VISTA_API
904 #define GetTickCount64 GetTickCount
906 return (uint64_t)(GetTickCount64()*1000);
909 static Status CreateDirInner( const std::string& dirname )
913 DWORD attr = ::GetFileAttributesA(dirname.c_str());
915 DWORD attr = ::GetFileAttributes(dirname.c_str());
918 if (attr == INVALID_FILE_ATTRIBUTES) { // doesn't exist:
919 std::size_t slash = dirname.find_last_of("\\");
920 if (slash != std::string::npos){
921 sRet = CreateDirInner(dirname.substr(0, slash));
922 if (!sRet.ok()) return sRet;
925 BOOL result = ::CreateDirectoryA(dirname.c_str(), NULL);
927 BOOL result = ::CreateDirectory(dirname.c_str(), NULL);
929 if (result == FALSE) {
930 sRet = Status::IOError(dirname, "Could not create directory.");
937 Status Win32Env::CreateDir( const std::string& dirname )
939 std::string path = dirname;
940 if(path[path.length() - 1] != '\\'){
945 return CreateDirInner(path);
948 Status Win32Env::DeleteDir( const std::string& dirname )
952 ToWidePath(dirname, path);
954 if(!::RemoveDirectoryW( path.c_str() ) ){
955 sRet = Status::IOError(dirname, "Could not delete directory.");
960 Status Win32Env::NewSequentialFile( const std::string& fname, SequentialFile** result )
963 std::string path = fname;
965 Win32SequentialFile* pFile = new Win32SequentialFile(path);
966 if(pFile->isEnable()){
970 sRet = Status::IOError(path, Win32::GetLastErrSz());
975 Status Win32Env::NewRandomAccessFile( const std::string& fname, RandomAccessFile** result )
978 std::string path = fname;
979 Win32RandomAccessFile* pFile = new Win32RandomAccessFile(ModifyPath(path));
980 if(!pFile->isEnable()){
983 sRet = Status::IOError(path, Win32::GetLastErrSz());
989 Status Win32Env::NewLogger( const std::string& fname, Logger** result )
992 std::string path = fname;
993 Win32MapFile* pMapFile = new Win32MapFile(ModifyPath(path));
994 if(!pMapFile->isEnable()){
997 sRet = Status::IOError(path,"could not create a logger.");
999 *result = new Win32Logger(pMapFile);
1003 Status Win32Env::NewWritableFile( const std::string& fname, WritableFile** result )
1006 std::string path = fname;
1007 Win32MapFile* pFile = new Win32MapFile(ModifyPath(path));
1008 if(!pFile->isEnable()){
1010 sRet = Status::IOError(fname,Win32::GetLastErrSz());
1016 Win32Env::Win32Env()
1021 Win32Env::~Win32Env()
1027 } // Win32 namespace
1029 static port::OnceType once = LEVELDB_ONCE_INIT;
1030 static Env* default_env;
1031 static void InitDefaultEnv() { default_env = new Win32::Win32Env(); }
1033 Env* Env::Default() {
1034 port::InitOnce(&once, InitDefaultEnv);
1038 } // namespace leveldb
1040 #endif // defined(LEVELDB_PLATFORM_WINDOWS)