diff options
Diffstat (limited to 'zen')
42 files changed, 613 insertions, 1493 deletions
diff --git a/zen/assert_static.h b/zen/assert_static.h deleted file mode 100644 index 17d5c370..00000000 --- a/zen/assert_static.h +++ /dev/null @@ -1,43 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef ASSERTSTATIC_H_INCLUDED -#define ASSERTSTATIC_H_INCLUDED - -/* -//compile time assert based on Loki (http://loki-lib.sourceforge.net) - -#ifdef NDEBUG - -#define assert_static(x) //((void)0) -> leads to error when seen in namespace scope! - -#else // debugging enabled -namespace static_check_impl -{ -template<int> -struct CompileTimeError; - -template<> -struct CompileTimeError<true> {}; -} - -#define LOKI_CONCAT(X, Y) LOKI_CONCAT_SUB(X, Y) -#define LOKI_CONCAT_SUB(X, Y) X ## Y - -#define assert_static(expr) \ - enum { LOKI_CONCAT(loki_enum_dummy_value, __LINE__) = sizeof(StaticCheckImpl::CompileTimeError<static_cast<bool>(expr) >) } - -// #define assert_static(expr) \ -// { Loki::CompileTimeError<((expr) != 0)> Static_Assert_Has_Failed; (void)Static_Assert_Has_Failed; } - -#endif -*/ - -//C++11: please get rid of this pointless string literal requirement! -#define assert_static(X) \ - static_assert(X, "Static assert has failed!"); - -#endif //ASSERTSTATIC_H_INCLUDED diff --git a/zen/basic_math.h b/zen/basic_math.h index 69e861be..41f42dbe 100644 --- a/zen/basic_math.h +++ b/zen/basic_math.h @@ -145,7 +145,7 @@ T clampCpy(const T& val, const T& minVal, const T& maxVal) } template <class T> inline -void clamp(T& val, const T& minVal, const T& maxVal) +void clamp(T& val, const T& minVal, const T& maxVal) { assert(minVal <= maxVal); if (val < minVal) diff --git a/zen/dir_watcher.cpp b/zen/dir_watcher.cpp index 3751e5dd..9a685fc5 100644 --- a/zen/dir_watcher.cpp +++ b/zen/dir_watcher.cpp @@ -40,7 +40,7 @@ public: boost::lock_guard<boost::mutex> dummy(lockAccess); if (bytesWritten == 0) //according to docu this may happen in case of internal buffer overflow: report some "dummy" change - changedFiles.push_back(DirWatcher::Entry(DirWatcher::ACTION_CREATE, L"Overflow.")); + changedFiles.emplace_back(DirWatcher::ACTION_CREATE, L"Overflow."); else { const char* bufPos = &buffer[0]; @@ -67,14 +67,14 @@ public: { case FILE_ACTION_ADDED: case FILE_ACTION_RENAMED_NEW_NAME: //harmonize with "move" which is notified as "create + delete" - changedFiles.push_back(DirWatcher::Entry(DirWatcher::ACTION_CREATE, fullpath)); + changedFiles.emplace_back(DirWatcher::ACTION_CREATE, fullpath); break; case FILE_ACTION_REMOVED: case FILE_ACTION_RENAMED_OLD_NAME: - changedFiles.push_back(DirWatcher::Entry(DirWatcher::ACTION_DELETE, fullpath)); + changedFiles.emplace_back(DirWatcher::ACTION_DELETE, fullpath); break; case FILE_ACTION_MODIFIED: - changedFiles.push_back(DirWatcher::Entry(DirWatcher::ACTION_UPDATE, fullpath)); + changedFiles.emplace_back(DirWatcher::ACTION_UPDATE, fullpath); break; } }(); @@ -193,8 +193,8 @@ public: nullptr); //__in_opt LPCTSTR lpName if (overlapped.hEvent == nullptr) { - const DWORD lastError = ::GetLastError(); //copy before directly or indirectly making other system calls! - return shared_->reportError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(dirpathPf)), formatSystemError(L"CreateEvent", lastError), lastError); + const DWORD ec = ::GetLastError(); //copy before directly or indirectly making other system calls! + return shared_->reportError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(dirpathPf)), formatSystemError(L"CreateEvent", ec), ec); } ZEN_ON_SCOPE_EXIT(::CloseHandle(overlapped.hEvent)); @@ -213,8 +213,8 @@ public: &overlapped, // __inout_opt LPOVERLAPPED lpOverlapped, nullptr)) // __in_opt LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine { - const DWORD lastError = ::GetLastError(); //copy before directly or indirectly making other system calls! - return shared_->reportError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(dirpathPf)), formatSystemError(L"ReadDirectoryChangesW", lastError), lastError); + const DWORD ec = ::GetLastError(); //copy before directly or indirectly making other system calls! + return shared_->reportError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(dirpathPf)), formatSystemError(L"ReadDirectoryChangesW", ec), ec); } //async I/O is a resource that needs to be guarded since it will write to local variable "buffer"! @@ -236,9 +236,9 @@ public: &bytesWritten, //__out LPDWORD lpNumberOfBytesTransferred, false)) //__in BOOL bWait { - const DWORD lastError = ::GetLastError(); //copy before directly or indirectly making other system calls! - if (lastError != ERROR_IO_INCOMPLETE) - return shared_->reportError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(dirpathPf)), formatSystemError(L"GetOverlappedResult", lastError), lastError); + const DWORD ec = ::GetLastError(); //copy before directly or indirectly making other system calls! + if (ec != ERROR_IO_INCOMPLETE) + return shared_->reportError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(dirpathPf)), formatSystemError(L"GetOverlappedResult", ec), ec); //execute asynchronous procedure calls (APC) queued on this thread ::SleepEx(50, // __in DWORD dwMilliseconds, @@ -287,7 +287,7 @@ public: bool finished() const { return operationComplete; } private: - virtual void onRequestRemoval(HANDLE hnd) + void onRequestRemoval(HANDLE hnd) override { //must release hDir immediately => stop monitoring! if (worker_.joinable()) //= join() precondition: play safe; can't trust Windows to only call-back once @@ -300,7 +300,7 @@ private: removalRequested = true; } //don't throw! - virtual void onRemovalFinished(HANDLE hnd, bool successful) { operationComplete = true; } //throw()! + void onRemovalFinished(HANDLE hnd, bool successful) override { operationComplete = true; } //throw()! boost::thread& worker_; bool removalRequested; @@ -320,13 +320,13 @@ struct DirWatcher::Pimpl DirWatcher::DirWatcher(const Zstring& directory) : //throw FileError - pimpl_(new Pimpl) + pimpl_(zen::make_unique<Pimpl>()) { pimpl_->shared = std::make_shared<SharedData>(); pimpl_->dirpath = directory; ReadChangesAsync reader(directory, pimpl_->shared); //throw FileError - pimpl_->volRemoval.reset(new HandleVolumeRemoval(reader.getDirHandle(), pimpl_->worker)); //throw FileError + pimpl_->volRemoval = zen::make_unique<HandleVolumeRemoval>(reader.getDirHandle(), pimpl_->worker); //throw FileError pimpl_->worker = boost::thread(std::move(reader)); } @@ -360,7 +360,7 @@ std::vector<DirWatcher::Entry> DirWatcher::getChanges(const std::function<void() boost::thread::sleep(boost::get_system_time() + boost::posix_time::milliseconds(50)); } - output.push_back(Entry(ACTION_DELETE, pimpl_->dirpath)); //report removal as change to main directory + output.emplace_back(ACTION_DELETE, pimpl_->dirpath); //report removal as change to main directory } else //the normal case... pimpl_->shared->fetchChanges(output); //throw FileError @@ -376,15 +376,15 @@ class DirsOnlyTraverser : public zen::TraverseCallback public: DirsOnlyTraverser(std::vector<Zstring>& dirs) : dirs_(dirs) {} - virtual void onFile (const Zchar* shortName, const Zstring& filepath, const FileInfo& details) {} - virtual HandleLink onSymlink(const Zchar* shortName, const Zstring& linkpath, const SymlinkInfo& details) { return LINK_SKIP; } - virtual TraverseCallback* onDir(const Zchar* shortName, const Zstring& dirpath) + void onFile (const Zchar* shortName, const Zstring& filepath, const FileInfo& details ) override {} + HandleLink onSymlink(const Zchar* shortName, const Zstring& linkpath, const SymlinkInfo& details) override { return LINK_SKIP; } + TraverseCallback* onDir (const Zchar* shortName, const Zstring& dirpath ) override { dirs_.push_back(dirpath); return this; } - virtual HandleError reportDirError (const std::wstring& msg, size_t retryNumber) { throw FileError(msg); } - virtual HandleError reportItemError(const std::wstring& msg, size_t retryNumber, const Zchar* shortName) { throw FileError(msg); } + HandleError reportDirError (const std::wstring& msg, size_t retryNumber) override { throw FileError(msg); } + HandleError reportItemError(const std::wstring& msg, size_t retryNumber, const Zchar* shortName) override { throw FileError(msg); } private: std::vector<Zstring>& dirs_; @@ -403,7 +403,7 @@ struct DirWatcher::Pimpl DirWatcher::DirWatcher(const Zstring& directory) : //throw FileError - pimpl_(new Pimpl) + pimpl_(zen::make_unique<Pimpl>()) { //get all subdirectories Zstring dirpathFmt = directory; @@ -449,9 +449,15 @@ DirWatcher::DirWatcher(const Zstring& directory) : //throw FileError IN_MOVED_TO | IN_MOVE_SELF); if (wd == -1) - throwFileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(subdir)), L"inotify_add_watch", getLastError()); + { + const auto ec = getLastError(); + if (ec == ENOSPC) //fix misleading system message "No space left on device" + throw FileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(subdir)), formatSystemError(L"inotify_add_watch", ec, L"The user limit on the total number of inotify watches was reached or the kernel failed to allocate a needed resource.")); + + throw FileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(subdir)), formatSystemError(L"inotify_add_watch", ec)); + } - pimpl_->watchDescrs.insert(std::make_pair(wd, appendSeparator(subdir))); + pimpl_->watchDescrs.emplace(wd, appendSeparator(subdir)); } guardDescr.dismiss(); @@ -502,15 +508,15 @@ std::vector<DirWatcher::Entry> DirWatcher::getChanges(const std::function<void() if ((evt.mask & IN_CREATE) || (evt.mask & IN_MOVED_TO)) - output.push_back(Entry(ACTION_CREATE, fullname)); + output.emplace_back(ACTION_CREATE, fullname); else if ((evt.mask & IN_MODIFY) || (evt.mask & IN_CLOSE_WRITE)) - output.push_back(Entry(ACTION_UPDATE, fullname)); + output.emplace_back(ACTION_UPDATE, fullname); else if ((evt.mask & IN_DELETE ) || (evt.mask & IN_DELETE_SELF) || (evt.mask & IN_MOVE_SELF ) || (evt.mask & IN_MOVED_FROM)) - output.push_back(Entry(ACTION_DELETE, fullname)); + output.emplace_back(ACTION_DELETE, fullname); } } bytePos += sizeof(struct ::inotify_event) + evt.len; @@ -541,7 +547,7 @@ void eventCallback(ConstFSEventStreamRef streamRef, if (eventFlags[i] & kFSEventStreamEventFlagItemCreated || eventFlags[i] & kFSEventStreamEventFlagMount) - changedFiles.push_back(DirWatcher::Entry(DirWatcher::ACTION_CREATE, paths[i])); + changedFiles.emplace_back(DirWatcher::ACTION_CREATE, paths[i]); if (eventFlags[i] & kFSEventStreamEventFlagItemModified || // eventFlags[i] & kFSEventStreamEventFlagItemXattrMod || // eventFlags[i] & kFSEventStreamEventFlagItemChangeOwner || //aggregate these into a single event @@ -549,11 +555,11 @@ void eventCallback(ConstFSEventStreamRef streamRef, eventFlags[i] & kFSEventStreamEventFlagItemFinderInfoMod || // eventFlags[i] & kFSEventStreamEventFlagItemRenamed || //OS X sends the same event flag for both old and new names!!! eventFlags[i] & kFSEventStreamEventFlagMustScanSubDirs) //something changed in one of the subdirs: NOT expected due to kFSEventStreamCreateFlagFileEvents - changedFiles.push_back(DirWatcher::Entry(DirWatcher::ACTION_UPDATE, paths[i])); + changedFiles.emplace_back(DirWatcher::ACTION_UPDATE, paths[i]); if (eventFlags[i] & kFSEventStreamEventFlagItemRemoved || eventFlags[i] & kFSEventStreamEventFlagRootChanged || //root is (indirectly) deleted or renamed eventFlags[i] & kFSEventStreamEventFlagUnmount) - changedFiles.push_back(DirWatcher::Entry(DirWatcher::ACTION_DELETE, paths[i])); + changedFiles.emplace_back(DirWatcher::ACTION_DELETE, paths[i]); //kFSEventStreamEventFlagEventIdsWrapped -> irrelevant! //kFSEventStreamEventFlagHistoryDone -> not expected due to kFSEventStreamEventIdSinceNow below @@ -571,7 +577,7 @@ struct DirWatcher::Pimpl DirWatcher::DirWatcher(const Zstring& directory) : - pimpl_(new Pimpl) + pimpl_(zen::make_unique<Pimpl>()) { CFStringRef dirpathCf = osx::createCFString(directory.c_str()); //returns nullptr on error if (!dirpathCf) diff --git a/zen/dst_hack.cpp b/zen/dst_hack.cpp deleted file mode 100644 index b38f73b4..00000000 --- a/zen/dst_hack.cpp +++ /dev/null @@ -1,368 +0,0 @@ -#include "dst_hack.h" -#include <bitset> -#include "basic_math.h" -#include "long_path_prefix.h" -#include "utf.h" -#include "assert_static.h" -#include "int64.h" -#include "file_error.h" -#include "dll.h" - -using namespace zen; - - -namespace -{ -//fast ::GetVolumePathName() clone: let's hope it's not too simple (doesn't honor mount points) -Zstring getVolumeName(const Zstring& filepath) -{ - //this call is expensive: ~1.5 ms! - // if (!::GetVolumePathName(filepath.c_str(), //__in LPCTSTR lpszFileName, - // fsName, //__out LPTSTR lpszVolumePathName, - // BUFFER_SIZE)) //__in DWORD cchBufferLength - // ... - // Zstring volumePath = appendSeparator(fsName); - - const Zstring nameFmt = appendSeparator(removeLongPathPrefix(filepath)); //throw() - - if (startsWith(nameFmt, Zstr("\\\\"))) //UNC path: "\\ComputerName\SharedFolder\" - { - size_t nameSize = nameFmt.size(); - const size_t posFirstSlash = nameFmt.find(Zstr("\\"), 2); - if (posFirstSlash != Zstring::npos) - { - nameSize = posFirstSlash + 1; - const size_t posSecondSlash = nameFmt.find(Zstr("\\"), posFirstSlash + 1); - if (posSecondSlash != Zstring::npos) - nameSize = posSecondSlash + 1; - } - return Zstring(nameFmt.c_str(), nameSize); //include trailing backslash! - } - else //local path: "C:\Folder\" - { - const size_t pos = nameFmt.find(Zstr(":\\")); - if (pos == 1) //expect single letter volume - return Zstring(nameFmt.c_str(), 3); - } - - return Zstring(); -} -} - - -bool dst::isFatDrive(const Zstring& filepath) //throw() -{ - const Zstring volumePath = getVolumeName(filepath); - if (volumePath.empty()) - return false; - - const DWORD bufferSize = MAX_PATH + 1; - wchar_t fsName[bufferSize] = {}; - - //suprisingly fast: ca. 0.03 ms per call! - if (!::GetVolumeInformation(appendSeparator(volumePath).c_str(), //__in_opt LPCTSTR lpRootPathName, - nullptr, //__out LPTSTR lpVolumeNameBuffer, - 0, //__in DWORD nVolumeNameSize, - nullptr, //__out_opt LPDWORD lpVolumeSerialNumber, - nullptr, //__out_opt LPDWORD lpMaximumComponentLength, - nullptr, //__out_opt LPDWORD lpFileSystemFlags, - fsName, //__out LPTSTR lpFileSystemNameBuffer, - bufferSize)) //__in DWORD nFileSystemNameSize - { - assert(false); //shouldn't happen - return false; - } - //DST hack seems to be working equally well for FAT and FAT32 (in particular creation time has 10^-2 s precision as advertised) - return fsName == Zstring(L"FAT") || - fsName == Zstring(L"FAT32"); -} - - -/* -bool dst::isFatDrive(HANDLE hFile) //throw() -{ -Requires Windows Vista! - //dynamically load windows API function - typedef BOOL (WINAPI* GetVolumeInformationByHandleWFunc)(HANDLE hFile, - LPWSTR lpVolumeNameBuffer, - DWORD nVolumeNameSize, - LPDWORD lpVolumeSerialNumber, - LPDWORD lpMaximumComponentLength, - LPDWORD lpFileSystemFlags, - LPWSTR lpFileSystemNameBuffer, - DWORD nFileSystemNameSize); - - const SysDllFun<GetVolumeInformationByHandleWFunc> getVolumeInformationByHandle(L"kernel32.dll", "GetVolumeInformationByHandleW"); - if (!getVolumeInformationByHandle) - { - assert(false); - return false; - } - - const size_t BUFFER_SIZE = MAX_PATH + 1; - wchar_t fsName[BUFFER_SIZE]; - - if (!getVolumeInformationByHandle(hFile, //__in HANDLE hFile, - nullptr, //__out LPTSTR lpVolumeNameBuffer, - 0, //__in DWORD nVolumeNameSize, - nullptr, //__out_opt LPDWORD lpVolumeSerialNumber, - nullptr, //__out_opt LPDWORD lpMaximumComponentLength, - nullptr, //__out_opt LPDWORD lpFileSystemFlags, - fsName, //__out LPTSTR lpFileSystemNameBuffer, - BUFFER_SIZE)) //__in DWORD nFileSystemNameSize - { - assert(false); //shouldn't happen - return false; - } - //DST hack seems to be working equally well for FAT and FAT32 (in particular creation time has 10^-2 s precision as advertised) - return fsName == Zstring(L"FAT") || - fsName == Zstring(L"FAT32"); -} -*/ - - -namespace -{ -//convert std::uint64_t and std::int64_t to FILETIME -inline -FILETIME toFiletime(std::uint64_t number) -{ - ULARGE_INTEGER cvt = {}; - cvt.QuadPart = number; - - const FILETIME output = { cvt.LowPart, cvt.HighPart }; - return output; -} - -inline -FILETIME toFiletime(std::int64_t number) -{ - return toFiletime(static_cast<std::uint64_t>(number)); -} - -inline -std::uint64_t toUInt64(const FILETIME& fileTime) -{ - return get64BitUInt(fileTime.dwLowDateTime, fileTime.dwHighDateTime); -} - - -inline -std::int64_t toInt64(const FILETIME& fileTime) -{ - return get64BitUInt(fileTime.dwLowDateTime, fileTime.dwHighDateTime); //convert unsigned to signed in return -} - - -inline -FILETIME utcToLocal(const FILETIME& utcTime) //throw std::runtime_error -{ - //treat binary local time representation (which is invariant under DST time zone shift) as logical UTC: - FILETIME localTime = {}; - if (!::FileTimeToLocalFileTime( - &utcTime, //__in const FILETIME *lpFileTime, - &localTime)) //__out LPFILETIME lpLocalFileTime - { - const DWORD lastError = ::GetLastError(); //copy before directly or indirectly making other system calls! - throw std::runtime_error(utfCvrtTo<std::string>(_("Conversion error:") + L" FILETIME -> local FILETIME: " + L"(" + - L"High: " + numberTo<std::wstring>(utcTime.dwHighDateTime) + L" " + - L"Low: " + numberTo<std::wstring>(utcTime.dwLowDateTime) + L") " + L"\n\n" + formatSystemError(L"FileTimeToLocalFileTime", lastError))); - } - return localTime; -} - - -inline -FILETIME localToUtc(const FILETIME& localTime) //throw std::runtime_error -{ - //treat binary local time representation (which is invariant under DST time zone shift) as logical UTC: - FILETIME utcTime = {}; - if (!::LocalFileTimeToFileTime( - &localTime, //__in const FILETIME *lpLocalFileTime, - &utcTime)) //__out LPFILETIME lpFileTime - { - const DWORD lastError = ::GetLastError(); //copy before directly or indirectly making other system calls! - throw std::runtime_error(utfCvrtTo<std::string>(_("Conversion error:") + L" local FILETIME -> FILETIME: " + L"(" + - L"High: " + numberTo<std::wstring>(localTime.dwHighDateTime) + L" " + - L"Low: " + numberTo<std::wstring>(localTime.dwLowDateTime) + L") " + L"\n\n" + formatSystemError(L"LocalFileTimeToFileTime", lastError))); - } - return utcTime; -} - - -//struct FILETIME {DWORD dwLowDateTime; DWORD dwHighDateTime;}; -const FILETIME FAT_MIN_TIME = { 13374976, 27846544 }; //1980 \ both are valid max/min FAT dates for 2 second precision -const FILETIME FAT_MAX_TIME = { 14487552, 37251238 }; //2107 / - -//http://en.wikipedia.org/wiki/File_Allocation_Table -const size_t PRECISION_WRITE_TIME = 20000000; //number of 100 ns per step -> 2 s -const size_t PRECISION_CREATE_TIME = 100000; // -> 1/100 s - -/* -Number of bits of information in create time: ln_2((FAT_MAX_TIME - FAT_MIN_TIME) / PRECISION_CREATE_TIME) = 38.55534023 -Number of bits of information in write time: 30.91148404 -*/ -//total size available to store data: -const size_t CREATE_TIME_INFO_BITS = 38; -// I. indicator that offset in II) is present -const size_t INDICATOR_EXISTING_BITS = 1; -// II. local<->UTC time offset -const size_t UTC_LOCAL_OFFSET_BITS = 7; -// III. indicator that offset in II) corresponds to current local write time (this could be a hash of the write time) -const size_t WRITE_TIME_HASH_BITS = CREATE_TIME_INFO_BITS - INDICATOR_EXISTING_BITS - UTC_LOCAL_OFFSET_BITS; - - -template <size_t precision> inline -FILETIME encodeRawInformation(std::uint64_t rawInfo) -{ - rawInfo *= precision; - rawInfo += toUInt64(FAT_MIN_TIME); - - assert(rawInfo <= toUInt64(FAT_MAX_TIME)); - return toFiletime(rawInfo); -} - - -template <size_t precision> inline -std::uint64_t extractRawInformation(const FILETIME& createTime) -{ - assert(toUInt64(FAT_MIN_TIME) <= toUInt64(createTime)); - assert(toUInt64(createTime) <= toUInt64(FAT_MAX_TIME)); - - //FAT create time ranges from 1980 - 2107 (2^7 years) with 1/100 seconds precision - std::uint64_t rawInfo = toUInt64(createTime); - - rawInfo -= toUInt64(FAT_MIN_TIME); - rawInfo /= precision; //reduce precision (FILETIME has unit 10^-7 s) - - assert(toUInt64(encodeRawInformation<precision>(rawInfo)) == toUInt64(createTime)); //must be reversible - return rawInfo; -} - - -//convert write time to it's minimal representation (no restriction to FAT range "1980 - 2107") -inline -std::uint64_t extractRawWriteTime(const FILETIME& writeTime) -{ - std::uint64_t rawInfo = toUInt64(writeTime); - assert(rawInfo % PRECISION_WRITE_TIME == 0U); - rawInfo /= PRECISION_WRITE_TIME; //reduce precision (FILETIME has unit 10^-7 s) - return rawInfo; -} - - -//files with different resolution than 2 seconds are rounded up when written to FAT -inline -FILETIME roundToFatWriteTime(const FILETIME& writeTime) -{ - std::uint64_t rawData = toUInt64(writeTime); - - if (rawData % PRECISION_WRITE_TIME != 0U) - rawData += PRECISION_WRITE_TIME; - - rawData /= PRECISION_WRITE_TIME; - rawData *= PRECISION_WRITE_TIME; - return toFiletime(rawData); -} - - -//get 7-bit value representing time shift in number of quarter-hours -std::bitset<UTC_LOCAL_OFFSET_BITS> getUtcLocalShift() -{ - FILETIME utcTime = FAT_MIN_TIME; - utcTime.dwHighDateTime += 5000000; //some arbitrary valid time - - const FILETIME localTime = utcToLocal(utcTime); - - const int timeShiftSec = static_cast<int>((toInt64(localTime) - toInt64(utcTime)) / 10000000); //time shift in seconds - - const int timeShiftQuarter = timeShiftSec / (60 * 15); //time shift in quarter-hours - - const int absValue = numeric::abs(timeShiftQuarter); //MSVC C++0x bug: std::bitset<>(unsigned long) is ambiguous - - if (std::bitset < UTC_LOCAL_OFFSET_BITS - 1 > (absValue).to_ulong() != static_cast<unsigned long>(absValue) || //time shifts that big shouldn't be possible! - timeShiftSec % (60 * 15) != 0) //all known time shift have at least 15 minute granularity! - { - const std::wstring errorMsg = _("Conversion error:") + L" Unexpected UTC <-> local time shift: " + L"(" + numberTo<std::wstring>(timeShiftSec) + L")"; - throw std::runtime_error(utfCvrtTo<std::string>(errorMsg)); - } - - std::bitset<UTC_LOCAL_OFFSET_BITS> output(absValue); - output[UTC_LOCAL_OFFSET_BITS - 1] = timeShiftQuarter < 0; //sign bit - return output; -} - - -//get time-zone shift in seconds -inline -int convertUtcLocalShift(std::bitset<UTC_LOCAL_OFFSET_BITS> rawShift) -{ - const bool hasSign = rawShift[UTC_LOCAL_OFFSET_BITS - 1]; - rawShift[UTC_LOCAL_OFFSET_BITS - 1] = false; - - assert_static(UTC_LOCAL_OFFSET_BITS <= sizeof(unsigned long) * 8); - return hasSign ? - static_cast<int>(rawShift.to_ulong()) * 15 * 60 * -1 : - static_cast<int>(rawShift.to_ulong()) * 15 * 60; -} -} - - -bool dst::fatHasUtcEncoded(const RawTime& rawTime) //"createTimeRaw" as retrieved by ::FindFirstFile() and ::GetFileAttributesEx(); throw std::runtime_error -{ - if (toUInt64(rawTime.createTimeRaw) < toUInt64(FAT_MIN_TIME) || - toUInt64(FAT_MAX_TIME) < toUInt64(rawTime.createTimeRaw)) - { - assert(false); //shouldn't be possible according to FAT specification - return false; - } - - const std::uint64_t rawInfo = extractRawInformation<PRECISION_CREATE_TIME>(utcToLocal(rawTime.createTimeRaw)); - - assert_static(WRITE_TIME_HASH_BITS == 30); - return (extractRawWriteTime(utcToLocal(rawTime.writeTimeRaw)) & 0x3FFFFFFFU) == (rawInfo & 0x3FFFFFFFU) && //ensure write time wasn't changed externally - rawInfo >> (CREATE_TIME_INFO_BITS - INDICATOR_EXISTING_BITS) == 1U; //extended data available -} - - -dst::RawTime dst::fatEncodeUtcTime(const FILETIME& writeTimeRealUtc) //throw std::runtime_error -{ - const FILETIME fatWriteTimeUtc = roundToFatWriteTime(writeTimeRealUtc); //writeTimeRealUtc may have incompatible precision (NTFS) - - //create time lets us store 40 bit of information - - //indicator that utc time is encoded -> hopefully results in a date long way in the future; but even if this bit is accidentally set, we still have the hash! - std::uint64_t data = 1U; - - const std::bitset<UTC_LOCAL_OFFSET_BITS> utcShift = getUtcLocalShift(); - data <<= UTC_LOCAL_OFFSET_BITS; - data |= utcShift.to_ulong(); - - data <<= WRITE_TIME_HASH_BITS; - data |= extractRawWriteTime(utcToLocal(fatWriteTimeUtc)) & 0x3FFFFFFFU; //trim to last 30 bit of information - assert_static(WRITE_TIME_HASH_BITS == 30); - - const FILETIME encodedData = localToUtc(encodeRawInformation<PRECISION_CREATE_TIME>(data)); //localToUtc: make sure data is physically saved as FAT local time - assert(toUInt64(FAT_MIN_TIME) <= toUInt64(encodedData)); - assert(toUInt64(encodedData) <= toUInt64(FAT_MAX_TIME)); - - return RawTime(encodedData, fatWriteTimeUtc); //keep compatible with other applications, at least until DST shift actually occurs -} - - -FILETIME dst::fatDecodeUtcTime(const RawTime& rawTime) //return real UTC time; throw (std::runtime_error) -{ - if (!fatHasUtcEncoded(rawTime)) - return rawTime.writeTimeRaw; - - const std::uint64_t rawInfo = extractRawInformation<PRECISION_CREATE_TIME>(utcToLocal(rawTime.createTimeRaw)); - - const std::bitset<UTC_LOCAL_OFFSET_BITS> rawShift(static_cast<int>((rawInfo >> WRITE_TIME_HASH_BITS) & 0x7FU)); //static_cast<int>: a shame MSC... "unsigned long long" should be supported instead! - assert_static(UTC_LOCAL_OFFSET_BITS == 7); - - const std::int64_t timeShiftSec = convertUtcLocalShift(rawShift); - const FILETIME writeTimeLocal = utcToLocal(rawTime.writeTimeRaw); - - const std::int64_t realUTC = toInt64(writeTimeLocal) - timeShiftSec * 10000000; - return toFiletime(realUTC); -} diff --git a/zen/dst_hack.h b/zen/dst_hack.h deleted file mode 100644 index 600107bb..00000000 --- a/zen/dst_hack.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef DST_HACK_H_INCLUDED -#define DST_HACK_H_INCLUDED - -#include "win.h" //includes "windows.h" -#include "zstring.h" -#include <stdexcept> - - -namespace dst -{ -/* -Solve DST +-1h and time zone shift issues on FAT drives -------------------------------------------------------- -- (local) last write time is not touched! -- all additional metadata is encoded in local create time: - I. indicator that offset in II) is present - II. local<->UTC time offset - III. indicator that offset in II) corresponds to current local write time (a hash of local last write time) -*/ - -bool isFatDrive(const Zstring& fileName); //throw () - -//all subsequent functions may throw the std::runtime_error exception! - -struct RawTime //time as retrieved by ::FindFirstFile() and ::GetFileAttributesEx() -{ - RawTime(const FILETIME& create, const FILETIME& lastWrite) : createTimeRaw(create), writeTimeRaw(lastWrite) {} - FILETIME createTimeRaw; - FILETIME writeTimeRaw; -}; -//save UTC time resistant against DST/time zone shifts -bool fatHasUtcEncoded(const RawTime& rawTime); //throw std::runtime_error; as retrieved by ::FindFirstFile() and ::GetFileAttributesEx() - -RawTime fatEncodeUtcTime(const FILETIME& writeTimeRealUtc); //throw std::runtime_error -FILETIME fatDecodeUtcTime(const RawTime& rawTime); //throw std::runtime_error; return last write time in real UTC -} - -#endif // DST_HACK_H_INCLUDED diff --git a/zen/file_handling.cpp b/zen/file_access.cpp index 75062462..c4768dd2 100644 --- a/zen/file_handling.cpp +++ b/zen/file_access.cpp @@ -4,7 +4,7 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#include "file_handling.h" +#include "file_access.h" #include <map> #include <algorithm> #include <stdexcept> @@ -13,7 +13,6 @@ #include "scope_guard.h" #include "symlink_target.h" #include "file_io.h" -#include "assert_static.h" #include "file_id_def.h" #ifdef ZEN_WIN @@ -21,7 +20,6 @@ #include "privilege.h" #include "dll.h" #include "long_path_prefix.h" -#include "dst_hack.h" #include "win_ver.h" #include "IFileOperation/file_op.h" @@ -134,6 +132,70 @@ bool zen::somethingExists(const Zstring& objname) namespace { #ifdef ZEN_WIN +//fast ::GetVolumePathName() clone: let's hope it's not too simple (doesn't honor mount points) +Zstring getVolumeNameFast(const Zstring& filepath) +{ + //this call is expensive: ~1.5 ms! + // if (!::GetVolumePathName(filepath.c_str(), //__in LPCTSTR lpszFileName, + // fsName, //__out LPTSTR lpszVolumePathName, + // BUFFER_SIZE)) //__in DWORD cchBufferLength + // ... + // Zstring volumePath = appendSeparator(fsName); + + const Zstring nameFmt = appendSeparator(removeLongPathPrefix(filepath)); //throw() + + if (startsWith(nameFmt, Zstr("\\\\"))) //UNC path: "\\ComputerName\SharedFolder\" + { + size_t nameSize = nameFmt.size(); + const size_t posFirstSlash = nameFmt.find(Zstr("\\"), 2); + if (posFirstSlash != Zstring::npos) + { + nameSize = posFirstSlash + 1; + const size_t posSecondSlash = nameFmt.find(Zstr("\\"), posFirstSlash + 1); + if (posSecondSlash != Zstring::npos) + nameSize = posSecondSlash + 1; + } + return Zstring(nameFmt.c_str(), nameSize); //include trailing backslash! + } + else //local path: "C:\Folder\" + { + const size_t pos = nameFmt.find(Zstr(":\\")); + if (pos == 1) //expect single letter volume + return Zstring(nameFmt.c_str(), 3); + } + + return Zstring(); +} + + +bool isFatDrive(const Zstring& filepath) //throw() +{ + const Zstring volumePath = getVolumeNameFast(filepath); + if (volumePath.empty()) + return false; + + const DWORD bufferSize = MAX_PATH + 1; + wchar_t fsName[bufferSize] = {}; + + //suprisingly fast: ca. 0.03 ms per call! + if (!::GetVolumeInformation(appendSeparator(volumePath).c_str(), //__in_opt LPCTSTR lpRootPathName, + nullptr, //__out LPTSTR lpVolumeNameBuffer, + 0, //__in DWORD nVolumeNameSize, + nullptr, //__out_opt LPDWORD lpVolumeSerialNumber, + nullptr, //__out_opt LPDWORD lpMaximumComponentLength, + nullptr, //__out_opt LPDWORD lpFileSystemFlags, + fsName, //__out LPTSTR lpFileSystemNameBuffer, + bufferSize)) //__in DWORD nFileSystemNameSize + { + assert(false); //shouldn't happen + return false; + } + //DST hack seems to be working equally well for FAT and FAT32 (in particular creation time has 10^-2 s precision as advertised) + return fsName == Zstring(L"FAT") || + fsName == Zstring(L"FAT32"); +} + + //(try to) enhance error messages by showing which processes lock the file Zstring getLockingProcessNames(const Zstring& filepath) //throw(), empty string if none found or error occurred { @@ -159,40 +221,38 @@ Zstring getLockingProcessNames(const Zstring& filepath) //throw(), empty string std::uint64_t zen::getFilesize(const Zstring& filepath) //throw FileError { #ifdef ZEN_WIN - WIN32_FIND_DATA fileInfo = {}; { + WIN32_FIND_DATA fileInfo = {}; const HANDLE searchHandle = ::FindFirstFile(applyLongPathPrefix(filepath).c_str(), &fileInfo); if (searchHandle == INVALID_HANDLE_VALUE) throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filepath)), L"FindFirstFile", getLastError()); ::FindClose(searchHandle); + + if (!isSymlink(fileInfo)) + return get64BitUInt(fileInfo.nFileSizeLow, fileInfo.nFileSizeHigh); } // WIN32_FILE_ATTRIBUTE_DATA sourceAttr = {}; - // if (!::GetFileAttributesEx(applyLongPathPrefix(sourceObj).c_str(), //__in LPCTSTR lpFileName, - // GetFileExInfoStandard, //__in GET_FILEEX_INFO_LEVELS fInfoLevelId, - // &sourceAttr)) //__out LPVOID lpFileInformation - - if (!isSymlink(fileInfo)) - return get64BitUInt(fileInfo.nFileSizeLow, fileInfo.nFileSizeHigh); - else - { - //open handle to target of symbolic link - const HANDLE hFile = ::CreateFile(applyLongPathPrefix(filepath).c_str(), //_In_ LPCTSTR lpFileName, - 0, //_In_ DWORD dwDesiredAccess, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, //_In_ DWORD dwShareMode, - nullptr, //_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes, - OPEN_EXISTING, //_In_ DWORD dwCreationDisposition, - FILE_FLAG_BACKUP_SEMANTICS, /*needed to open a directory*/ //_In_ DWORD dwFlagsAndAttributes, - nullptr); //_In_opt_ HANDLE hTemplateFile - if (hFile == INVALID_HANDLE_VALUE) - throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filepath)), L"CreateFile", getLastError()); - ZEN_ON_SCOPE_EXIT(::CloseHandle(hFile)); + // if (!::GetFileAttributesEx(applyLongPathPrefix(filepath).c_str(), //__in LPCTSTR lpFileName, + // GetFileExInfoStandard, //__in GET_FILEEX_INFO_LEVELS fInfoLevelId, + // &sourceAttr)) //__out LPVOID lpFileInformation + + //open handle to target of symbolic link + const HANDLE hFile = ::CreateFile(applyLongPathPrefix(filepath).c_str(), //_In_ LPCTSTR lpFileName, + 0, //_In_ DWORD dwDesiredAccess, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, //_In_ DWORD dwShareMode, + nullptr, //_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes, + OPEN_EXISTING, //_In_ DWORD dwCreationDisposition, + FILE_FLAG_BACKUP_SEMANTICS, /*needed to open a directory*/ //_In_ DWORD dwFlagsAndAttributes, + nullptr); //_In_opt_ HANDLE hTemplateFile + if (hFile == INVALID_HANDLE_VALUE) + throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filepath)), L"CreateFile", getLastError()); + ZEN_ON_SCOPE_EXIT(::CloseHandle(hFile)); - BY_HANDLE_FILE_INFORMATION fileInfoHnd = {}; - if (!::GetFileInformationByHandle(hFile, &fileInfoHnd)) - throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filepath)), L"GetFileInformationByHandle", getLastError()); + BY_HANDLE_FILE_INFORMATION fileInfoHnd = {}; + if (!::GetFileInformationByHandle(hFile, &fileInfoHnd)) + throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filepath)), L"GetFileInformationByHandle", getLastError()); - return get64BitUInt(fileInfoHnd.nFileSizeLow, fileInfoHnd.nFileSizeHigh); - } + return get64BitUInt(fileInfoHnd.nFileSizeLow, fileInfoHnd.nFileSizeHigh); #elif defined ZEN_LINUX || defined ZEN_MAC struct ::stat fileInfo = {}; @@ -494,11 +554,11 @@ public: files_(files), dirs_(dirs) {} - virtual void onFile(const Zchar* shortName, const Zstring& filepath, const FileInfo& details) + void onFile(const Zchar* shortName, const Zstring& filepath, const FileInfo& details) override { files_.push_back(filepath); } - virtual HandleLink onSymlink(const Zchar* shortName, const Zstring& linkpath, const SymlinkInfo& details) + HandleLink onSymlink(const Zchar* shortName, const Zstring& linkpath, const SymlinkInfo& details) override { if (dirExists(linkpath)) //dir symlink dirs_.push_back(shortName); @@ -506,13 +566,13 @@ public: files_.push_back(shortName); return LINK_SKIP; } - virtual TraverseCallback* onDir(const Zchar* shortName, const Zstring& dirpath) + TraverseCallback* onDir(const Zchar* shortName, const Zstring& dirpath) override { dirs_.push_back(dirpath); return nullptr; //DON'T traverse into subdirs; removeDirectory works recursively! } - virtual HandleError reportDirError (const std::wstring& msg, size_t retryNumber) { throw FileError(msg); } - virtual HandleError reportItemError(const std::wstring& msg, size_t retryNumber, const Zchar* shortName) { throw FileError(msg); } + HandleError reportDirError (const std::wstring& msg, size_t retryNumber) override { throw FileError(msg); } + HandleError reportItemError(const std::wstring& msg, size_t retryNumber, const Zchar* shortName) override { throw FileError(msg); } private: CollectFilesFlat (const CollectFilesFlat&) = delete; @@ -525,7 +585,7 @@ private: void removeDirectoryImpl(const Zstring& directory, //throw FileError const std::function<void (const Zstring& filepath)>& onBeforeFileDeletion, - const std::function<void (const Zstring& dirpath)>& onBeforeDirDeletion) + const std::function<void (const Zstring& dirpath )>& onBeforeDirDeletion) { assert(somethingExists(directory)); //[!] @@ -592,7 +652,10 @@ void removeDirectoryImpl(const Zstring& directory, //throw FileError #ifdef ZEN_WIN -void setFileTimeRaw(const Zstring& filepath, const FILETIME& creationTime, const FILETIME& lastWriteTime, ProcSymlink procSl) //throw FileError +void setFileTimeRaw(const Zstring& filepath, + const FILETIME* creationTime, //optional + const FILETIME& lastWriteTime, + ProcSymlink procSl) //throw FileError { { //extra scope for debug check below @@ -696,7 +759,7 @@ void setFileTimeRaw(const Zstring& filepath, const FILETIME& creationTime, const 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, + creationTime, //__in_opt const FILETIME *lpCreationTime, nullptr, //__in_opt const FILETIME *lpLastAccessTime, &lastWriteTime)) //__in_opt const FILETIME *lpLastWriteTime { @@ -736,8 +799,8 @@ void setFileTimeRaw(const Zstring& filepath, const FILETIME& creationTime, const FILE_BASIC_INFO basicInfo = {}; //undocumented: file times of "0" stand for "don't change" basicInfo.FileAttributes = FILE_ATTRIBUTE_NORMAL; //[!] the bug in the ticket above requires we set this together with file times!!! basicInfo.LastWriteTime = toLargeInteger(lastWriteTime); // - if (!isNullTime(creationTime)) - basicInfo.CreationTime = toLargeInteger(creationTime); + if (creationTime) + basicInfo.CreationTime = toLargeInteger(*creationTime); //set file time + attributes setFileInfo(basicInfo); //throw FileError @@ -759,7 +822,7 @@ void setFileTimeRaw(const Zstring& filepath, const FILETIME& creationTime, const //add more meaningful message: FAT accepts only a subset of the NTFS date range if (lastError == ERROR_INVALID_PARAMETER && - dst::isFatDrive(filepath)) + isFatDrive(filepath)) { //we need a low-level reliable routine to format a potentially invalid date => don't use strftime!!! auto fmtDate = [](const FILETIME& ft) -> Zstring @@ -800,7 +863,7 @@ void setFileTimeRaw(const Zstring& filepath, const FILETIME& creationTime, const errorMsg += std::wstring(L"\nA FAT volume can only store dates between 1980 and 2107:\n") + L"\twrite (UTC): \t" + fmtDate(lastWriteTime) + - (!isNullTime(creationTime) ? L"\n\tcreate (UTC): \t" + fmtDate(creationTime) : L""); + (creationTime ? L"\n\tcreate (UTC): \t" + fmtDate(*creationTime) : L""); } if (lastError != ERROR_SUCCESS) @@ -827,8 +890,8 @@ void setFileTimeRaw(const Zstring& filepath, const FILETIME& creationTime, const nullptr, &lastWriteTimeDbg)); - assert(std::abs(filetimeToTimeT(lastWriteTimeDbg) - filetimeToTimeT(lastWriteTime)) <= 2); //respect 2 second FAT/FAT32 precision - //assert(std::abs(filetimeToTimeT(creationTimeDbg ) - filetimeToTimeT(creationTime )) <= 2); -> creation time not available for Linux-hosted Samba shares! + assert(std::abs(filetimeToTimeT(lastWriteTimeDbg) - filetimeToTimeT(lastWriteTime)) <= 2); //respect 2 second FAT/FAT32 precision + //assert(std::abs(filetimeToTimeT(creationTimeDbg ) - filetimeToTimeT(creationTime )) <= 2); -> creation time not available for Linux-hosted Samba shares! #endif } #endif @@ -849,22 +912,7 @@ void zen::removeDirectory(const Zstring& directory, //throw FileError void zen::setFileTime(const Zstring& filepath, std::int64_t modTime, ProcSymlink procSl) //throw FileError { #ifdef ZEN_WIN - FILETIME creationTime = {}; - FILETIME lastWriteTime = timetToFileTime(modTime); - - //####################################### DST hack ########################################### - warn_static("let's tentatively disable the DST hack:") -#if 0 - if (dst::isFatDrive(filepath)) //throw(); hacky: does not consider symlinks pointing to FAT! - { - const dst::RawTime encodedTime = dst::fatEncodeUtcTime(lastWriteTime); //throw std::runtime_error - creationTime = encodedTime.createTimeRaw; - lastWriteTime = encodedTime.writeTimeRaw; - } -#endif - //####################################### DST hack ########################################### - - setFileTimeRaw(filepath, creationTime, lastWriteTime, procSl); //throw FileError + setFileTimeRaw(filepath, nullptr, timetToFileTime(modTime), procSl); //throw FileError #elif defined ZEN_LINUX || defined ZEN_MAC //sigh, we can't use utimensat on NTFS volumes on Ubuntu: silent failure!!! what morons are programming this shit??? @@ -878,7 +926,7 @@ void zen::setFileTime(const Zstring& filepath, std::int64_t modTime, ProcSymlink //=> fallback to "retarded-idiot version"! -- DarkByte - //OS X: utime() is obsoleted by utimes()! utimensat() not yet implemented + //OS X: utime() is obsoleted by utimes()! utimensat() not yet implemented struct ::timeval newTimes[2] = {}; newTimes[0].tv_sec = ::time(nullptr); //access time (seconds) @@ -1300,7 +1348,7 @@ void zen::makeDirectoryPlain(const Zstring& directory, //throw FileError, ErrorT if (!templateDir.empty()) { #ifdef ZEN_WIN - //try to copy file attributes (dereference symlinks and junctions) + //optional: try to copy file attributes (dereference symlinks and junctions) const HANDLE hDirSrc = ::CreateFile(zen::applyLongPathPrefix(templateDir).c_str(), //_In_ LPCTSTR lpFileName, 0, //_In_ DWORD dwDesiredAccess, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, //_In_ DWORD dwShareMode, @@ -1343,14 +1391,14 @@ void zen::makeDirectoryPlain(const Zstring& directory, //throw FileError, ErrorT { USHORT cmpState = COMPRESSION_FORMAT_DEFAULT; DWORD bytesReturned = 0; - /*bool rv = */::DeviceIoControl(hDirTrg, //handle to file or directory - FSCTL_SET_COMPRESSION, //dwIoControlCode - &cmpState, //input buffer - sizeof(cmpState), //size of input buffer - nullptr, //lpOutBuffer - 0, //OutBufferSize - &bytesReturned, //number of bytes returned - nullptr); //OVERLAPPED structure + /*bool rv = */::DeviceIoControl(hDirTrg, //_In_ HANDLE hDevice, + FSCTL_SET_COMPRESSION, //_In_ DWORD dwIoControlCode, + &cmpState, //_In_opt_ LPVOID lpInBuffer, + sizeof(cmpState), //_In_ DWORD nInBufferSize, + nullptr, //_Out_opt_ LPVOID lpOutBuffer, + 0, //_In_ DWORD nOutBufferSize, + &bytesReturned, //_Out_opt_ LPDWORD lpBytesReturned, + nullptr); //_Inout_opt_ LPOVERLAPPED lpOverlapped } //(try to) set creation and modification time @@ -1385,12 +1433,12 @@ void zen::copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool return ret != INVALID_FILE_ATTRIBUTES && (ret & FILE_ATTRIBUTE_DIRECTORY); }(); - //dynamically load windows API function typedef BOOLEAN (WINAPI* CreateSymbolicLinkFunc)(LPCTSTR lpSymlinkFileName, LPCTSTR lpTargetFileName, DWORD dwFlags); const SysDllFun<CreateSymbolicLinkFunc> createSymbolicLink(L"kernel32.dll", "CreateSymbolicLinkW"); if (!createSymbolicLink) - throw FileError(replaceCpy(_("Cannot create symbolic link %x."), L"%x", fmtFileName(targetLink)), replaceCpy(_("Cannot find system function %x."), L"%x", L"\"CreateSymbolicLinkW\"")); + throw FileError(replaceCpy(replaceCpy(_("Cannot copy symbolic link %x to %y."), L"%x", L"\n" + fmtFileName(sourceLink)), L"%y", L"\n" + fmtFileName(targetLink)), + replaceCpy(_("Cannot find system function %x."), L"%x", L"\"CreateSymbolicLinkW\"")); const wchar_t functionName[] = L"CreateSymbolicLinkW"; if (!createSymbolicLink(targetLink.c_str(), //__in LPTSTR lpSymlinkFileName, - seems no long path prefix is required... @@ -1400,9 +1448,9 @@ void zen::copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool const wchar_t functionName[] = L"symlink"; if (::symlink(linkPath.c_str(), targetLink.c_str()) != 0) #endif - throwFileError(replaceCpy(_("Cannot create symbolic link %x."), L"%x", fmtFileName(targetLink)), functionName, getLastError()); + throwFileError(replaceCpy(replaceCpy(_("Cannot copy symbolic link %x to %y."), L"%x", L"\n" + fmtFileName(sourceLink)), L"%y", L"\n" + fmtFileName(targetLink)), functionName, getLastError()); - //allow only consistent objects to be created -> don't place before ::symlink, targetLink may already exist + //allow only consistent objects to be created -> don't place before ::symlink, targetLink may already exist! zen::ScopeGuard guardNewLink = zen::makeGuard([&] { try @@ -1425,7 +1473,7 @@ void zen::copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool &sourceAttr)) //__out LPVOID lpFileInformation throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(sourceLink)), L"GetFileAttributesEx", getLastError()); - setFileTimeRaw(targetLink, sourceAttr.ftCreationTime, sourceAttr.ftLastWriteTime, ProcSymlink::DIRECT); //throw FileError + setFileTimeRaw(targetLink, &sourceAttr.ftCreationTime, sourceAttr.ftLastWriteTime, ProcSymlink::DIRECT); //throw FileError #elif defined ZEN_LINUX || defined ZEN_MAC struct ::stat srcInfo = {}; @@ -1547,7 +1595,7 @@ bool canCopyAsSparse(const Zstring& sourceFile, const Zstring& targetFile) //thr void copyFileWindowsSparse(const Zstring& sourceFile, const Zstring& targetFile, const std::function<void(std::int64_t bytesDelta)>& onUpdateCopyStatus, - InSyncAttributes* newAttrib) //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting, ErrorFileLocked + InSyncAttributes* newAttrib) //throw FileError, ErrorTargetExisting, ErrorFileLocked { assert(canCopyAsSparse(sourceFile, targetFile)); @@ -1626,8 +1674,7 @@ void copyFileWindowsSparse(const Zstring& sourceFile, lastError == ERROR_ALREADY_EXISTS) //comment on msdn claims, this one is used on Windows Mobile 6 throw ErrorTargetExisting(errorMsg, errorDescr); - if (lastError == ERROR_PATH_NOT_FOUND) - throw ErrorTargetPathMissing(errorMsg, errorDescr); + //if (lastError == ERROR_PATH_NOT_FOUND) throw ErrorTargetPathMissing(errorMsg, errorDescr); throw FileError(errorMsg, errorDescr); } @@ -1655,14 +1702,14 @@ void copyFileWindowsSparse(const Zstring& sourceFile, { USHORT cmpState = COMPRESSION_FORMAT_DEFAULT; DWORD bytesReturned = 0; - if (!::DeviceIoControl(hFileTarget, //handle to file or directory - FSCTL_SET_COMPRESSION, //dwIoControlCode - &cmpState, //input buffer - sizeof(cmpState), //size of input buffer - nullptr, //lpOutBuffer - 0, //OutBufferSize - &bytesReturned, //number of bytes returned - nullptr)) //OVERLAPPED structure + if (!::DeviceIoControl(hFileTarget, //_In_ HANDLE hDevice, + FSCTL_SET_COMPRESSION, //_In_ DWORD dwIoControlCode, + &cmpState, //_In_opt_ LPVOID lpInBuffer, + sizeof(cmpState), //_In_ DWORD nInBufferSize, + nullptr, //_Out_opt_ LPVOID lpOutBuffer, + 0, //_In_ DWORD nOutBufferSize, + &bytesReturned, //_Out_opt_ LPDWORD lpBytesReturned, + nullptr)) //_Inout_opt_ LPOVERLAPPED lpOverlapped {} //may legitimately fail with ERROR_INVALID_FUNCTION if: // - target folder is encrypted // - target volume does not support compressed attribute -> unlikely in this context @@ -1678,14 +1725,14 @@ void copyFileWindowsSparse(const Zstring& sourceFile, //if (sourceIsSparse && targetSupportsSparse) -> no need to check, this is our precondition! { DWORD bytesReturned = 0; - if (!::DeviceIoControl(hFileTarget, //handle to file - FSCTL_SET_SPARSE, //dwIoControlCode - nullptr, //input buffer - 0, //size of input buffer - nullptr, //lpOutBuffer - 0, //OutBufferSize - &bytesReturned, //number of bytes returned - nullptr)) //OVERLAPPED structure + if (!::DeviceIoControl(hFileTarget, //_In_ HANDLE hDevice, + FSCTL_SET_SPARSE, //_In_ DWORD dwIoControlCode, + nullptr, //_In_opt_ LPVOID lpInBuffer, + 0, //_In_ DWORD nInBufferSize, + nullptr, //_Out_opt_ LPVOID lpOutBuffer, + 0, //_In_ DWORD nOutBufferSize, + &bytesReturned, //_Out_opt_ LPDWORD lpBytesReturned, + nullptr)) //_Inout_opt_ LPOVERLAPPED lpOverlapped throwFileError(replaceCpy(_("Cannot write file attributes of %x."), L"%x", fmtFileName(targetFile)), L"DeviceIoControl, FSCTL_SET_SPARSE", getLastError()); } @@ -1805,7 +1852,7 @@ public: //call context: copyCallbackInternal() void reportErrorShouldCopyAsSparse() { shouldCopyAsSparse = true; } - void reportUserException(const std::function<void(std::int64_t bytesDelta)>& onUpdateCopyStatus) { exceptionInUserCallback = onUpdateCopyStatus; } + void reportUserException(const std::exception_ptr& e) { exception = e; } void reportError(const std::wstring& msg, const std::wstring& description) { errorMsg = std::make_pair(msg, description); } @@ -1815,11 +1862,8 @@ public: if (shouldCopyAsSparse) throw ErrorShouldCopyAsSparse(L"sparse dummy value"); - if (exceptionInUserCallback) - { - exceptionInUserCallback(0); //should throw again!!! - assert(false); // - } + if (exception) + std::rethrow_exception(exception); if (!errorMsg.first.empty()) throw FileError(errorMsg.first, errorMsg.second); @@ -1828,7 +1872,7 @@ public: private: bool shouldCopyAsSparse; // std::pair<std::wstring, std::wstring> errorMsg; //these are exclusive! - std::function<void(std::int64_t bytesDelta)> exceptionInUserCallback; // -> optional + std::exception_ptr exception; }; @@ -1924,15 +1968,16 @@ DWORD CALLBACK copyCallbackInternal(LARGE_INTEGER totalFileSize, { USHORT cmpState = COMPRESSION_FORMAT_DEFAULT; DWORD bytesReturned = 0; - if (!::DeviceIoControl(hDestinationFile, //handle to file or directory - FSCTL_SET_COMPRESSION, //dwIoControlCode - &cmpState, //input buffer - sizeof(cmpState), //size of input buffer - nullptr, //lpOutBuffer - 0, //OutBufferSize - &bytesReturned, //number of bytes returned - nullptr)) //OVERLAPPED structure + if (!::DeviceIoControl(hDestinationFile, //_In_ HANDLE hDevice, + FSCTL_SET_COMPRESSION, //_In_ DWORD dwIoControlCode, + &cmpState, //_In_opt_ LPVOID lpInBuffer, + sizeof(cmpState), //_In_ DWORD nInBufferSize, + nullptr, //_Out_opt_ LPVOID lpOutBuffer, + 0, //_In_ DWORD nOutBufferSize, + &bytesReturned, //_Out_opt_ LPDWORD lpBytesReturned + nullptr)) //_Inout_opt_ LPOVERLAPPED lpOverlapped {} //may legitimately fail with ERROR_INVALID_FUNCTION if + // - if target folder is encrypted // - target volume does not support compressed attribute //############################################################################# @@ -1949,9 +1994,7 @@ DWORD CALLBACK copyCallbackInternal(LARGE_INTEGER totalFileSize, } catch (...) { - //#warning migrate to std::exception_ptr when available - - cbd.errorHandler.reportUserException(cbd.onUpdateCopyStatus_); + cbd.errorHandler.reportUserException(std::current_exception()); return PROGRESS_CANCEL; } return PROGRESS_CONTINUE; @@ -1965,7 +2008,7 @@ const bool supportNonEncryptedDestination = winXpOrLater(); //encrypted destinat void copyFileWindowsDefault(const Zstring& sourceFile, const Zstring& targetFile, const std::function<void(std::int64_t bytesDelta)>& onUpdateCopyStatus, - InSyncAttributes* newAttrib) //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting, ErrorFileLocked, ErrorShouldCopyAsSparse + InSyncAttributes* newAttrib) //throw FileError, ErrorTargetExisting, ErrorFileLocked, ErrorShouldCopyAsSparse { //try to get backup read and write privileges: who knows, maybe this helps solve some obscure "access denied" errors try { activatePrivilege(SE_BACKUP_NAME); } @@ -1979,7 +2022,7 @@ void copyFileWindowsDefault(const Zstring& sourceFile, DWORD copyFlags = COPY_FILE_FAIL_IF_EXISTS; if (supportNonEncryptedDestination) - copyFlags |= COPY_FILE_ALLOW_DECRYPTED_DESTINATION; //allow copying from encrypted to non-encrytped location + copyFlags |= COPY_FILE_ALLOW_DECRYPTED_DESTINATION; //allow copying from encrypted to non-encrypted location //if (vistaOrLater()) //see http://blogs.technet.com/b/askperf/archive/2007/05/08/slow-large-file-copy-issues.aspx // copyFlags |= COPY_FILE_NO_BUFFERING; //no perf difference at worst, huge improvement for large files (20% in test NTFS -> NTFS) @@ -1989,13 +2032,12 @@ void copyFileWindowsDefault(const Zstring& sourceFile, CallbackData cbd(onUpdateCopyStatus, sourceFile, targetFile); - const bool success = ::CopyFileEx( //same performance like CopyFile() - applyLongPathPrefix(sourceFile).c_str(), //__in LPCTSTR lpExistingFileName, - applyLongPathPrefix(targetFile).c_str(), //__in LPCTSTR lpNewFileName, - copyCallbackInternal, //__in_opt LPPROGRESS_ROUTINE lpProgressRoutine, - &cbd, //__in_opt LPVOID lpData, - nullptr, //__in_opt LPBOOL pbCancel, - copyFlags) != FALSE; //__in DWORD dwCopyFlags + const bool success = ::CopyFileEx(applyLongPathPrefix(sourceFile).c_str(), //__in LPCTSTR lpExistingFileName, + applyLongPathPrefix(targetFile).c_str(), //__in LPCTSTR lpNewFileName, + copyCallbackInternal, //__in_opt LPPROGRESS_ROUTINE lpProgressRoutine, + &cbd, //__in_opt LPVOID lpData, + nullptr, //__in_opt LPBOOL pbCancel, + copyFlags) != FALSE; //__in DWORD dwCopyFlags cbd.errorHandler.evaluateErrors(); //throw ?, process errors in callback first! if (!success) @@ -2005,7 +2047,7 @@ void copyFileWindowsDefault(const Zstring& sourceFile, //don't suppress "lastError == ERROR_REQUEST_ABORTED": a user aborted operation IS an error condition! //trying to copy huge sparse files may directly fail with ERROR_DISK_FULL before entering the callback function - if (canCopyAsSparse(sourceFile, targetFile)) //throw () + if (canCopyAsSparse(sourceFile, targetFile)) //noexcept throw ErrorShouldCopyAsSparse(L"sparse dummy value2"); //assemble error message... @@ -2030,20 +2072,16 @@ void copyFileWindowsDefault(const Zstring& sourceFile, throw ErrorTargetExisting(errorMsg, errorDescr); } - if (lastError == ERROR_PATH_NOT_FOUND) - { - guardTarget.dismiss(); //not relevant - throw ErrorTargetPathMissing(errorMsg, errorDescr); //could this also be source path missing!? - } + //if (lastError == ERROR_PATH_NOT_FOUND) throw ErrorTargetPathMissing(errorMsg, errorDescr); //could this also be source path missing!? try //add more meaningful message { //trying to copy > 4GB file to FAT/FAT32 volume gives obscure ERROR_INVALID_PARAMETER (FAT can indeed handle files up to 4 Gig, tested!) if (lastError == ERROR_INVALID_PARAMETER && - dst::isFatDrive(targetFile) && + isFatDrive(targetFile) && getFilesize(sourceFile) >= 4U * std::uint64_t(1024U * 1024 * 1024)) //throw FileError errorDescr += L"\nFAT volumes cannot store files larger than 4 gigabyte."; - //see "Limitations of the FAT32 File System": http://support.microsoft.com/kb/314463/en-us + //see "Limitations of the FAT32 File System": http://support.microsoft.com/kb/314463/en-us //note: ERROR_INVALID_PARAMETER can also occur when copying to a SharePoint server or MS SkyDrive and the target filepath is of a restricted type. } @@ -2055,51 +2093,15 @@ void copyFileWindowsDefault(const Zstring& sourceFile, if (newAttrib) { newAttrib->fileSize = get64BitUInt(cbd.fileInfoSrc.nFileSizeLow, cbd.fileInfoSrc.nFileSizeHigh); - //newAttrib->modificationTime = -> set further below + newAttrib->modificationTime = filetimeToTimeT(cbd.fileInfoSrc.ftLastWriteTime); newAttrib->sourceFileId = extractFileId(cbd.fileInfoSrc); newAttrib->targetFileId = extractFileId(cbd.fileInfoTrg); } + warn_static("new perf check + investigate improvements now that DST hcak is gone! =>") - { - FILETIME creationtime = cbd.fileInfoSrc.ftCreationTime; - FILETIME lastWriteTimeRaw = cbd.fileInfoSrc.ftLastWriteTime; - //####################################### DST hack ########################################### - warn_static("let's tentatively disable the DST hack:") -#if 0 - if (dst::isFatDrive(sourceFile)) //throw(); hacky: does not consider symlinks pointing to FAT! - { - const dst::RawTime rawTime(creationtime, lastWriteTimeRaw); - if (dst::fatHasUtcEncoded(rawTime)) //throw std::runtime_error - { - lastWriteTimeRaw = dst::fatDecodeUtcTime(rawTime); //return last write time in real UTC, throw (std::runtime_error) - ::GetSystemTimeAsFileTime(&creationtime); //real creation time information is not available... - } - } -#endif - //####################################### DST hack ########################################### - - if (newAttrib) - newAttrib->modificationTime = filetimeToTimeT(lastWriteTimeRaw); - - //caveat: - ::CopyFileEx() silently *ignores* failure to set modification time!!! => we always need to set it again but with proper error checking! - // - perf-loss on USB sticks with many small files of about 30%! - FILETIME creationTimeOut = creationtime; - FILETIME lastWriteTimeOut = lastWriteTimeRaw; - - //####################################### DST hack ########################################### - warn_static("let's tentatively disable the DST hack:") -#if 0 - if (dst::isFatDrive(targetFile)) //throw(); target cannot be a symlink in this context! - { - const dst::RawTime encodedTime = dst::fatEncodeUtcTime(lastWriteTimeRaw); //throw std::runtime_error - creationTimeOut = encodedTime.createTimeRaw; - lastWriteTimeOut = encodedTime.writeTimeRaw; - } -#endif - //####################################### DST hack ########################################### - - setFileTimeRaw(targetFile, creationTimeOut, lastWriteTimeOut, ProcSymlink::FOLLOW); //throw FileError - } + //caveat: - ::CopyFileEx() silently *ignores* failure to set modification time!!! => we always need to set it again but with proper error checking! + // - perf-loss on USB sticks with many small files of about 30%! + setFileTimeRaw(targetFile, &cbd.fileInfoSrc.ftCreationTime, cbd.fileInfoSrc.ftLastWriteTime, ProcSymlink::FOLLOW); //throw FileError guardTarget.dismiss(); //target has been created successfully! } @@ -2111,11 +2113,11 @@ void copyFileWindowsSelectRoutine(const Zstring& sourceFile, const Zstring& targ { try { - copyFileWindowsDefault(sourceFile, targetFile, onUpdateCopyStatus, sourceAttr); //throw ErrorShouldCopyAsSparse et al. + copyFileWindowsDefault(sourceFile, targetFile, onUpdateCopyStatus, sourceAttr); //throw FileError, ErrorTargetExisting, ErrorFileLocked, ErrorShouldCopyAsSparse } - catch (ErrorShouldCopyAsSparse&) //we cheaply check for this condition within callback of ::CopyFileEx()! + catch (ErrorShouldCopyAsSparse&) //we quickly check for this condition within callback of ::CopyFileEx()! { - copyFileWindowsSparse(sourceFile, targetFile, onUpdateCopyStatus, sourceAttr); + copyFileWindowsSparse(sourceFile, targetFile, onUpdateCopyStatus, sourceAttr); //throw FileError, ErrorTargetExisting, ErrorFileLocked } } @@ -2129,7 +2131,7 @@ void copyFileWindows(const Zstring& sourceFile, { try { - copyFileWindowsSelectRoutine(sourceFile, targetFile, onUpdateCopyStatus, sourceAttr); //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting, ErrorFileLocked + copyFileWindowsSelectRoutine(sourceFile, targetFile, onUpdateCopyStatus, sourceAttr); //throw FileError, ErrorTargetExisting, ErrorFileLocked } catch (const ErrorTargetExisting&) { @@ -2149,22 +2151,19 @@ void copyFileWindows(const Zstring& sourceFile, void copyFileLinuxMac(const Zstring& sourceFile, const Zstring& targetFile, const std::function<void(std::int64_t bytesDelta)>& onUpdateCopyStatus, - InSyncAttributes* newAttrib) //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting + InSyncAttributes* newAttrib) //throw FileError, ErrorTargetExisting { - //open sourceFile for reading FileInputUnbuffered fileIn(sourceFile); //throw FileError struct ::stat sourceInfo = {}; - if (::fstat(fileIn.getDescriptor(), &sourceInfo) != 0) //read file attributes from source + if (::fstat(fileIn.getDescriptor(), &sourceInfo) != 0) throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(sourceFile)), L"fstat", getLastError()); zen::ScopeGuard guardTarget = zen::makeGuard([&] { try { removeFile(targetFile); } catch (FileError&) {} }); //transactional behavior: place guard before lifetime of FileOutput try { - //create targetFile and open it for writing - FileOutputUnbuffered fileOut(targetFile, sourceInfo.st_mode); //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting + FileOutputUnbuffered fileOut(targetFile, sourceInfo.st_mode); //throw FileError, ErrorTargetExisting - //copy contents of sourceFile to targetFile std::vector<char> buffer(128 * 1024); //see comment in FileInputUnbuffered::read do { @@ -2172,7 +2171,6 @@ void copyFileLinuxMac(const Zstring& sourceFile, fileOut.write(&buffer[0], bytesRead); //throw FileError - //invoke callback method to update progress indicators if (onUpdateCopyStatus) onUpdateCopyStatus(bytesRead); //throw X! } @@ -2229,20 +2227,31 @@ copyFileWindowsDefault(::CopyFileEx) copyFileWindowsSparse(::BackupRead/::Backu inline void copyFileSelectOs(const Zstring& sourceFile, const Zstring& targetFile, + bool copyFilePermissions, const std::function<void(std::int64_t bytesDelta)>& onUpdateCopyStatus, InSyncAttributes* sourceAttr) { #ifdef ZEN_WIN - copyFileWindows(sourceFile, targetFile, onUpdateCopyStatus, sourceAttr); //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting, ErrorFileLocked + copyFileWindows(sourceFile, targetFile, onUpdateCopyStatus, sourceAttr); //throw FileError, ErrorTargetExisting, ErrorFileLocked #elif defined ZEN_LINUX || defined ZEN_MAC - copyFileLinuxMac(sourceFile, targetFile, onUpdateCopyStatus, sourceAttr); //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting + copyFileLinuxMac(sourceFile, targetFile, onUpdateCopyStatus, sourceAttr); //throw FileError, ErrorTargetExisting #endif + + if (copyFilePermissions) + { + //at this point we know we created a new file, so it's fine to delete it for cleanup! + zen::ScopeGuard guardTargetFile = zen::makeGuard([&] { try { removeFile(targetFile); } catch (FileError&) {}}); + + copyObjectPermissions(sourceFile, targetFile, ProcSymlink::FOLLOW); //throw FileError + + guardTargetFile.dismiss(); //target has been created successfully! + } } } -void zen::copyFile(const Zstring& sourceFile, //throw FileError, ErrorTargetPathMissing, ErrorFileLocked +void zen::copyFile(const Zstring& sourceFile, //throw FileError, ErrorFileLocked const Zstring& targetFile, bool copyFilePermissions, bool transactionalCopy, @@ -2252,13 +2261,12 @@ void zen::copyFile(const Zstring& sourceFile, //throw FileError, ErrorTargetPath { if (transactionalCopy) { - Zstring tmpTarget = targetFile + TEMP_FILE_ENDING; //use temporary file until a correct date has been set + Zstring tmpTarget = targetFile + TEMP_FILE_ENDING; - //raw file copy for (int i = 0;; ++i) try { - copyFileSelectOs(sourceFile, tmpTarget, onUpdateCopyStatus, sourceAttr); //throw FileError: ErrorTargetPathMissing, ErrorTargetExisting, ErrorFileLocked + copyFileSelectOs(sourceFile, tmpTarget, copyFilePermissions, onUpdateCopyStatus, sourceAttr); //throw FileError, ErrorTargetExisting, ErrorFileLocked break; } catch (const ErrorTargetExisting&) //optimistic strategy: assume everything goes well, but recover on error -> minimize file accesses @@ -2274,7 +2282,6 @@ void zen::copyFile(const Zstring& sourceFile, //throw FileError, ErrorTargetPath if (onDeleteTargetFile) onDeleteTargetFile(); //throw X - //rename temporary file: //perf: this call is REALLY expensive on unbuffered volumes! ~40% performance decrease on FAT USB stick! renameFile(tmpTarget, targetFile); //throw FileError @@ -2284,45 +2291,23 @@ void zen::copyFile(const Zstring& sourceFile, //throw FileError, ErrorTargetPath This "feature" is called "File System Tunneling": http://blogs.msdn.com/b/oldnewthing/archive/2005/07/15/439261.aspx http://support.microsoft.com/kb/172190/en-us - - However during the next comparison the DST hack will be applied correctly since the DST-hash of the mod.time is invalid. - - EXCEPTION: the hash may match!!! reproduce: - 1. set system time back to date within previous DST - 2. save some file on FAT32 usb stick and FFS-compare to make sure the DST hack is applied correctly - 4. pull out usb stick, put back in - 3. restore system time - 4. copy file from USB to local drive via explorer - => - NTFS <-> FAT, file exists on both sides; mod times match, DST hack on USB stick causes 1-hour offset when comparing in FFS. - When syncing, modification time is copied correctly, but new DST hack fails to apply and old creation time is reused (see above). - Unfortunately, the old DST hash matches mod time! => On next comparison FFS will *still* see both sides as different!!!!!!!!! */ guardTempFile.dismiss(); } else { + /* + Note: non-transactional file copy solves at least four problems: + -> skydrive - doesn't allow for .ffs_tmp extension and returns ERROR_INVALID_PARAMETER + -> network renaming issues + -> allow for true delete before copy to handle low disk space problems + -> higher performance on non-buffered drives (e.g. usb sticks) + */ + if (onDeleteTargetFile) onDeleteTargetFile(); - copyFileSelectOs(sourceFile, targetFile, onUpdateCopyStatus, sourceAttr); //throw FileError: ErrorTargetPathMissing, ErrorTargetExisting, ErrorFileLocked - } - /* - Note: non-transactional file copy solves at least four problems: - -> skydrive - doesn't allow for .ffs_tmp extension and returns ERROR_INVALID_PARAMETER - -> network renaming issues - -> allow for true delete before copy to handle low disk space problems - -> higher performance on non-buffered drives (e.g. usb sticks) - */ - - //file permissions - if (copyFilePermissions) - { - zen::ScopeGuard guardTargetFile = zen::makeGuard([&] { try { removeFile(targetFile); } catch (FileError&) {}}); - - copyObjectPermissions(sourceFile, targetFile, ProcSymlink::FOLLOW); //throw FileError - - guardTargetFile.dismiss(); //target has been created successfully! + copyFileSelectOs(sourceFile, targetFile, copyFilePermissions, onUpdateCopyStatus, sourceAttr); //throw FileError, ErrorTargetExisting, ErrorFileLocked } } diff --git a/zen/file_handling.h b/zen/file_access.h index b58f6c07..7cac889c 100644 --- a/zen/file_handling.h +++ b/zen/file_access.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef FILE_HANDLING_H_8017341345614857 -#define FILE_HANDLING_H_8017341345614857 +#ifndef FILE_ACCESS_H_8017341345614857 +#define FILE_ACCESS_H_8017341345614857 #include <functional> #include "zstring.h" @@ -57,15 +57,13 @@ struct InSyncAttributes FileId targetFileId; }; -void copyFile(const Zstring& sourceFile, //throw FileError, ErrorTargetPathMissing, ErrorFileLocked (Windows-only) +void copyFile(const Zstring& sourceFile, //throw FileError, ErrorFileLocked (Windows-only) const Zstring& targetFile, //symlink handling: dereference source bool copyFilePermissions, bool transactionalCopy, //if target is existing user needs to implement deletion: copyFile() NEVER overwrites target if already existing! //if transactionalCopy == true, full read access on source had been proven at this point, so it's safe to delete it. const std::function<void()>& onDeleteTargetFile, //may be nullptr; may throw! - //Linux: unconditionally - //Windows: first exception is swallowed, updateCopyStatus() is then called again where it should throw again and the exception will propagate as expected //accummulated delta != file size! consider ADS, sparse, compressed files const std::function<void(std::int64_t bytesDelta)>& onUpdateCopyStatus, //may be nullptr; may throw! @@ -79,4 +77,4 @@ const Zchar TEMP_FILE_ENDING[] = Zstr(".ffs_tmp"); //don't use Zstring as global void copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool copyFilePermissions); //throw FileError } -#endif //FILE_HANDLING_H_8017341345614857 +#endif //FILE_ACCESS_H_8017341345614857 diff --git a/zen/file_id_def.h b/zen/file_id_def.h index daadd391..3b217b3d 100644 --- a/zen/file_id_def.h +++ b/zen/file_id_def.h @@ -8,7 +8,6 @@ #define FILE_ID_INTERNAL_HEADER_013287632486321493 #include <utility> -#include "assert_static.h" #ifdef ZEN_WIN #include "win.h" //includes "windows.h" @@ -44,9 +43,9 @@ FileId extractFileId(DWORD volumeSerialNumber, ULONGLONG fileIndex) FileId(volumeSerialNumber, fileIndex) : FileId(); } -assert_static(sizeof(FileId().first ) == sizeof(BY_HANDLE_FILE_INFORMATION().dwVolumeSerialNumber)); -assert_static(sizeof(FileId().second) == sizeof(BY_HANDLE_FILE_INFORMATION().nFileIndexHigh) + sizeof(BY_HANDLE_FILE_INFORMATION().nFileIndexLow)); -assert_static(sizeof(FileId().second) == sizeof(ULARGE_INTEGER)); +static_assert(sizeof(FileId().first ) == sizeof(BY_HANDLE_FILE_INFORMATION().dwVolumeSerialNumber), ""); +static_assert(sizeof(FileId().second) == sizeof(BY_HANDLE_FILE_INFORMATION().nFileIndexHigh) + sizeof(BY_HANDLE_FILE_INFORMATION().nFileIndexLow), ""); +static_assert(sizeof(FileId().second) == sizeof(ULARGE_INTEGER), ""); #elif defined ZEN_LINUX || defined ZEN_MAC diff --git a/zen/file_io.cpp b/zen/file_io.cpp index e11a1201..8f4b6cb2 100644 --- a/zen/file_io.cpp +++ b/zen/file_io.cpp @@ -187,7 +187,7 @@ size_t FileInput::read(void* buffer, size_t bytesToRead) //returns actual number FileOutput::FileOutput(FileHandle handle, const Zstring& filepath) : FileOutputBase(filepath), fileHandle(handle) {} -FileOutput::FileOutput(const Zstring& filepath, AccessFlag access) : //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting +FileOutput::FileOutput(const Zstring& filepath, AccessFlag access) : //throw FileError, ErrorTargetExisting FileOutputBase(filepath) { #ifdef ZEN_WIN @@ -246,8 +246,7 @@ FileOutput::FileOutput(const Zstring& filepath, AccessFlag access) : //throw Fil lastError == ERROR_ALREADY_EXISTS) //comment on msdn claims, this one is used on Windows Mobile 6 throw ErrorTargetExisting(errorMsg, errorDescr); - if (lastError == ERROR_PATH_NOT_FOUND) - throw ErrorTargetPathMissing(errorMsg, errorDescr); + //if (lastError == ERROR_PATH_NOT_FOUND) throw ErrorTargetPathMissing(errorMsg, errorDescr); throw FileError(errorMsg, errorDescr); } @@ -267,8 +266,7 @@ FileOutput::FileOutput(const Zstring& filepath, AccessFlag access) : //throw Fil if (lastError == EEXIST) throw ErrorTargetExisting(errorMsg, errorDescr); - if (lastError == ENOENT) - throw ErrorTargetPathMissing(errorMsg, errorDescr); + //if (lastError == ENOENT) throw ErrorTargetPathMissing(errorMsg, errorDescr); throw FileError(errorMsg, errorDescr); } @@ -348,7 +346,7 @@ size_t FileInputUnbuffered::read(void* buffer, size_t bytesToRead) //throw FileE } -FileOutputUnbuffered::FileOutputUnbuffered(const Zstring& filepath, mode_t mode) : FileOutputBase(filepath) //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting +FileOutputUnbuffered::FileOutputUnbuffered(const Zstring& filepath, mode_t mode) : FileOutputBase(filepath) //throw FileError, ErrorTargetExisting { //checkForUnsupportedType(filepath); -> not needed, open() + O_EXCL shoul fail fast @@ -363,8 +361,7 @@ FileOutputUnbuffered::FileOutputUnbuffered(const Zstring& filepath, mode_t mode) if (lastError == EEXIST) throw ErrorTargetExisting(errorMsg, errorDescr); - if (lastError == ENOENT) - throw ErrorTargetPathMissing(errorMsg, errorDescr); + //if (lastError == ENOENT) throw ErrorTargetPathMissing(errorMsg, errorDescr); throw FileError(errorMsg, errorDescr); } diff --git a/zen/file_io.h b/zen/file_io.h index 5ae6eb1c..7ccef05b 100644 --- a/zen/file_io.h +++ b/zen/file_io.h @@ -26,7 +26,7 @@ static const char LINE_BREAK[] = "\r\n"; static const char LINE_BREAK[] = "\n"; //since OS X apple uses newline, too #endif -//buffered file IO optimized for sequential read/write accesses + better error reporting + long path support (following symlinks) +//buffered file IO optimized for sequential read/write accesses + better error reporting + long path support + following symlinks #ifdef ZEN_WIN typedef HANDLE FileHandle; @@ -41,8 +41,7 @@ public: FileInput(FileHandle handle, const Zstring& filepath); //takes ownership! ~FileInput(); - virtual size_t read(void* buffer, size_t bytesToRead) override; //throw FileError; returns actual number of bytes read - //expected to fill buffer completely unless "end of file" + size_t read(void* buffer, size_t bytesToRead) override; //throw FileError; returns actual number of bytes read private: FileHandle fileHandle; @@ -52,17 +51,19 @@ private: class FileOutput : public FileOutputBase { public: - FileOutput(const Zstring& filepath, AccessFlag access); //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting + FileOutput(const Zstring& filepath, AccessFlag access); //throw FileError, ErrorTargetExisting FileOutput(FileHandle handle, const Zstring& filepath); //takes ownership! ~FileOutput(); - virtual void write(const void* buffer, size_t bytesToWrite) override; //throw FileError + void write(const void* buffer, size_t bytesToWrite) override; //throw FileError private: FileHandle fileHandle; }; #if defined ZEN_LINUX || defined ZEN_MAC +warn_static("get rid of FileInputUnbuffered/FileOutputUnbuffered, use fdopen instead") + class FileInputUnbuffered : public FileInputBase { public: @@ -70,7 +71,7 @@ public: ~FileInputUnbuffered(); //considering safe-read.c it seems buffer size should be a multiple of 8192 - virtual size_t read(void* buffer, size_t bytesToRead) override; //throw FileError; returns actual number of bytes read + size_t read(void* buffer, size_t bytesToRead) override; //throw FileError; returns actual number of bytes read //do NOT rely on partially filled buffer meaning EOF! int getDescriptor() { return fdFile;} @@ -83,11 +84,11 @@ class FileOutputUnbuffered : public FileOutputBase { public: //creates a new file (no overwrite allowed!) - FileOutputUnbuffered(const Zstring& filepath, mode_t mode); //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting + FileOutputUnbuffered(const Zstring& filepath, mode_t mode); //throw FileError, ErrorTargetExisting FileOutputUnbuffered(int fd, const Zstring& filepath); //takes ownership! ~FileOutputUnbuffered(); - virtual void write(const void* buffer, size_t bytesToWrite) override; //throw FileError + void write(const void* buffer, size_t bytesToWrite) override; //throw FileError int getDescriptor() { return fdFile;} private: diff --git a/zen/file_io_base.h b/zen/file_io_base.h index 8e9bf12e..e56ea189 100644 --- a/zen/file_io_base.h +++ b/zen/file_io_base.h @@ -21,8 +21,8 @@ protected: ~FileBase() {} private: - FileBase(const FileBase&); //=delete - FileBase& operator=(const FileBase&); // + FileBase (const FileBase&) = delete; + FileBase& operator=(const FileBase&) = delete; const Zstring filename_; }; diff --git a/zen/file_traverser.cpp b/zen/file_traverser.cpp index d8f2d0cf..7b423faa 100644 --- a/zen/file_traverser.cpp +++ b/zen/file_traverser.cpp @@ -6,15 +6,13 @@ #include "file_traverser.h" #include "sys_error.h" -#include "assert_static.h" #include "symlink_target.h" #include "int64.h" #ifdef ZEN_WIN #include "win_ver.h" #include "long_path_prefix.h" -#include "dst_hack.h" -#include "file_handling.h" //remove this huge dependency when getting rid of DST hack!! until then we need "setFileTime" +#include "file_access.h" #include "dll.h" #include "FindFilePlus/find_file_plus.h" @@ -30,7 +28,7 @@ using namespace zen; - + namespace { //implement "retry" in a generic way: @@ -179,8 +177,8 @@ struct Win32Traverser { struct DirHandle { - DirHandle(HANDLE hnd, const WIN32_FIND_DATA& d) : searchHandle(hnd), haveData(true), data(d) {} - explicit DirHandle(HANDLE hnd) : searchHandle(hnd), haveData(false) {} + DirHandle(HANDLE hnd, const WIN32_FIND_DATA& d) : searchHandle(hnd), haveData(true), data(d) {} + explicit DirHandle(HANDLE hnd) : searchHandle(hnd), haveData(false) {} HANDLE searchHandle; bool haveData; @@ -193,7 +191,7 @@ struct Win32Traverser { const Zstring& dirpathPf = appendSeparator(dirpath); - WIN32_FIND_DATA fileData = {}; + WIN32_FIND_DATA fileData = {}; HANDLE hnd = ::FindFirstFile(applyLongPathPrefix(dirpathPf + L'*').c_str(), &fileData); //no noticable performance difference compared to FindFirstFileEx with FindExInfoBasic, FIND_FIRST_EX_CASE_SENSITIVE and/or FIND_FIRST_EX_LARGE_FETCH if (hnd == INVALID_HANDLE_VALUE) @@ -208,7 +206,7 @@ struct Win32Traverser } throwFileError(replaceCpy(_("Cannot open directory %x."), L"%x", fmtFileName(dirpath)), L"FindFirstFile", lastError); } - return DirHandle(hnd, fileData); + return DirHandle(hnd, fileData); } static void destroy(const DirHandle& hnd) { ::FindClose(hnd.searchHandle); } //throw() @@ -271,11 +269,11 @@ struct FilePlusTraverser static DirHandle create(const Zstring& dirpath) //throw FileError { - const findplus::FindHandle hnd = ::openDir(applyLongPathPrefix(dirpath).c_str()); + const findplus::FindHandle hnd = ::openDir(applyLongPathPrefix(dirpath).c_str()); if (!hnd) - throwFileError(replaceCpy(_("Cannot open directory %x."), L"%x", fmtFileName(dirpath)), L"openDir", getLastError()); + throwFileError(replaceCpy(_("Cannot open directory %x."), L"%x", fmtFileName(dirpath)), L"openDir", getLastError()); - return DirHandle(hnd); + return DirHandle(hnd); } static void destroy(DirHandle hnd) { ::closeDir(hnd.searchHandle); } //throw() @@ -325,13 +323,13 @@ struct FilePlusTraverser class DirTraverser { public: - static void execute(const Zstring& baseDirectory, TraverseCallback& sink, DstHackCallback* dstCallback) + static void execute(const Zstring& baseDirectory, TraverseCallback& sink) { - DirTraverser(baseDirectory, sink, dstCallback); + DirTraverser(baseDirectory, sink); } private: - DirTraverser(const Zstring& baseDirectory, TraverseCallback& sink, DstHackCallback* dstCallback); + DirTraverser(const Zstring& baseDirectory, TraverseCallback& sink); DirTraverser (const DirTraverser&) = delete; DirTraverser& operator=(const DirTraverser&) = delete; @@ -340,58 +338,6 @@ private: template <class Trav> void traverseWithException(const Zstring& dirpath, TraverseCallback& sink, DWORD volumeSerial /*may be 0!*/); //throw FileError, NeedFallbackToWin32Traverser - - //####################################### DST hack ########################################### - void applyDstHack(zen::DstHackCallback& dstCallback) - { - int failedAttempts = 0; - int filesToValidate = 50; //don't let data verification become a performance issue - - for (auto it = markForDstHack.begin(); it != markForDstHack.end(); ++it) - { - 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(it->first); - - try - { - //set modification time including DST hack: this function is too clever to not introduce this dependency - setFileTime(it->first, it->second, ProcSymlink::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) - { - const dst::RawTime encodedTime = dst::fatEncodeUtcTime(timetToFileTime(it->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(it->first).c_str(), //__in LPCTSTR lpFileName, - GetFileExInfoStandard, //__in GET_FILEEX_INFO_LEVELS fInfoLevelId, - &debugeAttr); //__out LPVOID lpFileInformation - - if (::CompareFileTime(&debugeAttr.ftCreationTime, &encodedTime.createTimeRaw) != 0 || - ::CompareFileTime(&debugeAttr.ftLastWriteTime, &encodedTime.writeTimeRaw) != 0) - { - ++failedAttempts; - assert(false); //don't throw exceptions due to dst hack here - continue; - } - } - } - } - - const bool needDstHack; - typedef std::vector<std::pair<Zstring, std::int64_t>> FilenameTimeList; - FilenameTimeList markForDstHack; - //####################################### DST hack ########################################### }; @@ -420,8 +366,7 @@ void DirTraverser::traverse<FilePlusTraverser>(const Zstring& dirpath, TraverseC inline -DirTraverser::DirTraverser(const Zstring& baseDirectory, TraverseCallback& sink, DstHackCallback* dstCallback) : - needDstHack(dstCallback ? dst::isFatDrive(baseDirectory) : false) +DirTraverser::DirTraverser(const Zstring& baseDirectory, TraverseCallback& sink) { try //traversing certain folders with restricted permissions requires this privilege! (but copying these files may still fail) { @@ -433,11 +378,6 @@ DirTraverser::DirTraverser(const Zstring& baseDirectory, TraverseCallback& sink, traverse<FilePlusTraverser>(baseDirectory, sink, retrieveVolumeSerial(baseDirectory)); //retrieveVolumeSerial returns 0 on error else //fallback traverse<Win32Traverser>(baseDirectory, sink, 0); - - //apply daylight saving time hack AFTER file traversing, to give separate feedback to user - if (needDstHack) - if (dstCallback) //bound if "needDstHack == true" - applyDstHack(*dstCallback); } @@ -507,20 +447,7 @@ void DirTraverser::traverseWithException(const Zstring& dirpath, TraverseCallbac } else //a file { - TraverseCallback::FileInfo fileInfo = Trav::extractFileInfo(findData, volumeSerial); - - //####################################### DST hack ########################################### - if (needDstHack) - { - const dst::RawTime rawTime(Trav::getCreateTimeRaw(findData), Trav::getModTimeRaw(findData)); - - if (dst::fatHasUtcEncoded(rawTime)) //throw std::runtime_error - fileInfo.lastWriteTime = filetimeToTimeT(dst::fatDecodeUtcTime(rawTime)); //return real UTC time; throw std::runtime_error - else - markForDstHack.push_back(std::make_pair(itempath, filetimeToTimeT(rawTime.writeTimeRaw))); - } - //####################################### DST hack ########################################### - + const TraverseCallback::FileInfo fileInfo = Trav::extractFileInfo(findData, volumeSerial); sink.onFile(shortName, itempath, fileInfo); } } @@ -531,13 +458,13 @@ void DirTraverser::traverseWithException(const Zstring& dirpath, TraverseCallbac class DirTraverser { public: - static void execute(const Zstring& baseDirectory, TraverseCallback& sink, DstHackCallback* dstCallback) + static void execute(const Zstring& baseDirectory, TraverseCallback& sink) { - DirTraverser(baseDirectory, sink, dstCallback); + DirTraverser(baseDirectory, sink); } private: - DirTraverser(const Zstring& baseDirectory, zen::TraverseCallback& sink, zen::DstHackCallback* dstCallback) + DirTraverser(const Zstring& baseDirectory, zen::TraverseCallback& sink) { const Zstring directoryFormatted = //remove trailing slash baseDirectory.size() > 1 && endsWith(baseDirectory, FILE_NAME_SEPARATOR) ? //exception: allow '/' @@ -701,12 +628,4 @@ private: } -void zen::traverseFolder(const Zstring& dirpath, TraverseCallback& sink, DstHackCallback* dstCallback) -{ - warn_static("let's tentatively disable the DST hack:") - DirTraverser::execute(dirpath, sink, nullptr); - return; -#if 0 - DirTraverser::execute(dirpath, sink, dstCallback); -#endif -} +void zen::traverseFolder(const Zstring& dirpath, TraverseCallback& sink) { DirTraverser::execute(dirpath, sink); } diff --git a/zen/file_traverser.h b/zen/file_traverser.h index f240d8c7..c9efbdb1 100644 --- a/zen/file_traverser.h +++ b/zen/file_traverser.h @@ -58,23 +58,10 @@ struct TraverseCallback virtual HandleError reportItemError(const std::wstring& msg, size_t retryNumber, const Zchar* shortName) = 0; //failed to get data for single file/dir/symlink only! }; - -#ifdef ZEN_WIN -struct DstHackCallback -{ - virtual ~DstHackCallback() {} - virtual void requestUiRefresh(const Zstring& filepath) = 0; //applying DST hack imposes significant one-time performance drawback => callback to inform user -}; -#elif defined ZEN_LINUX || defined ZEN_MAC -struct DstHackCallback; //DST hack not required on Unix -#endif - //custom traverser with detail information about files //- client needs to handle duplicate file reports! (FilePlusTraverser fallback, retrying to read directory contents, ...) //- directory may end with PATH_SEPARATOR -void traverseFolder(const Zstring& dirpath, //throw() - TraverseCallback& sink, - DstHackCallback* dstCallback = nullptr); //apply DST hack if callback is supplied +void traverseFolder(const Zstring& dirpath, TraverseCallback& sink); //noexcept } #endif // FILETRAVERSER_H_INCLUDED diff --git a/zen/fixed_list.h b/zen/fixed_list.h index 63ef3f2f..627584df 100644 --- a/zen/fixed_list.h +++ b/zen/fixed_list.h @@ -11,22 +11,15 @@ namespace zen { -//std::list(C++11) compatible class for inplace element construction supporting non-copyable/movable types +//std::list(C++11)-like class for inplace element construction supporting non-copyable/movable types //may be replaced by C++11 std::list when available...or never... template <class T> class FixedList { struct Node { - Node() : next(nullptr), val() {} - //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)) {} - template <class A, class B, class C, class D, class E, class F, class G> Node(A&& a, B&& b, C&& c, D&& d, E&& e, F&& f, G&& g) : 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), std::forward<G>(g)) {} + template <class... Args> + Node(Args&& ... args) : next(nullptr), val(std::forward<Args>(args)...) {} Node* next; //singly linked list is sufficient T val; @@ -48,20 +41,20 @@ public: ListIterator& operator++() { iter = iter->next; return *this; } inline friend bool operator==(const ListIterator& lhs, const ListIterator& rhs) { return lhs.iter == rhs.iter; } inline friend bool operator!=(const ListIterator& lhs, const ListIterator& rhs) { return !(lhs == rhs); } - U& operator* () { return iter->val; } - U* operator->() { return &iter->val; } + U& operator* () const { return iter->val; } + U* operator->() const { return &iter->val; } private: NodeT* iter; }; typedef T value_type; - typedef ListIterator<Node, T> iterator; + typedef ListIterator< Node, T> iterator; typedef ListIterator<const Node, const T> const_iterator; - typedef T& reference; + typedef T& reference; typedef const T& const_reference; iterator begin() { return firstInsert; } - iterator end() { return iterator(); } + iterator end () { return iterator(); } const_iterator begin() const { return firstInsert; } const_iterator end () const { return const_iterator(); } @@ -75,14 +68,8 @@ public: reference& back() { return lastInsert->val; } const_reference& back() const { return lastInsert->val; } - void emplace_back() { pushNode(new Node); } - template <class A> void emplace_back(A&& a) { pushNode(new Node(std::forward<A>(a))); } - template <class A, class B> void emplace_back(A&& a, B&& b) { pushNode(new Node(std::forward<A>(a), std::forward<B>(b))); } - template <class A, class B, class C> void emplace_back(A&& a, B&& b, C&& c) { pushNode(new Node(std::forward<A>(a), std::forward<B>(b), std::forward<C>(c))); } - template <class A, class B, class C, class D> void emplace_back(A&& a, B&& b, C&& c, D&& d) { pushNode(new Node(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> void emplace_back(A&& a, B&& b, C&& c, D&& d, E&& e) { pushNode(new Node(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> void emplace_back(A&& a, B&& b, C&& c, D&& d, E&& e, F&& f) { pushNode(new Node(std::forward<A>(a), std::forward<B>(b), std::forward<C>(c), std::forward<D>(d), std::forward<E>(e), std::forward<F>(f))); } - template <class A, class B, class C, class D, class E, class F, class G> void emplace_back(A&& a, B&& b, C&& c, D&& d, E&& e, F&& f, G&& g) { pushNode(new Node(std::forward<A>(a), std::forward<B>(b), std::forward<C>(c), std::forward<D>(d), std::forward<E>(e), std::forward<F>(f), std::forward<G>(g))); } + template <class... Args> + void emplace_back(Args&& ... args) { pushNode(new Node(std::forward<Args>(args)...)); } template <class Predicate> void remove_if(Predicate pred) @@ -126,6 +113,7 @@ public: } bool empty() const { return firstInsert == nullptr; } + size_t size() const { return sz; } private: diff --git a/zen/format_unit.h b/zen/format_unit.h index 237b9f04..9416e3e5 100644 --- a/zen/format_unit.h +++ b/zen/format_unit.h @@ -44,7 +44,7 @@ std::wstring includeNumberSeparator(const std::wstring& number); template <class NumberType> inline std::wstring toGuiString(NumberType number) { - assert_static(IsInteger<NumberType>::value); + static_assert(IsInteger<NumberType>::value, ""); return ffs_Impl::includeNumberSeparator(zen::numberTo<std::wstring>(number)); } } @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef GUID_H_INCLUDED -#define GUID_H_INCLUDED +#ifndef GUID_H_INCLUDED_80425780237502345 +#define GUID_H_INCLUDED_80425780237502345 #include <string> #include <boost/uuid/uuid.hpp> @@ -35,4 +35,4 @@ std::string generateGUID() //creates a 16 byte GUID } } -#endif // GUID_H_INCLUDED +#endif //GUID_H_INCLUDED_80425780237502345 @@ -37,7 +37,7 @@ struct TranslationHandler virtual std::wstring translate(const std::wstring& singular, const std::wstring& plural, std::int64_t n) = 0; }; -void setTranslator(TranslationHandler* newHandler = nullptr); //takes ownership +void setTranslator(std::unique_ptr<TranslationHandler>&& newHandler = nullptr); //take ownership TranslationHandler* getTranslator(); @@ -58,7 +58,10 @@ namespace implementation inline std::wstring translate(const std::wstring& text) { - return getTranslator() ? getTranslator()->translate(text) : text; + if (TranslationHandler* t = getTranslator()) + return t->translate(text); + + return text; } //translate plural forms: "%x day" "%x days" @@ -68,13 +71,13 @@ std::wstring translate(const std::wstring& singular, const std::wstring& plural, { assert(contains(plural, L"%x")); - if (getTranslator()) + if (TranslationHandler* t = getTranslator()) { - std::wstring translation = getTranslator()->translate(singular, plural, n); + std::wstring translation = t->translate(singular, plural, n); assert(!contains(translation, L"%x")); return translation; } - else + return replaceCpy(std::abs(n) == 1 ? singular : plural, L"%x", toGuiString(n)); } @@ -94,7 +97,7 @@ std::unique_ptr<TranslationHandler>& globalHandler() } inline -void setTranslator(TranslationHandler* newHandler) { implementation::globalHandler().reset(newHandler); } //takes ownership +void setTranslator(std::unique_ptr<TranslationHandler>&& newHandler) { implementation::globalHandler() = std::move(newHandler); } inline TranslationHandler* getTranslator() { return implementation::globalHandler().get(); } diff --git a/zen/int64.h b/zen/int64.h index 4183c8da..671f3372 100644 --- a/zen/int64.h +++ b/zen/int64.h @@ -18,29 +18,29 @@ namespace zen { #ifdef ZEN_WIN inline - std::int64_t get64BitInt(DWORD low, LONG high) - { - static_assert(sizeof(low) + sizeof(high) == sizeof(std::int64_t), ""); +std::int64_t get64BitInt(DWORD low, LONG high) +{ + static_assert(sizeof(low) + sizeof(high) == sizeof(std::int64_t), ""); - LARGE_INTEGER cvt = {}; - cvt.LowPart = low; - cvt.HighPart = high; - return cvt.QuadPart; - } + LARGE_INTEGER cvt = {}; + cvt.LowPart = low; + cvt.HighPart = high; + return cvt.QuadPart; +} std::int64_t get64BitInt(std::uint64_t low, std::int64_t high) = delete; inline std::uint64_t get64BitUInt(DWORD low, DWORD high) - { - static_assert(sizeof(low) + sizeof(high) == sizeof(std::uint64_t), ""); +{ + static_assert(sizeof(low) + sizeof(high) == sizeof(std::uint64_t), ""); - ULARGE_INTEGER cvt = {}; - cvt.LowPart = low; - cvt.HighPart = high; - return cvt.QuadPart; - } + ULARGE_INTEGER cvt = {}; + cvt.LowPart = low; + cvt.HighPart = high; + return cvt.QuadPart; +} std::int64_t get64BitUInt(std::uint64_t low, std::uint64_t high) = delete; @@ -52,15 +52,15 @@ std::int64_t get64BitUInt(std::uint64_t low, std::uint64_t high) = delete; inline std::int64_t filetimeToTimeT(const FILETIME& ft) { - return static_cast<std::int64_t>(get64BitUInt(ft.dwLowDateTime, ft.dwHighDateTime) / 10000000U) - get64BitInt(3054539008UL, 2); //caveat: signed/unsigned arithmetics! + return static_cast<std::int64_t>(get64BitUInt(ft.dwLowDateTime, ft.dwHighDateTime) / 10000000U) - get64BitInt(3054539008UL, 2); //caveat: signed/unsigned arithmetics! //timeshift between ansi C time and FILETIME in seconds == 11644473600s } inline FILETIME timetToFileTime(std::int64_t utcTime) -{ - ULARGE_INTEGER cvt = {}; - cvt.QuadPart = (utcTime + get64BitInt(3054539008UL, 2)) * 10000000U; //caveat: signed/unsigned arithmetics! +{ + ULARGE_INTEGER cvt = {}; + cvt.QuadPart = (utcTime + get64BitInt(3054539008UL, 2)) * 10000000U; //caveat: signed/unsigned arithmetics! const FILETIME output = { cvt.LowPart, cvt.HighPart }; return output; diff --git a/zen/notify_removal.cpp b/zen/notify_removal.cpp index 45a39c48..639264a6 100644 --- a/zen/notify_removal.cpp +++ b/zen/notify_removal.cpp @@ -169,7 +169,7 @@ private: Pimpl (const Pimpl&) = delete; Pimpl& operator=(const Pimpl&) = delete; - virtual void onMessage(UINT message, WPARAM wParam, LPARAM lParam) //throw()! + void onMessage(UINT message, WPARAM wParam, LPARAM lParam) override //throw()! { //DBT_DEVICEQUERYREMOVE example: http://msdn.microsoft.com/en-us/library/aa363427(v=VS.85).aspx if (message == WM_DEVICECHANGE) @@ -205,10 +205,7 @@ private: //#################################################################################################### -NotifyRequestDeviceRemoval::NotifyRequestDeviceRemoval(HANDLE hDir) -{ - pimpl.reset(new Pimpl(*this, hDir)); -} +NotifyRequestDeviceRemoval::NotifyRequestDeviceRemoval(HANDLE hDir) : pimpl(zen::make_unique<Pimpl>(*this, hDir)) {} NotifyRequestDeviceRemoval::~NotifyRequestDeviceRemoval() {} //make sure ~unique_ptr() works with complete type diff --git a/zen/notify_removal.h b/zen/notify_removal.h index aca0912f..ba560f96 100644 --- a/zen/notify_removal.h +++ b/zen/notify_removal.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef NOTIFY_H_INCLUDED -#define NOTIFY_H_INCLUDED +#ifndef NOTIFY_H_INCLUDED_257804267562 +#define NOTIFY_H_INCLUDED_257804267562 #include <memory> #include "win.h" //includes "windows.h" @@ -24,12 +24,11 @@ private: //NOTE: onRemovalFinished is NOT guaranteed to execute after onRequestRemoval()! but most likely will virtual void onRemovalFinished(HANDLE hnd, bool successful) = 0; //throw()! - NotifyRequestDeviceRemoval(NotifyRequestDeviceRemoval&); //no copying - void operator=(NotifyRequestDeviceRemoval&); // + NotifyRequestDeviceRemoval (NotifyRequestDeviceRemoval&) = delete; + NotifyRequestDeviceRemoval& operator=(NotifyRequestDeviceRemoval&) = delete; class Pimpl; std::unique_ptr<Pimpl> pimpl; }; - -#endif // NOTIFY_H_INCLUDED +#endif //NOTIFY_H_INCLUDED_257804267562 diff --git a/zen/osx_string.h b/zen/osx_string.h deleted file mode 100644 index ba83ca27..00000000 --- a/zen/osx_string.h +++ /dev/null @@ -1,82 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef OSX_STRING_1873641732143214324 -#define OSX_STRING_1873641732143214324 - -#include <CoreFoundation/CoreFoundation.h> //CFString -#include "zstring.h" - -namespace osx -{ -Zstring cfStringToZstring(const CFStringRef& cfStr); - -CFStringRef createCFString (const char* utf8Str); //returns nullptr on error -CFMutableStringRef createMutableCFString(const char* utf8Str); //pass ownership! => ZEN_ON_SCOPE_EXIT(::CFRelease(str)); - - - - - - - - - - - - - -//################# implementation ##################### -inline -Zstring cfStringToZstring(const CFStringRef& cfStr) -{ - if (cfStr) - { - //perf: try to get away cheap: - if (const char* utf8Str = ::CFStringGetCStringPtr(cfStr, kCFStringEncodingUTF8)) - return utf8Str; - - CFIndex length = ::CFStringGetLength(cfStr); - if (length > 0) - { - CFIndex bufferSize = ::CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8); - Zstring buffer; - buffer.resize(bufferSize); - - if (::CFStringGetCString(cfStr, &*buffer.begin(), bufferSize, kCFStringEncodingUTF8)) - { - buffer.resize(zen::strLength(buffer.c_str())); //caveat: memory consumption of returned string! - return buffer; - } - } - } - return Zstring(); -} - - -inline -CFStringRef createCFString(const char* utf8Str) -{ - //don't bother with CFStringCreateWithBytes: it's slightly slower, despite passing length info - return ::CFStringCreateWithCString(nullptr, //CFAllocatorRef alloc, - utf8Str, //const char *cStr, - kCFStringEncodingUTF8); //CFStringEncoding encoding -} - - -inline -CFMutableStringRef createMutableCFString(const char* utf8Str) -{ - if (CFMutableStringRef strRef = ::CFStringCreateMutable(NULL, 0)) - { - ::CFStringAppendCString(strRef, utf8Str, kCFStringEncodingUTF8); - return strRef; - } - return nullptr; -} -} - -#endif //OSX_STRING_1873641732143214324 diff --git a/zen/osx_throw_exception.h b/zen/osx_throw_exception.h deleted file mode 100644 index 07e3af3e..00000000 --- a/zen/osx_throw_exception.h +++ /dev/null @@ -1,56 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef OSX_EXCEPTION_89274305834255 -#define OSX_EXCEPTION_89274305834255 - -#import <Cocoa/Cocoa.h> -#include "sys_error.h" -#include "utf.h" - -namespace osx -{ -//for use in Objective C implementation files only! -void throwSysError(NSException* e); //throw SysError - -#define ZEN_OSX_ASSERT(obj) ZEN_OSX_ASSERT_IMPL(obj, #obj) //throw SysError -/* -Example: ZEN_OSX_ASSERT(obj); - -Equivalent to: - if (!obj) - throw zen::SysError(L"Assertion failed: \"obj\"."); -*/ - - - - - - -//######################## implmentation ############################ -inline -void throwSysError(NSException* e) //throw SysError -{ - std::string msg; - if (const char* name = [[e name ] cStringUsingEncoding:NSUTF8StringEncoding]) //"const char*" NOT owned by us! - msg += name; - if (const char* descr = [[e reason] cStringUsingEncoding:NSUTF8StringEncoding]) - { - msg += "\n"; - msg += descr; - } - throw zen::SysError(zen::utfCvrtTo<std::wstring>(msg)); - /* - e.g. - NSInvalidArgumentException - *** +[NSString stringWithCString:encoding:]: NULL cString - */ -} -} - -#define ZEN_OSX_ASSERT_IMPL(obj, txt) if (!(obj)) throw zen::SysError(std::wstring(L"Assertion failed: \"") + L ## txt + L"\"."); - -#endif //OSX_EXCEPTION_89274305834255 diff --git a/zen/privilege.cpp b/zen/privilege.cpp deleted file mode 100644 index c2db4701..00000000 --- a/zen/privilege.cpp +++ /dev/null @@ -1,144 +0,0 @@ -#include "privilege.h" -#include <map> -//#include <mutex> -#include "win.h" //includes "windows.h" -#include "thread.h" -#include "zstring.h" -#include "scope_guard.h" -#include "win_ver.h" - -using namespace zen; - - -namespace -{ -bool privilegeIsActive(const wchar_t* privilege) //throw FileError -{ - HANDLE hToken = nullptr; - if (!::OpenProcessToken(::GetCurrentProcess(), //__in HANDLE ProcessHandle, - TOKEN_QUERY, //__in DWORD DesiredAccess, - &hToken)) //__out PHANDLE TokenHandle - throwFileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\""), L"OpenProcessToken", getLastError()); - ZEN_ON_SCOPE_EXIT(::CloseHandle(hToken)); - - LUID luid = {}; - if (!::LookupPrivilegeValue(nullptr, //__in_opt LPCTSTR lpSystemName, - privilege, //__in LPCTSTR lpName, - &luid )) //__out PLUID lpLuid - throwFileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\""), L"LookupPrivilegeValue", getLastError()); - - PRIVILEGE_SET priv = {}; - priv.PrivilegeCount = 1; - priv.Control = PRIVILEGE_SET_ALL_NECESSARY; - priv.Privilege[0].Luid = luid; - priv.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED; - - BOOL alreadyGranted = FALSE; - if (!::PrivilegeCheck(hToken, //__in HANDLE ClientToken, - &priv, //__inout PPRIVILEGE_SET RequiredPrivileges, - &alreadyGranted)) //__out LPBOOL pfResult - throwFileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\""), L"PrivilegeCheck", getLastError()); - - return alreadyGranted != FALSE; -} - - -void setPrivilege(const wchar_t* privilege, bool enable) //throw FileError -{ - HANDLE hToken = nullptr; - if (!::OpenProcessToken(::GetCurrentProcess(), //__in HANDLE ProcessHandle, - TOKEN_ADJUST_PRIVILEGES, //__in DWORD DesiredAccess, - &hToken)) //__out PHANDLE TokenHandle - throwFileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\""), L"OpenProcessToken", getLastError()); - ZEN_ON_SCOPE_EXIT(::CloseHandle(hToken)); - - LUID luid = {}; - if (!::LookupPrivilegeValue(nullptr, //__in_opt LPCTSTR lpSystemName, - privilege, //__in LPCTSTR lpName, - &luid )) //__out PLUID lpLuid - throwFileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\""), L"LookupPrivilegeValue", getLastError()); - - TOKEN_PRIVILEGES tp = {}; - tp.PrivilegeCount = 1; - tp.Privileges[0].Luid = luid; - tp.Privileges[0].Attributes = enable ? SE_PRIVILEGE_ENABLED : 0; - - if (!::AdjustTokenPrivileges(hToken, //__in HANDLE TokenHandle, - false, //__in BOOL DisableAllPrivileges, - &tp, //__in_opt PTOKEN_PRIVILEGES NewState, - 0, //__in DWORD BufferLength, - nullptr, //__out_opt PTOKEN_PRIVILEGES PreviousState, - nullptr)) //__out_opt PDWORD ReturnLength - throwFileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\""), L"AdjustTokenPrivileges", getLastError()); - - DWORD lastError = ::GetLastError(); //copy before directly or indirectly making other system calls! - if (lastError == ERROR_NOT_ALL_ASSIGNED) //check although previous function returned with success! - { -#ifdef __MINGW32__ //Shobjidl.h -#define ERROR_ELEVATION_REQUIRED 740L -#endif - if (vistaOrLater()) //replace this useless error code with what it *really* means! - lastError = ERROR_ELEVATION_REQUIRED; - - throwFileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\""), L"AdjustTokenPrivileges", lastError); - } -} - - -class Privileges -{ -public: - static Privileges& getInstance() - { - //meyers singleton: avoid static initialization order problem in global namespace! - static Privileges inst; - return inst; - } - - void ensureActive(const wchar_t* privilege) //throw FileError - { - boost::lock_guard<boost::mutex> dummy(lockPrivileges); - - if (activePrivileges.find(privilege) != activePrivileges.end()) - return; //privilege already active - - if (privilegeIsActive(privilege)) //privilege was already active before starting this tool - activePrivileges.insert(std::make_pair(privilege, false)); - else - { - setPrivilege(privilege, true); - activePrivileges.insert(std::make_pair(privilege, true)); - } - } - -private: - Privileges() {} - Privileges (const Privileges&) = delete; - Privileges& operator=(const Privileges&) = delete; - - ~Privileges() //clean up: deactivate all privileges that have been activated by this application - { - for (const auto& priv : activePrivileges) - if (priv.second) - { - try - { - setPrivilege(priv.first.c_str(), false); //throw FileError - } - catch (FileError&) {} - } - } - - std::map<Zstring, bool> activePrivileges; //bool: enabled by this application -boost::mutex lockPrivileges; -}; - -//caveat: function scope static initialization is not thread-safe in VS 2010! -auto& dummy = Privileges::getInstance(); -} - - -void zen::activatePrivilege(const wchar_t* privilege) //throw FileError -{ - Privileges::getInstance().ensureActive(privilege); -} diff --git a/zen/privilege.h b/zen/privilege.h deleted file mode 100644 index e9b83be9..00000000 --- a/zen/privilege.h +++ /dev/null @@ -1,17 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef PRIVILEGE_H_INCLUDED -#define PRIVILEGE_H_INCLUDED - -#include "file_error.h" - -namespace zen -{ -void activatePrivilege(const wchar_t* privilege); //throw FileError; thread-safe!!! -} - -#endif // PRIVILEGE_H_INCLUDED diff --git a/zen/recycler.cpp b/zen/recycler.cpp index 930b05ac..5b5e44d4 100644 --- a/zen/recycler.cpp +++ b/zen/recycler.cpp @@ -5,12 +5,11 @@ // ************************************************************************** #include "recycler.h" -#include "file_handling.h" +#include "file_access.h" #ifdef ZEN_WIN #include "thread.h" #include "dll.h" -#include "assert_static.h" #include "win_ver.h" #include "long_path_prefix.h" #include "IFileOperation/file_op.h" @@ -46,11 +45,10 @@ Nevertheless, let's use IFileOperation for better error reporting (including det struct CallbackData { CallbackData(const std::function<void (const Zstring& currentItem)>& notifyDeletionStatus) : - notifyDeletionStatus_(notifyDeletionStatus), - exceptionInUserCallback(false) {} + notifyDeletionStatus_(notifyDeletionStatus) {} - const std::function<void (const Zstring& currentItem)>& notifyDeletionStatus_; //optional! - bool exceptionInUserCallback; + const std::function<void (const Zstring& currentItem)>& notifyDeletionStatus_; //in, optional + std::exception_ptr exception; //out }; @@ -65,7 +63,7 @@ bool onRecyclerCallback(const wchar_t* itempath, void* sink) } catch (...) { - cbd.exceptionInUserCallback = true; //try again outside the C call stack! + cbd.exception = std::current_exception(); return false; } return true; @@ -85,8 +83,8 @@ void zen::recycleOrDelete(const std::vector<Zstring>& itempaths, const std::func if (vistaOrLater()) //new recycle bin usage: available since Vista { #define DEF_DLL_FUN(name) const DllFun<fileop::FunType_##name> name(fileop::getDllName(), fileop::funName_##name); -DEF_DLL_FUN(moveToRecycleBin); -DEF_DLL_FUN(getLastErrorMessage); + DEF_DLL_FUN(moveToRecycleBin); + DEF_DLL_FUN(getLastErrorMessage); #undef DEF_DLL_FUN if (!moveToRecycleBin || !getLastErrorMessage) @@ -100,12 +98,8 @@ DEF_DLL_FUN(getLastErrorMessage); CallbackData cbd(notifyDeletionStatus); if (!moveToRecycleBin(&cNames[0], cNames.size(), onRecyclerCallback, &cbd)) { - if (cbd.exceptionInUserCallback) - { - assert(notifyDeletionStatus); - notifyDeletionStatus(Zstring()); //should throw again!!! - assert(false); - } + if (cbd.exception) + std::rethrow_exception(cbd.exception); std::wstring itempathFmt = fmtFileName(itempaths[0]); //probably not the correct file name for file lists larger than 1! if (itempaths.size() > 1) @@ -188,7 +182,7 @@ bool zen::recycleOrDelete(const Zstring& itempath) //throw FileError #elif defined ZEN_MAC //we cannot use FSPathMoveObjectToTrashSync directly since it follows symlinks! - assert_static(sizeof(Zchar) == sizeof(char)); + static_assert(sizeof(Zchar) == sizeof(char), ""); const UInt8* itempathUtf8 = reinterpret_cast<const UInt8*>(itempath.c_str()); auto throwFileError = [&](OSStatus oss) diff --git a/zen/recycler.h b/zen/recycler.h index e900dfa3..b406408f 100644 --- a/zen/recycler.h +++ b/zen/recycler.h @@ -39,7 +39,6 @@ bool recycleOrDelete(const Zstring& itempath); //throw FileError, return "true" bool recycleBinExists(const Zstring& pathName, const std::function<void ()>& onUpdateGui); //throw FileError void recycleOrDelete(const std::vector<Zstring>& filepaths, //throw FileError, return "true" if file/dir was actually deleted - //may throw: first exception is swallowed, updateStatus() is then called again where it should throw again and the exception will propagate as expected const std::function<void (const Zstring& currentItem)>& notifyDeletionStatus); //optional; currentItem may be empty #endif } diff --git a/zen/scope_guard.h b/zen/scope_guard.h index d48eb922..761c6aea 100644 --- a/zen/scope_guard.h +++ b/zen/scope_guard.h @@ -40,8 +40,8 @@ protected: bool isDismissed() const { return dismissed_; } private: - ScopeGuardBase (const ScopeGuardBase&); // = delete - ScopeGuardBase& operator=(const ScopeGuardBase&); // + ScopeGuardBase (const ScopeGuardBase&) = delete; + ScopeGuardBase& operator=(const ScopeGuardBase&) = delete; bool dismissed_; }; @@ -52,7 +52,7 @@ class ScopeGuardImpl : public ScopeGuardBase { public: explicit ScopeGuardImpl(const F& fun) : fun_(fun) {} - explicit ScopeGuardImpl(F&& fun) : fun_(std::move(fun)) {} + explicit ScopeGuardImpl( F&& fun) : fun_(std::move(fun)) {} ScopeGuardImpl(ScopeGuardImpl&& other) : ScopeGuardBase(std::move(other)), fun_(std::move(other.fun_)) {} ~ScopeGuardImpl() diff --git a/zen/serialize.h b/zen/serialize.h index 64df0329..cd427c9c 100644 --- a/zen/serialize.h +++ b/zen/serialize.h @@ -7,6 +7,7 @@ #ifndef SERIALIZE_H_INCLUDED_83940578357 #define SERIALIZE_H_INCLUDED_83940578357 +#include <functional> #include <cstdint> #include "string_base.h" #include "file_io.h" @@ -54,8 +55,8 @@ private: //---------------------------------------------------------------------- //functions based on binary container abstraction -template <class BinContainer> void saveBinStream(const Zstring& filepath, const BinContainer& cont); //throw FileError -template <class BinContainer> BinContainer loadBinStream(const Zstring& filepath); //throw FileError +template <class BinContainer> void saveBinStream(const Zstring& filepath, const BinContainer& cont, const std::function<void(std::int64_t bytesDelta)>& onUpdateStatus); //throw FileError +template <class BinContainer> BinContainer loadBinStream(const Zstring& filepath, const std::function<void(std::int64_t bytesDelta)>& onUpdateStatus); //throw FileError /* @@ -135,20 +136,38 @@ template < class BinInputStream> void readArray (BinInputStream& stre //-----------------------implementation------------------------------- template <class BinContainer> inline -void saveBinStream(const Zstring& filepath, const BinContainer& cont) //throw FileError +void saveBinStream(const Zstring& filepath, //throw FileError + const BinContainer& cont, + const std::function<void(std::int64_t bytesDelta)>& onUpdateStatus) //optional { - assert_static(sizeof(typename BinContainer::value_type) == 1); //expect: bytes (until further) + static_assert(sizeof(typename BinContainer::value_type) == 1, ""); //expect: bytes (until further) FileOutput fileOut(filepath, zen::FileOutput::ACC_OVERWRITE); //throw FileError if (!cont.empty()) - fileOut.write(&*cont.begin(), cont.size()); //throw FileError + { + const size_t blockSize = 128 * 1024; + auto bytePtr = &*cont.begin(); + size_t bytesLeft = cont.size(); + + while (bytesLeft > blockSize) + { + fileOut.write(bytePtr, blockSize); //throw FileError + bytePtr += blockSize; + bytesLeft -= blockSize; + if (onUpdateStatus) onUpdateStatus(blockSize); + } + + fileOut.write(bytePtr, bytesLeft); //throw FileError + if (onUpdateStatus) onUpdateStatus(bytesLeft); + } } template <class BinContainer> inline -BinContainer loadBinStream(const Zstring& filepath) //throw FileError +BinContainer loadBinStream(const Zstring& filepath, //throw FileError + const std::function<void(std::int64_t bytesDelta)>& onUpdateStatus) //optional { - assert_static(sizeof(typename BinContainer::value_type) == 1); //expect: bytes (until further) + static_assert(sizeof(typename BinContainer::value_type) == 1, ""); //expect: bytes (until further) FileInput fileIn(filepath); //throw FileError @@ -161,6 +180,8 @@ BinContainer loadBinStream(const Zstring& filepath) //throw FileError const size_t bytesRead = fileIn.read(&*contOut.begin() + contOut.size() - blockSize, blockSize); //throw FileError if (bytesRead < blockSize) contOut.resize(contOut.size() - (blockSize - bytesRead)); //caveat: unsigned arithmetics + + if (onUpdateStatus) onUpdateStatus(bytesRead); } while (!fileIn.eof()); @@ -180,7 +201,7 @@ void writeArray(BinOutputStream& stream, const void* data, size_t len) template <class N, class BinOutputStream> inline void writeNumber(BinOutputStream& stream, const N& num) { - assert_static((IsArithmetic<N>::value || IsSameType<N, bool>::value)); + static_assert(IsArithmetic<N>::value || IsSameType<N, bool>::value, ""); writeArray(stream, &num, sizeof(N)); } @@ -198,7 +219,7 @@ void writeContainer(BinOutputStream& stream, const C& cont) //don't even conside template <class BinInputStream> inline void readArray(BinInputStream& stream, void* data, size_t len) //throw UnexpectedEndOfStreamError { - //expect external write of len bytes: + //expect external write of len bytes: const char* const src = static_cast<const char*>(stream.requestRead(len)); //throw UnexpectedEndOfStreamError std::copy(src, src + len, static_cast<char*>(data)); } @@ -207,7 +228,7 @@ void readArray(BinInputStream& stream, void* data, size_t len) //throw Unexpecte template <class N, class BinInputStream> inline N readNumber(BinInputStream& stream) //throw UnexpectedEndOfStreamError { - assert_static((IsArithmetic<N>::value || IsSameType<N, bool>::value)); + static_assert(IsArithmetic<N>::value || IsSameType<N, bool>::value, ""); N num = 0; readArray(stream, &num, sizeof(N)); //throw UnexpectedEndOfStreamError return num; @@ -219,8 +240,8 @@ C readContainer(BinInputStream& stream) //throw UnexpectedEndOfStreamError { C cont; auto strLength = readNumber<std::uint32_t>(stream); - if (strLength > 0) - { + if (strLength > 0) + { try { cont.resize(strLength); //throw std::bad_alloc @@ -229,8 +250,8 @@ C readContainer(BinInputStream& stream) //throw UnexpectedEndOfStreamError { throw UnexpectedEndOfStreamError(); } - readArray(stream, &*cont.begin(), sizeof(typename C::value_type) * strLength); //throw UnexpectedEndOfStreamError - } + readArray(stream, &*cont.begin(), sizeof(typename C::value_type) * strLength); //throw UnexpectedEndOfStreamError + } return cont; } } diff --git a/zen/shell_execute.h b/zen/shell_execute.h index 9f99315a..c2ccd837 100644 --- a/zen/shell_execute.h +++ b/zen/shell_execute.h @@ -31,7 +31,7 @@ enum ExecutionType namespace { -void shellExecute2(const Zstring& command, ExecutionType type) //throw FileError +void shellExecute(const Zstring& command, ExecutionType type) //throw FileError { #ifdef ZEN_WIN //parse commandline diff --git a/zen/stl_tools.h b/zen/stl_tools.h index 38752e67..eb94b4a1 100644 --- a/zen/stl_tools.h +++ b/zen/stl_tools.h @@ -9,13 +9,6 @@ #include <memory> #include <algorithm> -#if defined _MSC_VER && _MSC_VER <= 1600 -#include <set> -#include <map> -#else -#include <unordered_set> -#include <unordered_map> -#endif //enhancements for <algorithm> @@ -38,8 +31,8 @@ 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); +template <class ForwardIterator, class T, typename CompLess> +ForwardIterator binary_search(ForwardIterator first, ForwardIterator last, const T& value, CompLess less); template <class BidirectionalIterator, class T> BidirectionalIterator find_last(BidirectionalIterator first, BidirectionalIterator last, const T& value); @@ -53,26 +46,9 @@ template <class InputIterator1, class InputIterator2> bool equal(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2); -//hash container: proper name + mitigate MSVC performance bug -template <class T> class hash_set; -template <class K, class V> class hash_map; - -template<typename T, typename Arg1> -std::unique_ptr<T> make_unique(Arg1&& arg1); //should eventually make it into the std at some time - - - - - - - - - - - - - - +//until std::make_unique is available in GCC: +template <class T, class... Args> inline +std::unique_ptr<T> make_unique(Args&& ... args) { return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); } @@ -127,11 +103,11 @@ V& map_add_or_update(M& map, const K& key, const V& value) //efficient add or up } -template <class ForwardIterator, class T, typename Compare> inline -ForwardIterator binary_search(ForwardIterator first, ForwardIterator last, const T& value, Compare comp) +template <class ForwardIterator, class T, typename CompLess> inline +ForwardIterator binary_search(ForwardIterator first, ForwardIterator last, const T& value, CompLess less) { - first = std::lower_bound(first, last, value, comp); - if (first != last && !comp(value, *first)) + first = std::lower_bound(first, last, value, less); + if (first != last && !less(value, *first)) return first; else return last; @@ -187,32 +163,10 @@ bool equal(InputIterator1 first1, InputIterator1 last1, } -#if defined _MSC_VER && _MSC_VER <= 1600 //VS2010 performance bug in std::unordered_set<>: http://drdobbs.com/blogs/cpp/232200410 -> should be fixed in VS11 -template <class T> class hash_set : public std::set<T> {}; -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>; +#if defined _MSC_VER && _MSC_VER <= 1600 +//VS2010 performance bug in std::unordered_set<>: http://drdobbs.com/blogs/cpp/232200410 -> should be fixed in VS11 +static_assert(false, ""); #endif - -//as long as variadic templates are not available in MSVC -template<class T> inline std::unique_ptr<T> make_unique() { return std::unique_ptr<T>(new T); } -template<class T, class Arg1> inline std::unique_ptr<T> make_unique(Arg1&& arg1) { return std::unique_ptr<T>(new T(std::forward<Arg1>(arg1))); } -template<class T, class Arg1, class Arg2> inline std::unique_ptr<T> make_unique(Arg1&& arg1, Arg2&& arg2) { return std::unique_ptr<T>(new T(std::forward<Arg1>(arg1), std::forward<Arg2>(arg2))); } -template<class T, class Arg1, class Arg2, class Arg3> inline std::unique_ptr<T> make_unique(Arg1&& arg1, Arg2&& arg2, Arg3&& arg3) { return std::unique_ptr<T>(new T(std::forward<Arg1>(arg1), std::forward<Arg2>(arg2), std::forward<Arg3>(arg3))); } -template<class T, class Arg1, class Arg2, class Arg3, class Arg4> inline std::unique_ptr<T> make_unique(Arg1&& arg1, Arg2&& arg2, Arg3&& arg3, Arg4&& arg4) { return std::unique_ptr<T>(new T(std::forward<Arg1>(arg1), std::forward<Arg2>(arg2), std::forward<Arg3>(arg3), std::forward<Arg4>(arg4))); } -template<class T, class Arg1, class Arg2, class Arg3, class Arg4, class Arg5> inline std::unique_ptr<T> make_unique(Arg1&& arg1, Arg2&& arg2, Arg3&& arg3, Arg4&& arg4, Arg5&& arg5) { return std::unique_ptr<T>(new T(std::forward<Arg1>(arg1), std::forward<Arg2>(arg2), std::forward<Arg3>(arg3), std::forward<Arg4>(arg4), std::forward<Arg5>(arg5))); } -template<class T, class Arg1, class Arg2, class Arg3, class Arg4, class Arg5, class Arg6> inline std::unique_ptr<T> make_unique(Arg1&& arg1, Arg2&& arg2, Arg3&& arg3, Arg4&& arg4, Arg5&& arg5, Arg6&& arg6) { return std::unique_ptr<T>(new T(std::forward<Arg1>(arg1), std::forward<Arg2>(arg2), std::forward<Arg3>(arg3), std::forward<Arg4>(arg4), std::forward<Arg5>(arg5), std::forward<Arg6>(arg6))); } -template<class T, class Arg1, class Arg2, class Arg3, class Arg4, class Arg5, class Arg6, class Arg7> inline std::unique_ptr<T> make_unique(Arg1&& arg1, Arg2&& arg2, Arg3&& arg3, Arg4&& arg4, Arg5&& arg5, Arg6&& arg6, Arg7&& arg7) { return std::unique_ptr<T>(new T(std::forward<Arg1>(arg1), std::forward<Arg2>(arg2), std::forward<Arg3>(arg3), std::forward<Arg4>(arg4), std::forward<Arg5>(arg5), std::forward<Arg6>(arg6), std::forward<Arg7>(arg7))); } - -//template<typename T, typename ...Args> inline -//std::unique_ptr<T> make_unique(Args&& ...args) -//{ -// return std::unique_ptr<T>(new T( std::forward<Args>(args)... )); -//} } #endif //STL_TOOLS_HEADER_84567184321434 diff --git a/zen/string_base.h b/zen/string_base.h index a458ef15..0f9ad479 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) //must handle deallocate(nullptr)! + void deallocate(void* ptr) size_t calcCapacity(size_t length) */ class AllocatorOptimalSpeed //exponential growth + min size @@ -53,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) //must handle destroy(nullptr)! + 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) @@ -66,29 +66,34 @@ class StorageDeepCopy : public AP protected: ~StorageDeepCopy() {} - static Char* create(size_t size) { return create(size, size); } - static Char* create(size_t size, size_t minCapacity) + Char* create(size_t size) { return create(size, size); } + Char* create(size_t size, size_t minCapacity) { assert(size <= minCapacity); const size_t newCapacity = AP::calcCapacity(minCapacity); assert(newCapacity >= minCapacity); - Descriptor* const newDescr = static_cast<Descriptor*>(AP::allocate(sizeof(Descriptor) + (newCapacity + 1) * sizeof(Char))); - - newDescr->length = size; - newDescr->capacity = newCapacity; + Descriptor* const newDescr = static_cast<Descriptor*>(this->allocate(sizeof(Descriptor) + (newCapacity + 1) * sizeof(Char))); //throw std::bad_alloc + new (newDescr) Descriptor(size, newCapacity); return reinterpret_cast<Char*>(newDescr + 1); //alignment note: "newDescr + 1" is Descriptor-aligned, which is larger than alignment for Char-array! => no problem! } - static Char* clone(Char* ptr) + Char* clone(Char* ptr) { - Char* newData = create(length(ptr)); + Char* newData = create(length(ptr)); //throw std::bad_alloc std::copy(ptr, ptr + length(ptr) + 1, newData); return newData; } - static void destroy(Char* ptr) { AP::deallocate(descr(ptr)); } //should support destroy(nullptr)! + void destroy(Char* ptr) + { + if (!ptr) return; //support "destroy(nullptr)" + + Descriptor* const d = descr(ptr); + d->~Descriptor(); + this->deallocate(d); + } //this needs to be checked before writing to "ptr" static bool canWrite(const Char* ptr, size_t minCapacity) { return minCapacity <= descr(ptr)->capacity; } @@ -103,6 +108,10 @@ protected: private: struct Descriptor { + Descriptor(size_t len, size_t cap) : + length (static_cast<std::uint32_t>(len)), + capacity(static_cast<std::uint32_t>(cap)) {} + std::uint32_t length; std::uint32_t capacity; //allocated size without null-termination }; @@ -119,42 +128,44 @@ class StorageRefCountThreadSafe : public AP protected: ~StorageRefCountThreadSafe() {} - static Char* create(size_t size) { return create(size, size); } - static Char* create(size_t size, size_t minCapacity) + Char* create(size_t size) { return create(size, size); } + Char* create(size_t size, size_t minCapacity) { assert(size <= minCapacity); const size_t newCapacity = AP::calcCapacity(minCapacity); assert(newCapacity >= minCapacity); - Descriptor* const newDescr = static_cast<Descriptor*>(AP::allocate(sizeof(Descriptor) + (newCapacity + 1) * sizeof(Char))); - new (newDescr) Descriptor(1, size, newCapacity); + Descriptor* const newDescr = static_cast<Descriptor*>(this->allocate(sizeof(Descriptor) + (newCapacity + 1) * sizeof(Char))); //throw std::bad_alloc + new (newDescr) Descriptor(size, newCapacity); return reinterpret_cast<Char*>(newDescr + 1); } static Char* clone(Char* ptr) { - assert(descr(ptr)->refCount > 0); ++descr(ptr)->refCount; return ptr; } - static void destroy(Char* ptr) + 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! + if (!ptr) return; //support "destroy(nullptr)" + + Descriptor* const d = descr(ptr); + + if (--(d->refCount) == 0) //operator--() is overloaded to decrement and evaluate in a single atomic operation! { - descr(ptr)->~Descriptor(); - AP::deallocate(descr(ptr)); + d->~Descriptor(); + this->deallocate(d); } } static bool canWrite(const Char* ptr, size_t minCapacity) //needs to be checked before writing to "ptr" { - assert(descr(ptr)->refCount > 0); - return descr(ptr)->refCount == 1 && minCapacity <= descr(ptr)->capacity; + const Descriptor* const d = descr(ptr); + assert(d->refCount > 0); + return d->refCount == 1 && minCapacity <= d->capacity; } static size_t length(const Char* ptr) { return descr(ptr)->length; } @@ -168,14 +179,14 @@ protected: private: struct Descriptor { - Descriptor(int rc, size_t len, size_t cap) : + Descriptor(size_t len, size_t cap) : + refCount(1), length (static_cast<std::uint32_t>(len)), - capacity(static_cast<std::uint32_t>(cap)), - refCount(rc) { assert_static(ATOMIC_INT_LOCK_FREE == 2); } //2: "the types are always lock-free" + capacity(static_cast<std::uint32_t>(cap)) { static_assert(ATOMIC_INT_LOCK_FREE == 2, ""); } //2: "the types are always lock-free" + std::atomic<unsigned int> refCount; std::uint32_t length; std::uint32_t capacity; //allocated size without null-termination - std::atomic<int> refCount; //practically no perf loss: ~0.2%! (FFS comparison) }; static Descriptor* descr( Char* ptr) { return reinterpret_cast< Descriptor*>(ptr) - 1; } @@ -237,7 +248,7 @@ public: Zbase& assign(const Char* source, size_t len); Zbase& append(const Char* source, size_t len); void resize(size_t newSize, Char fillChar = 0); - void swap(Zbase& other); + void swap(Zbase& other); //make noexcept in C++11 void push_back(Char val) { operator+=(val); } //STL access Zbase& operator=(const Zbase& source); @@ -251,10 +262,10 @@ public: static const size_t npos = static_cast<size_t>(-1); private: - Zbase(int); // - Zbase& operator=(int); //detect usage errors by creating an intentional ambiguity with "Char" - Zbase& operator+=(int); // - void push_back(int); // + Zbase (int) = delete; // + Zbase& operator= (int) = delete; //detect usage errors by creating an intentional ambiguity with "Char" + Zbase& operator+=(int) = delete; // + void push_back (int) = delete; // Char* rawStr; }; @@ -626,8 +637,7 @@ Zbase<Char, SP, AP>& Zbase<Char, SP, AP>::operator=(const Zbase<Char, SP, AP>& o template <class Char, template <class, class> class SP, class AP> inline Zbase<Char, SP, AP>& Zbase<Char, SP, AP>::operator=(Zbase<Char, SP, AP>&& tmp) { - //don't use unifying assignment but save one move-construction in the r-value case instead! - swap(tmp); + swap(tmp); //don't use unifying assignment but save one move-construction in the r-value case instead! return *this; } diff --git a/zen/string_tools.h b/zen/string_tools.h index a0b02d14..addf2bd5 100644 --- a/zen/string_tools.h +++ b/zen/string_tools.h @@ -82,7 +82,7 @@ bool isWhiteSpace(wchar_t ch) { return std::iswspace(ch) != 0; } template <class Char> inline bool isDigit(Char ch) //similar to implmenetation of std::::isdigit()! { - assert_static((IsSameType<Char, char>::value || IsSameType<Char, wchar_t>::value)); + static_assert(IsSameType<Char, char>::value || IsSameType<Char, wchar_t>::value, ""); return static_cast<Char>('0') <= ch && ch <= static_cast<Char>('9'); } @@ -90,7 +90,7 @@ bool isDigit(Char ch) //similar to implmenetation of std::::isdigit()! template <class S, class T> inline bool startsWith(const S& str, const T& prefix) { - assert_static(IsStringLike<S>::value && IsStringLike<T>::value); + static_assert(IsStringLike<S>::value && IsStringLike<T>::value, ""); typedef typename GetCharType<S>::Type CharType; const size_t pfLength = strLength(prefix); @@ -106,7 +106,7 @@ bool startsWith(const S& str, const T& prefix) template <class S, class T> inline bool endsWith(const S& str, const T& postfix) { - assert_static(IsStringLike<S>::value && IsStringLike<T>::value); + static_assert(IsStringLike<S>::value && IsStringLike<T>::value, ""); typedef typename GetCharType<S>::Type CharType; const size_t strLen = strLength(str); @@ -123,7 +123,7 @@ bool endsWith(const S& str, const T& postfix) template <class S, class T> inline bool contains(const S& str, const T& term) { - assert_static(IsStringLike<S>::value && IsStringLike<T>::value); + static_assert(IsStringLike<S>::value && IsStringLike<T>::value, ""); typedef typename GetCharType<S>::Type CharType; const size_t strLen = strLength(str); @@ -144,7 +144,7 @@ bool contains(const S& str, const T& term) template <class S, class T> inline S afterLast(const S& str, const T& term) { - assert_static(IsStringLike<T>::value); + static_assert(IsStringLike<T>::value, ""); typedef typename GetCharType<S>::Type CharType; const size_t termLen = strLength(term); @@ -167,7 +167,7 @@ S afterLast(const S& str, const T& term) template <class S, class T> inline S beforeLast(const S& str, const T& term) { - assert_static(IsStringLike<T>::value); + static_assert(IsStringLike<T>::value, ""); typedef typename GetCharType<S>::Type CharType; const CharType* const strFirst = strBegin(str); @@ -187,7 +187,7 @@ S beforeLast(const S& str, const T& term) template <class S, class T> inline S afterFirst(const S& str, const T& term) { - assert_static(IsStringLike<T>::value); + static_assert(IsStringLike<T>::value, ""); typedef typename GetCharType<S>::Type CharType; const size_t termLen = strLength(term); @@ -209,7 +209,7 @@ S afterFirst(const S& str, const T& term) template <class S, class T> inline S beforeFirst(const S& str, const T& term) { - assert_static(IsStringLike<T>::value); + static_assert(IsStringLike<T>::value, ""); typedef typename GetCharType<S>::Type CharType; const CharType* const strFirst = strBegin(str); @@ -223,7 +223,7 @@ S beforeFirst(const S& str, const T& term) template <class S, class T> inline std::vector<S> split(const S& str, const T& delimiter) { - assert_static(IsStringLike<T>::value); + static_assert(IsStringLike<T>::value, ""); typedef typename GetCharType<S>::Type CharType; std::vector<S> output; @@ -245,7 +245,7 @@ std::vector<S> split(const S& str, const T& delimiter) const CharType* const blockEnd = std::search(blockStart, strLast, delimFirst, delimLast); - output.push_back(S(blockStart, blockEnd - blockStart)); + output.emplace_back(blockStart, blockEnd - blockStart); if (blockEnd == strLast) break; blockStart = blockEnd + delimLen; @@ -271,7 +271,7 @@ typename EnableIf<!HasMember_append<S>::value>::Type stringAppend(S& str, const template <class S, class T, class U> inline S replaceCpy(const S& str, const T& oldTerm, const U& newTerm, bool replaceAll) { - assert_static(IsStringLike<T>::value && IsStringLike<U>::value); + static_assert(IsStringLike<T>::value && IsStringLike<U>::value, ""); typedef typename GetCharType<S>::Type CharType; const size_t oldLen = strLength(oldTerm); @@ -395,10 +395,10 @@ int saferPrintf(wchar_t* buffer, size_t bufferSize, const wchar_t* format, const template <class S, class T, class Num> inline S printNumber(const T& format, const Num& number) //format a single number using ::sprintf { - assert_static(IsStringLike<T>::value); - assert_static((IsSameType< - typename GetCharType<S>::Type, - typename GetCharType<T>::Type>::value)); + static_assert(IsStringLike<T>::value, ""); + static_assert(IsSameType< + typename GetCharType<S>::Type, + typename GetCharType<T>::Type>::value, ""); typedef typename GetCharType<S>::Type CharType; diff --git a/zen/string_traits.h b/zen/string_traits.h index 69c1fbc8..ba97397c 100644 --- a/zen/string_traits.h +++ b/zen/string_traits.h @@ -8,8 +8,6 @@ #define STRING_TRAITS_HEADER_813274321443234 #include "type_tools.h" -#include "assert_static.h" - //uniform access to string-like types, both classes and character arrays namespace zen @@ -165,7 +163,11 @@ namespace implementation template <class C> inline size_t cStringLength(const C* str) //naive implementation seems somewhat faster than "optimized" strlen/wcslen! { - assert_static((IsSameType<C, char>::value || IsSameType<C, wchar_t>::value)); +#if defined _MSC_VER && _MSC_VER > 1800 + static_assert(false, "strlen/wcslen are vectorized in VS14 CTP3 -> test again!"); +#endif + + static_assert(IsSameType<C, char>::value || IsSameType<C, wchar_t>::value, ""); size_t len = 0; while (*str++ != 0) ++len; @@ -184,8 +186,8 @@ inline const char* strBegin(const char* str) { return str; } inline const wchar_t* strBegin(const wchar_t* str) { return str; } inline const char* strBegin(const char& ch) { return &ch; } inline const wchar_t* strBegin(const wchar_t& ch) { return &ch; } -inline const char* strBegin(const StringRef<char >& ref) { return ref.data(); } -inline const wchar_t* strBegin(const StringRef<wchar_t>& ref) { return ref.data(); } +inline const char* strBegin(const StringRef<char >& ref) { return ref.data(); } +inline const wchar_t* strBegin(const StringRef<wchar_t>& ref) { return ref.data(); } template <class S> inline diff --git a/zen/sys_error.h b/zen/sys_error.h index 8afab138..9cfc762f 100644 --- a/zen/sys_error.h +++ b/zen/sys_error.h @@ -35,7 +35,7 @@ typedef int ErrorCode; ErrorCode getLastError(); std::wstring formatSystemError(const std::wstring& functionName, ErrorCode lastError); - +std::wstring formatSystemError(const std::wstring& functionName, ErrorCode lastError, const std::wstring& lastErrorMsg); //A low-level exception class giving (non-translated) detail information only - same conceptional level like "GetLastError()"! class SysError @@ -75,11 +75,7 @@ std::wstring formatSystemError(const std::wstring& functionName, ErrorCode lastE { const ErrorCode currentError = getLastError(); //not necessarily == lastError - //determine error code if none was specified - if (lastError == 0) - lastError = currentError; - - std::wstring output = replaceCpy(_("Error Code %x:"), L"%x", numberTo<std::wstring>(lastError)); + std::wstring lastErrorMsg; #ifdef ZEN_WIN ZEN_ON_SCOPE_EXIT(::SetLastError(currentError)); //this function must not change active system error variable! @@ -92,22 +88,37 @@ std::wstring formatSystemError(const std::wstring& functionName, ErrorCode lastE if (buffer) //"don't trust nobody" { ZEN_ON_SCOPE_EXIT(::LocalFree(buffer)); - output += L" "; - output += buffer; + lastErrorMsg = buffer; } #elif defined ZEN_LINUX || defined ZEN_MAC ZEN_ON_SCOPE_EXIT(errno = currentError); - output += L" "; - output += utfCvrtTo<std::wstring>(::strerror(lastError)); + lastErrorMsg = utfCvrtTo<std::wstring>(::strerror(lastError)); #endif + + return formatSystemError(functionName, lastError, lastErrorMsg); +} + + +inline +std::wstring formatSystemError(const std::wstring& functionName, ErrorCode lastError, const std::wstring& lastErrorMsg) +{ + std::wstring output = replaceCpy(_("Error Code %x:"), L"%x", numberTo<std::wstring>(lastError)); + + if (!lastErrorMsg.empty()) + { + output += L" "; + output += lastErrorMsg; + } + if (!endsWith(output, L" ")) //Windows messages seem to end with a blank... output += L" "; output += L"(" + functionName + L")"; return output; } + } #endif //SYS_ERROR_H_3284791347018951324534 diff --git a/zen/thread.h b/zen/thread.h index 8c72e43f..a834f070 100644 --- a/zen/thread.h +++ b/zen/thread.h @@ -88,13 +88,15 @@ private: #error just some paranoia check... #endif -template <class T, class Function> inline -auto async2(Function fun) -> boost::unique_future<T> //support for workaround of VS2010 bug: bool (*fun)(); decltype(fun()) == int! +template <class Function> inline +auto async(Function fun) -> boost::unique_future<decltype(fun())> { + typedef decltype(fun()) ResultType; + #if defined BOOST_THREAD_PROVIDES_SIGNATURE_PACKAGED_TASK //mirror "boost/thread/future.hpp", hopefully they know what they're doing - boost::packaged_task<T()> pt(std::move(fun)); //packaged task seems to even require r-value reference: https://sourceforge.net/p/freefilesync/bugs/234/ + boost::packaged_task<ResultType()> pt(std::move(fun)); //packaged task seems to even require r-value reference: https://sourceforge.net/p/freefilesync/bugs/234/ #else - boost::packaged_task<T> pt(std::move(fun)); + boost::packaged_task<ResultType> pt(std::move(fun)); #endif auto fut = pt.get_future(); boost::thread(std::move(pt)).detach(); //we have to explicitly detach since C++11: [thread.thread.destr] ~thread() calls std::terminate() if joinable()!!! @@ -102,10 +104,6 @@ auto async2(Function fun) -> boost::unique_future<T> //support for workaround of } -template <class Function> inline -auto async(Function fun) -> boost::unique_future<decltype(fun())> { return async2<decltype(fun())>(fun); } - - template<class InputIterator, class Duration> inline bool wait_for_all_timed(InputIterator first, InputIterator last, const Duration& wait_duration) { diff --git a/zen/tick_count.h b/zen/tick_count.h index 482689d7..56179b62 100644 --- a/zen/tick_count.h +++ b/zen/tick_count.h @@ -64,9 +64,9 @@ public: #ifdef ZEN_WIN return numeric::dist(lhs.val_.QuadPart, rhs.val_.QuadPart); //std::abs(a - b) can lead to overflow! #elif defined ZEN_LINUX -//structure timespec documented with members: -// time_t tv_sec seconds -// long tv_nsec nanoseconds + //structure timespec documented with members: + // time_t tv_sec seconds + // long tv_nsec nanoseconds const int64_t deltaSec = lhs.val_.tv_sec - rhs.val_.tv_sec; const int64_t deltaNsec = lhs.val_.tv_nsec - rhs.val_.tv_nsec; return numeric::abs(deltaSec * 1000000000 + deltaNsec); @@ -113,9 +113,9 @@ int64_t ticksPerSec() //return 0 on error mach_timebase_info_data_t tbi = {}; if (::mach_timebase_info(&tbi) != KERN_SUCCESS) return 0; -//structure mach_timebase_info_data_t documented with members: -// uint32_t numer; -// uint32_t denom; + //structure mach_timebase_info_data_t documented with members: + // uint32_t numer; + // uint32_t denom; return static_cast<int64_t>(1000000000) * tbi.denom / tbi.numer; #endif } @@ -126,10 +126,10 @@ TickVal getTicks() //return !isValid() on error { #ifdef ZEN_WIN LARGE_INTEGER now = {}; - if (!::QueryPerformanceCounter(&now)) + if (!::QueryPerformanceCounter(&now)) return TickVal(); - //detailed info about QPC: http://msdn.microsoft.com/en-us/library/windows/desktop/dn553408%28v=vs.85%29.aspx - //- MSDN: "No need to set the thread affinity" + //detailed info about QPC: http://msdn.microsoft.com/en-us/library/windows/desktop/dn553408%28v=vs.85%29.aspx + //- MSDN: "No need to set the thread affinity" #elif defined ZEN_LINUX //gettimeofday() seems fine but is deprecated @@ -49,8 +49,8 @@ const struct FormatIsoDateTimeTag {} FORMAT_ISO_DATE_TIME = {}; //%Y-%m-%d %H:%M //---------------------------------------------------------------------------------------------------------------------------------- -template <class String> -bool parseTime(const String& format, const String& str, TimeComp& comp); //similar to ::strptime(), return true on success +template <class String, class String2> +bool parseTime(const String& format, const String2& str, TimeComp& comp); //similar to ::strptime(), return true on success //---------------------------------------------------------------------------------------------------------------------------------- @@ -272,10 +272,11 @@ String formatTime(const String2& format, const TimeComp& comp) } -template <class String> -bool parseTime(const String& format, const String& str, TimeComp& comp) //return true on success +template <class String, class String2> +bool parseTime(const String& format, const String2& str, TimeComp& comp) //return true on success { typedef typename GetCharType<String>::Type CharType; + static_assert(IsSameType<CharType, typename GetCharType<String2>::Type>::value, ""); const CharType* iterFmt = strBegin(format); const CharType* const fmtLast = iterFmt + strLength(format); @@ -285,7 +286,7 @@ bool parseTime(const String& format, const String& str, TimeComp& comp) //return auto extractNumber = [&](int& result, size_t digitCount) -> bool { - if (strLast - iterStr < digitCount) + if (strLast - iterStr < makeSigned(digitCount)) return false; if (std::any_of(iterStr, iterStr + digitCount, [](CharType c) { return !isDigit(c); })) @@ -104,7 +104,7 @@ size_t getUtf16Len(Char16 ch) //ch must be first code unit! returns 0 on error! template <class CharIterator, class Function> inline void utf16ToCodePoint(CharIterator first, CharIterator last, Function writeOutput) //"writeOutput" is a unary function taking a CodePoint { - assert_static(sizeof(typename std::iterator_traits<CharIterator>::value_type) == 2); + static_assert(sizeof(typename std::iterator_traits<CharIterator>::value_type) == 2, ""); for ( ; first != last; ++first) { @@ -201,7 +201,7 @@ bool decodeTrail(CharIterator& first, CharIterator last, CodePoint& cp) //decode template <class CharIterator, class Function> inline void utf8ToCodePoint(CharIterator first, CharIterator last, Function writeOutput) //"writeOutput" is a unary function taking a CodePoint { - assert_static(sizeof(typename std::iterator_traits<CharIterator>::value_type) == 1); + static_assert(sizeof(typename std::iterator_traits<CharIterator>::value_type) == 1, ""); for ( ; first != last; ++first) { @@ -337,12 +337,12 @@ size_t findUnicodePosWide(const WideString& str, size_t unicodePos, Int2Type<2>) if (utfPos >= strLen) return strLen; - size_t utf16len = getUtf16Len(strFirst[utfPos]); + size_t utf16len = getUtf16Len(strFirst[utfPos]); if (utf16len == 0) ++utf16len; //invalid utf16 character utfPos += utf16len; } - if (utfPos >= strLen) - return strLen; + if (utfPos >= strLen) + return strLen; return utfPos; } @@ -416,8 +416,8 @@ CharString wideToUtf8(const WideString& str, Int2Type<4>) //other OS: convert ut template <class WideString, class CharString> inline WideString utf8ToWide(const CharString& str) { - assert_static((IsSameType<typename GetCharType<CharString>::Type, char >::value)); - assert_static((IsSameType<typename GetCharType<WideString>::Type, wchar_t>::value)); + static_assert(IsSameType<typename GetCharType<CharString>::Type, char >::value, ""); + static_assert(IsSameType<typename GetCharType<WideString>::Type, wchar_t>::value, ""); return implementation::utf8ToWide<WideString>(str, Int2Type<sizeof(wchar_t)>()); } @@ -426,8 +426,8 @@ WideString utf8ToWide(const CharString& str) template <class CharString, class WideString> inline CharString wideToUtf8(const WideString& str) { - assert_static((IsSameType<typename GetCharType<CharString>::Type, char >::value)); - assert_static((IsSameType<typename GetCharType<WideString>::Type, wchar_t>::value)); + static_assert(IsSameType<typename GetCharType<CharString>::Type, char >::value, ""); + static_assert(IsSameType<typename GetCharType<WideString>::Type, wchar_t>::value, ""); return implementation::wideToUtf8<CharString>(str, Int2Type<sizeof(wchar_t)>()); } diff --git a/zen/win_ver.h b/zen/win_ver.h index 0d3f8d70..1d7ce7f0 100644 --- a/zen/win_ver.h +++ b/zen/win_ver.h @@ -7,24 +7,26 @@ #ifndef WINDOWS_VERSION_HEADER_238470348254325 #define WINDOWS_VERSION_HEADER_238470348254325 +#include <cassert> #include <utility> #include "win.h" //includes "windows.h" namespace zen { - struct OsVersion - { - OsVersion() : major(), minor() {} - OsVersion(DWORD high, DWORD low) : major(high), minor(low) {} +struct OsVersion +{ + OsVersion() : major(), minor() {} + OsVersion(DWORD high, DWORD low) : major(high), minor(low) {} - DWORD major; - DWORD minor; - }; - inline bool operator< (const OsVersion& lhs, const OsVersion& rhs) { return lhs.major != rhs.major ? lhs.major < rhs.major : lhs.minor < rhs.minor; } - inline bool operator==(const OsVersion& lhs, const OsVersion& rhs) { return lhs.major == rhs.major && lhs.minor == rhs.minor; } + DWORD major; + DWORD minor; +}; +inline bool operator< (const OsVersion& lhs, const OsVersion& rhs) { return lhs.major != rhs.major ? lhs.major < rhs.major : lhs.minor < rhs.minor; } +inline bool operator==(const OsVersion& lhs, const OsVersion& rhs) { return lhs.major == rhs.major && lhs.minor == rhs.minor; } //version overview: http://msdn.microsoft.com/en-us/library/ms724834(VS.85).aspx +const OsVersion osVersionWin10 (6, 4); const OsVersion osVersionWin81 (6, 3); const OsVersion osVersionWin8 (6, 2); const OsVersion osVersionWin7 (6, 1); @@ -61,11 +63,18 @@ OsVersion getOsVersion() { OSVERSIONINFO osvi = {}; osvi.dwOSVersionInfoSize = sizeof(osvi); +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4996) //"'GetVersionExW': was declared deprecated" +#endif if (!::GetVersionEx(&osvi)) //38 ns per call! (yes, that's nano!) -> we do NOT miss C++11 thread-safe statics right now... - { - assert(false); - return OsVersion(); - } +#ifdef _MSC_VER +#pragma warning(pop) +#endif + { + assert(false); + return OsVersion(); + } return OsVersion(osvi.dwMajorVersion, osvi.dwMinorVersion); } @@ -78,16 +87,16 @@ bool isRealOsVersion(const OsVersion& ver) verInfo.dwMajorVersion = ver.major; verInfo.dwMinorVersion = ver.minor; - //Syntax: http://msdn.microsoft.com/en-us/library/windows/desktop/ms725491%28v=vs.85%29.aspx + //Syntax: http://msdn.microsoft.com/en-us/library/windows/desktop/ms725491%28v=vs.85%29.aspx DWORDLONG conditionMask = 0; VER_SET_CONDITION(conditionMask, VER_MAJORVERSION, VER_EQUAL); VER_SET_CONDITION(conditionMask, VER_MINORVERSION, VER_EQUAL); const bool rv = ::VerifyVersionInfo(&verInfo, VER_MAJORVERSION | VER_MINORVERSION, conditionMask) - == TRUE; //silence VC "performance warnings" - assert(rv || GetLastError() == ERROR_OLD_WIN_VERSION); + == TRUE; //silence VC "performance warnings" + assert(rv || GetLastError() == ERROR_OLD_WIN_VERSION); - return rv; + return rv; } } diff --git a/zen/xml_io.cpp b/zen/xml_io.cpp index 485d78bb..9ac4b87f 100644 --- a/zen/xml_io.cpp +++ b/zen/xml_io.cpp @@ -5,7 +5,7 @@ // ************************************************************************** #include "xml_io.h" -#include "file_handling.h" +#include "file_access.h" #include "file_io.h" #include "serialize.h" @@ -66,7 +66,7 @@ void zen::saveXmlDocument(const XmlDoc& doc, const Zstring& filepath) //throw Fi try { if (getFilesize(filepath) == stream.size()) //throw FileError - if (loadBinStream<std::string>(filepath) == stream) //throw FileError + if (loadBinStream<std::string>(filepath, nullptr) == stream) //throw FileError return; } catch (FileError&) {} diff --git a/zen/zstring.cpp b/zen/zstring.cpp index f16af6a0..a2a26f83 100644 --- a/zen/zstring.cpp +++ b/zen/zstring.cpp @@ -6,6 +6,7 @@ #include "zstring.h" #include <stdexcept> +#include <unordered_map> #ifdef ZEN_WIN #include "dll.h" @@ -40,7 +41,7 @@ public: void insert(const void* ptr, size_t size) { boost::lock_guard<boost::mutex> dummy(lockActStrings); - if (!activeStrings.insert(std::make_pair(ptr, size)).second) + if (!activeStrings.emplace(ptr, size).second) reportProblem("Serious Error: New memory points into occupied space: " + rawMemToString(ptr, size)); } @@ -96,7 +97,7 @@ private: } boost::mutex lockActStrings; - zen::hash_map<const void*, size_t> activeStrings; + std::unordered_map<const void*, size_t> activeStrings; }; //caveat: function scope static initialization is not thread-safe in VS 2010! @@ -148,15 +149,15 @@ const SysDllFun<CompareStringOrdinalFunc> compareStringOrdinal = SysDllFun<Compa } -int z_impl::compareFilenamesNoCase(const wchar_t* lhs, const wchar_t* rhs, size_t sizeLhs, size_t sizeRhs) +int cmpFileName(const Zstring& lhs, const Zstring& rhs) { if (compareStringOrdinal) //this additional test has no noticeable performance impact { - const int rv = compareStringOrdinal(lhs, //__in LPCWSTR lpString1, - static_cast<int>(sizeLhs), //__in int cchCount1, - rhs, //__in LPCWSTR lpString2, - static_cast<int>(sizeRhs), //__in int cchCount2, - true); //__in BOOL bIgnoreCase + const int rv = compareStringOrdinal(lhs.c_str(), //__in LPCWSTR lpString1, + static_cast<int>(lhs.size()), //__in int cchCount1, + rhs.c_str(), //__in LPCWSTR lpString2, + static_cast<int>(rhs.size()), //__in int cchCount2, + true); //__in BOOL bIgnoreCase if (rv <= 0) throw std::runtime_error("Error comparing strings (CompareStringOrdinal)."); else @@ -167,31 +168,35 @@ int z_impl::compareFilenamesNoCase(const wchar_t* lhs, const wchar_t* rhs, size_ //do NOT use "CompareString"; this function is NOT accurate (even with LOCALE_INVARIANT and SORT_STRINGSORT): for example "weiß" == "weiss"!!! //the only reliable way to compare filepaths (with XP) is to call "CharUpper" or "LCMapString": - auto copyToUpperCase = [](const wchar_t* strIn, wchar_t* strOut, size_t len) + const size_t sizeLhs = lhs.size(); + const size_t sizeRhs = rhs.size(); + + const auto minSize = std::min(sizeLhs, sizeRhs); + + if (minSize == 0) //LCMapString does not allow input sizes of 0! + return static_cast<int>(sizeLhs) - static_cast<int>(sizeRhs); + + auto copyToUpperCase = [&](const wchar_t* strIn, wchar_t* strOut) { //faster than CharUpperBuff + wmemcpy or CharUpper + wmemcpy and same speed like ::CompareString() - if (::LCMapString(ZSTRING_INVARIANT_LOCALE, //__in LCID Locale, - LCMAP_UPPERCASE, //__in DWORD dwMapFlags, - strIn, //__in LPCTSTR lpSrcStr, - static_cast<int>(len), //__in int cchSrc, - strOut, //__out LPTSTR lpDestStr, - static_cast<int>(len)) == 0) //__in int cchDest + if (::LCMapString(ZSTRING_INVARIANT_LOCALE, //__in LCID Locale, + LCMAP_UPPERCASE, //__in DWORD dwMapFlags, + strIn, //__in LPCTSTR lpSrcStr, + static_cast<int>(minSize), //__in int cchSrc, + strOut, //__out LPTSTR lpDestStr, + static_cast<int>(minSize)) == 0) //__in int cchDest throw std::runtime_error("Error comparing strings (LCMapString)."); }; - const auto minSize = std::min(sizeLhs, sizeRhs); - auto eval = [&](wchar_t* bufL, wchar_t* bufR) { - if (minSize > 0) //LCMapString does not allow input sizes of 0! - { - copyToUpperCase(lhs, bufL, minSize); - copyToUpperCase(rhs, bufR, minSize); - - const int rv = ::wmemcmp(bufL, bufR, minSize); - if (rv != 0) - return rv; - } + copyToUpperCase(lhs.c_str(), bufL); + copyToUpperCase(rhs.c_str(), bufR); + + const int rv = ::wmemcmp(bufL, bufR, minSize); + if (rv != 0) + return rv; + return static_cast<int>(sizeLhs) - static_cast<int>(sizeRhs); }; @@ -203,35 +208,55 @@ int z_impl::compareFilenamesNoCase(const wchar_t* lhs, const wchar_t* rhs, size_ } else //use freestore { - std::vector<wchar_t> bufferL(minSize); - std::vector<wchar_t> bufferR(minSize); - return eval(&bufferL[0], &bufferR[0]); + std::vector<wchar_t> buffer(2 * minSize); + return eval(&buffer[0], &buffer[minSize]); } } } -void z_impl::makeFilenameUpperCase(wchar_t* str, size_t size) +Zstring makeUpperCopy(const Zstring& str) { - if (size == 0) //LCMapString does not allow input sizes of 0! - return; + const int len = static_cast<int>(str.size()); + + if (len == 0) //LCMapString does not allow input sizes of 0! + return str; + + Zstring output; + output.resize(len); //use Windows' upper case conversion: faster than ::CharUpper() - if (::LCMapString(ZSTRING_INVARIANT_LOCALE, LCMAP_UPPERCASE, str, static_cast<int>(size), str, static_cast<int>(size)) == 0) - throw std::runtime_error("Error converting to upper case! (LCMapString)"); + if (::LCMapString(ZSTRING_INVARIANT_LOCALE, //__in LCID Locale, + LCMAP_UPPERCASE, //__in DWORD dwMapFlags, + str.c_str(), //__in LPCTSTR lpSrcStr, + len, //__in int cchSrc, + &*output.begin(), //__out LPTSTR lpDestStr, + len) == 0) //__in int cchDest + throw std::runtime_error("Error comparing strings (LCMapString)."); + + return output; } + #elif defined ZEN_MAC -int z_impl::compareFilenamesNoCase(const char* lhs, const char* rhs, size_t sizeLhs, size_t sizeRhs) +int cmpFileName(const Zstring& lhs, const Zstring& rhs) { - return ::strcasecmp(lhs, rhs); //locale-dependent! + return ::strcasecmp(lhs.c_str(), rhs.c_str()); //locale-dependent! } -void z_impl::makeFilenameUpperCase(char* str, size_t size) +Zstring makeUpperCopy(const Zstring& str) { - std::for_each(str, str + size, [](char& c) { c = static_cast<char>(::toupper(static_cast<unsigned char>(c))); }); //locale-dependent! + const size_t len = str.size(); + + Zstring output; + output.resize(len); + + std::transform(str.begin(), str.end(), output.begin(), [](char c) { return static_cast<char>(::toupper(static_cast<unsigned char>(c))); }); //locale-dependent! + //result of toupper() is an unsigned char mapped to int range, so the char representation is in the last 8 bits and we need not care about signedness! //this should work for UTF-8, too: all chars >= 128 are mapped upon themselves! + + return output; } #endif diff --git a/zen/zstring.h b/zen/zstring.h index 2152954d..94144386 100644 --- a/zen/zstring.h +++ b/zen/zstring.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef ZSTRING_H_INCLUDED -#define ZSTRING_H_INCLUDED +#ifndef ZSTRING_H_INCLUDED_73425873425789 +#define ZSTRING_H_INCLUDED_73425873425789 #include "string_base.h" @@ -66,30 +66,26 @@ typedef zen::Zbase<Zchar, zen::StorageRefCountThreadSafe, AllocatorFreeStoreChec //Compare filepaths: Windows does NOT distinguish between upper/lower-case, while Linux DOES -template <template <class, class> class SP, class AP> -int cmpFileName(const zen::Zbase<Zchar, SP, AP>& lhs, const zen::Zbase<Zchar, SP, AP>& rhs); +int cmpFileName(const Zstring& lhs, const Zstring& rhs); struct LessFilename //case-insensitive on Windows, case-sensitive on Linux { - template <template <class, class> class SP, class AP> - bool operator()(const zen::Zbase<Zchar, SP, AP>& lhs, const zen::Zbase<Zchar, SP, AP>& rhs) const { return cmpFileName(lhs, rhs) < 0; } + bool operator()(const Zstring& lhs, const Zstring& rhs) const { return cmpFileName(lhs, rhs) < 0; } }; struct EqualFilename //case-insensitive on Windows, case-sensitive on Linux { - template <template <class, class> class SP, class AP> - bool operator()(const zen::Zbase<Zchar, SP, AP>& lhs, const zen::Zbase<Zchar, SP, AP>& rhs) const { return cmpFileName(lhs, rhs) == 0; } + bool operator()(const Zstring& lhs, const Zstring& rhs) const { return cmpFileName(lhs, rhs) == 0; } }; #if defined ZEN_WIN || defined ZEN_MAC -template <template <class, class> class SP, class AP> -void makeUpper(zen::Zbase<Zchar, SP, AP>& str); +Zstring makeUpperCopy(const Zstring& str); #endif inline Zstring appendSeparator(Zstring path) //support rvalue references! { - return endsWith(path, FILE_NAME_SEPARATOR) ? path : (path += FILE_NAME_SEPARATOR); + return zen::endsWith(path, FILE_NAME_SEPARATOR) ? path : (path += FILE_NAME_SEPARATOR); } @@ -98,44 +94,13 @@ Zstring appendSeparator(Zstring path) //support rvalue references! //################################# inline implementation ######################################## -namespace z_impl -{ -#if defined ZEN_WIN || defined ZEN_MAC -int compareFilenamesNoCase(const Zchar* lhs, const Zchar* rhs, size_t sizeLhs, size_t sizeRhs); -void makeFilenameUpperCase(Zchar* str, size_t size); -#endif -} - -template <template <class, class> class SP, class AP> inline -int cmpFileName(const zen::Zbase<Zchar, SP, AP>& lhs, const zen::Zbase<Zchar, SP, AP>& rhs) +#ifdef ZEN_LINUX +inline +int cmpFileName(const Zstring& lhs, const Zstring& rhs) { -#if defined ZEN_WIN || defined ZEN_MAC - return z_impl::compareFilenamesNoCase(lhs.data(), rhs.data(), lhs.length(), rhs.length()); -#elif defined ZEN_LINUX return std::strcmp(lhs.c_str(), rhs.c_str()); //POSIX filepaths don't have embedded 0 - //#elif defined ZEN_MAC - // return ::strcasecmp(lhs.c_str(), rhs.c_str()); //locale-dependent! -#endif -} - - -#if defined ZEN_WIN || defined ZEN_MAC -template <template <class, class> class SP, class AP> inline -void makeUpper(zen::Zbase<Zchar, SP, AP>& str) -{ - z_impl::makeFilenameUpperCase(str.begin(), str.length()); } #endif - -namespace std -{ -template<> inline -void swap(Zstring& rhs, Zstring& lhs) -{ - rhs.swap(lhs); -} -} - -#endif //ZSTRING_H_INCLUDED +#endif //ZSTRING_H_INCLUDED_73425873425789 |