1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
|
// **************************************************************************
// * This file is part of the FreeFileSync project. It is distributed under *
// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
// **************************************************************************
#include "binary.h"
#include <zen/tick_count.h>
#include <vector>
#include <zen/file_io.h>
#include <zen/int64.h>
#include <boost/thread/tss.hpp>
using namespace zen;
namespace
{
inline
void setMinSize(std::vector<char>& buffer, size_t minSize)
{
if (buffer.size() < minSize) //this is similar to reserve(), but we need a "properly initialized" array here
buffer.resize(minSize);
}
class BufferSize
{
public:
BufferSize() : bufSize(BUFFER_SIZE_START) {}
void inc()
{
if (bufSize < BUFFER_SIZE_MAX)
bufSize *= 2;
}
void dec()
{
if (bufSize > BUFFER_SIZE_MIN)
bufSize /= 2;
}
operator size_t() const { return bufSize; }
private:
static const size_t BUFFER_SIZE_MIN = 64 * 1024;
static const size_t BUFFER_SIZE_START = 128 * 1024; //initial buffer size
static const size_t BUFFER_SIZE_MAX = 16 * 1024 * 1024;
/*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
*/
size_t bufSize;
};
const std::int64_t TICKS_PER_SEC = ticksPerSec();
}
bool zen::filesHaveSameContent(const Zstring& filename1, const Zstring& filename2, CompareCallback& callback)
{
static boost::thread_specific_ptr<std::vector<char>> cpyBuf1;
static boost::thread_specific_ptr<std::vector<char>> cpyBuf2;
if (!cpyBuf1.get())
cpyBuf1.reset(new std::vector<char>());
if (!cpyBuf2.get())
cpyBuf2.reset(new std::vector<char>());
std::vector<char>& memory1 = *cpyBuf1;
std::vector<char>& memory2 = *cpyBuf2;
FileInput file1(filename1); //throw FileError
FileInput file2(filename2); //
BufferSize bufferSize;
TickVal lastDelayViolation = getTicks();
do
{
setMinSize(memory1, bufferSize);
setMinSize(memory2, bufferSize);
const TickVal startTime = getTicks();
const size_t length1 = file1.read(&memory1[0], bufferSize); //throw FileError
const size_t length2 = file2.read(&memory2[0], bufferSize); //returns actual number of bytes read
//send progress updates immediately after reading to reliably allow speed calculations for our clients!
callback.updateCompareStatus(to<Int64>(std::max(length1, length2)));
if (length1 != length2 || ::memcmp(&memory1[0], &memory2[0], length1) != 0)
return false;
//-------- dynamically set buffer size to keep callback interval between 100 - 500ms ---------------------
if (TICKS_PER_SEC > 0)
{
const TickVal now = getTicks();
const std::int64_t loopTime = dist(startTime, now) * 1000 / TICKS_PER_SEC; //unit: [ms]
if (loopTime < 100)
{
if (dist(lastDelayViolation, now) / TICKS_PER_SEC > 2) //avoid "flipping back": e.g. DVD-Roms read 32MB at once, so first read may be > 500 ms, but second one will be 0ms!
{
lastDelayViolation = now;
bufferSize.inc();
}
}
else if (loopTime > 500)
{
lastDelayViolation = now;
bufferSize.dec();
}
}
//------------------------------------------------------------------------------------------------
}
while (!file1.eof());
if (!file2.eof()) //highly unlikely, but possible! (but then again, not in this context where both files have same size...)
return false;
return true;
}
|