summaryrefslogtreecommitdiff
path: root/zen
diff options
context:
space:
mode:
authorB Stack <bgstack15@gmail.com>2019-01-15 08:16:08 -0500
committerB Stack <bgstack15@gmail.com>2019-01-15 08:16:08 -0500
commit7c855d11f23dc03d64a0099473a95402f3175280 (patch)
tree41b8a3085df4de883f2993773b138f1436e041a4 /zen
parentMerge branch '10.7' into 'master' (diff)
downloadFreeFileSync-7c855d11f23dc03d64a0099473a95402f3175280.tar.gz
FreeFileSync-7c855d11f23dc03d64a0099473a95402f3175280.tar.bz2
FreeFileSync-7c855d11f23dc03d64a0099473a95402f3175280.zip
10.8
Diffstat (limited to 'zen')
-rwxr-xr-xzen/file_access.cpp89
-rwxr-xr-xzen/file_access.h20
-rwxr-xr-xzen/file_id_def.h14
-rwxr-xr-xzen/file_io.cpp4
-rwxr-xr-xzen/file_io.h16
-rwxr-xr-xzen/format_unit.h1
-rwxr-xr-xzen/legacy_compiler.h4
-rwxr-xr-xzen/shutdown.cpp15
-rwxr-xr-xzen/shutdown.h1
-rwxr-xr-xzen/stl_tools.h56
-rwxr-xr-xzen/string_base.h2
-rwxr-xr-xzen/string_tools.h4
-rwxr-xr-xzen/thread.h2
-rwxr-xr-xzen/zlib_wrap.cpp53
-rwxr-xr-xzen/zlib_wrap.h114
-rwxr-xr-xzen/zstring.h6
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);} };
bgstack15