diff options
Diffstat (limited to 'zen')
-rw-r--r-- | zen/IFileOperation/file_op.cpp | 20 | ||||
-rw-r--r-- | zen/basic_math.h | 2 | ||||
-rw-r--r-- | zen/debug_memory_leaks.cpp | 4 | ||||
-rw-r--r-- | zen/file_handling.cpp | 120 | ||||
-rw-r--r-- | zen/file_handling.h | 11 | ||||
-rw-r--r-- | zen/file_traverser.h | 6 | ||||
-rw-r--r-- | zen/format_unit.cpp | 4 | ||||
-rw-r--r-- | zen/guid.h | 3 | ||||
-rw-r--r-- | zen/perf.h | 6 | ||||
-rw-r--r-- | zen/process_priority.h | 6 | ||||
-rw-r--r-- | zen/recycler.h | 3 | ||||
-rw-r--r-- | zen/string_base.h | 19 | ||||
-rw-r--r-- | zen/thread.h | 14 |
13 files changed, 125 insertions, 93 deletions
diff --git a/zen/IFileOperation/file_op.cpp b/zen/IFileOperation/file_op.cpp index 5d4cfdc9..0691ac5b 100644 --- a/zen/IFileOperation/file_op.cpp +++ b/zen/IFileOperation/file_op.cpp @@ -84,14 +84,14 @@ public: //IFileOperationProgressSink virtual HRESULT STDMETHODCALLTYPE StartOperations() { return S_OK; } virtual HRESULT STDMETHODCALLTYPE FinishOperations(HRESULT hrResult) { return S_OK; } - virtual HRESULT STDMETHODCALLTYPE PreRenameItem(DWORD dwFlags, __RPC__in_opt IShellItem* psiItem, __RPC__in_opt_string LPCWSTR pszNewName) { return S_OK; } - virtual HRESULT STDMETHODCALLTYPE PostRenameItem(DWORD dwFlags, __RPC__in_opt IShellItem* psiItem, __RPC__in_string LPCWSTR pszNewName, HRESULT hrRename, __RPC__in_opt IShellItem* psiNewlyCreated) { return S_OK; } - virtual HRESULT STDMETHODCALLTYPE PreMoveItem(DWORD dwFlags, __RPC__in_opt IShellItem* psiItem, __RPC__in_opt IShellItem* psiDestinationFolder, __RPC__in_opt_string LPCWSTR pszNewName) { return S_OK; } - virtual HRESULT STDMETHODCALLTYPE PostMoveItem(DWORD dwFlags, __RPC__in_opt IShellItem* psiItem, __RPC__in_opt IShellItem* psiDestinationFolder, __RPC__in_opt_string LPCWSTR pszNewName, HRESULT hrMove, __RPC__in_opt IShellItem* psiNewlyCreated) { return S_OK; } - virtual HRESULT STDMETHODCALLTYPE PreCopyItem(DWORD dwFlags, __RPC__in_opt IShellItem* psiItem, __RPC__in_opt IShellItem* psiDestinationFolder, __RPC__in_opt_string LPCWSTR pszNewName) { return S_OK; } - virtual HRESULT STDMETHODCALLTYPE PostCopyItem(DWORD dwFlags, __RPC__in_opt IShellItem* psiItem, __RPC__in_opt IShellItem* psiDestinationFolder, __RPC__in_opt_string LPCWSTR pszNewName, HRESULT hrCopy, __RPC__in_opt IShellItem* psiNewlyCreated) { return S_OK; } - virtual HRESULT STDMETHODCALLTYPE PreNewItem(DWORD dwFlags, __RPC__in_opt IShellItem* psiDestinationFolder, __RPC__in_opt_string LPCWSTR pszNewName) { return S_OK; } - virtual HRESULT STDMETHODCALLTYPE PostNewItem(DWORD dwFlags, __RPC__in_opt IShellItem* psiDestinationFolder, __RPC__in_opt_string LPCWSTR pszNewName, __RPC__in_opt_string LPCWSTR pszTemplateName, DWORD dwFileAttributes, HRESULT hrNew, __RPC__in_opt IShellItem* psiNewItem) { return S_OK; } + virtual HRESULT STDMETHODCALLTYPE PreRenameItem (DWORD dwFlags, __RPC__in_opt IShellItem* psiItem, __RPC__in_opt_string LPCWSTR pszNewName) { return S_OK; } + virtual HRESULT STDMETHODCALLTYPE PostRenameItem (DWORD dwFlags, __RPC__in_opt IShellItem* psiItem, __RPC__in_string LPCWSTR pszNewName, HRESULT hrRename, __RPC__in_opt IShellItem* psiNewlyCreated) { return S_OK; } + virtual HRESULT STDMETHODCALLTYPE PreMoveItem (DWORD dwFlags, __RPC__in_opt IShellItem* psiItem, __RPC__in_opt IShellItem* psiDestinationFolder, __RPC__in_opt_string LPCWSTR pszNewName) { return S_OK; } + virtual HRESULT STDMETHODCALLTYPE PostMoveItem (DWORD dwFlags, __RPC__in_opt IShellItem* psiItem, __RPC__in_opt IShellItem* psiDestinationFolder, __RPC__in_opt_string LPCWSTR pszNewName, HRESULT hrMove, __RPC__in_opt IShellItem* psiNewlyCreated) { return S_OK; } + virtual HRESULT STDMETHODCALLTYPE PreCopyItem (DWORD dwFlags, __RPC__in_opt IShellItem* psiItem, __RPC__in_opt IShellItem* psiDestinationFolder, __RPC__in_opt_string LPCWSTR pszNewName) { return S_OK; } + virtual HRESULT STDMETHODCALLTYPE PostCopyItem (DWORD dwFlags, __RPC__in_opt IShellItem* psiItem, __RPC__in_opt IShellItem* psiDestinationFolder, __RPC__in_opt_string LPCWSTR pszNewName, HRESULT hrCopy, __RPC__in_opt IShellItem* psiNewlyCreated) { return S_OK; } + virtual HRESULT STDMETHODCALLTYPE PreNewItem (DWORD dwFlags, __RPC__in_opt IShellItem* psiDestinationFolder, __RPC__in_opt_string LPCWSTR pszNewName) { return S_OK; } + virtual HRESULT STDMETHODCALLTYPE PostNewItem (DWORD dwFlags, __RPC__in_opt IShellItem* psiDestinationFolder, __RPC__in_opt_string LPCWSTR pszNewName, __RPC__in_opt_string LPCWSTR pszTemplateName, DWORD dwFileAttributes, HRESULT hrNew, __RPC__in_opt IShellItem* psiNewItem) { return S_OK; } virtual HRESULT STDMETHODCALLTYPE PreDeleteItem(DWORD dwFlags, __RPC__in_opt IShellItem* psiItem) { @@ -138,8 +138,8 @@ public: //=> defer cancellation to PreDeleteItem()/PostDeleteItem() return S_OK; } - virtual HRESULT STDMETHODCALLTYPE ResetTimer() { return S_OK; } - virtual HRESULT STDMETHODCALLTYPE PauseTimer() { return S_OK; } + virtual HRESULT STDMETHODCALLTYPE ResetTimer () { return S_OK; } + virtual HRESULT STDMETHODCALLTYPE PauseTimer () { return S_OK; } virtual HRESULT STDMETHODCALLTYPE ResumeTimer() { return S_OK; } //call after IFileOperation::PerformOperations() diff --git a/zen/basic_math.h b/zen/basic_math.h index f8a7affd..d83a7f77 100644 --- a/zen/basic_math.h +++ b/zen/basic_math.h @@ -146,7 +146,7 @@ T confineCpy(const T& val, const T& minVal, const T& maxVal) } template <class T> inline -void confine(T& val, const T& minVal, const T& maxVal) //name trim? +void confine(T& val, const T& minVal, const T& maxVal) //name trim, clamp? { assert(minVal <= maxVal); if (val < minVal) diff --git a/zen/debug_memory_leaks.cpp b/zen/debug_memory_leaks.cpp index 990f2ec7..2359b6ef 100644 --- a/zen/debug_memory_leaks.cpp +++ b/zen/debug_memory_leaks.cpp @@ -8,7 +8,7 @@ //Usage: just include this file into a Visual Studio project -#ifndef NDEBUG +#ifdef _DEBUG //When _DEBUG is not defined, calls to _CrtSetDbgFlag are removed during preprocessing. #define _CRTDBG_MAP_ALLOC // #include <stdlib.h> //keep this order: "The #include statements must be in the order shown here. If you change the order, the functions you use may not work properly." #include <crtdbg.h> //overwrites "operator new" ect; no need to include this in every compilation unit! @@ -27,4 +27,4 @@ struct OnStartup } dummy; } -#endif
\ No newline at end of file +#endif //_DEBUG
\ No newline at end of file diff --git a/zen/file_handling.cpp b/zen/file_handling.cpp index c052435a..4f34814f 100644 --- a/zen/file_handling.cpp +++ b/zen/file_handling.cpp @@ -479,8 +479,7 @@ Zstring findUnused8Dot3Name(const Zstring& filename) //find a unique 8.3 short n if (!somethingExists(output)) //ensure uniqueness return output; } - - throw std::runtime_error(std::string("100000000 files, one for each number, exist in this directory? You're kidding...\n") + utfCvrtTo<std::string>(pathPrefix)); + throw std::runtime_error(std::string("100000000 files, one for each number, exist in this directory? You're kidding...") + utfCvrtTo<std::string>(pathPrefix)); } @@ -566,10 +565,12 @@ void zen::renameFile(const Zstring& oldName, const Zstring& newName) //throw Fil } -class FilesDirsOnlyTraverser : public zen::TraverseCallback +namespace +{ +class CollectFilesFlat : public zen::TraverseCallback { public: - FilesDirsOnlyTraverser(std::vector<Zstring>& files, std::vector<Zstring>& dirs) : + CollectFilesFlat(std::vector<Zstring>& files, std::vector<Zstring>& dirs) : m_files(files), m_dirs(dirs) {} @@ -593,19 +594,17 @@ public: virtual HandleError onError(const std::wstring& msg) { throw FileError(msg); } private: - FilesDirsOnlyTraverser(const FilesDirsOnlyTraverser&); - FilesDirsOnlyTraverser& operator=(const FilesDirsOnlyTraverser&); + CollectFilesFlat(const CollectFilesFlat&); + CollectFilesFlat& operator=(const CollectFilesFlat&); std::vector<Zstring>& m_files; std::vector<Zstring>& m_dirs; }; -void zen::removeDirectory(const Zstring& directory, CallbackRemoveDir* callback) +void removeDirectoryImpl(const Zstring& directory, CallbackRemoveDir* callback) //throw FileError { - //no error situation if directory is not existing! manual deletion relies on it! - if (!somethingExists(directory)) - return; //neither directory nor any other object (e.g. broken symlink) with that name existing + assert(somethingExists(directory)); //[!] #ifdef FFS_WIN const Zstring directoryFmt = applyLongPathPrefix(directory); //support for \\?\-prefix @@ -617,55 +616,66 @@ void zen::removeDirectory(const Zstring& directory, CallbackRemoveDir* callback) //attention: check if directory is a symlink! Do NOT traverse into it deleting contained files!!! if (symlinkExists(directory)) //remove symlink directly { + if (callback) callback->onBeforeDirDeletion(directory); //once per symlink #ifdef FFS_WIN if (!::RemoveDirectory(directoryFmt.c_str())) #elif defined FFS_LINUX if (::unlink(directory.c_str()) != 0) #endif throw FileError(replaceCpy(_("Cannot delete directory %x."), L"%x", fmtFileName(directory)) + L"\n\n" + getLastErrorFormatted()); - - if (callback) - callback->notifyDirDeletion(directory); //once per symlink - return; } - - std::vector<Zstring> fileList; - std::vector<Zstring> dirList; + else { - //get all files and directories from current directory (WITHOUT subdirectories!) - FilesDirsOnlyTraverser traverser(fileList, dirList); - traverseFolder(directory, traverser); //don't follow symlinks - } + std::vector<Zstring> fileList; + std::vector<Zstring> dirList; + { + //get all files and directories from current directory (WITHOUT subdirectories!) + CollectFilesFlat cff(fileList, dirList); + traverseFolder(directory, cff); //don't follow symlinks + } - //delete directories recursively - for (auto it = dirList.begin(); it != dirList.end(); ++it) - removeDirectory(*it, callback); //call recursively to correctly handle symbolic links + //delete directories recursively + std::for_each(dirList.begin(), dirList.end(), + [&](const Zstring& dirname) + { + removeDirectoryImpl(dirname, callback); //throw FileError; call recursively to correctly handle symbolic links + }); - //delete files - for (auto it = fileList.begin(); it != fileList.end(); ++it) - { - const bool workDone = removeFile(*it); - if (callback && workDone) - callback->notifyFileDeletion(*it); //call once per file - } + //delete files + std::for_each(fileList.begin(), fileList.end(), + [&](const Zstring& filename) + { + if (callback) callback->onBeforeFileDeletion(filename); //call once per file + removeFile(filename); //throw FileError + }); - //parent directory is deleted last + //parent directory is deleted last + if (callback) callback->onBeforeDirDeletion(directory); //and once per folder #ifdef FFS_WIN - if (!::RemoveDirectory(directoryFmt.c_str())) + if (!::RemoveDirectory(directoryFmt.c_str())) #else - if (::rmdir(directory.c_str()) != 0) + if (::rmdir(directory.c_str()) != 0) #endif - throw FileError(replaceCpy(_("Cannot delete directory %x."), L"%x", fmtFileName(directory)) + L"\n\n" + getLastErrorFormatted()); - //may spuriously fail with ERROR_DIR_NOT_EMPTY(145) even though all child items have - //successfully been *marked* for deletion, but some application still has a handle open! - //e.g. Open "C:\Test\Dir1\Dir2" (filled with lots of files) in Explorer, then delete "C:\Test\Dir1" via ::RemoveDirectory() => Error 145 - //Sample code: http://us.generation-nt.com/answer/createfile-directory-handles-removing-parent-help-29126332.html - - if (callback) - callback->notifyDirDeletion(directory); //and once per folder + throw FileError(replaceCpy(_("Cannot delete directory %x."), L"%x", fmtFileName(directory)) + L"\n\n" + getLastErrorFormatted()); + //may spuriously fail with ERROR_DIR_NOT_EMPTY(145) even though all child items have + //successfully been *marked* for deletion, but some application still has a handle open! + //e.g. Open "C:\Test\Dir1\Dir2" (filled with lots of files) in Explorer, then delete "C:\Test\Dir1" via ::RemoveDirectory() => Error 145 + //Sample code: http://us.generation-nt.com/answer/createfile-directory-handles-removing-parent-help-29126332.html + } +} } +void zen::removeDirectory(const Zstring& directory, CallbackRemoveDir* callback) +{ + //no error situation if directory is not existing! manual deletion relies on it! + if (!somethingExists(directory)) + return; //neither directory nor any other object (e.g. broken symlink) with that name existing + removeDirectoryImpl(directory, callback); +} + + + void zen::setFileTime(const Zstring& filename, const Int64& modificationTime, ProcSymlink procSl) //throw FileError { #ifdef FFS_WIN @@ -917,11 +927,6 @@ void zen::setFileTime(const Zstring& filename, const Int64& modificationTime, Pr assert(::CompareFileTime(&creationTimeDbg, &creationTime) == 0); assert(::CompareFileTime(&lastWriteTimeDbg, &lastWriteTime) == 0); } - //CAVEAT on FAT/FAT32: the sequence of deleting the target file and renaming "file.txt.ffs_tmp" to "file.txt" seems to - //NOT PRESERVE the creation time of the .ffs_tmp file, but "reuses" whatever creation time the old "file.txt" had! - //this problem is therefore NOT detected by the check above! - //However during the next comparison the DST hack will be applied correctly. - #endif #elif defined FFS_LINUX @@ -2303,6 +2308,27 @@ void zen::copyFile(const Zstring& sourceFile, //throw FileError, ErrorTargetPath //perf: this call is REALLY expensive on unbuffered volumes! ~40% performance decrease on FAT USB stick! renameFile(temporary, targetFile); //throw FileError + /* + CAVEAT on FAT/FAT32: the sequence of deleting the target file and renaming "file.txt.ffs_tmp" to "file.txt" does + NOT PRESERVE the creation time of the .ffs_tmp file, but SILENTLY "reuses" whatever creation time the old "file.txt" had! + 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 diff --git a/zen/file_handling.h b/zen/file_handling.h index e9e1685d..5739dc2a 100644 --- a/zen/file_handling.h +++ b/zen/file_handling.h @@ -47,7 +47,7 @@ UInt64 getFilesize(const Zstring& filename); //throw FileError UInt64 getFreeDiskSpace(const Zstring& path); //throw FileError //file handling -bool removeFile(const Zstring& filename); //throw FileError; return "true" if file was actually deleted +bool removeFile(const Zstring& filename); //throw FileError; return "false" if file is not existing void removeDirectory(const Zstring& directory, CallbackRemoveDir* callback = nullptr); //throw FileError //rename file or directory: no copying!!! @@ -57,7 +57,7 @@ bool supportsPermissions(const Zstring& dirname); //throw FileError, derefernces //creates superdirectories automatically: void makeDirectory(const Zstring& directory); //throw FileError; do nothing if directory already exists! -void makeNewDirectory(const Zstring& directory, const Zstring& templateDir, bool copyFilePermissions); //FileError, ErrorTargetExisting +void makeNewDirectory(const Zstring& directory, const Zstring& templateDir, bool copyFilePermissions); //throw FileError, ErrorTargetExisting struct FileAttrib { @@ -85,16 +85,15 @@ void copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool copy struct CallbackRemoveDir { virtual ~CallbackRemoveDir() {} - virtual void notifyFileDeletion(const Zstring& filename) = 0; //one call for each (existing) object! - virtual void notifyDirDeletion (const Zstring& dirname ) = 0; // + virtual void onBeforeFileDeletion(const Zstring& filename) = 0; //one call for each *existing* object! + virtual void onBeforeDirDeletion (const Zstring& dirname ) = 0; // }; - struct CallbackCopyFile { virtual ~CallbackCopyFile() {} - //if target is existing user needs to implement deletion: copyFile() NEVER deletes target if already existing! + //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. virtual void deleteTargetFile(const Zstring& targetFile) = 0; //may throw exceptions diff --git a/zen/file_traverser.h b/zen/file_traverser.h index d8a99a4d..c8ef6550 100644 --- a/zen/file_traverser.h +++ b/zen/file_traverser.h @@ -12,7 +12,6 @@ #include "int64.h" #include "file_id_def.h" - //advanced file traverser returning metadata and hierarchical information on files and directories namespace zen @@ -25,8 +24,8 @@ struct TraverseCallback { UInt64 fileSize; //unit: bytes! Int64 lastWriteTimeRaw; //number of seconds since Jan. 1st 1970 UTC - FileId id; //optional: may be initial! - //bool isFollowedSymlink; + FileId id; //optional: initial if not supported! + //std::unique_ptr<SymlinkInfo> symlinkInfo; //only filled if file is dereferenced symlink }; struct SymlinkInfo @@ -48,7 +47,6 @@ struct TraverseCallback ON_ERROR_IGNORE }; - //overwrite these virtual methods virtual std::shared_ptr<TraverseCallback> //nullptr: ignore directory, non-nullptr: traverse into using the (new) callback /**/ onDir (const Zchar* shortName, const Zstring& fullName) = 0; virtual void onFile (const Zchar* shortName, const Zstring& fullName, const FileInfo& details) = 0; diff --git a/zen/format_unit.cpp b/zen/format_unit.cpp index 8e5b04d3..6455029f 100644 --- a/zen/format_unit.cpp +++ b/zen/format_unit.cpp @@ -199,7 +199,7 @@ private: //convert LOCALE_SGROUPING to Grouping: http://blogs.msdn.com/b/oldnewthing/archive/2006/04/18/578251.aspx replace(grouping, L';', L""); if (endsWith(grouping, L'0')) - grouping.resize(grouping.size() - 1); + grouping.pop_back(); else grouping += L'0'; fmt.Grouping = stringTo<UINT>(grouping); @@ -253,7 +253,7 @@ std::wstring zen::ffs_Impl::includeNumberSeparator(const std::wstring& number) if (i <= 3) break; i -= 3; - if (!isDigit(output[i - 1])) + if (!isDigit(output[i - 1])) //stop on +, - signs break; output.insert(i, thousandSep); } @@ -28,7 +28,8 @@ namespace zen inline std::string generateGUID() //creates a 16 byte GUID { - boost::uuids::uuid nativeRep = boost::uuids::random_generator()(); //generator is thread-safe like an int + boost::uuids::uuid nativeRep = boost::uuids::random_generator()(); + //generator is only thread-safe like an int, so we keep it local until we need to optimize perf //perf: generator: 0.22ms per call; retrieve GUID: 0.12µs per call return std::string(nativeRep.begin(), nativeRep.end()); } @@ -29,11 +29,13 @@ public: class TimerError {}; ZEN_DEPRECATE - PerfTimer() : ticksPerSec_(ticksPerSec()), startTime(), resultShown(false) + PerfTimer() : //throw TimerError + ticksPerSec_(ticksPerSec()), startTime(), resultShown(false) { //std::clock() - "counts CPU time in C and wall time in VC++" - WTF!??? #ifdef FFS_WIN - if (::SetThreadAffinityMask(::GetCurrentThread(), 1) == 0) throw TimerError(); //"should not be required unless there are bugs in BIOS or HAL" - msdn, QueryPerformanceCounter + if (::SetThreadAffinityMask(::GetCurrentThread(), 1) == 0) //"should not be required unless there are bugs in BIOS or HAL" - msdn, QueryPerformanceCounter + throw TimerError(); #endif startTime = getTicks(); if (ticksPerSec_ == 0 || !startTime.isValid()) diff --git a/zen/process_priority.h b/zen/process_priority.h index 15266b28..c0bae667 100644 --- a/zen/process_priority.h +++ b/zen/process_priority.h @@ -12,7 +12,7 @@ namespace zen struct PreventStandby //signal a "busy" state to the operating system { #ifdef FFS_WIN - PreventStandby() { ::SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED /* | ES_AWAYMODE_REQUIRED*/ ); } + PreventStandby () { ::SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED /* | ES_AWAYMODE_REQUIRED*/ ); } ~PreventStandby() { ::SetThreadExecutionState(ES_CONTINUOUS); } #endif }; @@ -26,8 +26,8 @@ struct ScheduleForBackgroundProcessing //lower CPU and file I/O priorities #define PROCESS_MODE_BACKGROUND_END 0x00200000 // #endif - ScheduleForBackgroundProcessing() { ::SetPriorityClass(::GetCurrentProcess(), PROCESS_MODE_BACKGROUND_BEGIN); } //this call lowers CPU priority, too!! - ~ScheduleForBackgroundProcessing() { ::SetPriorityClass(::GetCurrentProcess(), PROCESS_MODE_BACKGROUND_END); } + ScheduleForBackgroundProcessing () { ::SetPriorityClass(::GetCurrentProcess(), PROCESS_MODE_BACKGROUND_BEGIN); } //this call lowers CPU priority, too!! + ~ScheduleForBackgroundProcessing() { ::SetPriorityClass(::GetCurrentProcess(), PROCESS_MODE_BACKGROUND_END ); } #elif defined FFS_LINUX /* diff --git a/zen/recycler.h b/zen/recycler.h index 4d33477d..8aca0ff3 100644 --- a/zen/recycler.h +++ b/zen/recycler.h @@ -42,13 +42,14 @@ enum StatusRecycler STATUS_REC_UNKNOWN }; StatusRecycler recycleBinStatus(const Zstring& pathName); //test existence of Recycle Bin API for certain path +//Win: blocks heavily if recycle bin is really full and drive is slow!!! struct CallbackRecycling { virtual ~CallbackRecycling() {} //may throw: first exception is swallowed, updateStatus() is then called again where it should throw again and the exception will propagate as expected - virtual void updateStatus(const Zstring& currentItem) = 0; + virtual void updateStatus(const Zstring& currentItem) = 0; //currentItem may be empty }; void recycleOrDelete(const std::vector<Zstring>& filenames, //throw FileError, return "true" if file/dir was actually deleted diff --git a/zen/string_base.h b/zen/string_base.h index bfe573e9..e4e21716 100644 --- a/zen/string_base.h +++ b/zen/string_base.h @@ -15,7 +15,6 @@ //Zbase - a policy based string class optimizing performance and genericity - namespace zen { /* @@ -59,8 +58,8 @@ template <typename Char, //Character Type void setLength(Char* ptr, size_t newLength) */ -template <typename Char, //Character Type - class AP> //Allocator Policy +template <class Char, //Character Type + class AP> //Allocator Policy class StorageDeepCopy : public AP { protected: @@ -112,8 +111,8 @@ private: }; -template <typename Char, //Character Type - class AP> //Allocator Policy +template <class Char, //Character Type + class AP> //Allocator Policy class StorageRefCountThreadSafe : public AP { protected: @@ -169,7 +168,7 @@ private: { Descriptor(long rc, size_t len, size_t cap) : refCount(rc), - length(static_cast<std::uint32_t>(len)), + length (static_cast<std::uint32_t>(len)), capacity(static_cast<std::uint32_t>(cap)) {} boost::detail::atomic_count refCount; //practically no perf loss: ~0.2%! (FFS comparison) @@ -181,8 +180,8 @@ private: static Descriptor* descr( Char* ptr) { return reinterpret_cast< Descriptor*>(ptr) - 1; } static const Descriptor* descr(const Char* ptr) { return reinterpret_cast<const Descriptor*>(ptr) - 1; } }; -//################################################################################################################################################################ +//################################################################################################################################################################ //perf note: interstingly StorageDeepCopy and StorageRefCountThreadSafe show same performance in FFS comparison @@ -386,7 +385,7 @@ size_t Zbase<Char, SP, AP>::find(const Zbase& str, size_t pos) const assert(pos <= length()); const Char* thisEnd = end(); //respect embedded 0 const Char* it = std::search(begin() + pos, thisEnd, - str.begin(), str.end()); + str.begin(), str.end()); return it == thisEnd ? npos : it - begin(); } @@ -397,7 +396,7 @@ size_t Zbase<Char, SP, AP>::find(const Char* str, size_t pos) const assert(pos <= length()); const Char* thisEnd = end(); //respect embedded 0 const Char* it = std::search(begin() + pos, thisEnd, - str, str + strLength(str)); + str, str + strLength(str)); return it == thisEnd ? npos : it - begin(); } @@ -433,7 +432,7 @@ size_t Zbase<Char, SP, AP>::rfind(const Char* str, size_t pos) const const Char* currEnd = pos == npos ? end() : begin() + std::min(pos + strLen, length()); const Char* it = search_last(begin(), currEnd, - str, str + strLen); + str, str + strLen); return it == currEnd ? npos : it - begin(); } diff --git a/zen/thread.h b/zen/thread.h index 31d762c7..43917d13 100644 --- a/zen/thread.h +++ b/zen/thread.h @@ -33,8 +33,11 @@ namespace zen { -//until std::async is available: /* +std::async replacement without crappy semantics: + 1. guaranteed to run asynchronous + 2. does not follow C++11 [futures.async], Paragraph 5, where std::future waits for thread in destructor + Example: Zstring dirname = ... auto ft = zen::async([=](){ return zen::dirExists(dirname); }); @@ -93,10 +96,13 @@ private: 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! { - boost::packaged_task<T> pt(std::move(fun)); //packaged task seems to even require r-value reference: https://sourceforge.net/p/freefilesync/bugs/234/ +#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/ +#else + boost::packaged_task<T> pt(std::move(fun)); +#endif auto fut = pt.get_future(); - boost::thread t(std::move(pt)); - t.detach(); //we have to be explicit since C++11: [thread.thread.destr] ~thread() calls std::terminate() if joinable()!!! + boost::thread(std::move(pt)).detach(); //we have to explicitly detach since C++11: [thread.thread.destr] ~thread() calls std::terminate() if joinable()!!! return std::move(fut); //compiler error without "move", why needed??? } |