diff options
author | Daniel Wilhelm <shieldwed@outlook.com> | 2019-01-16 00:44:17 +0000 |
---|---|---|
committer | Daniel Wilhelm <shieldwed@outlook.com> | 2019-01-16 00:44:17 +0000 |
commit | c03ea582bff36fe020af1aa329b34f3e6d580f92 (patch) | |
tree | 41b8a3085df4de883f2993773b138f1436e041a4 /zen | |
parent | Merge branch '10.7' into 'master' (diff) | |
parent | 10.8 (diff) | |
download | FreeFileSync-c03ea582bff36fe020af1aa329b34f3e6d580f92.tar.gz FreeFileSync-c03ea582bff36fe020af1aa329b34f3e6d580f92.tar.bz2 FreeFileSync-c03ea582bff36fe020af1aa329b34f3e6d580f92.zip |
Merge branch '10.8' into 'master'10.8
10.8
See merge request opensource-tracking/FreeFileSync!5
Diffstat (limited to 'zen')
-rwxr-xr-x | zen/file_access.cpp | 89 | ||||
-rwxr-xr-x | zen/file_access.h | 20 | ||||
-rwxr-xr-x | zen/file_id_def.h | 14 | ||||
-rwxr-xr-x | zen/file_io.cpp | 4 | ||||
-rwxr-xr-x | zen/file_io.h | 16 | ||||
-rwxr-xr-x | zen/format_unit.h | 1 | ||||
-rwxr-xr-x | zen/legacy_compiler.h | 4 | ||||
-rwxr-xr-x | zen/shutdown.cpp | 15 | ||||
-rwxr-xr-x | zen/shutdown.h | 1 | ||||
-rwxr-xr-x | zen/stl_tools.h | 56 | ||||
-rwxr-xr-x | zen/string_base.h | 2 | ||||
-rwxr-xr-x | zen/string_tools.h | 4 | ||||
-rwxr-xr-x | zen/thread.h | 2 | ||||
-rwxr-xr-x | zen/zlib_wrap.cpp | 53 | ||||
-rwxr-xr-x | zen/zlib_wrap.h | 114 | ||||
-rwxr-xr-x | zen/zstring.h | 6 |
16 files changed, 318 insertions, 83 deletions
diff --git a/zen/file_access.cpp b/zen/file_access.cpp index ea19b6c7..d257b7c2 100755 --- a/zen/file_access.cpp +++ b/zen/file_access.cpp @@ -170,16 +170,6 @@ namespace } -uint64_t zen::getFileSize(const Zstring& filePath) //throw FileError -{ - struct ::stat fileInfo = {}; - if (::stat(filePath.c_str(), &fileInfo) != 0) - THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(filePath)), L"stat"); - - return fileInfo.st_size; -} - - uint64_t zen::getFreeDiskSpace(const Zstring& path) //throw FileError, returns 0 if not available { struct ::statfs info = {}; @@ -190,13 +180,19 @@ uint64_t zen::getFreeDiskSpace(const Zstring& path) //throw FileError, returns 0 } -FileId /*optional*/ zen::getFileId(const Zstring& itemPath) //throw FileError +FileDetails zen::getFileDetails(const Zstring& itemPath) //throw FileError { struct ::stat fileInfo = {}; if (::stat(itemPath.c_str(), &fileInfo) != 0) THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(itemPath)), L"stat"); - return extractFileId(fileInfo); + return + { + makeUnsigned(fileInfo.st_size), + fileInfo.st_mtime, + fileInfo.st_dev, + //FileIndex fileIndex = fileInfo.st_ino; + }; } @@ -301,19 +297,14 @@ namespace /* Usage overview: (avoid circular pattern!) - renameFile() --> renameFile_sub() - | /|\ - \|/ | - Fix8Dot3NameClash() + moveAndRenameItem() --> moveAndRenameFileSub() + | /|\ + \|/ | + Fix8Dot3NameClash() */ //wrapper for file system rename function: -void renameFile_sub(const Zstring& pathSource, const Zstring& pathTarget) //throw FileError, ErrorDifferentVolume, ErrorTargetExisting +void moveAndRenameFileSub(const Zstring& pathSource, const Zstring& pathTarget, bool replaceExisting) //throw FileError, ErrorDifferentVolume, ErrorTargetExisting { - //rename() will never fail with EEXIST, but always (atomically) overwrite! - //=> equivalent to SetFileInformationByHandle() + FILE_RENAME_INFO::ReplaceIfExists or ::MoveFileEx + MOVEFILE_REPLACE_EXISTING - //Linux: renameat2() with RENAME_NOREPLACE -> still new, probably buggy - //macOS: no solution - auto throwException = [&](int ec) { const std::wstring errorMsg = replaceCpy(replaceCpy(_("Cannot move file %x to %y."), L"%x", L"\n" + fmtPath(pathSource)), L"%y", L"\n" + fmtPath(pathTarget)); @@ -326,19 +317,27 @@ void renameFile_sub(const Zstring& pathSource, const Zstring& pathTarget) //thro throw FileError(errorMsg, errorDescr); }; - struct ::stat infoSrc = {}; - if (::lstat(pathSource.c_str(), &infoSrc) != 0) - THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(pathSource)), L"stat"); + //rename() will never fail with EEXIST, but always (atomically) overwrite! + //=> equivalent to SetFileInformationByHandle() + FILE_RENAME_INFO::ReplaceIfExists or ::MoveFileEx() + MOVEFILE_REPLACE_EXISTING + //Linux: renameat2() with RENAME_NOREPLACE -> still new, probably buggy + //macOS: no solution https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man2/rename.2.html + if (!replaceExisting) + { + struct ::stat infoSrc = {}; + if (::lstat(pathSource.c_str(), &infoSrc) != 0) + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(pathSource)), L"stat"); - struct ::stat infoTrg = {}; - if (::lstat(pathTarget.c_str(), &infoTrg) == 0) - { - if (infoSrc.st_dev != infoTrg.st_dev || - infoSrc.st_ino != infoTrg.st_ino) - throwException(EEXIST); //that's what we're really here for - //else: continue with a rename in case - } - //else: not existing or access error (hopefully ::rename will also fail!) + struct ::stat infoTrg = {}; + if (::lstat(pathTarget.c_str(), &infoTrg) == 0) + { + if (infoSrc.st_dev != infoTrg.st_dev || + infoSrc.st_ino != infoTrg.st_ino) + throwException(EEXIST); //that's what we're really here for + //else: continue with a rename in case + //caveat: if we have a hardlink referenced by two different paths, the source one will be unlinked => fine, but not exactly a "rename"... + } + //else: not existing or access error (hopefully ::rename will also fail!) + } if (::rename(pathSource.c_str(), pathTarget.c_str()) != 0) throwException(errno); @@ -349,11 +348,11 @@ void renameFile_sub(const Zstring& pathSource, const Zstring& pathTarget) //thro //rename file: no copying!!! -void zen::renameFile(const Zstring& pathSource, const Zstring& pathTarget) //throw FileError, ErrorDifferentVolume, ErrorTargetExisting +void zen::moveAndRenameItem(const Zstring& pathSource, const Zstring& pathTarget, bool replaceExisting) //throw FileError, ErrorDifferentVolume, ErrorTargetExisting { try { - renameFile_sub(pathSource, pathTarget); //throw FileError, ErrorDifferentVolume, ErrorTargetExisting + moveAndRenameFileSub(pathSource, pathTarget, replaceExisting); //throw FileError, ErrorDifferentVolume, ErrorTargetExisting } catch (ErrorTargetExisting&) { @@ -369,9 +368,9 @@ void zen::renameFile(const Zstring& pathSource, const Zstring& pathTarget) //thr return; //non-sensical request const Zstring tempFilePath = getTemporaryPath8Dot3(pathSource); //throw FileError - renameFile_sub(pathSource, tempFilePath); //throw FileError, ErrorDifferentVolume, ErrorTargetExisting - ZEN_ON_SCOPE_FAIL(renameFile_sub(tempFilePath, pathSource)); //"try" our best to be "atomic" :> - renameFile_sub(tempFilePath, pathTarget); //throw FileError, ErrorDifferentVolume, ErrorTargetExisting + moveAndRenameFileSub(pathSource, tempFilePath); //throw FileError, ErrorDifferentVolume, ErrorTargetExisting + ZEN_ON_SCOPE_FAIL(moveAndRenameFileSub(tempFilePath, pathSource)); //"try" our best to be "atomic" :> + moveAndRenameFileSub(tempFilePath, pathTarget); //throw FileError, ErrorDifferentVolume, ErrorTargetExisting return; } #endif @@ -398,7 +397,7 @@ void setWriteTimeNative(const Zstring& itemPath, const struct ::timespec& modTim struct ::timespec newTimes[2] = {}; newTimes[0].tv_sec = ::time(nullptr); //access time; using UTIME_OMIT for tv_nsec would trigger even more bugs: https://freefilesync.org/forum/viewtopic.php?t=1701 newTimes[1] = modTime; //modification time - //test: even modTime == 0 is correctly applied (no NOOP!) test2: same behavior for "utime()" + //test: even modTime == 0 is correctly applied (no NOOP!) test2: same behavior for "utime()" if (procSl == ProcSymlink::FOLLOW) { @@ -680,8 +679,8 @@ FileCopyResult copyFileOsSpecific(const Zstring& sourceFile, //throw FileError, FileCopyResult result; result.fileSize = sourceInfo.st_size; result.modTime = sourceInfo.st_mtim.tv_sec; // - result.sourceFileId = extractFileId(sourceInfo); - result.targetFileId = extractFileId(targetInfo); + result.sourceFileId = generateFileId(sourceInfo); + result.targetFileId = generateFileId(targetInfo); result.errorModTime = errorModTime; return result; } @@ -700,10 +699,10 @@ copyFileWindowsDefault(::CopyFileEx) copyFileWindowsStream(::BackupRead/::Backu } -FileCopyResult zen::copyNewFile(const Zstring& sourceFile, const Zstring& targetFile, bool copyFilePermissions, //throw FileError, ErrorTargetExisting, ErrorFileLocked - const IOCallback& notifyUnbufferedIO) +FileCopyResult zen::copyNewFile(const Zstring& sourceFile, const Zstring& targetFile, bool copyFilePermissions, //throw FileError, ErrorTargetExisting, ErrorFileLocked, X + const IOCallback& notifyUnbufferedIO /*throw X*/) { - const FileCopyResult result = copyFileOsSpecific(sourceFile, targetFile, notifyUnbufferedIO); //throw FileError, ErrorTargetExisting, ErrorFileLocked + const FileCopyResult result = copyFileOsSpecific(sourceFile, targetFile, notifyUnbufferedIO); //throw FileError, ErrorTargetExisting, ErrorFileLocked, X //at this point we know we created a new file, so it's fine to delete it for cleanup! ZEN_ON_SCOPE_FAIL(try { removeFilePlain(targetFile); } diff --git a/zen/file_access.h b/zen/file_access.h index 514d798e..d981dcc3 100755 --- a/zen/file_access.h +++ b/zen/file_access.h @@ -52,10 +52,17 @@ enum class ProcSymlink }; void setFileTime(const Zstring& filePath, time_t modTime, ProcSymlink procSl); //throw FileError -//symlink handling: always evaluate target -uint64_t getFileSize(const Zstring& filePath); //throw FileError +//symlink handling: always follow uint64_t getFreeDiskSpace(const Zstring& path); //throw FileError, returns 0 if not available -FileId /*optional*/ getFileId(const Zstring& itemPath); //throw FileError + +struct FileDetails +{ + uint64_t fileSize = 0; + time_t modTime = 0; //number of seconds since Jan. 1st 1970 UTC + VolumeId volumeId = 0; +}; +//symlink handling: always follow +FileDetails getFileDetails(const Zstring& itemPath); //throw FileError //get per-user directory designated for temporary files: Zstring getTempFolderPath(); //throw FileError @@ -65,8 +72,7 @@ void removeSymlinkPlain (const Zstring& linkPath); //throw FileError; E void removeDirectoryPlain(const Zstring& dirPath ); //throw FileError; ERROR if not existing void removeDirectoryPlainRecursion(const Zstring& dirPath); //throw FileError; ERROR if not existing -//rename file or directory: no copying!!! -void renameFile(const Zstring& itemPathOld, const Zstring& itemPathNew); //throw FileError, ErrorDifferentVolume, ErrorTargetExisting +void moveAndRenameItem(const Zstring& itemPathOld, const Zstring& itemPathNew, bool replaceExisting); //throw FileError, ErrorDifferentVolume, ErrorTargetExisting bool supportsPermissions(const Zstring& dirPath); //throw FileError, follows symlinks //copy permissions for files, directories or symbolic links: requires admin rights @@ -94,9 +100,9 @@ struct FileCopyResult std::optional<FileError> errorModTime; //failure to set modification time }; -FileCopyResult copyNewFile(const Zstring& sourceFile, const Zstring& targetFile, bool copyFilePermissions, //throw FileError, ErrorTargetExisting, ErrorFileLocked +FileCopyResult copyNewFile(const Zstring& sourceFile, const Zstring& targetFile, bool copyFilePermissions, //throw FileError, ErrorTargetExisting, ErrorFileLocked, X //accummulated delta != file size! consider ADS, sparse, compressed files - const IOCallback& notifyUnbufferedIO); //may be nullptr; throw X! + const IOCallback& notifyUnbufferedIO /*throw X*/); } #endif //FILE_ACCESS_H_8017341345614857 diff --git a/zen/file_id_def.h b/zen/file_id_def.h index f58cb479..55ee77f5 100755 --- a/zen/file_id_def.h +++ b/zen/file_id_def.h @@ -21,7 +21,14 @@ using FileIndex = decltype(impl::StatDummy::st_ino); struct FileId //always available on Linux, and *generally* available on Windows) { FileId() {} - FileId(VolumeId volId, FileIndex fIdx) : volumeId(volId), fileIndex(fIdx) {} + FileId(VolumeId volId, FileIndex fIdx) : volumeId(volId), fileIndex(fIdx) + { + if (volId == 0 || fIdx == 0) + { + volumeId = 0; + fileIndex = 0; + } + } VolumeId volumeId = 0; FileIndex fileIndex = 0; }; @@ -29,10 +36,9 @@ inline bool operator==(const FileId& lhs, const FileId& rhs) { return lhs.volume inline -FileId extractFileId(const struct ::stat& fileInfo) +FileId generateFileId(const struct ::stat& fileInfo) { - return fileInfo.st_dev != 0 && fileInfo.st_ino != 0 ? - FileId(fileInfo.st_dev, fileInfo.st_ino) : FileId(); + return FileId(fileInfo.st_dev, fileInfo.st_ino); } } diff --git a/zen/file_io.cpp b/zen/file_io.cpp index 80fb3153..1c6ab6f2 100755 --- a/zen/file_io.cpp +++ b/zen/file_io.cpp @@ -128,7 +128,7 @@ size_t FileInput::tryRead(void* buffer, size_t bytesToRead) //throw FileError, E return bytesRead; //"zero indicates end of file" } - + size_t FileInput::read(void* buffer, size_t bytesToRead) //throw FileError, ErrorFileLocked, X; return "bytesToRead" bytes unless end of stream! { /* @@ -203,7 +203,7 @@ FileOutput::FileOutput(FileHandle handle, const Zstring& filePath, const IOCallb FileBase(handle, filePath), notifyUnbufferedIO_(notifyUnbufferedIO) {} -FileOutput::FileOutput(const Zstring& filePath, AccessFlag access, const IOCallback& notifyUnbufferedIO) : +FileOutput::FileOutput(AccessFlag access, const Zstring& filePath, const IOCallback& notifyUnbufferedIO) : FileBase(openHandleForWrite(filePath, access), filePath), notifyUnbufferedIO_(notifyUnbufferedIO) {} //throw FileError, ErrorTargetExisting diff --git a/zen/file_io.h b/zen/file_io.h index d05d97db..bf23d22c 100755 --- a/zen/file_io.h +++ b/zen/file_io.h @@ -56,8 +56,8 @@ private: class FileInput : public FileBase { public: - FileInput(const Zstring& filePath, const IOCallback& notifyUnbufferedIO); //throw FileError, ErrorFileLocked - FileInput(FileHandle handle, const Zstring& filePath, const IOCallback& notifyUnbufferedIO); //takes ownership! + FileInput( const Zstring& filePath, const IOCallback& notifyUnbufferedIO /*throw X*/); //throw FileError, ErrorFileLocked + FileInput(FileHandle handle, const Zstring& filePath, const IOCallback& notifyUnbufferedIO /*throw X*/); //takes ownership! size_t read(void* buffer, size_t bytesToRead); //throw FileError, ErrorFileLocked, X; return "bytesToRead" bytes unless end of stream! @@ -80,8 +80,8 @@ public: ACC_OVERWRITE, ACC_CREATE_NEW }; - FileOutput(const Zstring& filePath, AccessFlag access, const IOCallback& notifyUnbufferedIO); //throw FileError, ErrorTargetExisting - FileOutput(FileHandle handle, const Zstring& filePath, const IOCallback& notifyUnbufferedIO); //takes ownership! + FileOutput(AccessFlag access, const Zstring& filePath, const IOCallback& notifyUnbufferedIO /*throw X*/); //throw FileError, ErrorTargetExisting + FileOutput(FileHandle handle, const Zstring& filePath, const IOCallback& notifyUnbufferedIO /*throw X*/); //takes ownership! ~FileOutput(); void preAllocateSpaceBestEffort(uint64_t expectedSize); //throw FileError @@ -105,8 +105,7 @@ private: //native stream I/O convenience functions: template <class BinContainer> inline -BinContainer loadBinContainer(const Zstring& filePath, //throw FileError - const IOCallback& notifyUnbufferedIO) +BinContainer loadBinContainer(const Zstring& filePath, const IOCallback& notifyUnbufferedIO /*throw X*/) //throw FileError, X { FileInput streamIn(filePath, notifyUnbufferedIO); //throw FileError, ErrorFileLocked return bufferedLoad<BinContainer>(streamIn); //throw FileError, X; @@ -114,10 +113,9 @@ BinContainer loadBinContainer(const Zstring& filePath, //throw FileError template <class BinContainer> inline -void saveBinContainer(const Zstring& filePath, const BinContainer& buffer, //throw FileError - const IOCallback& notifyUnbufferedIO) +void saveBinContainer(const Zstring& filePath, const BinContainer& buffer, const IOCallback& notifyUnbufferedIO /*throw X*/) //throw FileError, X { - FileOutput fileOut(filePath, FileOutput::ACC_OVERWRITE, notifyUnbufferedIO); //throw FileError, (ErrorTargetExisting) + FileOutput fileOut(FileOutput::ACC_OVERWRITE, filePath, notifyUnbufferedIO); //throw FileError, (ErrorTargetExisting) if (!buffer.empty()) { /*snake oil?*/ fileOut.preAllocateSpaceBestEffort(buffer.size()); //throw FileError diff --git a/zen/format_unit.h b/zen/format_unit.h index 3686f39c..de5a0811 100755 --- a/zen/format_unit.h +++ b/zen/format_unit.h @@ -24,7 +24,6 @@ std::wstring formatThreeDigitPrecision(double value); //(unless value is too lar std::wstring formatNumber(int64_t n); //format integer number including thousands separator - } #endif diff --git a/zen/legacy_compiler.h b/zen/legacy_compiler.h index bebf5a05..54605945 100755 --- a/zen/legacy_compiler.h +++ b/zen/legacy_compiler.h @@ -27,10 +27,10 @@ public: template <class Container> span(Container& cont) : span(cont.begin(), cont.end()) {} - + using iterator = T*; using const_iterator = const T*; - + iterator begin() { return data_; } iterator end () { return data_ + size_; } diff --git a/zen/shutdown.cpp b/zen/shutdown.cpp index 1794e4a8..a25e9c64 100755 --- a/zen/shutdown.cpp +++ b/zen/shutdown.cpp @@ -18,8 +18,7 @@ void zen::shutdownSystem() //throw FileError //https://linux.die.net/man/2/reboot => needs admin rights! //"systemctl" should work without admin rights: - shellExecute("sleep 1; systemctl poweroff", ExecutionType::ASYNC); //throw FileError - //sleep 1: give FFS some time to properly shut down! + shellExecute("systemctl poweroff", ExecutionType::SYNC); //throw FileError } @@ -27,10 +26,20 @@ void zen::shutdownSystem() //throw FileError void zen::suspendSystem() //throw FileError { //"systemctl" should work without admin rights: - shellExecute("systemctl suspend", ExecutionType::ASYNC); //throw FileError + shellExecute("systemctl suspend", ExecutionType::SYNC); //throw FileError } + +void zen::terminateProcess(int exitCode) +{ + std::exit(exitCode); //[[noreturn]]; "Stack is not unwound: destructors of variables with automatic storage duration are not called." => perfect + //don't use std::abort() => crashes process with "EXC_CRASH (SIGABRT)" on macOS + for (;;) //why still here?? => crash deliberately! + *reinterpret_cast<volatile int*>(0) = 0; //crude but at least we'll get crash dumps if it happens +} + + /* Command line alternatives: diff --git a/zen/shutdown.h b/zen/shutdown.h index df2314f8..2d66d1e8 100755 --- a/zen/shutdown.h +++ b/zen/shutdown.h @@ -14,6 +14,7 @@ namespace zen { void shutdownSystem(); //throw FileError void suspendSystem(); // +void terminateProcess(int exitCode); //will NOT return! } #endif //SHUTDOWN_H_3423847870238407783265 diff --git a/zen/stl_tools.h b/zen/stl_tools.h index d8bda888..f1ab7c16 100755 --- a/zen/stl_tools.h +++ b/zen/stl_tools.h @@ -57,7 +57,15 @@ BidirectionalIterator findLast(BidirectionalIterator first, BidirectionalIterato //replacement for std::find_end taking advantage of bidirectional iterators (and giving the algorithm a reasonable name) template <class BidirectionalIterator1, class BidirectionalIterator2> BidirectionalIterator1 searchLast(BidirectionalIterator1 first1, BidirectionalIterator1 last1, - BidirectionalIterator2 first2, BidirectionalIterator2 last2); + BidirectionalIterator2 first2, BidirectionalIterator2 last2); + + +//read-only variant of std::merge; input: two sorted ranges +template <class Iterator, class FunctionLeftOnly, class FunctionBoth, class FunctionRightOnly> +void mergeTraversal(Iterator first1, Iterator last1, + Iterator first2, Iterator last2, + FunctionLeftOnly lo, FunctionBoth bo, FunctionRightOnly ro); + template <class Num, class ByteIterator> Num hashBytes (ByteIterator first, ByteIterator last); template <class Num, class ByteIterator> Num hashBytesAppend(Num hashVal, ByteIterator first, ByteIterator last); @@ -109,11 +117,13 @@ private: }; template <class T, class... Args> inline -SharedRef<T> makeSharedRef(Args&&... args) { return SharedRef<T>(std::make_shared<T>(std::forward<Args>(args)...)); } +SharedRef<T> makeSharedRef(Args&& ... args) { return SharedRef<T>(std::make_shared<T>(std::forward<Args>(args)...)); } //=========================================================================== + + //######################## implementation ######################## template <class T, class Alloc, class Predicate> inline @@ -208,7 +218,7 @@ BidirectionalIterator findLast(const BidirectionalIterator first, const Bidirect template <class BidirectionalIterator1, class BidirectionalIterator2> inline BidirectionalIterator1 searchLast(const BidirectionalIterator1 first1, BidirectionalIterator1 last1, - const BidirectionalIterator2 first2, const BidirectionalIterator2 last2) + const BidirectionalIterator2 first2, const BidirectionalIterator2 last2) { const BidirectionalIterator1 itNotFound = last1; @@ -233,6 +243,46 @@ BidirectionalIterator1 searchLast(const BidirectionalIterator1 first1, Bid } +//read-only variant of std::merge; input: two sorted ranges +template <class Iterator, class FunctionLeftOnly, class FunctionBoth, class FunctionRightOnly> inline +void mergeTraversal(Iterator first1, Iterator last1, + Iterator first2, Iterator last2, + FunctionLeftOnly lo, FunctionBoth bo, FunctionRightOnly ro) +{ + auto itL = first1; + auto itR = first2; + + auto finishLeft = [&] { std::for_each(itL, last1, lo); }; + auto finishRight = [&] { std::for_each(itR, last2, ro); }; + + if (itL == last1) return finishRight(); + if (itR == last2) return finishLeft (); + + for (;;) + if (itL->first < itR->first) + { + lo(*itL); + if (++itL == last1) + return finishRight(); + } + else if (itR->first < itL->first) + { + ro(*itR); + if (++itR == last2) + return finishLeft(); + } + else + { + bo(*itL, *itR); + ++itL; // + ++itR; //increment BOTH before checking for end of range! + if (itL == last1) return finishRight(); + if (itR == last2) return finishLeft (); + //simplify loop by placing both EOB checks at the beginning? => slightly slower + } +} + + //FNV-1a: http://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function template <class Num, class ByteIterator> inline Num hashBytes(ByteIterator first, ByteIterator last) diff --git a/zen/string_base.h b/zen/string_base.h index 91a6d5bd..2247f93a 100755 --- a/zen/string_base.h +++ b/zen/string_base.h @@ -423,7 +423,7 @@ size_t Zbase<Char, SP>::rfind(const Char* str, size_t pos) const const size_t len = length(); const Char* currEnd = begin() + (pos == npos ? len : std::min(pos + strLen, len)); const Char* it = searchLast(begin(), currEnd, - str, str + strLen); + str, str + strLen); return it == currEnd ? npos : it - begin(); } diff --git a/zen/string_tools.h b/zen/string_tools.h index 8579a460..c3970d05 100755 --- a/zen/string_tools.h +++ b/zen/string_tools.h @@ -300,7 +300,7 @@ S afterLast(const S& str, const T& term, FailureReturnVal rv) const auto* const termFirst = strBegin(term); const auto* it = searchLast(strFirst, strLast, - termFirst, termFirst + termLen); + termFirst, termFirst + termLen); if (it == strLast) return rv == IF_MISSING_RETURN_ALL ? str : S(); @@ -321,7 +321,7 @@ S beforeLast(const S& str, const T& term, FailureReturnVal rv) const auto* const termFirst = strBegin(term); const auto* it = searchLast(strFirst, strLast, - termFirst, termFirst + termLen); + termFirst, termFirst + termLen); if (it == strLast) return rv == IF_MISSING_RETURN_ALL ? str : S(); diff --git a/zen/thread.h b/zen/thread.h index 8a9adc87..5e298ba1 100755 --- a/zen/thread.h +++ b/zen/thread.h @@ -119,7 +119,7 @@ class Protected public: Protected() {} Protected(T& value) : value_(value) {} - //Protected( T&& tmp ) : value_(std::move(tmp)) {} <- wait until needed + //Protected(T&& tmp ) : value_(std::move(tmp)) {} <- wait until needed template <class Function> auto access(Function fun) //-> decltype(fun(std::declval<T&>())) diff --git a/zen/zlib_wrap.cpp b/zen/zlib_wrap.cpp new file mode 100755 index 00000000..fbbe2f09 --- /dev/null +++ b/zen/zlib_wrap.cpp @@ -0,0 +1,53 @@ +// ***************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 * +// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved * +// ***************************************************************************** + +#include "zlib_wrap.h" +//Windows: use the SAME zlib version that wxWidgets is linking against! //C:\Data\Projects\wxWidgets\Source\src\zlib\zlib.h +//Linux/macOS: use zlib system header for both wxWidgets and Curl (zlib is required for HTTP) +// => don't compile wxWidgets with: --with-zlib=builtin +#include <zlib.h> + +using namespace zen; + + +size_t zen::impl::zlib_compressBound(size_t len) +{ + return ::compressBound(static_cast<uLong>(len)); //upper limit for buffer size, larger than input size!!! +} + + +size_t zen::impl::zlib_compress(const void* src, size_t srcLen, void* trg, size_t trgLen, int level) //throw ZlibInternalError +{ + uLongf bufferSize = static_cast<uLong>(trgLen); + const int rv = ::compress2(static_cast<Bytef*>(trg), //Bytef* dest, + &bufferSize, //uLongf* destLen, + static_cast<const Bytef*>(src), //const Bytef* source, + static_cast<uLong>(srcLen), //uLong sourceLen, + level); //int level + // Z_OK: success + // Z_MEM_ERROR: not enough memory + // Z_BUF_ERROR: not enough room in the output buffer + if (rv != Z_OK || bufferSize > trgLen) + throw ZlibInternalError(); + return bufferSize; +} + + +size_t zen::impl::zlib_decompress(const void* src, size_t srcLen, void* trg, size_t trgLen) //throw ZlibInternalError +{ + uLongf bufferSize = static_cast<uLong>(trgLen); + const int rv = ::uncompress(static_cast<Bytef*>(trg), //Bytef* dest, + &bufferSize, //uLongf* destLen, + static_cast<const Bytef*>(src), //const Bytef* source, + static_cast<uLong>(srcLen)); //uLong sourceLen + // Z_OK: success + // Z_MEM_ERROR: not enough memory + // Z_BUF_ERROR: not enough room in the output buffer + // Z_DATA_ERROR: input data was corrupted or incomplete + if (rv != Z_OK || bufferSize > trgLen) + throw ZlibInternalError(); + return bufferSize; +} diff --git a/zen/zlib_wrap.h b/zen/zlib_wrap.h new file mode 100755 index 00000000..b92a8eba --- /dev/null +++ b/zen/zlib_wrap.h @@ -0,0 +1,114 @@ +// ***************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 * +// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved * +// ***************************************************************************** + +#ifndef ZLIB_WRAP_H_428597064566 +#define ZLIB_WRAP_H_428597064566 + +#include "serialize.h" + + +namespace zen +{ +class ZlibInternalError {}; + +// compression level must be between 0 and 9: +// 0: no compression +// 9: best compression +template <class BinContainer> //as specified in serialize.h +BinContainer compress(const BinContainer& stream, int level); //throw ZlibInternalError +//caveat: output stream is physically larger than input! => strip additional reserved space if needed: "BinContainer(output.begin(), output.end())" + +template <class BinContainer> +BinContainer decompress(const BinContainer& stream); //throw ZlibInternalError + + + + + + + + + + + +//######################## implementation ########################## +namespace impl +{ +size_t zlib_compressBound(size_t len); +size_t zlib_compress (const void* src, size_t srcLen, void* trg, size_t trgLen, int level); //throw ZlibInternalError +size_t zlib_decompress(const void* src, size_t srcLen, void* trg, size_t trgLen); //throw ZlibInternalError +} + + +template <class BinContainer> +BinContainer compress(const BinContainer& stream, int level) //throw ZlibInternalError +{ + BinContainer contOut; + if (!stream.empty()) //don't dereference iterator into empty container! + { + //save uncompressed stream size for decompression + const uint64_t uncompressedSize = stream.size(); //use portable number type! + contOut.resize(sizeof(uncompressedSize)); + std::copy(reinterpret_cast<const std::byte*>(&uncompressedSize), + reinterpret_cast<const std::byte*>(&uncompressedSize) + sizeof(uncompressedSize), + &*contOut.begin()); + + const size_t bufferEstimate = impl::zlib_compressBound(stream.size()); //upper limit for buffer size, larger than input size!!! + + contOut.resize(contOut.size() + bufferEstimate); + + const size_t bytesWritten = impl::zlib_compress(&*stream.begin(), + stream.size(), + &*contOut.begin() + contOut.size() - bufferEstimate, + bufferEstimate, + level); //throw ZlibInternalError + if (bytesWritten < bufferEstimate) + contOut.resize(contOut.size() - (bufferEstimate - bytesWritten)); //caveat: unsigned arithmetics + //caveat: physical memory consumption still *unchanged*! + } + return contOut; +} + + +template <class BinContainer> +BinContainer decompress(const BinContainer& stream) //throw ZlibInternalError +{ + BinContainer contOut; + if (!stream.empty()) //don't dereference iterator into empty container! + { + //retrieve size of uncompressed data + uint64_t uncompressedSize = 0; //use portable number type! + if (stream.size() < sizeof(uncompressedSize)) + throw ZlibInternalError(); + std::copy(&*stream.begin(), + &*stream.begin() + sizeof(uncompressedSize), + reinterpret_cast<std::byte*>(&uncompressedSize)); + //attention: contOut MUST NOT be empty! Else it will pass a nullptr to zlib_decompress() => Z_STREAM_ERROR although "uncompressedSize == 0"!!! + //secondary bug: don't dereference iterator into empty container! + if (uncompressedSize == 0) //cannot be 0: compress() directly maps empty -> empty container skipping zlib! + throw ZlibInternalError(); + + try + { + contOut.resize(static_cast<size_t>(uncompressedSize)); //throw std::bad_alloc + } + catch (std::bad_alloc&) //most likely due to data corruption! + { + throw ZlibInternalError(); + } + + const size_t bytesWritten = impl::zlib_decompress(&*stream.begin() + sizeof(uncompressedSize), + stream.size() - sizeof(uncompressedSize), + &*contOut.begin(), + static_cast<size_t>(uncompressedSize)); //throw ZlibInternalError + if (bytesWritten != static_cast<size_t>(uncompressedSize)) + throw ZlibInternalError(); + } + return contOut; +} +} + +#endif //ZLIB_WRAP_H_428597064566 diff --git a/zen/zstring.h b/zen/zstring.h index a511e4e0..6727253b 100755 --- a/zen/zstring.h +++ b/zen/zstring.h @@ -32,9 +32,9 @@ Zstring makeUpperCopy(const Zstring& str); //Windows, Linux: precomposed //macOS: decomposed Zstring getUnicodeNormalForm(const Zstring& str); -// "In fact, Unicode declares that there is an equivalence relationship between decomposed and composed sequences, -// and conformant software should not treat canonically equivalent sequences, whether composed or decomposed or something inbetween, as different." -// http://www.win.tue.nl/~aeb/linux/uc/nfc_vs_nfd.html +// "In fact, Unicode declares that there is an equivalence relationship between decomposed and composed sequences, +// and conformant software should not treat canonically equivalent sequences, whether composed or decomposed or something inbetween, as different." +// http://www.win.tue.nl/~aeb/linux/uc/nfc_vs_nfd.html struct LessUnicodeNormal { bool operator()(const Zstring& lhs, const Zstring& rhs) const { return getUnicodeNormalForm(lhs) < getUnicodeNormalForm(rhs);} }; |