summaryrefslogtreecommitdiff
path: root/FreeFileSync/Source/lib/binary.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'FreeFileSync/Source/lib/binary.cpp')
-rwxr-xr-x[-rw-r--r--]FreeFileSync/Source/lib/binary.cpp296
1 files changed, 141 insertions, 155 deletions
diff --git a/FreeFileSync/Source/lib/binary.cpp b/FreeFileSync/Source/lib/binary.cpp
index 0d9b9549..5519bd37 100644..100755
--- a/FreeFileSync/Source/lib/binary.cpp
+++ b/FreeFileSync/Source/lib/binary.cpp
@@ -1,155 +1,141 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#include "binary.h"
-#include <vector>
-#include <chrono>
-//#include <zen/tick_count.h>
-
-using namespace zen;
-using AFS = AbstractFileSystem;
-
-namespace
-{
-/*
-1. there seems to be no perf improvement possible when using file mappings instad of ::ReadFile() calls on Windows:
- => buffered access: same perf
- => unbuffered access: same perf on USB stick, file mapping 30% slower on local disk
-
-2. Tests on Win7 x64 show that buffer size does NOT matter if files are located on different physical disks!
-Impact of buffer size when files are on same disk:
-
-buffer MB/s
-------------
-64 10
-128 19
-512 40
-1024 48
-2048 56
-4096 56
-8192 56
-*/
-
-const size_t BLOCK_SIZE_MAX = 16 * 1024 * 1024;
-
-
-struct StreamReader
-{
- StreamReader(const AbstractPath& filePath, const std::function<void(std::int64_t bytesDelta)>& notifyProgress, size_t& unevenBytes) :
- stream_(AFS::getInputStream(filePath)), //throw FileError, (ErrorFileLocked)
- defaultBlockSize_(stream_->getBlockSize()),
- dynamicBlockSize_(defaultBlockSize_),
- notifyProgress_(notifyProgress),
- unevenBytes_(unevenBytes) {}
-
- void appendChunk(std::vector<char>& buffer) //throw FileError
- {
- assert(!eof_);
- if (eof_) return;
-
- const auto startTime = std::chrono::steady_clock::now();
-
- buffer.resize(buffer.size() + dynamicBlockSize_);
- const size_t bytesRead = stream_->tryRead(&*(buffer.end() - dynamicBlockSize_), dynamicBlockSize_); //throw FileError; may return short, only 0 means EOF! => CONTRACT: bytesToRead > 0
- buffer.resize(buffer.size() - dynamicBlockSize_ + bytesRead); //caveat: unsigned arithmetics
-
- const auto stopTime = std::chrono::steady_clock::now();
-
- //report bytes processed
- if (notifyProgress_)
- {
- const size_t bytesToReport = (unevenBytes_ + bytesRead) / 2;
- notifyProgress_(bytesToReport); //throw X!
- unevenBytes_ = (unevenBytes_ + bytesRead) - bytesToReport * 2; //unsigned arithmetics!
- }
-
- if (bytesRead == 0)
- {
- eof_ = true;
- return;
- }
-
- size_t proposedBlockSize = 0;
- const auto loopTimeMs = std::chrono::duration_cast<std::chrono::milliseconds>(stopTime - startTime).count();
-
- if (loopTimeMs >= 100)
- lastDelayViolation_ = stopTime;
-
- //avoid "flipping back": e.g. DVD-ROMs read 32MB at once, so first read may be > 500 ms, but second one will be 0ms!
- if (stopTime >= lastDelayViolation_ + std::chrono::seconds(2))
- {
- lastDelayViolation_ = stopTime;
- proposedBlockSize = dynamicBlockSize_ * 2;
- }
- if (loopTimeMs > 500)
- proposedBlockSize = dynamicBlockSize_ / 2;
-
- if (defaultBlockSize_ <= proposedBlockSize && proposedBlockSize <= BLOCK_SIZE_MAX)
- dynamicBlockSize_ = proposedBlockSize;
- }
-
- bool isEof() const { return eof_; }
-
-private:
- const std::unique_ptr<AFS::InputStream> stream_;
- const size_t defaultBlockSize_;
- size_t dynamicBlockSize_;
- const std::function<void(std::int64_t bytesDelta)> notifyProgress_;
- size_t& unevenBytes_;
- std::chrono::steady_clock::time_point lastDelayViolation_ = std::chrono::steady_clock::now();
- bool eof_ = false;
-};
-}
-
-
-bool zen::filesHaveSameContent(const AbstractPath& filePath1, const AbstractPath& filePath2, const std::function<void(std::int64_t bytesDelta)>& notifyProgress) //throw FileError
-{
- size_t unevenBytes = 0;
- StreamReader reader1(filePath1, notifyProgress, unevenBytes); //throw FileError, (ErrorFileLocked)
- StreamReader reader2(filePath2, notifyProgress, unevenBytes); //
-
- StreamReader* readerLow = &reader1;
- StreamReader* readerHigh = &reader2;
-
- std::vector<char> bufferLow;
- std::vector<char> bufferHigh;
-
- for (;;)
- {
- const size_t bytesChecked = bufferLow.size();
-
- readerLow->appendChunk(bufferLow); //throw FileError
-
- if (bufferLow.size() > bufferHigh.size())
- {
- bufferLow.swap(bufferHigh);
- std::swap(readerLow, readerHigh);
- }
-
- if (!std::equal(bufferLow. begin() + bytesChecked, bufferLow.end(),
- bufferHigh.begin() + bytesChecked))
- return false;
-
- if (readerLow->isEof())
- {
- if (bufferLow.size() < bufferHigh.size())
- return false;
- if (readerHigh->isEof())
- break;
- //bufferLow.swap(bufferHigh); not needed
- std::swap(readerLow, readerHigh);
- }
-
- //don't let sliding buffer grow too large
- bufferHigh.erase(bufferHigh.begin(), bufferHigh.begin() + bufferLow.size());
- bufferLow.clear();
- }
-
- if (unevenBytes != 0)
- throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__));
-
- return true;
-}
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#include "binary.h"
+#include <vector>
+#include <chrono>
+
+using namespace zen;
+using AFS = AbstractFileSystem;
+
+namespace
+{
+/*
+1. there seems to be no perf improvement possible when using file mappings instad of ::ReadFile() calls on Windows:
+ => buffered access: same perf
+ => unbuffered access: same perf on USB stick, file mapping 30% slower on local disk
+
+2. Tests on Win7 x64 show that buffer size does NOT matter if files are located on different physical disks!
+
+Impact of buffer size when files are on same disk:
+
+buffer MB/s
+------------
+ 64 10
+ 128 19
+ 512 40
+1024 48
+2048 56
+4096 56
+8192 56
+*/
+const size_t BLOCK_SIZE_MAX = 16 * 1024 * 1024;
+
+
+struct StreamReader
+{
+ StreamReader(const AbstractPath& filePath, const IOCallback& notifyUnbufferedIO) :
+ stream_(AFS::getInputStream(filePath, notifyUnbufferedIO)), //throw FileError, (ErrorFileLocked)
+ defaultBlockSize_(stream_->getBlockSize()),
+ dynamicBlockSize_(defaultBlockSize_) { assert(defaultBlockSize_ > 0); }
+
+ void appendChunk(std::vector<char>& buffer) //throw FileError, X
+ {
+ assert(!eof_);
+ if (eof_) return;
+
+ buffer.resize(buffer.size() + dynamicBlockSize_);
+
+ const auto startTime = std::chrono::steady_clock::now();
+ const size_t bytesRead = stream_->read(&*(buffer.end() - dynamicBlockSize_), dynamicBlockSize_); //throw FileError, X; return "bytesToRead" bytes unless end of stream!
+ const auto stopTime = std::chrono::steady_clock::now();
+
+ buffer.resize(buffer.size() - dynamicBlockSize_ + bytesRead); //caveat: unsigned arithmetics
+
+ if (bytesRead < dynamicBlockSize_)
+ {
+ eof_ = true;
+ return;
+ }
+
+ size_t proposedBlockSize = 0;
+ const auto loopTimeMs = std::chrono::duration_cast<std::chrono::milliseconds>(stopTime - startTime).count();
+
+ if (loopTimeMs >= 100)
+ lastDelayViolation_ = stopTime;
+
+ //avoid "flipping back": e.g. DVD-ROMs read 32MB at once, so first read may be > 500 ms, but second one will be 0ms!
+ if (stopTime >= lastDelayViolation_ + std::chrono::seconds(2))
+ {
+ lastDelayViolation_ = stopTime;
+ proposedBlockSize = dynamicBlockSize_ * 2;
+ }
+ if (loopTimeMs > 500)
+ proposedBlockSize = dynamicBlockSize_ / 2;
+
+ if (defaultBlockSize_ <= proposedBlockSize && proposedBlockSize <= BLOCK_SIZE_MAX)
+ dynamicBlockSize_ = proposedBlockSize;
+ }
+
+ bool isEof() const { return eof_; }
+
+private:
+ const std::unique_ptr<AFS::InputStream> stream_;
+ const size_t defaultBlockSize_;
+ size_t dynamicBlockSize_;
+ std::chrono::steady_clock::time_point lastDelayViolation_ = std::chrono::steady_clock::now();
+ bool eof_ = false;
+};
+}
+
+
+bool zen::filesHaveSameContent(const AbstractPath& filePath1, const AbstractPath& filePath2, const IOCallback& notifyUnbufferedIO) //throw FileError
+{
+ int64_t totalUnbufferedIO = 0;
+
+ StreamReader reader1(filePath1, IOCallbackDivider(notifyUnbufferedIO, totalUnbufferedIO)); //throw FileError, (ErrorFileLocked)
+ StreamReader reader2(filePath2, IOCallbackDivider(notifyUnbufferedIO, totalUnbufferedIO)); //
+
+ StreamReader* readerLow = &reader1;
+ StreamReader* readerHigh = &reader2;
+
+ std::vector<char> bufferLow;
+ std::vector<char> bufferHigh;
+
+ for (;;)
+ {
+ readerLow->appendChunk(bufferLow); //throw FileError, X
+
+ if (bufferLow.size() > bufferHigh.size())
+ {
+ bufferLow.swap(bufferHigh);
+ std::swap(readerLow, readerHigh);
+ }
+
+ if (!std::equal(bufferLow. begin(), bufferLow.end(),
+ bufferHigh.begin()))
+ return false;
+
+ if (readerLow->isEof())
+ {
+ if (bufferLow.size() < bufferHigh.size())
+ return false;
+ if (readerHigh->isEof())
+ break;
+ //bufferLow.swap(bufferHigh); not needed
+ std::swap(readerLow, readerHigh);
+ }
+
+ //don't let sliding buffer grow too large
+ bufferHigh.erase(bufferHigh.begin(), bufferHigh.begin() + bufferLow.size());
+ bufferLow.clear();
+ }
+
+ if (totalUnbufferedIO % 2 != 0)
+ throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__));
+
+ return true;
+}
bgstack15