diff options
author | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 17:26:50 +0200 |
---|---|---|
committer | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 17:26:50 +0200 |
commit | 669df123648aaa6aeccc70206b5417bc48b4e9ae (patch) | |
tree | 463c107a8d6405020bb304f7a7253e6b64afeee0 /zen | |
parent | 5.18 (diff) | |
download | FreeFileSync-669df123648aaa6aeccc70206b5417bc48b4e9ae.tar.gz FreeFileSync-669df123648aaa6aeccc70206b5417bc48b4e9ae.tar.bz2 FreeFileSync-669df123648aaa6aeccc70206b5417bc48b4e9ae.zip |
5.19
Diffstat (limited to 'zen')
-rw-r--r-- | zen/FindFilePlus/FindFilePlus.vcxproj | 4 | ||||
-rw-r--r-- | zen/FindFilePlus/find_file_plus.cpp | 38 | ||||
-rw-r--r-- | zen/basic_math.h | 9 | ||||
-rw-r--r-- | zen/dir_watcher.cpp | 12 | ||||
-rw-r--r-- | zen/dir_watcher.h | 4 | ||||
-rw-r--r-- | zen/file_error.h | 1 | ||||
-rw-r--r-- | zen/file_handling.cpp | 90 | ||||
-rw-r--r-- | zen/file_handling.h | 9 | ||||
-rw-r--r-- | zen/file_io.cpp | 26 | ||||
-rw-r--r-- | zen/file_io.h | 4 | ||||
-rw-r--r-- | zen/format_unit.cpp | 50 | ||||
-rw-r--r-- | zen/format_unit.h | 13 | ||||
-rw-r--r-- | zen/i18n.h | 1 | ||||
-rw-r--r-- | zen/long_path_prefix.h | 5 | ||||
-rw-r--r-- | zen/optional.h | 1 | ||||
-rw-r--r-- | zen/serialize.h | 6 | ||||
-rw-r--r-- | zen/stl_tools.h | 3 | ||||
-rw-r--r-- | zen/string_base.h | 61 | ||||
-rw-r--r-- | zen/sys_error.h | 18 | ||||
-rw-r--r-- | zen/thread.h | 3 | ||||
-rw-r--r-- | zen/type_tools.h | 4 | ||||
-rw-r--r-- | zen/type_traits.h | 15 | ||||
-rw-r--r-- | zen/win.h | 1 | ||||
-rw-r--r-- | zen/zstring.cpp | 8 | ||||
-rw-r--r-- | zen/zstring.h | 10 |
25 files changed, 195 insertions, 201 deletions
diff --git a/zen/FindFilePlus/FindFilePlus.vcxproj b/zen/FindFilePlus/FindFilePlus.vcxproj index d7dfe219..73b7f70e 100644 --- a/zen/FindFilePlus/FindFilePlus.vcxproj +++ b/zen/FindFilePlus/FindFilePlus.vcxproj @@ -172,7 +172,7 @@ <Link> <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> - <GenerateDebugInformation>false</GenerateDebugInformation> + <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Windows</SubSystem> <OptimizeReferences>true</OptimizeReferences> <EnableCOMDATFolding>true</EnableCOMDATFolding> @@ -209,7 +209,7 @@ <Link> <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> - <GenerateDebugInformation>false</GenerateDebugInformation> + <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Windows</SubSystem> <OptimizeReferences>true</OptimizeReferences> <EnableCOMDATFolding>true</EnableCOMDATFolding> diff --git a/zen/FindFilePlus/find_file_plus.cpp b/zen/FindFilePlus/find_file_plus.cpp index fdabc46f..ea58005e 100644 --- a/zen/FindFilePlus/find_file_plus.cpp +++ b/zen/FindFilePlus/find_file_plus.cpp @@ -113,10 +113,10 @@ public: FileSearcher(const wchar_t* dirname); //throw NtFileError ~FileSearcher(); - void readDir(FileInformation& output); //throw NtFileError + bool readDir(FileInformation& output); //throw NtFileError; returns false if "no more files" private: - template <class QueryPolicy> void readDirImpl(FileInformation& output); //throw NtFileError + template <class QueryPolicy> bool readDirImpl(FileInformation& output); //throw NtFileError UNICODE_STRING dirnameNt; //it seems hDir implicitly keeps a reference to this, at least ::FindFirstFile() does no cleanup before ::FindClose()! HANDLE hDir; @@ -271,11 +271,11 @@ struct DirQueryFileId inline -void FileSearcher::readDir(FileInformation& output) { readDirImpl<DirQueryFileId>(output); } //throw NtFileError +bool FileSearcher::readDir(FileInformation& output) { return readDirImpl<DirQueryFileId>(output); } //throw NtFileError template <class QueryPolicy> -void FileSearcher::readDirImpl(FileInformation& output) //throw NtFileError +bool FileSearcher::readDirImpl(FileInformation& output) //throw NtFileError; returns false if "no more files" { //although FILE_ID_FULL_DIR_INFORMATION should suffice for our purposes, there are problems on Windows XP for certain directories, e.g. "\\Vboxsvr\build" //making NtQueryDirectoryFile() return with STATUS_INVALID_PARAMETER while other directories, e.g. "C:\" work fine for some reason @@ -337,25 +337,30 @@ void FileSearcher::readDirImpl(FileInformation& output) //throw NtFileError STATUS_NO_SUCH_FILE is abused by some citrix shares instead of "STATUS_INVALID_PARAMETER" so we treat it as such! => since the directory is "truly empty" a fallback won't hurt */ - if (rv == STATUS_INVALID_LEVEL || - rv == STATUS_NOT_SUPPORTED || + if (rv == STATUS_NO_MORE_FILES) //perf: don't throw an exception in this common case! => 8% perf boost for FFS comparison phase! + return false; + + if (rv == STATUS_NOT_SUPPORTED || + rv == STATUS_INVALID_LEVEL || + rv == STATUS_NO_SUCH_FILE || //[!] rv == STATUS_UNEXPECTED_NETWORK_ERROR || rv == STATUS_INVALID_PARAMETER || rv == STATUS_INVALID_NETWORK_RESPONSE || rv == STATUS_INVALID_INFO_CLASS || + //rv == STATUS_NOT_IMPLEMENTED || -> first confirm that these codes + //rv == STATUS_INVALID_DEVICE_REQUEST || -> are in fact used! rv == STATUS_UNSUCCESSFUL || - rv == STATUS_ACCESS_VIOLATION || - rv == STATUS_NO_SUCH_FILE) //[!] + rv == STATUS_ACCESS_VIOLATION) rv = STATUS_NOT_SUPPORTED; throw NtFileError(rv); //throws STATUS_NO_MORE_FILES when finished } - //for (NTSTATUS i = 0xC0000000L; i != -1; ++i) - //{ - // if (rtlNtStatusToDosError(i) == 59) //ERROR_UNEXP_NET_ERR - // __asm int 3; - //} + // for (NTSTATUS i = 0xC0000000L; i != -1; ++i) + // { + // if (rtlNtStatusToDosError(i) == 59) //ERROR_UNEXP_NET_ERR + // __debugbreak(); //__asm int 3; + // } if (status.Information == 0) //except for the first call to call ::NtQueryDirectoryFile(): throw NtFileError(STATUS_BUFFER_OVERFLOW); //if buffer size is too small, return value is STATUS_SUCCESS and Information == 0 -> we don't expect this! @@ -399,6 +404,7 @@ void FileSearcher::readDirImpl(FileInformation& output) //throw NtFileError static_assert(sizeof(output.lastWriteTime) == sizeof(dirInfo.LastWriteTime), "dang!"); static_assert(sizeof(output.fileSize) == sizeof(dirInfo.EndOfFile), "dang!"); static_assert(sizeof(output.fileAttributes) == sizeof(dirInfo.FileAttributes), "dang!"); + return true; } @@ -425,7 +431,11 @@ bool findplus::readDir(FindHandle hnd, FileInformation& output) { try { - hnd->readDir(output); //throw NtFileError + if (!hnd->readDir(output)) //throw NtFileError + { + setWin32Error(rtlNtStatusToDosError(STATUS_NO_MORE_FILES)); + return false; + } return true; } catch (const NtFileError& e) diff --git a/zen/basic_math.h b/zen/basic_math.h index f01421c7..05d892ee 100644 --- a/zen/basic_math.h +++ b/zen/basic_math.h @@ -36,11 +36,6 @@ void confine(T& val, const T& minVal, const T& maxVal); //make sure minVal <= va template <class T> T confineCpy(const T& val, const T& minVal, const T& maxVal); -template <class InputIterator> -std::pair<InputIterator, InputIterator> minMaxElement(InputIterator first, InputIterator last); -template <class InputIterator, class Compare> -std::pair<InputIterator, InputIterator> minMaxElement(InputIterator first, InputIterator last, Compare comp); - template <class T, class InputIterator> //precondition: range must be sorted! auto nearMatch(const T& val, InputIterator first, InputIterator last) -> typename std::iterator_traits<InputIterator>::value_type; @@ -160,6 +155,8 @@ void confine(T& val, const T& minVal, const T& maxVal) //name trim, clamp? } +/* +part of C++11 now! template <class InputIterator, class Compare> inline std::pair<InputIterator, InputIterator> minMaxElement(InputIterator first, InputIterator last, Compare compLess) { @@ -200,7 +197,7 @@ std::pair<InputIterator, InputIterator> minMaxElement(InputIterator first, Input { return minMaxElement(first, last, std::less<typename std::iterator_traits<InputIterator>::value_type>()); } - +*/ template <class T, class InputIterator> inline auto nearMatch(const T& val, InputIterator first, InputIterator last) -> typename std::iterator_traits<InputIterator>::value_type diff --git a/zen/dir_watcher.cpp b/zen/dir_watcher.cpp index a800fb80..e53b63e2 100644 --- a/zen/dir_watcher.cpp +++ b/zen/dir_watcher.cpp @@ -96,7 +96,7 @@ public: //context of main thread - void getChanges(std::vector<DirWatcher::Entry>& output) //throw FileError, ErrorNotExisting + void getChanges(std::vector<DirWatcher::Entry>& output) //throw FileError { boost::lock_guard<boost::mutex> dummy(lockAccess); @@ -105,9 +105,6 @@ public: { const std::wstring msg = copyStringTo<std::wstring>(errorInfo->msg); const std::wstring descr = copyStringTo<std::wstring>(errorInfo->descr); - - if (errorCodeForNotExisting(errorInfo->errorCode)) - throw ErrorNotExisting(msg, descr); throw FileError(msg, descr); } @@ -163,9 +160,6 @@ public: const DWORD lastError = ::GetLastError(); //copy before making other system calls! const std::wstring errorMsg = replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(directory)); const std::wstring errorDescr = formatSystemError(L"CreateFile", lastError); - - if (errorCodeForNotExisting(lastError)) - throw ErrorNotExisting(errorMsg, errorDescr); throw FileError(errorMsg, errorDescr); } @@ -199,7 +193,6 @@ public: const DWORD lastError = ::GetLastError(); //copy before making other system calls! return shared_->reportError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(dirnamePf)), formatSystemError(L"CreateEvent", lastError), lastError); } - ZEN_ON_SCOPE_EXIT(::CloseHandle(overlapped.hEvent)); DWORD bytesReturned = 0; //should not be needed for async calls, still pass it to help broken drivers @@ -465,9 +458,6 @@ DirWatcher::DirWatcher(const Zstring& directory) : //throw FileError const ErrorCode lastError = getLastError(); //copy before making other system calls! const std::wstring errorMsg = replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(subdir)); const std::wstring errorDescr = formatSystemError(L"inotify_add_watch", lastError); - - if (errorCodeForNotExisting(lastError)) - throw ErrorNotExisting(errorMsg, errorDescr); throw FileError(errorMsg, errorDescr); } diff --git a/zen/dir_watcher.h b/zen/dir_watcher.h index b0df48bd..eaee5aab 100644 --- a/zen/dir_watcher.h +++ b/zen/dir_watcher.h @@ -27,12 +27,12 @@ namespace zen Renaming of top watched directory handled incorrectly: Not notified(!) + additional changes in subfolders now do report FILE_ACTION_MODIFIED for directory (check that should prevent this fails!) - Overcome all issues portably: check existence of top watched directory externally + reinstall watch after changes in directory structure (added directories) are possible + Overcome all issues portably: check existence of top watched directory externally + reinstall watch after changes in directory structure (added directories) are detected */ class DirWatcher { public: - DirWatcher(const Zstring& directory); //throw FileError, ErrorNotExisting + DirWatcher(const Zstring& directory); //throw FileError ~DirWatcher(); enum ActionType diff --git a/zen/file_error.h b/zen/file_error.h index 71619834..db8b371d 100644 --- a/zen/file_error.h +++ b/zen/file_error.h @@ -30,7 +30,6 @@ private: #define DEFINE_NEW_FILE_ERROR(X) struct X : public FileError { X(const std::wstring& msg) : FileError(msg) {} X(const std::wstring& msg, const std::wstring& descr) : FileError(msg, descr) {} }; -DEFINE_NEW_FILE_ERROR(ErrorNotExisting); DEFINE_NEW_FILE_ERROR(ErrorTargetExisting); DEFINE_NEW_FILE_ERROR(ErrorTargetPathMissing); DEFINE_NEW_FILE_ERROR(ErrorFileLocked); diff --git a/zen/file_handling.cpp b/zen/file_handling.cpp index 398e88e8..103a39f7 100644 --- a/zen/file_handling.cpp +++ b/zen/file_handling.cpp @@ -45,67 +45,111 @@ using namespace zen; +warn_static("remove after test") -bool zen::fileExists(const Zstring& filename) +namespace +{ +void writeSysErrorIfNeeded(std::wstring* sysErrorMsg, const wchar_t* functionName, ErrorCode lastError) +{ + if (sysErrorMsg) + { + //skip uninteresting error codes: +#ifdef ZEN_WIN + if (lastError == ERROR_FILE_NOT_FOUND || + lastError == ERROR_PATH_NOT_FOUND) return; + //lastError == ERROR_BAD_NETPATH || //e.g. for a path like: \\192.168.1.1\test + //lastError == ERROR_NETNAME_DELETED; + +#elif defined ZEN_LINUX || defined ZEN_MAC + if (lastError == ENOENT) return; +#endif + *sysErrorMsg = formatSystemError(functionName, lastError); + } +} +} + + +bool zen::fileExists(const Zstring& filename, std::wstring* sysErrorMsg) { //symbolic links (broken or not) are also treated as existing files! #ifdef ZEN_WIN + const wchar_t functionName[] = L"GetFileAttributes"; const DWORD attr = ::GetFileAttributes(applyLongPathPrefix(filename).c_str()); - return attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY) == 0; //returns true for (file-)symlinks also + if (attr != INVALID_FILE_ATTRIBUTES) + return (attr & FILE_ATTRIBUTE_DIRECTORY) == 0; //returns true for (file-)symlinks also #elif defined ZEN_LINUX || defined ZEN_MAC + const wchar_t functionName[] = L"lstat"; struct ::stat fileInfo = {}; - return ::lstat(filename.c_str(), &fileInfo) == 0 && - (S_ISLNK(fileInfo.st_mode) || S_ISREG(fileInfo.st_mode)); //in Linux a symbolic link is neither file nor directory + if (::lstat(filename.c_str(), &fileInfo) == 0) + return S_ISREG(fileInfo.st_mode) || S_ISLNK(fileInfo.st_mode); //in Linux a symbolic link is neither file nor directory #endif + writeSysErrorIfNeeded(sysErrorMsg, functionName, getLastError()); + return false; } -bool zen::dirExists(const Zstring& dirname) +bool zen::dirExists(const Zstring& dirname, std::wstring* sysErrorMsg) { //symbolic links (broken or not) are also treated as existing directories! #ifdef ZEN_WIN + const wchar_t functionName[] = L"GetFileAttributes"; const DWORD attr = ::GetFileAttributes(applyLongPathPrefix(dirname).c_str()); - return attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY) != 0; //returns true for (dir-)symlinks also + if (attr != INVALID_FILE_ATTRIBUTES) + return (attr & FILE_ATTRIBUTE_DIRECTORY) != 0; //returns true for (dir-)symlinks also #elif defined ZEN_LINUX || defined ZEN_MAC + const wchar_t functionName[] = L"lstat"; struct ::stat dirInfo = {}; - return ::lstat(dirname.c_str(), &dirInfo) == 0 && - (S_ISLNK(dirInfo.st_mode) || S_ISDIR(dirInfo.st_mode)); //in Linux a symbolic link is neither file nor directory + if (::lstat(dirname.c_str(), &dirInfo) == 0) + return S_ISDIR(dirInfo.st_mode) || S_ISLNK(dirInfo.st_mode); //in Linux a symbolic link is neither file nor directory #endif + writeSysErrorIfNeeded(sysErrorMsg, functionName, getLastError()); + return false; } -bool zen::symlinkExists(const Zstring& linkname) +bool zen::symlinkExists(const Zstring& linkname, std::wstring* sysErrorMsg) { #ifdef ZEN_WIN - WIN32_FIND_DATA fileInfo = {}; + const wchar_t functionName[] = L"FindFirstFile"; + WIN32_FIND_DATA linkInfo = {}; + const HANDLE searchHandle = ::FindFirstFile(applyLongPathPrefix(linkname).c_str(), &linkInfo); + if (searchHandle != INVALID_HANDLE_VALUE) { - const HANDLE searchHandle = ::FindFirstFile(applyLongPathPrefix(linkname).c_str(), &fileInfo); - if (searchHandle == INVALID_HANDLE_VALUE) - return false; ::FindClose(searchHandle); + return isSymlink(linkInfo); } - return isSymlink(fileInfo); #elif defined ZEN_LINUX || defined ZEN_MAC - struct ::stat fileInfo = {}; - return ::lstat(linkname.c_str(), &fileInfo) == 0 && - S_ISLNK(fileInfo.st_mode); //symbolic link + const wchar_t functionName[] = L"lstat"; + struct ::stat linkInfo = {}; + if (::lstat(linkname.c_str(), &linkInfo) == 0) + return S_ISLNK(linkInfo.st_mode); #endif + writeSysErrorIfNeeded(sysErrorMsg, functionName, getLastError()); + return false; } -bool zen::somethingExists(const Zstring& objname) +bool zen::somethingExists(const Zstring& objname, std::wstring* sysErrorMsg) { #ifdef ZEN_WIN + const wchar_t functionName[] = L"GetFileAttributes"; const DWORD attr = ::GetFileAttributes(applyLongPathPrefix(objname).c_str()); - return attr != INVALID_FILE_ATTRIBUTES || ::GetLastError() == ERROR_SHARING_VIOLATION; //"C:\pagefile.sys" + if (attr != INVALID_FILE_ATTRIBUTES) + return true; + if (::GetLastError() == ERROR_SHARING_VIOLATION) //"C:\pagefile.sys" + return true; #elif defined ZEN_LINUX || defined ZEN_MAC + const wchar_t functionName[] = L"lstat"; struct ::stat fileInfo = {}; - return ::lstat(objname.c_str(), &fileInfo) == 0; + if (::lstat(objname.c_str(), &fileInfo) == 0) + return true; #endif + writeSysErrorIfNeeded(sysErrorMsg, functionName, getLastError()); + return false; } @@ -275,9 +319,6 @@ bool zen::removeFile(const Zstring& filename) //throw FileError #endif { ErrorCode lastError = getLastError(); - if (errorCodeForNotExisting(lastError)) //no error situation if file is not existing! manual deletion relies on it! - return false; - #ifdef ZEN_WIN if (lastError == ERROR_ACCESS_DENIED) //function fails if file is read-only { @@ -288,7 +329,6 @@ bool zen::removeFile(const Zstring& filename) //throw FileError lastError = ::GetLastError(); } #endif - //after "lastError" evaluation it *may* be redundant to check existence again, but better be safe than sorry: if (!somethingExists(filename)) //warning: changes global error code!! return false; //neither file nor any other object (e.g. broken symlink) with that name existing @@ -2119,7 +2159,7 @@ void copyFileLinuxMac(const Zstring& sourceFile, FileAttrib* newAttrib) //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting { //open sourceFile for reading - FileInputUnbuffered fileIn(sourceFile); //throw FileError, ErrorNotExisting + FileInputUnbuffered fileIn(sourceFile); //throw FileError struct ::stat sourceInfo = {}; if (::fstat(fileIn.getDescriptor(), &sourceInfo) != 0) //read file attributes from source diff --git a/zen/file_handling.h b/zen/file_handling.h index f677358b..3cb8a68c 100644 --- a/zen/file_handling.h +++ b/zen/file_handling.h @@ -18,10 +18,11 @@ struct CallbackRemoveDir; struct CallbackCopyFile; -bool fileExists (const Zstring& filename); //noexcept; check whether file *or* (file) symlink exists -bool dirExists (const Zstring& dirname); //noexcept; check whether directory *or* (dir) symlink exists -bool symlinkExists (const Zstring& linkname); //noexcept; check whether a symbolic link exists -bool somethingExists(const Zstring& objname); //noexcept; check whether any object with this name exists +bool fileExists (const Zstring& filename, std::wstring* sysErrorMsg = nullptr); //noexcept; check whether file *or* (file) symlink exists +bool dirExists (const Zstring& dirname , std::wstring* sysErrorMsg = nullptr); //noexcept; check whether directory *or* (dir) symlink exists +bool symlinkExists (const Zstring& linkname, std::wstring* sysErrorMsg = nullptr); //noexcept; check whether a symbolic link exists +bool somethingExists(const Zstring& objname , std::wstring* sysErrorMsg = nullptr); //noexcept; check whether any object with this name exists +//sysErrorMsg: optional in + optional out! written only for non-expected errors other than ERROR_FILE_NOT_FOUND/ENOENT, ect... enum SymLinkType { diff --git a/zen/file_io.cpp b/zen/file_io.cpp index e0f8c12e..45cbd028 100644 --- a/zen/file_io.cpp +++ b/zen/file_io.cpp @@ -77,8 +77,7 @@ void checkForUnsupportedType(const Zstring& filename) //throw FileError FileInput::FileInput(FileHandle handle, const Zstring& filename) : FileInputBase(filename), fileHandle(handle) {} -FileInput::FileInput(const Zstring& filename) : //throw FileError, ErrorNotExisting - FileInputBase(filename) +FileInput::FileInput(const Zstring& filename) : FileInputBase(filename) //throw FileError { #ifdef ZEN_WIN const wchar_t functionName[] = L"CreateFile"; @@ -104,8 +103,8 @@ FileInput::FileInput(const Zstring& filename) : //throw FileError, ErrorNotExist while FILE_FLAG_RANDOM_ACCESS offers best performance for - same physical disk (HDD <-> HDD) - Problem: bad XP implementation of prefetch makes flag FILE_FLAG_SEQUENTIAL_SCAN effectively load two files at once from one drive - swapping every 64 kB (or similar). File access times explode! + Problem: bad XP implementation of prefetch makes flag FILE_FLAG_SEQUENTIAL_SCAN effectively load two files at the same time + from one drive, swapping every 64 kB (or similar). File access times explode! => For XP it is critical to use FILE_FLAG_RANDOM_ACCESS (to disable prefetch) if reading two files on same disk and FILE_FLAG_SEQUENTIAL_SCAN when reading from different disk (e.g. massive performance improvement compared to random access for DVD <-> HDD!) => there is no compromise that satisfies all cases! (on XP) @@ -122,9 +121,7 @@ FileInput::FileInput(const Zstring& filename) : //throw FileError, ErrorNotExist #endif { const ErrorCode lastError = getLastError(); //copy before making other system calls! - const std::wstring errorMsg = errorCodeForNotExisting(lastError) ? - replaceCpy(_("Cannot find file %x."), L"%x", fmtFileName(filename)) : - replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(filename)); + const std::wstring errorMsg = replaceCpy(_("Cannot open file %x."), L"%x", fmtFileName(filename)); std::wstring errorDescr = formatSystemError(functionName, lastError); #ifdef ZEN_WIN @@ -136,10 +133,6 @@ FileInput::FileInput(const Zstring& filename) : //throw FileError, ErrorNotExist errorDescr = _("The file is locked by another process:") + L"\n" + procList; } #endif - - if (errorCodeForNotExisting(lastError)) - throw ErrorNotExisting(errorMsg, errorDescr); - throw FileError(errorMsg, errorDescr); } } @@ -319,7 +312,7 @@ void FileOutput::write(const void* buffer, size_t bytesToWrite) //throw FileErro #if defined ZEN_LINUX || defined ZEN_MAC //Compare copy_reg() in copy.c: ftp://ftp.gnu.org/gnu/coreutils/coreutils-5.0.tar.gz -FileInputUnbuffered::FileInputUnbuffered(const Zstring& filename) : FileInputBase(filename) //throw FileError, ErrorNotExisting +FileInputUnbuffered::FileInputUnbuffered(const Zstring& filename) : FileInputBase(filename) //throw FileError { checkForUnsupportedType(filename); //throw FileError; reading a named pipe would block forever! @@ -327,15 +320,8 @@ FileInputUnbuffered::FileInputUnbuffered(const Zstring& filename) : FileInputBas if (fdFile == -1) //don't check "< 0" -> docu seems to allow "-2" to be a valid file handle { const ErrorCode lastError = getLastError(); //copy before making other system calls! - - const std::wstring errorMsg = errorCodeForNotExisting(lastError) ? - replaceCpy(_("Cannot find file %x."), L"%x", fmtFileName(filename)) : - replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(filename)); + const std::wstring errorMsg = replaceCpy(_("Cannot open file %x."), L"%x", fmtFileName(filename)); const std::wstring errorDescr = formatSystemError(L"open", lastError); - - if (errorCodeForNotExisting(lastError)) - throw ErrorNotExisting(errorMsg, errorDescr); - throw FileError(errorMsg, errorDescr); } } diff --git a/zen/file_io.h b/zen/file_io.h index 93267a39..0c7506a1 100644 --- a/zen/file_io.h +++ b/zen/file_io.h @@ -39,7 +39,7 @@ typedef FILE* FileHandle; class FileInput : public FileInputBase { public: - FileInput(const Zstring& filename); //throw FileError, ErrorNotExisting + FileInput(const Zstring& filename); //throw FileError FileInput(FileHandle handle, const Zstring& filename); //takes ownership! ~FileInput(); @@ -68,7 +68,7 @@ private: class FileInputUnbuffered : public FileInputBase { public: - FileInputUnbuffered(const Zstring& filename); //throw FileError, ErrorNotExisting + FileInputUnbuffered(const Zstring& filename); //throw FileError ~FileInputUnbuffered(); //considering safe-read.c it seems buffer size should be a multiple of 8192 diff --git a/zen/format_unit.cpp b/zen/format_unit.cpp index 9e5975d9..4f16e3e1 100644 --- a/zen/format_unit.cpp +++ b/zen/format_unit.cpp @@ -24,47 +24,45 @@ using namespace zen; +std::wstring zen::formatThreeDigitPrecision(double value) +{ + //print at least three digits: 0,01 | 0,11 | 1,11 | 11,1 | 111 + if (numeric::abs(value) < 9.995) //9.999 must not be formatted as "10.00" + return printNumber<std::wstring>(L"%.2f", value); + if (numeric::abs(value) < 99.95) //99.99 must not be formatted as "100.0" + return printNumber<std::wstring>(L"%.1f", value); + return numberTo<std::wstring>(numeric::round(value)); +} + + std::wstring zen::filesizeToShortString(Int64 size) { //if (size < 0) return _("Error"); -> really? if (numeric::abs(size) <= 999) - return replaceCpy(_P("1 Byte", "%x Bytes", to<int>(size)), L"%x", numberTo<std::wstring>(size)); - - auto formatUnitSize = [](double sizeInUnit, const std::wstring& unitTxt) -> std::wstring - { - //print just three significant digits: 0,01 | 0,11 | 1,11 | 11,1 | 111 - const size_t fullunits = static_cast<size_t>(numeric::abs(sizeInUnit)); - const int precisionDigits = fullunits < 10 ? 2 : fullunits < 100 ? 1 : 0; //sprintf requires "int" - - wchar_t buffer[50]; -#ifdef __MINGW32__ - int charsWritten = ::_snwprintf(buffer, 50, L"%.*f", precisionDigits, sizeInUnit); //MinGW does not comply to the C standard here -#else - int charsWritten = std::swprintf(buffer, 50, L"%.*f", precisionDigits, sizeInUnit); -#endif - return charsWritten > 0 ? replaceCpy(unitTxt, L"%x", std::wstring(buffer, charsWritten)) : _("Error"); - }; + return replaceCpy(_P("1 byte", "%x bytes", to<int>(size)), L"%x", numberTo<std::wstring>(size)); double sizeInUnit = to<double>(size); + auto formatUnit = [&](const std::wstring& unitTxt) { return replaceCpy(unitTxt, L"%x", formatThreeDigitPrecision(sizeInUnit)); }; + sizeInUnit /= 1024; - if (numeric::abs(sizeInUnit) <= 999) - return formatUnitSize(sizeInUnit, _("%x KB")); + if (numeric::abs(sizeInUnit) < 999.5) + return formatUnit(_("%x KB")); sizeInUnit /= 1024; - if (numeric::abs(sizeInUnit) <= 999) - return formatUnitSize(sizeInUnit, _("%x MB")); + if (numeric::abs(sizeInUnit) < 999.5) + return formatUnit(_("%x MB")); sizeInUnit /= 1024; - if (numeric::abs(sizeInUnit) <= 999) - return formatUnitSize(sizeInUnit, _("%x GB")); + if (numeric::abs(sizeInUnit) < 999.5) + return formatUnit(_("%x GB")); sizeInUnit /= 1024; - if (numeric::abs(sizeInUnit) <= 999) - return formatUnitSize(sizeInUnit, _("%x TB")); + if (numeric::abs(sizeInUnit) < 999.5) + return formatUnit(_("%x TB")); sizeInUnit /= 1024; - return formatUnitSize(sizeInUnit, _("%x PB")); + return formatUnit(_("%x PB")); } @@ -148,7 +146,7 @@ std::wstring zen::remainingTimeToString(double timeInSec) std::wstring zen::fractionToString(double fraction) { - return printNumber<std::wstring>(L"%3.2f", fraction * 100.0) + L'%'; //no need to internationalize fraction!? + return printNumber<std::wstring>(L"%.2f", fraction * 100.0) + L'%'; //no need to internationalize fraction!? } diff --git a/zen/format_unit.h b/zen/format_unit.h index 86477999..e3e2d107 100644 --- a/zen/format_unit.h +++ b/zen/format_unit.h @@ -16,19 +16,12 @@ namespace zen std::wstring filesizeToShortString(Int64 filesize); std::wstring remainingTimeToString(double timeInSec); std::wstring fractionToString(double fraction); //within [0, 1] - -template <class NumberType> -std::wstring toGuiString(NumberType number); //format integer number including thousands separator - std::wstring utcToLocalTimeString(Int64 utcTime); //like Windows Explorer would... +std::wstring formatThreeDigitPrecision(double value); //= *at least* three digits - - - - - - +template <class NumberType> +std::wstring toGuiString(NumberType number); //format integer number including thousands separator @@ -68,6 +68,7 @@ std::wstring translate(const std::wstring& text) inline std::wstring translate(const std::wstring& singular, const std::wstring& plural, int n) { + if (n < 0) n = -n; return getTranslator() ? getTranslator()->translate(singular, plural, n) : n == 1 ? singular : plural; } diff --git a/zen/long_path_prefix.h b/zen/long_path_prefix.h index db1fbdca..cdff09fa 100644 --- a/zen/long_path_prefix.h +++ b/zen/long_path_prefix.h @@ -57,6 +57,11 @@ Zstring applyLongPathPrefixImpl(const Zstring& path) assert(!path.empty()); //nicely check almost all WinAPI accesses! assert(!zen::isWhiteSpace(path[0])); + //http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx#naming_conventions)) + /* + - special names like ".NUL" create all kinds of trouble (e.g. CreateDirectory() reports success, but does nothing) + unless prefix is supplied => accept as limitation + */ if (path.length() >= maxPath || //maximum allowed path length without prefix is (MAX_PATH - 1) endsWith(path, L' ') || //by default all Win32 APIs trim trailing spaces and period, unless long path prefix is supplied! endsWith(path, L'.')) //note: adding long path prefix might screw up relative paths "." and ".."! diff --git a/zen/optional.h b/zen/optional.h index a6a53103..6e54408a 100644 --- a/zen/optional.h +++ b/zen/optional.h @@ -45,6 +45,7 @@ public: if (tmp.valid) value = tmp.value; valid = tmp.valid; + return *this; } ////rvalue optimization: only basic exception safety: diff --git a/zen/serialize.h b/zen/serialize.h index 415bd430..64c07213 100644 --- a/zen/serialize.h +++ b/zen/serialize.h @@ -56,7 +56,7 @@ private: //---------------------------------------------------------------------- //functions based on binary container abstraction template <class BinContainer> void saveBinStream(const Zstring& filename, const BinContainer& cont); //throw FileError -template <class BinContainer> BinContainer loadBinStream(const Zstring& filename); //throw FileError, ErrorNotExisting +template <class BinContainer> BinContainer loadBinStream(const Zstring& filename); //throw FileError /* @@ -157,11 +157,11 @@ void saveBinStream(const Zstring& filename, const BinContainer& cont) //throw Fi template <class BinContainer> inline -BinContainer loadBinStream(const Zstring& filename) //throw FileError, ErrorNotExisting +BinContainer loadBinStream(const Zstring& filename) //throw FileError { assert_static(sizeof(typename BinContainer::value_type) == 1); //expect: bytes (until further) - FileInput fileIn(filename); //throw FileError, ErrorNotExisting + FileInput fileIn(filename); //throw FileError BinContainer contOut; const size_t blockSize = 128 * 1024; diff --git a/zen/stl_tools.h b/zen/stl_tools.h index c93f2d61..bf47bb1c 100644 --- a/zen/stl_tools.h +++ b/zen/stl_tools.h @@ -183,6 +183,9 @@ template <class K, class V> class hash_map : public std::map<K, V> {}; #else template <class T> class hash_set : public std::unordered_set<T> {}; template <class K, class V> class hash_map : public std::unordered_map<K, V> {}; +//C++11: +//template <class T> using hash_set = std::unordered_set<T>; +//template <class K, class V> using hash_map = std::unordered_map<K, V>; #endif //as long as variadic templates are not available in MSVC diff --git a/zen/string_base.h b/zen/string_base.h index 591ed62b..31a09e63 100644 --- a/zen/string_base.h +++ b/zen/string_base.h @@ -21,7 +21,7 @@ namespace zen Allocator Policy: ----------------- void* allocate(size_t size) //throw std::bad_alloc - void deallocate(void* ptr) + void deallocate(void* ptr) //must handle deallocate(nullptr)! size_t calcCapacity(size_t length) */ class AllocatorOptimalSpeed //exponential growth + min size @@ -30,8 +30,9 @@ 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>(std::max<size_t>(16, length), length + length / 2); } //size_t might overflow! - //any growth rate should not exceed golden ratio: 1.618033989 + static size_t calcCapacity(size_t length) { return std::max<size_t>(16, std::max(length + length / 2, length)); } + //- size_t might overflow! => better catch here than return a too small size covering up the real error: a way too large length! + //- any growth rate should not exceed golden ratio: 1.618033989 }; @@ -52,7 +53,7 @@ template <typename Char, //Character Type Char* create(size_t size) Char* create(size_t size, size_t minCapacity) Char* clone(Char* ptr) - void destroy(Char* ptr) + void destroy(Char* ptr) //must handle destroy(nullptr)! bool canWrite(const Char* ptr, size_t minCapacity) //needs to be checked before writing to "ptr" size_t length(const Char* ptr) void setLength(Char* ptr, size_t newLength) @@ -87,7 +88,7 @@ protected: return newData; } - static void destroy(Char* ptr) { AP::deallocate(descr(ptr)); } + static void destroy(Char* ptr) { AP::deallocate(descr(ptr)); } //should support destroy(nullptr)! //this needs to be checked before writing to "ptr" static bool canWrite(const Char* ptr, size_t minCapacity) { return minCapacity <= descr(ptr)->capacity; } @@ -141,6 +142,7 @@ protected: static void destroy(Char* ptr) { + if (!ptr) return; //support destroy(nullptr) assert(descr(ptr)->refCount > 0); if (--descr(ptr)->refCount == 0) //operator--() is overloaded to decrement and evaluate in a single atomic operation! { @@ -251,7 +253,7 @@ public: private: Zbase(int); // - Zbase& operator=(int); //detect usage errors + Zbase& operator=(int); //detect usage errors by creating an intentional ambiguity with "Char" Zbase& operator+=(int); // void push_back(int); // @@ -294,15 +296,6 @@ template <class Char, template <class, class> class SP, class AP> inline Zbase<C - - - - - - - - - //################################# implementation ######################################## template <class Char, template <class, class> class SP, class AP> inline Zbase<Char, SP, AP>::Zbase() @@ -361,16 +354,9 @@ Zbase<Char, SP, AP>::Zbase(const Zbase<Char, SP, AP>& source) template <class Char, template <class, class> class SP, class AP> inline Zbase<Char, SP, AP>::Zbase(Zbase<Char, SP, AP>&& tmp) { - if (this->canWrite(tmp.rawStr, 0)) //perf: following optimization saves about 4% - { - //do not increment ref-count of an unshared string! We'd lose optimization opportunity of reusing its memory! - //instead create a dummy string and swap: - rawStr = this->create(0); //no perf issue! see comment in default constructor - rawStr[0] = 0; - swap(tmp); - } - else //shared representation: yet another "add ref" won't hurt - rawStr = this->clone(tmp.rawStr); + rawStr = tmp.rawStr; + tmp.rawStr = nullptr; //usually nullptr would violate the class invarants, but it is good enough for the destructor! + //caveat: do not increment ref-count of an unshared string! We'd lose optimization opportunity of reusing its memory! } @@ -388,7 +374,7 @@ Zbase<Char, SP, AP>::Zbase(const S& other, typename S::value_type) template <class Char, template <class, class> class SP, class AP> inline Zbase<Char, SP, AP>::~Zbase() { - this->destroy(rawStr); + this->destroy(rawStr); //rawStr may be nullptr; see move constructor! } @@ -499,25 +485,25 @@ Zbase<Char, SP, AP>& Zbase<Char, SP, AP>::replace(size_t pos1, size_t n1, const template <class Char, template <class, class> class SP, class AP> inline void Zbase<Char, SP, AP>::resize(size_t newSize, Char fillChar) { + const size_t oldSize = length(); if (this->canWrite(rawStr, newSize)) { - if (length() < newSize) - std::fill(rawStr + length(), rawStr + newSize, fillChar); + if (oldSize < newSize) + std::fill(rawStr + oldSize, rawStr + newSize, fillChar); rawStr[newSize] = 0; - this->setLength(rawStr, newSize); //keep after call to length() + this->setLength(rawStr, newSize); } else { Char* newStr = this->create(newSize); - newStr[newSize] = 0; - - if (length() < newSize) + if (oldSize < newSize) { - std::copy(rawStr, rawStr + length(), newStr); - std::fill(newStr + length(), newStr + newSize, fillChar); + std::copy(rawStr, rawStr + oldSize, newStr); + std::fill(newStr + oldSize, newStr + newSize, fillChar); } else std::copy(rawStr, rawStr + newSize, newStr); + newStr[newSize] = 0; this->destroy(rawStr); rawStr = newStr; @@ -614,7 +600,7 @@ void Zbase<Char, SP, AP>::clear() { if (this->canWrite(rawStr, 0)) { - rawStr[0] = 0; //keep allocated memory + rawStr[0] = 0; //keep allocated memory this->setLength(rawStr, 0); // } else @@ -636,8 +622,9 @@ void Zbase<Char, SP, AP>::reserve(size_t minCapacity) //make unshared and check if (!this->canWrite(rawStr, minCapacity)) { //allocate a new string - Char* newStr = this->create(length(), std::max(minCapacity, length())); //reserve() must NEVER shrink the string: logical const! - std::copy(rawStr, rawStr + length() + 1, newStr); //include 0-termination + const size_t len = length(); + Char* newStr = this->create(len, std::max(len, minCapacity)); //reserve() must NEVER shrink the string: logical const! + std::copy(rawStr, rawStr + len + 1, newStr); //include 0-termination this->destroy(rawStr); rawStr = newStr; diff --git a/zen/sys_error.h b/zen/sys_error.h index bbac2eaa..cea2f5f9 100644 --- a/zen/sys_error.h +++ b/zen/sys_error.h @@ -28,14 +28,14 @@ namespace zen typedef DWORD ErrorCode; #elif defined ZEN_LINUX || defined ZEN_MAC typedef int ErrorCode; +#else +#error define a platform! #endif ErrorCode getLastError(); std::wstring formatSystemError(const std::wstring& functionName, ErrorCode lastError); -bool errorCodeForNotExisting(ErrorCode lastError); //check for "not existing" aliases - //A low-level exception class giving (non-translated) detail information only - same conceptional level like "GetLastError()"! class SysError @@ -105,20 +105,6 @@ std::wstring formatSystemError(const std::wstring& functionName, ErrorCode lastE return output; } - - -inline -bool errorCodeForNotExisting(ErrorCode lastError) -{ -#ifdef ZEN_WIN - return lastError == ERROR_FILE_NOT_FOUND || - lastError == ERROR_PATH_NOT_FOUND || - lastError == ERROR_BAD_NETPATH || - lastError == ERROR_NETNAME_DELETED; -#elif defined ZEN_LINUX || defined ZEN_MAC - return lastError == ENOENT; -#endif -} } #endif //LAST_ERROR_H_3284791347018951324534 diff --git a/zen/thread.h b/zen/thread.h index 638d9474..76596513 100644 --- a/zen/thread.h +++ b/zen/thread.h @@ -128,7 +128,7 @@ public: void reportFinished(std::unique_ptr<T>&& result) { { - boost::unique_lock<boost::mutex> dummy(lockResult); + boost::lock_guard<boost::mutex> dummy(lockResult); ++jobsFinished; if (!result_) result_ = std::move(result); @@ -149,7 +149,6 @@ public: std::unique_ptr<T> getResult(size_t jobsTotal) { boost::unique_lock<boost::mutex> dummy(lockResult); - while (!jobDone(jobsTotal)) conditionJobDone.timed_wait(dummy, boost::posix_time::milliseconds(50)); //interruption point! diff --git a/zen/type_tools.h b/zen/type_tools.h index e5ce29bd..76a12a5a 100644 --- a/zen/type_tools.h +++ b/zen/type_tools.h @@ -29,10 +29,10 @@ template <class T, class U> struct SelectIf<false, T, U> : ResultType<U> {}; //------------------------------------------------------ template <class T, class U> -struct IsSameType : StaticBool<false> {}; +struct IsSameType : FalseType {}; template <class T> -struct IsSameType<T, T> : StaticBool<true> {}; +struct IsSameType<T, T> : TrueType {}; //------------------------------------------------------ template <bool, class T = void> diff --git a/zen/type_traits.h b/zen/type_traits.h index 4f71f961..a90b9793 100644 --- a/zen/type_traits.h +++ b/zen/type_traits.h @@ -21,6 +21,9 @@ struct StaticInt template <bool b> struct StaticBool : StaticInt<b> {}; +typedef StaticBool<true> TrueType; +typedef StaticBool<false> FalseType; + template <class EnumType, EnumType val> struct StaticEnum { @@ -45,7 +48,7 @@ template <class T> struct IsArithmetic; //IsInteger or IsFloat //remaining non-arithmetic types: bool, char, wchar_t //optional: specialize new types like: -//template <> struct IsUnsignedInt<UInt64> : StaticBool<true> {}; +//template <> struct IsUnsignedInt<UInt64> : TrueType {}; //################# Class Members ######################## @@ -81,10 +84,10 @@ template <class T> struct IsArithmetic; //IsInteger or IsFloat //################ implementation ###################### -#define ZEN_SPECIALIZE_TRAIT(X, Y) template <> struct X<Y> : StaticBool<true> {}; +#define ZEN_SPECIALIZE_TRAIT(X, Y) template <> struct X<Y> : TrueType {}; template <class T> -struct IsUnsignedInt : StaticBool<false> {}; +struct IsUnsignedInt : FalseType {}; ZEN_SPECIALIZE_TRAIT(IsUnsignedInt, unsigned char); ZEN_SPECIALIZE_TRAIT(IsUnsignedInt, unsigned short int); @@ -94,7 +97,7 @@ ZEN_SPECIALIZE_TRAIT(IsUnsignedInt, unsigned long long int); //new with C++11 - //------------------------------------------------------ template <class T> -struct IsSignedInt : StaticBool<false> {}; +struct IsSignedInt : FalseType {}; ZEN_SPECIALIZE_TRAIT(IsSignedInt, signed char); ZEN_SPECIALIZE_TRAIT(IsSignedInt, short int); @@ -104,7 +107,7 @@ ZEN_SPECIALIZE_TRAIT(IsSignedInt, long long int); //new with C++11 - same type a //------------------------------------------------------ template <class T> -struct IsFloat : StaticBool<false> {}; +struct IsFloat : FalseType {}; ZEN_SPECIALIZE_TRAIT(IsFloat, float); ZEN_SPECIALIZE_TRAIT(IsFloat, double); @@ -143,7 +146,7 @@ struct IsArithmetic : StaticBool<IsInteger<T>::value || IsFloat<T>::value> {}; }; \ \ template<class T> \ - struct HasMemberImpl_##NAME<false, T> : StaticBool<false> {}; \ + struct HasMemberImpl_##NAME<false, T> : FalseType {}; \ \ template<typename T> \ struct HasMember_##NAME : StaticBool<HasMemberImpl_##NAME<std::is_class<T>::value, T>::value> {}; @@ -23,7 +23,6 @@ #endif #include <windows.h> - #endif //------------------------------------------------------ diff --git a/zen/zstring.cpp b/zen/zstring.cpp index a469ade2..1cc037e8 100644 --- a/zen/zstring.cpp +++ b/zen/zstring.cpp @@ -33,19 +33,15 @@ public: void insert(const void* ptr, size_t size) { boost::lock_guard<boost::mutex> dummy(lockActStrings); - if (activeStrings.find(ptr) != activeStrings.end()) + if (!activeStrings.insert(std::make_pair(ptr, size)).second) reportProblem("Fatal Error: New memory points into occupied space: " + rawMemToString(ptr, size)); - - activeStrings[ptr] = size; } void remove(const void* ptr) { boost::lock_guard<boost::mutex> dummy(lockActStrings); - if (activeStrings.find(ptr) == activeStrings.end()) + if (activeStrings.erase(ptr) != 1) reportProblem("Fatal Error: No memory available for deallocation at this location!"); - - activeStrings.erase(ptr); } static LeakChecker& instance() { static LeakChecker inst; return inst; } diff --git a/zen/zstring.h b/zen/zstring.h index 80c267e3..f103faf7 100644 --- a/zen/zstring.h +++ b/zen/zstring.h @@ -28,11 +28,11 @@ class AllocatorFreeStoreChecked public: static void* allocate(size_t size) //throw std::bad_alloc { - void* newMem = ::operator new(size); + void* ptr = zen::AllocatorOptimalSpeed::allocate(size); #ifndef NDEBUG - z_impl::leakCheckerInsert(newMem, size); //test Zbase for memory leaks + z_impl::leakCheckerInsert(ptr, size); //test Zbase for memory leaks #endif - return newMem; + return ptr; } static void deallocate(void* ptr) @@ -40,10 +40,10 @@ public: #ifndef NDEBUG z_impl::leakCheckerRemove(ptr); //check for memory leaks #endif - ::operator delete(ptr); + zen::AllocatorOptimalSpeed::deallocate(ptr); } - static size_t calcCapacity(size_t length) { return std::max<size_t>(16, length + length / 2); } //exponential growth + min size + static size_t calcCapacity(size_t length) { return zen::AllocatorOptimalSpeed::calcCapacity(length); } }; |