diff options
author | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 17:20:29 +0200 |
---|---|---|
committer | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 17:20:29 +0200 |
commit | b8f13e45be884dc12884ebe8f3dcd9eecb23a106 (patch) | |
tree | 22a6d8b96815d626061ff3e2d432c13078fca5c4 /zen | |
parent | 5.4 (diff) | |
download | FreeFileSync-b8f13e45be884dc12884ebe8f3dcd9eecb23a106.tar.gz FreeFileSync-b8f13e45be884dc12884ebe8f3dcd9eecb23a106.tar.bz2 FreeFileSync-b8f13e45be884dc12884ebe8f3dcd9eecb23a106.zip |
5.5
Diffstat (limited to 'zen')
-rw-r--r-- | zen/FindFilePlus/find_file_plus.cpp | 2 | ||||
-rw-r--r-- | zen/error_log.h | 10 | ||||
-rw-r--r-- | zen/file_handling.cpp | 238 | ||||
-rw-r--r-- | zen/file_handling.h | 10 | ||||
-rw-r--r-- | zen/file_io.cpp | 2 | ||||
-rw-r--r-- | zen/file_io.h | 10 | ||||
-rw-r--r-- | zen/file_traverser.cpp | 71 | ||||
-rw-r--r-- | zen/file_update_handle.h | 70 | ||||
-rw-r--r-- | zen/fixed_list.h | 53 | ||||
-rw-r--r-- | zen/long_path_prefix.h | 2 | ||||
-rw-r--r-- | zen/optional.h | 15 | ||||
-rw-r--r-- | zen/read_txt.cpp | 12 | ||||
-rw-r--r-- | zen/recycler.cpp | 68 | ||||
-rw-r--r-- | zen/recycler.h | 4 | ||||
-rw-r--r-- | zen/stl_tools.h | 17 | ||||
-rw-r--r-- | zen/string_base.h | 8 | ||||
-rw-r--r-- | zen/string_tools.h | 6 | ||||
-rw-r--r-- | zen/string_traits.h | 5 | ||||
-rw-r--r-- | zen/symlink_target.h | 2 | ||||
-rw-r--r-- | zen/thread.h | 15 |
20 files changed, 334 insertions, 286 deletions
diff --git a/zen/FindFilePlus/find_file_plus.cpp b/zen/FindFilePlus/find_file_plus.cpp index 9fba5f1a..19f43998 100644 --- a/zen/FindFilePlus/find_file_plus.cpp +++ b/zen/FindFilePlus/find_file_plus.cpp @@ -296,7 +296,7 @@ void FileSearcher::readDirImpl(FileInformation& output) //throw FileError rv == STATUS_INVALID_INFO_CLASS || rv == STATUS_UNSUCCESSFUL || rv == STATUS_ACCESS_VIOLATION || - rv == STATUS_NO_SUCH_FILE) + rv == STATUS_NO_SUCH_FILE) //[!] rv = STATUS_NOT_SUPPORTED; throw NtFileError(rv); //throws STATUS_NO_MORE_FILES when finished diff --git a/zen/error_log.h b/zen/error_log.h index f3f67233..bbb36f00 100644 --- a/zen/error_log.h +++ b/zen/error_log.h @@ -17,10 +17,10 @@ namespace zen { enum MessageType { - TYPE_INFO = 1, - TYPE_WARNING = 2, - TYPE_ERROR = 4, - TYPE_FATAL_ERROR = 8, + TYPE_INFO = 0x1, + TYPE_WARNING = 0x2, + TYPE_ERROR = 0x4, + TYPE_FATAL_ERROR = 0x8, }; struct LogEntry @@ -130,4 +130,4 @@ inline std::wstring formatMessage(const LogEntry& entry) { return formatMessageI } -#endif // ERRORLOGGING_H_INCLUDED +#endif //ERRORLOGGING_H_INCLUDED diff --git a/zen/file_handling.cpp b/zen/file_handling.cpp index f004e09c..ebfe4d19 100644 --- a/zen/file_handling.cpp +++ b/zen/file_handling.cpp @@ -23,7 +23,6 @@ #include "long_path_prefix.h" #include <Aclapi.h> #include "dst_hack.h" -#include "file_update_handle.h" #include "win_ver.h" #include "IFileOperation/file_op.h" @@ -703,18 +702,25 @@ void moveDirectoryImpl(const Zstring& sourceDir, const Zstring& targetDir, Callb } //if moving failed treat as error (except when it tried to move to a different volume: in this case we will copy the directory) catch (const ErrorDifferentVolume&) {} - catch (const ErrorTargetExisting&) {} + catch (const ErrorTargetExisting& ) {} //create target if (symlinkExists(sourceDir)) { - if (!dirExists(targetDir)) + if (!symlinkExists(targetDir)) copySymlink(sourceDir, targetDir, false); //throw FileError -> don't copy permissions } else { - if (!dirExists(targetDir)) //check even if ErrorTargetExisting: me may have clashed with a file of the same name!!! - createDirectory(targetDir, sourceDir, false); //throw FileError + try + { + makeNewDirectory(targetDir, sourceDir, false); //FileError, ErrorTargetExisting + } + catch (const ErrorTargetExisting&) + { + if (!dirExists(targetDir)) + throw; //clashed with a file or symlink of the same name!!! + } //move files/folders recursively TraverseOneLevel::NameList fileList; //list of names: 1. short 2.long @@ -888,23 +894,6 @@ void zen::setFileTime(const Zstring& filename, const Int64& modificationTime, Pr { */ - //may need to remove the readonly-attribute (e.g. FAT usb drives) - FileUpdateHandle targetHandle(filename, [=] - { - return ::CreateFile(applyLongPathPrefix(filename).c_str(), - FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES, - //avoids mysterious "access denied" when using "GENERIC_READ | GENERIC_WRITE" on a read-only file, even after read-only was removed right before: - //https://sourceforge.net/tracker/?func=detail&atid=1093080&aid=3514569&group_id=234430 - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - nullptr, - OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS | //needed to open a directory - (procSl == SYMLINK_DIRECT ? FILE_FLAG_OPEN_REPARSE_POINT : 0), //process symlinks - nullptr); - }); - - if (targetHandle.get() == INVALID_HANDLE_VALUE) - throw FileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted()); /* if (hTarget == INVALID_HANDLE_VALUE && ::GetLastError() == ERROR_SHARING_VIOLATION) ::Sleep(retryInterval); //wait then retry @@ -913,12 +902,87 @@ void zen::setFileTime(const Zstring& filename, const Int64& modificationTime, Pr } */ - auto isNullTime = [](const FILETIME & ft) { return ft.dwLowDateTime == 0 && ft.dwHighDateTime == 0; }; + //temporarily reset read-only flag if required + DWORD attribs = INVALID_FILE_ATTRIBUTES; + ZEN_ON_SCOPE_EXIT( + if (attribs != INVALID_FILE_ATTRIBUTES) + ::SetFileAttributes(applyLongPathPrefix(filename).c_str(), attribs); + ); + + auto removeReadonly = [&]() -> bool //may need to remove the readonly-attribute (e.g. on FAT usb drives) + { + if (attribs == INVALID_FILE_ATTRIBUTES) + { + const DWORD tmpAttr = ::GetFileAttributes(applyLongPathPrefix(filename).c_str()); + if (tmpAttr == INVALID_FILE_ATTRIBUTES) + throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted()); + + if (tmpAttr & FILE_ATTRIBUTE_READONLY) + { + if (!::SetFileAttributes(applyLongPathPrefix(filename).c_str(), FILE_ATTRIBUTE_NORMAL)) + throw FileError(replaceCpy(_("Cannot write file attributes of %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted()); + + attribs = tmpAttr; //reapplied on scope exit + return true; + } + } + return false; + }; + + auto openFile = [&](bool conservativeApproach) + { + return ::CreateFile(applyLongPathPrefix(filename).c_str(), + (conservativeApproach ? + //some NAS seem to have issues with FILE_WRITE_ATTRIBUTES, even worse, they may fail silently! + //http://sourceforge.net/tracker/?func=detail&atid=1093081&aid=3536680&group_id=234430 + //Citrix shares seem to have this issue, too, but at least fail with "access denied" => try generic access first: + GENERIC_READ | GENERIC_WRITE : + //avoids mysterious "access denied" when using "GENERIC_READ | GENERIC_WRITE" on a read-only file, even *after* read-only was removed directly before the call! + //http://sourceforge.net/tracker/?func=detail&atid=1093080&aid=3514569&group_id=234430 + //since former gives an error notification we may very well try FILE_WRITE_ATTRIBUTES second. + FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES), + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + nullptr, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS | //needed to open a directory + (procSl == SYMLINK_DIRECT ? FILE_FLAG_OPEN_REPARSE_POINT : 0), //process symlinks + nullptr); + }; + + HANDLE hFile = INVALID_HANDLE_VALUE; + for (int i = 0; i < 2; ++i) //we will get this handle, no matter what! :) + { + //1. be conservative + hFile = openFile(true); + if (hFile == INVALID_HANDLE_VALUE) + { + if (::GetLastError() == ERROR_ACCESS_DENIED) //fails if file is read-only (or for "other" reasons) + if (removeReadonly()) + continue; - if (!::SetFileTime(targetHandle.get(), //__in HANDLE hFile, + //2. be a *little* fancy + hFile = openFile(false); + if (hFile == INVALID_HANDLE_VALUE) + { + if (::GetLastError() == ERROR_ACCESS_DENIED) + if (removeReadonly()) + continue; + + //3. after these herculean stunts we give up... + throw FileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted()); + } + } + break; + } + ZEN_ON_SCOPE_EXIT(::CloseHandle(hFile)); + + + auto isNullTime = [](const FILETIME& ft) { return ft.dwLowDateTime == 0 && ft.dwHighDateTime == 0; }; + + if (!::SetFileTime(hFile, //__in HANDLE hFile, !isNullTime(creationTime) ? &creationTime : nullptr, //__in_opt const FILETIME *lpCreationTime, - nullptr, //__in_opt const FILETIME *lpLastAccessTime, - &lastWriteTime)) //__in_opt const FILETIME *lpLastWriteTime + nullptr, //__in_opt const FILETIME *lpLastAccessTime, + &lastWriteTime)) //__in_opt const FILETIME *lpLastWriteTime { auto lastErr = ::GetLastError(); @@ -933,7 +997,7 @@ void zen::setFileTime(const Zstring& filename, const Int64& modificationTime, Pr { auto setFileInfo = [&](FILE_BASIC_INFO basicInfo) //throw FileError; no const& since SetFileInformationByHandle() requires non-const parameter! { - if (!setFileInformationByHandle(targetHandle.get(), //__in HANDLE hFile, + if (!setFileInformationByHandle(hFile, //__in HANDLE hFile, FileBasicInfo, //__in FILE_INFO_BY_HANDLE_CLASS FileInformationClass, &basicInfo, //__in LPVOID lpFileInformation, sizeof(basicInfo))) //__in DWORD dwBufferSize @@ -950,7 +1014,7 @@ void zen::setFileTime(const Zstring& filename, const Int64& modificationTime, Pr //--------------------------------------------------------------------------- BY_HANDLE_FILE_INFORMATION fileInfo = {}; - if (::GetFileInformationByHandle(targetHandle.get(), &fileInfo)) + if (::GetFileInformationByHandle(hFile, &fileInfo)) if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_READONLY) { FILE_BASIC_INFO basicInfo = {}; //undocumented: file times of "0" stand for "don't change" @@ -959,6 +1023,7 @@ void zen::setFileTime(const Zstring& filename, const Int64& modificationTime, Pr if (!isNullTime(creationTime)) basicInfo.CreationTime = toLargeInteger(creationTime); + //set file time + attributes setFileInfo(basicInfo); //throw FileError try //... to restore original file attributes @@ -972,10 +1037,10 @@ void zen::setFileTime(const Zstring& filename, const Int64& modificationTime, Pr lastErr = ERROR_SUCCESS; } } - - if (lastErr != ERROR_SUCCESS) - throw FileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted(lastErr)); } + + if (lastErr != ERROR_SUCCESS) + throw FileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted(lastErr)); } #ifndef NDEBUG //dst hack: verify data written @@ -1005,8 +1070,8 @@ void zen::setFileTime(const Zstring& filename, const Int64& modificationTime, Pr else { struct timeval newTimes[2] = {}; - newTimes[0].tv_sec = ::time(nullptr); /* seconds */ - newTimes[0].tv_usec = 0; /* microseconds */ + newTimes[0].tv_sec = ::time(nullptr); //seconds + newTimes[0].tv_usec = 0; //microseconds newTimes[1].tv_sec = to<time_t>(modificationTime); newTimes[1].tv_usec = 0; @@ -1140,7 +1205,7 @@ void copySecurityContext(const Zstring& source, const Zstring& target, ProcSymli //copy permissions for files, directories or symbolic links: requires admin rights -void copyObjectPermissions(const Zstring& source, const Zstring& target, ProcSymlink procSl) //throw FileError; +void copyObjectPermissions(const Zstring& source, const Zstring& target, ProcSymlink procSl) //throw FileError { #ifdef FFS_WIN //setting privileges requires admin rights! @@ -1314,22 +1379,41 @@ void copyObjectPermissions(const Zstring& source, const Zstring& target, ProcSym } -void createDirectory_straight(const Zstring& directory, const Zstring& templateDir, bool copyFilePermissions, int level) +void createDirectoryStraight(const Zstring& directory, //throw FileError, ErrorTargetExisting, ErrorTargetPathMissing + const Zstring& templateDir, + bool copyFilePermissions) { - //default directory creation #ifdef FFS_WIN //don't use ::CreateDirectoryEx: //- it may fail with "wrong parameter (error code 87)" when source is on mapped online storage //- automatically copies symbolic links if encountered: unfortunately it doesn't copy symlinks over network shares but silently creates empty folders instead (on XP)! //- it isn't able to copy most junctions because of missing permissions (although target path can be retrieved alternatively!) - if (!::CreateDirectory(applyLongPathPrefixCreateDir(directory).c_str(), nullptr)) + if (!::CreateDirectory(applyLongPathPrefixCreateDir(directory).c_str(), //__in LPCTSTR lpPathName, + nullptr)) //__in_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes + { + const std::wstring msg = replaceCpy(_("Cannot create directory %x."), L"%x", fmtFileName(directory)) + L"\n\n" + getLastErrorFormatted(); + const ErrorCode lastError = getLastError(); + + if (lastError == ERROR_ALREADY_EXISTS) + throw ErrorTargetExisting(msg); + else if (lastError == ERROR_PATH_NOT_FOUND) + throw ErrorTargetPathMissing(msg); + throw FileError(msg); + } + #elif defined FFS_LINUX - if (::mkdir(directory.c_str(), 0755) != 0) -#endif + if (::mkdir(directory.c_str(), 0755) != 0) //mode: drwxr-xr-x { - if (level != 0) return; - throw FileError(replaceCpy(_("Cannot create directory %x."), L"%x", fmtFileName(directory)) + L"\n\n" + getLastErrorFormatted()); + const std::wstring msg = replaceCpy(_("Cannot create directory %x."), L"%x", fmtFileName(directory)) + L"\n\n" + getLastErrorFormatted(); + const ErrorCode lastError = getLastError(); + + if (lastError == EEXIST) + throw ErrorTargetExisting(msg); + else if (lastError == ENOENT) + throw ErrorTargetPathMissing(msg); + throw FileError(msg); } +#endif if (!templateDir.empty()) { @@ -1355,11 +1439,12 @@ void createDirectory_straight(const Zstring& directory, const Zstring& templateD const DWORD sourceAttr = ::GetFileAttributes(applyLongPathPrefix(sourcePath).c_str()); if (sourceAttr != INVALID_FILE_ATTRIBUTES) { + ::SetFileAttributes(applyLongPathPrefix(directory).c_str(), sourceAttr); + //copy "read-only and system attributes": http://blogs.msdn.com/b/oldnewthing/archive/2003/09/30/55100.aspx + const bool isCompressed = (sourceAttr & FILE_ATTRIBUTE_COMPRESSED) != 0; const bool isEncrypted = (sourceAttr & FILE_ATTRIBUTE_ENCRYPTED) != 0; - ::SetFileAttributes(applyLongPathPrefix(directory).c_str(), sourceAttr); - if (isEncrypted) ::EncryptFile(directory.c_str()); //seems no long path is required (check passed!) @@ -1391,6 +1476,7 @@ void createDirectory_straight(const Zstring& directory, const Zstring& templateD } } #endif + zen::ScopeGuard guardNewDir = zen::makeGuard([&] { try { removeDirectory(directory); } catch (...) {} }); //ensure cleanup: //enforce copying file permissions: it's advertized on GUI... @@ -1402,47 +1488,48 @@ void createDirectory_straight(const Zstring& directory, const Zstring& templateD } -void createDirectoryRecursively(const Zstring& directory, const Zstring& templateDir, bool copyFilePermissions, int level) +void createDirectoryRecursively(const Zstring& directory, const Zstring& templateDir, bool copyFilePermissions) //FileError, ErrorTargetExisting { - if (level == 100) //catch endless recursion - return; - -#ifdef FFS_WIN - std::unique_ptr<Fix8Dot3NameClash> fnc; - if (somethingExists(directory)) + try { - //handle issues with already existing short 8.3 file names on Windows - if (have8dot3NameClash(directory)) - fnc.reset(new Fix8Dot3NameClash(directory)); //move clashing object to the side - else if (dirExists(directory)) - return; + createDirectoryStraight(directory, templateDir, copyFilePermissions); //throw FileError, ErrorTargetExisting, ErrorTargetPathMissing } -#elif defined FFS_LINUX - if (somethingExists(directory)) + catch (const ErrorTargetExisting&) { - if (dirExists(directory)) +#ifdef FFS_WIN + //handle issues with already existing short 8.3 file names on Windows + if (have8dot3NameClash(directory)) + { + Fix8Dot3NameClash dummy(directory); //move clashing object to the side + + //now try again... + createDirectoryStraight(directory, templateDir, copyFilePermissions); //throw FileError, ErrorTargetExisting, ErrorTargetPathMissing return; - } + } #endif - else //if "not somethingExists" we need to create the parent directory + throw; + } + catch (const ErrorTargetPathMissing&) { - //try to create parent folders first + //we need to create parent directories first const Zstring dirParent = beforeLast(directory, FILE_NAME_SEPARATOR); - if (!dirParent.empty() && !dirExists(dirParent)) + if (!dirParent.empty()) { //call function recursively const Zstring templateParent = beforeLast(templateDir, FILE_NAME_SEPARATOR); //returns empty string if ch not found - createDirectoryRecursively(dirParent, templateParent, copyFilePermissions, level + 1); + createDirectoryRecursively(dirParent, templateParent, copyFilePermissions); //throw + + //now try again... + createDirectoryStraight(directory, templateDir, copyFilePermissions); //throw FileError, ErrorTargetExisting, ErrorTargetPathMissing + return; } + throw; } - - //now creation should be possible - createDirectory_straight(directory, templateDir, copyFilePermissions, level); //throw FileError } } -void zen::createDirectory(const Zstring& directory, const Zstring& templateDir, bool copyFilePermissions) +void zen::makeNewDirectory(const Zstring& directory, const Zstring& templateDir, bool copyFilePermissions) //FileError, ErrorTargetExisting { //remove trailing separator const Zstring dirFormatted = endsWith(directory, FILE_NAME_SEPARATOR) ? @@ -1453,13 +1540,22 @@ void zen::createDirectory(const Zstring& directory, const Zstring& templateDir, beforeLast(templateDir, FILE_NAME_SEPARATOR) : templateDir; - createDirectoryRecursively(dirFormatted, templateFormatted, copyFilePermissions, 0); + createDirectoryRecursively(dirFormatted, templateFormatted, copyFilePermissions); //FileError, ErrorTargetExisting } -void zen::createDirectory(const Zstring& directory) +void zen::makeDirectory(const Zstring& directory) { - zen::createDirectory(directory, Zstring(), false); + try + { + makeNewDirectory(directory, Zstring(), false); //FileError, ErrorTargetExisting + } + catch (const ErrorTargetExisting&) + { + if (dirExists(directory)) + return; + throw; //clash with file (dir symlink is okay) + } } @@ -2254,7 +2350,7 @@ Zstring findUnusedTempName(const Zstring& filename) { Zstring output = filename + zen::TEMP_FILE_ENDING; - //ensure uniqueness (+ minor race condition) + //ensure uniqueness (+ minor file system race condition!) for (int i = 1; somethingExists(output); ++i) output = filename + Zchar('_') + numberTo<Zstring>(i) + zen::TEMP_FILE_ENDING; diff --git a/zen/file_handling.h b/zen/file_handling.h index 8848fbc2..d6444da3 100644 --- a/zen/file_handling.h +++ b/zen/file_handling.h @@ -53,18 +53,18 @@ void removeDirectory(const Zstring& directory, CallbackRemoveDir* callback = nul //rename file or directory: no copying!!! -void renameFile(const Zstring& oldName, const Zstring& newName); //throw FileError; +void renameFile(const Zstring& oldName, const Zstring& newName); //throw FileError //move source to target across volumes; prerequisite: all super-directories of target exist //if target already contains some files/dirs they are seen as remnants of a previous incomplete move - see comment in moveDirectoryImpl -void moveFile(const Zstring& sourceFile, const Zstring& targetFile, CallbackMoveFile* callback); //throw FileError -void moveDirectory(const Zstring& sourceDir, const Zstring& targetDir, CallbackMoveFile* callback); //throw FileError +void moveFile (const Zstring& sourceFile, const Zstring& targetFile, CallbackMoveFile* callback); //throw FileError +void moveDirectory(const Zstring& sourceDir, const Zstring& targetDir, CallbackMoveFile* callback); //throw FileError bool supportsPermissions(const Zstring& dirname); //throw FileError, derefernces symlinks //creates superdirectories automatically: -void createDirectory(const Zstring& directory, const Zstring& templateDir, bool copyFilePermissions); //throw FileError; -void createDirectory(const Zstring& directory); //throw FileError; -> function overload avoids default parameter ambiguity issues! +void makeDirectory(const Zstring& directory); //throw FileError; do nothing if directory already exists! +void makeNewDirectory(const Zstring& directory, const Zstring& templateDir, bool copyFilePermissions); //FileError, ErrorTargetExisting struct FileAttrib { diff --git a/zen/file_io.cpp b/zen/file_io.cpp index 3b3c244d..77fcb691 100644 --- a/zen/file_io.cpp +++ b/zen/file_io.cpp @@ -81,7 +81,7 @@ FileInput::~FileInput() } -size_t FileInput::read(void* buffer, size_t bytesToRead) //returns actual number of bytes read; throw (FileError) +size_t FileInput::read(void* buffer, size_t bytesToRead) //returns actual number of bytes read; throw FileError { #ifdef FFS_WIN DWORD bytesRead = 0; diff --git a/zen/file_io.h b/zen/file_io.h index 33074d7e..b134e47a 100644 --- a/zen/file_io.h +++ b/zen/file_io.h @@ -19,6 +19,12 @@ namespace zen { +#ifdef FFS_WIN +static const char LINE_BREAK[] = "\r\n"; +#elif defined FFS_LINUX +static const char LINE_BREAK[] = "\n"; +#endif + //file IO optimized for sequential read/write accesses + better error reporting + long path support (following symlinks) #ifdef FFS_WIN @@ -37,6 +43,8 @@ public: size_t read(void* buffer, size_t bytesToRead); //throw FileError; returns actual number of bytes read bool eof() { return eofReached; } //end of file reached + const Zstring& getFilename() const { return filename_; } + private: FileInput(const FileInput&); FileInput& operator=(const FileInput&); @@ -61,6 +69,8 @@ public: void write(const void* buffer, size_t bytesToWrite); //throw FileError + const Zstring& getFilename() const { return filename_; } + private: FileOutput(const FileOutput&); FileOutput& operator=(const FileOutput&); diff --git a/zen/file_traverser.cpp b/zen/file_traverser.cpp index 3acb0edf..aa46d4f0 100644 --- a/zen/file_traverser.cpp +++ b/zen/file_traverser.cpp @@ -10,13 +10,12 @@ #include "symlink_target.h" #ifdef FFS_WIN -#include "win.h" //includes "windows.h" +#include <zen/win_ver.h> #include "long_path_prefix.h" #include "dst_hack.h" -#include "file_update_handle.h" +#include "file_handling.h" //remove this huge dependency when getting rid of DST hack!! until then we need "setFileTime" #include "dll.h" #include "FindFilePlus/find_file_plus.h" -#include <zen/win_ver.h> #elif defined FFS_LINUX #include <sys/stat.h> @@ -179,11 +178,12 @@ struct Win32Traverser { struct DirHandle { - DirHandle() : searchHandle(nullptr), firstRead(true), firstData() {} + DirHandle() : searchHandle(nullptr), haveData(true), data() {} HANDLE searchHandle; - bool firstRead; - WIN32_FIND_DATA firstData; + + bool haveData; + WIN32_FIND_DATA data; }; typedef WIN32_FIND_DATA FindData; @@ -192,7 +192,7 @@ struct Win32Traverser { const Zstring& directoryPf = appendSeparator(directory); - hnd.searchHandle = ::FindFirstFile(applyLongPathPrefix(directoryPf + L'*').c_str(), &hnd.firstData); + hnd.searchHandle = ::FindFirstFile(applyLongPathPrefix(directoryPf + L'*').c_str(), &hnd.data); //no noticable performance difference compared to FindFirstFileEx with FindExInfoBasic, FIND_FIRST_EX_CASE_SENSITIVE and/or FIND_FIRST_EX_LARGE_FETCH if (hnd.searchHandle == INVALID_HANDLE_VALUE) throw FileError(replaceCpy(_("Cannot read directory %x."), L"%x", fmtFileName(directory)) + L"\n\n" + getLastErrorFormatted()); @@ -208,10 +208,10 @@ struct Win32Traverser template <class FallbackFun> static bool getEntry(DirHandle& hnd, const Zstring& directory, FindData& fileInfo, FallbackFun) //throw FileError { - if (hnd.firstRead) + if (hnd.haveData) { - hnd.firstRead = false; - ::memcpy(&fileInfo, &hnd.firstData, sizeof(fileInfo)); + hnd.haveData = false; + ::memcpy(&fileInfo, &hnd.data, sizeof(fileInfo)); return true; } @@ -451,7 +451,7 @@ private: if (dst::fatHasUtcEncoded(rawTime)) //throw std::runtime_error fileInfo.lastWriteTimeRaw = toTimeT(dst::fatDecodeUtcTime(rawTime)); //return real UTC time; throw (std::runtime_error) else - markForDstHack.push_back(std::make_pair(fullName, Trav::getModTimeRaw(findData))); + markForDstHack.push_back(std::make_pair(fullName, toTimeT(rawTime.writeTimeRaw))); } //####################################### DST hack ########################################### @@ -467,52 +467,33 @@ private: int failedAttempts = 0; int filesToValidate = 50; //don't let data verification become a performance issue - for (FilenameTimeList::const_iterator i = markForDstHack.begin(); i != markForDstHack.end(); ++i) + for (auto iter = markForDstHack.begin(); iter != markForDstHack.end(); ++iter) { if (failedAttempts >= 10) //some cloud storages don't support changing creation/modification times => don't waste (a lot of) time trying to return; - dstCallback.requestUiRefresh(i->first); + dstCallback.requestUiRefresh(iter->first); - const dst::RawTime encodedTime = dst::fatEncodeUtcTime(i->second); //throw std::runtime_error + try { - //may need to remove the readonly-attribute (e.g. FAT usb drives) - FileUpdateHandle updateHandle(i->first, [=] - { - return ::CreateFile(zen::applyLongPathPrefix(i->first).c_str(), - FILE_WRITE_ATTRIBUTES, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - nullptr, - OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS, //needed to open a directory - nullptr); - }); - if (updateHandle.get() == INVALID_HANDLE_VALUE) - { - ++failedAttempts; - assert(false); //don't throw exceptions due to dst hack here - continue; - } - - if (!::SetFileTime(updateHandle.get(), - &encodedTime.createTimeRaw, - nullptr, - &encodedTime.writeTimeRaw)) - { - ++failedAttempts; - assert(false); //don't throw exceptions due to dst hack here - continue; - } + //set modification time including DST hack: this function is too clever to not introduce this dependency + setFileTime(iter->first, iter->second, SYMLINK_FOLLOW); //throw FileError + } + catch (FileError&) + { + ++failedAttempts; + assert(false); //don't throw exceptions due to dst hack here + continue; } //even at this point it's not sure whether data was written correctly, again cloud storages tend to lie about success status - if (filesToValidate > 0) + if (filesToValidate-- > 0) { - --filesToValidate; //don't change during check! + const dst::RawTime encodedTime = dst::fatEncodeUtcTime(tofiletime(iter->second)); //throw std::runtime_error //dst hack: verify data written; attention: this check may fail for "sync.ffs_lock" WIN32_FILE_ATTRIBUTE_DATA debugeAttr = {}; - ::GetFileAttributesEx(zen::applyLongPathPrefix(i->first).c_str(), //__in LPCTSTR lpFileName, + ::GetFileAttributesEx(zen::applyLongPathPrefix(iter->first).c_str(), //__in LPCTSTR lpFileName, GetFileExInfoStandard, //__in GET_FILEEX_INFO_LEVELS fInfoLevelId, &debugeAttr); //__out LPVOID lpFileInformation @@ -528,7 +509,7 @@ private: } const bool isFatFileSystem; - typedef std::vector<std::pair<Zstring, FILETIME> > FilenameTimeList; + typedef std::vector<std::pair<Zstring, Int64> > FilenameTimeList; FilenameTimeList markForDstHack; //####################################### DST hack ########################################### diff --git a/zen/file_update_handle.h b/zen/file_update_handle.h deleted file mode 100644 index 3df69f10..00000000 --- a/zen/file_update_handle.h +++ /dev/null @@ -1,70 +0,0 @@ -#ifndef FILE_UPDATE_HANDLE_H_INCLUDED -#define FILE_UPDATE_HANDLE_H_INCLUDED - -#include "win.h" //includes "windows.h" -#include "long_path_prefix.h" - -namespace -{ -//manage file handle to update existing files (temporarily resetting read-only if necessary) -//CreateFileCmd: lambda directly returning non-owned file handle from ::CreateFile() -class FileUpdateHandle -{ -public: - template <class CreateFileCmd> - FileUpdateHandle(const Zstring& filename, CreateFileCmd cmd) : - filenameFmt(zen::applyLongPathPrefix(filename)), - hFile(INVALID_HANDLE_VALUE), - attr(INVALID_FILE_ATTRIBUTES) - { - hFile = cmd(); - if (hFile == INVALID_HANDLE_VALUE) - { - //try to recover - if (::GetLastError() == ERROR_ACCESS_DENIED) //function fails if file is read-only - { - //read-only file attribute may cause trouble: temporarily reset it - const DWORD tmpAttr = ::GetFileAttributes(filenameFmt.c_str()); - if (tmpAttr != INVALID_FILE_ATTRIBUTES) - { - if (tmpAttr & FILE_ATTRIBUTE_READONLY) - { - if (::SetFileAttributes(filenameFmt.c_str(), FILE_ATTRIBUTE_NORMAL)) - { - //guardErrorCode.dismiss(); - attr = tmpAttr; //"create" guard on read-only attribute - - //now try again - hFile = cmd(); - } - } - else - ::SetLastError(ERROR_ACCESS_DENIED); - } - } - } - } - - ~FileUpdateHandle() - { - if (hFile != INVALID_HANDLE_VALUE) - ::CloseHandle(hFile); - - if (attr != INVALID_FILE_ATTRIBUTES) - ::SetFileAttributes(filenameFmt.c_str(), attr); - } - - //may return INVALID_FILE_ATTRIBUTES, in which case ::GetLastError() may be called directly after FileUpdateHandle() - HANDLE get() const { return hFile; } - -private: - FileUpdateHandle(const FileUpdateHandle&); - FileUpdateHandle& operator=(const FileUpdateHandle&); - - Zstring filenameFmt; - HANDLE hFile; - DWORD attr; -}; -} - -#endif // FILE_UPDATE_HANDLE_H_INCLUDED diff --git a/zen/fixed_list.h b/zen/fixed_list.h index eedbfab9..04a680ad 100644 --- a/zen/fixed_list.h +++ b/zen/fixed_list.h @@ -4,8 +4,8 @@ // * Copyright (C) ZenJu (zhnmju123 AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef PTR_WRAP_012384670856841394535 -#define PTR_WRAP_012384670856841394535 +#ifndef FIXED_LIST_01238467085684139453534 +#define FIXED_LIST_01238467085684139453534 #include <iterator> @@ -19,12 +19,13 @@ class FixedList struct Node { Node() : next(nullptr), val() {} - template <class A> Node(A&& a) : next(nullptr), val(a) {} - template <class A, class B> Node(A&& a, B&& b) : next(nullptr), val(a, b) {} - template <class A, class B, class C> Node(A&& a, B&& b, C&& c) : next(nullptr), val(a, b, c) {} - template <class A, class B, class C, class D> Node(A&& a, B&& b, C&& c, D&& d) : next(nullptr), val(a, b, c, d) {} - template <class A, class B, class C, class D, class E> Node(A&& a, B&& b, C&& c, D&& d, E&& e) : next(nullptr), val(a, b, c, d, e) {} - template <class A, class B, class C, class D, class E, class F> Node(A&& a, B&& b, C&& c, D&& d, E&& e, F&& f) : next(nullptr), val(a, b, c, d, e, f) {} + //no variadic templates on VC2010... :( + template <class A> Node(A&& a) : next(nullptr), val(std::forward<A>(a)) {} + template <class A, class B> Node(A&& a, B&& b) : next(nullptr), val(std::forward<A>(a), std::forward<B>(b)) {} + template <class A, class B, class C> Node(A&& a, B&& b, C&& c) : next(nullptr), val(std::forward<A>(a), std::forward<B>(b), std::forward<C>(c)) {} + template <class A, class B, class C, class D> Node(A&& a, B&& b, C&& c, D&& d) : next(nullptr), val(std::forward<A>(a), std::forward<B>(b), std::forward<C>(c), std::forward<D>(d)) {} + template <class A, class B, class C, class D, class E> Node(A&& a, B&& b, C&& c, D&& d, E&& e) : next(nullptr), val(std::forward<A>(a), std::forward<B>(b), std::forward<C>(c), std::forward<D>(d), std::forward<E>(e)) {} + template <class A, class B, class C, class D, class E, class F> Node(A&& a, B&& b, C&& c, D&& d, E&& e, F&& f) : next(nullptr), val(std::forward<A>(a), std::forward<B>(b), std::forward<C>(c), std::forward<D>(d), std::forward<E>(e), std::forward<F>(f)) {} Node* next; //singly linked list is sufficient T val; @@ -36,16 +37,7 @@ public: lastInsert(nullptr), sz(0) {} - ~FixedList() - { - Node* ptr = first; - while (ptr) - { - Node* tmp = ptr; - ptr = ptr->next; - delete tmp; - } - } + ~FixedList() { clear(); } template <class NodeT, class U> class ListIterator : public std::iterator<std::forward_iterator_tag, U> @@ -73,6 +65,9 @@ public: const_iterator begin() const { return first; } const_iterator end () const { return const_iterator(); } + const_iterator cbegin() const { return first; } + const_iterator cend () const { return const_iterator(); } + reference front() { return first->val; } const_reference front() const { return first->val; } @@ -112,7 +107,20 @@ public: } } - void clear() { remove_if([](T&) { return true; }); } + void clear() + { + Node* ptr = first; + while (ptr) + { + Node* tmp = ptr; + ptr = ptr->next; + delete tmp; + } + + first = lastInsert = nullptr; + sz = 0; + } + bool empty() const { return first == nullptr; } size_t size() const { return sz; } @@ -120,7 +128,7 @@ private: FixedList(const FixedList&); FixedList& operator=(const FixedList&); - void pushNode(Node* newNode) + void pushNode(Node* newNode) //throw() { ++sz; if (lastInsert == nullptr) @@ -144,10 +152,9 @@ private: } Node* first; - Node* lastInsert; //point to last insertion; required by emplace_back() + Node* lastInsert; //point to last insertion; required by efficient emplace_back() size_t sz; }; } - -#endif //PTR_WRAP_012384670856841394535 +#endif //FIXED_LIST_01238467085684139453534 diff --git a/zen/long_path_prefix.h b/zen/long_path_prefix.h index ed6308dc..1476e87d 100644 --- a/zen/long_path_prefix.h +++ b/zen/long_path_prefix.h @@ -19,7 +19,7 @@ namespace zen 3. path may already contain \\?\-prefix */ Zstring applyLongPathPrefix(const Zstring& path); //throw() -Zstring applyLongPathPrefixCreateDir(const Zstring& path); //throw() -> special rule for ::CreateDirectoryEx(): MAX_PATH - 12(=^ 8.3 filename) is threshold +Zstring applyLongPathPrefixCreateDir(const Zstring& path); //throw() -> special rule for ::CreateDirectory()/::CreateDirectoryEx(): MAX_PATH - 12(=^ 8.3 filename) is threshold Zstring removeLongPathPrefix(const Zstring& path); //throw() } diff --git a/zen/optional.h b/zen/optional.h index 2bd272ae..34e3d21d 100644 --- a/zen/optional.h +++ b/zen/optional.h @@ -38,9 +38,17 @@ public: Opt(NoValue) : value() , valid(false) {} Opt(const T& val) : value(val), valid(true ) {} - //rvalue optimization: only basic exception safety: - Opt(Opt&& tmp) : value(std::move(tmp.value)), valid(tmp.valid) {} - Opt& operator=(const Opt& tmp) { value = std::move(tmp.value); valid = tmp.valid; } + Opt(const Opt& tmp) : value(tmp.valid ? tmp.value : T()), valid(tmp.valid) {} + + Opt& operator=(const Opt& tmp) + { + if (tmp.valid) + value = tmp.value; + valid = tmp.valid; + } + + ////rvalue optimization: only basic exception safety: + // Opt(Opt&& tmp) : value(std::move(tmp.value)), valid(tmp.valid) {} #ifdef _MSC_VER private: @@ -58,6 +66,7 @@ public: /**/ T* operator->() { return &value; } void reset() { valid = false; } + private: T value; bool valid; diff --git a/zen/read_txt.cpp b/zen/read_txt.cpp index fd92a10c..0fd310c2 100644 --- a/zen/read_txt.cpp +++ b/zen/read_txt.cpp @@ -5,13 +5,13 @@ using namespace zen; namespace { -std::string detectLineBreak(const Zstring& filename) //throw (FileError) +std::string detectLineBreak(const Zstring& filename) //throw FileError { //read a (hopefully) significant portion of data zen::FileInput input(filename); std::vector<char> buffer(64 * 1024); - size_t bytesRead = input.read(&buffer[0], buffer.size()); //throw (FileError); + size_t bytesRead = input.read(&buffer[0], buffer.size()); //throw FileError buffer.resize(bytesRead); //detect line break @@ -41,15 +41,15 @@ std::string detectLineBreak(const Zstring& filename) //throw (FileError) } -ExtractLines::ExtractLines(const Zstring& filename, const std::string& lineBreak) : //throw (FileError) +ExtractLines::ExtractLines(const Zstring& filename, const std::string& lineBreak) : //throw FileError inputStream(filename), bufferLogBegin(buffer.begin()), lineBreak_(lineBreak) { if (lineBreak.empty()) - lineBreak_ = detectLineBreak(filename); //throw (FileError) + lineBreak_ = detectLineBreak(filename); //throw FileError } -bool ExtractLines::getLine(std::string& output) //throw (FileError) +bool ExtractLines::getLine(std::string& output) //throw FileError { for (;;) { @@ -80,7 +80,7 @@ bool ExtractLines::getLine(std::string& output) //throw (FileError) const size_t BLOCK_SIZE = 512 * 1024; buffer.resize(buffer.size() + BLOCK_SIZE); - size_t bytesRead = inputStream.read(&buffer[0] + buffer.size() - BLOCK_SIZE, BLOCK_SIZE); //throw (FileError); + size_t bytesRead = inputStream.read(&buffer[0] + buffer.size() - BLOCK_SIZE, BLOCK_SIZE); //throw FileError assert(bytesRead <= BLOCK_SIZE); //promised by FileInput() if (bytesRead < BLOCK_SIZE) diff --git a/zen/recycler.cpp b/zen/recycler.cpp index 5e5e56d4..3de795a5 100644 --- a/zen/recycler.cpp +++ b/zen/recycler.cpp @@ -71,7 +71,7 @@ Zstring getLockingProcessNames(const Zstring& filename) //throw(), empty string } -bool zen::moveToRecycleBin(const Zstring& filename) //throw FileError +bool zen::recycleOrDelete(const Zstring& filename) //throw FileError { if (!somethingExists(filename)) return false; //neither file nor any other object with that name existing: no error situation, manual deletion relies on it! @@ -165,12 +165,37 @@ bool zen::moveToRecycleBin(const Zstring& filename) //throw FileError #ifdef FFS_WIN -zen::StatusRecycler zen::recycleBinStatus(const Zstring& pathName) +StatusRecycler zen::recycleBinStatus(const Zstring& pathName) { - warn_static("fix XP not working + finish"); + const DWORD bufferSize = MAX_PATH + 1; + std::vector<wchar_t> buffer(bufferSize); + if (!::GetVolumePathName(pathName.c_str(), //__in LPCTSTR lpszFileName, + &buffer[0], //__out LPTSTR lpszVolumePathName, + bufferSize)) //__in DWORD cchBufferLength + return STATUS_REC_UNKNOWN; + + const Zstring rootPathPf = appendSeparator(&buffer[0]); + + SHQUERYRBINFO recInfo = {}; + recInfo.cbSize = sizeof(recInfo); + HRESULT rv = ::SHQueryRecycleBin(rootPathPf.c_str(), //__in_opt LPCTSTR pszRootPath, + &recInfo); //__inout LPSHQUERYRBINFO pSHQueryRBInfo + + return rv == S_OK ? STATUS_REC_EXISTS : STATUS_REC_MISSING; + //1. excessive: traverses whole C:\$Recycle.Bin directory tree each time!!!! But it's safe and correct. + + //2. we would prefer to use CLSID_RecycleBinManager beginning with Vista... if only this interface were documented!!! + + //3. check directory existence of "C:\$Recycle.Bin, C:\RECYCLER, C:\RECYCLED" + // -> not upward-compatible, wrong result for subst-alias: recycler assumed existing, although it is not! + + //4. alternative approach a'la Raymond Chen: http://blogs.msdn.com/b/oldnewthing/archive/2008/09/18/8956382.aspx + //caveat: might not be reliable, e.g. "subst"-alias of volume contains "$Recycle.Bin" although it is not available! /* - const bool canUseFastCheckForRecycler = winXpOrLater(); + Zstring rootPathPf = appendSeparator(&buffer[0]); + + const bool canUseFastCheckForRecycler = winXpOrLater(); if (!canUseFastCheckForRecycler) //== "checkForRecycleBin" return STATUS_REC_UNKNOWN; @@ -180,20 +205,7 @@ zen::StatusRecycler zen::recycleBinStatus(const Zstring& pathName) if (!checkRecycler) return STATUS_REC_UNKNOWN; //actually an error since we're >= XP - const DWORD bufferSize = MAX_PATH + 1; - std::vector<wchar_t> buffer(bufferSize); - if (!::GetVolumePathName(pathName.c_str(), //__in LPCTSTR lpszFileName, - &buffer[0], //__out LPTSTR lpszVolumePathName, - bufferSize)) //__in DWORD cchBufferLength - return STATUS_REC_UNKNOWN; - - Zstring rootPathPf = appendSeparator(&buffer[0]); - //search root directories for recycle bin folder... - //we would prefer to use CLSID_RecycleBinManager beginning with Vista... if this interface were documented - - //caveat: "subst"-alias of volume contains "$Recycle.Bin" although it is not available!!! - warn_static("check") WIN32_FIND_DATA dataRoot = {}; HANDLE hFindRoot = ::FindFirstFile(applyLongPathPrefix(rootPathPf + L'*').c_str(), &dataRoot); @@ -207,8 +219,8 @@ zen::StatusRecycler zen::recycleBinStatus(const Zstring& pathName) { if (!shouldSkip(dataRoot.cFileName) && (dataRoot.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0 && - (dataRoot.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM ) != 0 && //maybe a little risky to rely on these attributes, there may be a recycler - (dataRoot.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN ) != 0) //(in obscure cases) we don't find, but that's better than the other way round + (dataRoot.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM ) != 0 && //risky to rely on these attributes!!! + (dataRoot.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN ) != 0) // { WIN32_FIND_DATA dataChild = {}; const Zstring childDirPf = rootPathPf + dataRoot.cFileName + L"\\"; @@ -238,25 +250,7 @@ zen::StatusRecycler zen::recycleBinStatus(const Zstring& pathName) while (::FindNextFile(hFindRoot, &dataRoot)); // return STATUS_REC_MISSING; - */ - - const DWORD bufferSize = MAX_PATH + 1; - std::vector<wchar_t> buffer(bufferSize); - if (!::GetVolumePathName(pathName.c_str(), //__in LPCTSTR lpszFileName, - &buffer[0], //__out LPTSTR lpszVolumePathName, - bufferSize)) //__in DWORD cchBufferLength - return STATUS_REC_UNKNOWN; - - const Zstring rootPathPf = appendSeparator(&buffer[0]); - - SHQUERYRBINFO recInfo = {}; - recInfo.cbSize = sizeof(recInfo); - HRESULT rv = ::SHQueryRecycleBin(rootPathPf.c_str(), //__in_opt LPCTSTR pszRootPath, - &recInfo); //__inout LPSHQUERYRBINFO pSHQueryRBInfo - //traverses whole C:\$Recycle.Bin directory each time!!!! - - return rv == S_OK ? STATUS_REC_EXISTS : STATUS_REC_MISSING; } #elif defined FFS_LINUX diff --git a/zen/recycler.h b/zen/recycler.h index 952deec2..1778eb2e 100644 --- a/zen/recycler.h +++ b/zen/recycler.h @@ -29,8 +29,8 @@ Linker flags: `pkg-config --libs gio-2.0` Already included in package "gtk+-2.0"! */ -//move a file or folder to Recycle Bin (deletes permanently if recycle is not available) -bool moveToRecycleBin(const Zstring& filename); //throw FileError, return "true" if file/dir was actually deleted +//move a file or folder to Recycle Bin (deletes permanently if recycle is not available) -> crappy semantics, but we have no choice thanks to Windows' design +bool recycleOrDelete(const Zstring& filename); //throw FileError, return "true" if file/dir was actually deleted #ifdef FFS_WIN diff --git a/zen/stl_tools.h b/zen/stl_tools.h index 78d99832..ace6ebaa 100644 --- a/zen/stl_tools.h +++ b/zen/stl_tools.h @@ -33,6 +33,9 @@ void set_remove_if(S& set, Predicate p); template <class M, class Predicate> void map_remove_if(M& map, Predicate p); +template <class M, class K, class V> +V& map_add_or_update(M& map, const K& key, const V& value); //efficient add or update without "default-constructible" requirement (Effective STL, item 24) + //binary search returning an iterator template <class ForwardIterator, class T, typename Compare> ForwardIterator binary_search(ForwardIterator first, ForwardIterator last, const T& value, Compare comp); @@ -98,6 +101,20 @@ template <class M, class Predicate> inline void map_remove_if(M& map, Predicate p) { set_remove_if(map, p); } +template <class M, class K, class V> inline +V& map_add_or_update(M& map, const K& key, const V& value) //efficient add or update without "default-constructible" requirement (Effective STL, item 24) +{ + auto iter = map.lower_bound(key); + if (iter != map.end() && !(map.key_comp()(key, iter->first))) + { + iter->second = value; + return iter->second; + } + else + return map.insert(iter, typename M::value_type(key, value))->second; +} + + template <class ForwardIterator, class T, typename Compare> inline ForwardIterator binary_search(ForwardIterator first, ForwardIterator last, const T& value, Compare comp) { diff --git a/zen/string_base.h b/zen/string_base.h index 16731089..4c339d56 100644 --- a/zen/string_base.h +++ b/zen/string_base.h @@ -31,7 +31,8 @@ public: //::operator new/ ::operator delete show same performance characterisics like malloc()/free()! static void* allocate(size_t size) { return ::operator new(size); } //throw std::bad_alloc static void deallocate(void* ptr) { ::operator delete(ptr); } - static size_t calcCapacity(size_t length) { return std::max<size_t>(16, length + length / 2); } //any growth rate should not exceed golden ratio: 1.618033989 + static size_t calcCapacity(size_t length) { return std::max<size_t>(std::max<size_t>(16, length), length + length / 2); } //size_t might overflow! + //any growth rate should not exceed golden ratio: 1.618033989 }; @@ -68,9 +69,9 @@ protected: static Char* create(size_t size) { return create(size, size); } static Char* create(size_t size, size_t minCapacity) { + assert(size <= minCapacity); const size_t newCapacity = AP::calcCapacity(minCapacity); assert(newCapacity >= minCapacity); - assert(minCapacity >= size); Descriptor* const newDescr = static_cast<Descriptor*>(AP::allocate(sizeof(Descriptor) + (newCapacity + 1) * sizeof(Char))); @@ -121,9 +122,10 @@ protected: static Char* create(size_t size) { return create(size, size); } static Char* create(size_t size, size_t minCapacity) { + assert(size <= minCapacity); + const size_t newCapacity = AP::calcCapacity(minCapacity); assert(newCapacity >= minCapacity); - assert(minCapacity >= size); Descriptor* const newDescr = static_cast<Descriptor*>(AP::allocate(sizeof(Descriptor) + (newCapacity + 1) * sizeof(Char))); new (newDescr) Descriptor(1, size, newCapacity); diff --git a/zen/string_tools.h b/zen/string_tools.h index 04ba1eef..00eb50ee 100644 --- a/zen/string_tools.h +++ b/zen/string_tools.h @@ -90,7 +90,8 @@ bool isWhiteSpace(char ch) std::isspace(static_cast<unsigned char>(ch)) != 0; } -template <> inline bool isWhiteSpace(wchar_t ch) { return std::iswspace(ch) != 0; } +template <> inline +bool isWhiteSpace(wchar_t ch) { return std::iswspace(ch) != 0; } template <class Char> inline @@ -458,7 +459,7 @@ S formatInteger(Num n, bool hasMinus) assert(n >= 0); typedef typename GetCharType<S>::Type CharType; - const size_t bufferSize = 100; //sufficient for signed 256-bit numbers + const size_t bufferSize = 64; //sufficient for signed 128-bit numbers CharType buffer[bufferSize]; //it's generally faster to use a buffer than to rely on String::operator+=() (in)efficiency assert_static(2 + 5 * sizeof(n) / 2 <= bufferSize); //minimum required chars (+ sign char): 1 + ceil(ln_10 (256^sizeof(n))) =~ 1 + ceil(sizeof(n) * 2.4082) <= 2 + floor(sizeof(n) * 2.5) @@ -492,7 +493,6 @@ S numberTo(const Num& number, Int2Type<NUM_TYPE_UNSIGNED_INT>) //-------------------------------------------------------------------------------- - template <class Num, class S> inline Num stringTo(const S& str, Int2Type<NUM_TYPE_OTHER>) //default string to number conversion using streams: convenient, but SLOW { diff --git a/zen/string_traits.h b/zen/string_traits.h index cfcf78bb..eea9ae02 100644 --- a/zen/string_traits.h +++ b/zen/string_traits.h @@ -35,7 +35,7 @@ strLength(): strLength(array); //equals cStringLength(array) */ -//wrap a sub-string or a char* as an intermediate string class when the length is already known +//reference a sub-string or a char* as an intermediate string class when the length is already known template <class Char> class StringProxy { @@ -126,8 +126,7 @@ public: enum { - isStringLike = - IsSameType<CharType, char >::value || + isStringLike = IsSameType<CharType, char>::value || IsSameType<CharType, wchar_t>::value }; }; diff --git a/zen/symlink_target.h b/zen/symlink_target.h index dfd5ebfa..17ae1916 100644 --- a/zen/symlink_target.h +++ b/zen/symlink_target.h @@ -84,7 +84,7 @@ Zstring getSymlinkRawTargetString(const Zstring& linkPath) //throw FileError const DWORD bufferSize = REPARSE_DATA_BUFFER_HEADER_SIZE + MAXIMUM_REPARSE_DATA_BUFFER_SIZE; std::vector<char> buffer(bufferSize); - DWORD bytesReturned; //dummy value required by FSCTL_GET_REPARSE_POINT! + DWORD bytesReturned = 0; //dummy value required by FSCTL_GET_REPARSE_POINT! if (!::DeviceIoControl(hLink, //__in HANDLE hDevice, FSCTL_GET_REPARSE_POINT, //__in DWORD dwIoControlCode, nullptr, //__in_opt LPVOID lpInBuffer, diff --git a/zen/thread.h b/zen/thread.h index 3fb73e70..1fda016d 100644 --- a/zen/thread.h +++ b/zen/thread.h @@ -11,15 +11,15 @@ #include <memory> #include "fixed_list.h" +//fix this pathetic boost thread warning mess #ifdef __MINGW32__ #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wswitch-enum" #pragma GCC diagnostic ignored "-Wstrict-aliasing" -//#pragma GCC diagnostic ignored "-Wno-attributes" -//#pragma GCC diagnostic ignored "-Wredundant-decls" -//#pragma GCC diagnostic ignored "-Wcast-align" -//#pragma GCC diagnostic ignored "-Wunused-value" +#pragma GCC diagnostic ignored "-Wshadow" +#endif +#ifdef _MSC_VER +#pragma warning(disable : 4702) //unreachable code #endif #include <boost/thread.hpp> @@ -27,6 +27,9 @@ #ifdef __MINGW32__ #pragma GCC diagnostic pop #endif +#ifdef _MSC_VER +#pragma warning(default : 4702) //unreachable code +#endif namespace zen { @@ -89,7 +92,7 @@ auto async2(Function fun) -> boost::unique_future<T> //workaround VS2010 bug: bo boost::packaged_task<T> pt([=] { return fun(); }); auto fut = pt.get_future(); boost::thread(std::move(pt)); - return std::move(fut); + return std::move(fut); //compiler error without "move", why needed??? } |