2 * @file ByteCompare.cpp
4 * @brief Implementation file for ByteCompare
8 #include "ByteCompare.h"
11 #include "FileLocation.h"
12 #include "UnicodeString.h"
13 #include "IAbortable.h"
14 #include "CompareOptions.h"
15 #include "DiffContext.h"
17 #include "ByteComparator.h"
19 namespace CompareEngines
22 static const int KILO = 1024; // Kilo(byte)
24 /** @brief Quick contents compare's file buffer size. */
25 static const int WMCMPBUFF = 32 * KILO;
27 static void CopyTextStats(const FileTextStats * stats, FileTextStats * myTextStats);
30 * @brief Default constructor.
32 ByteCompare::ByteCompare()
34 , m_piAbortable(nullptr)
40 * @brief Default destructor.
42 ByteCompare::~ByteCompare()
47 * @brief Set compare options from general compare options.
48 * @param [in ]options General compare options.
49 * @return true if succeeded, false otherwise.
51 bool ByteCompare::SetCompareOptions(const CompareOptions & options)
53 m_pOptions.reset(new QuickCompareOptions(options));
54 if (m_pOptions.get() == nullptr)
60 * @brief Set compare-type specific options.
61 * @param [in] stopAfterFirstDiff Do we stop compare after first found diff.
63 void ByteCompare::SetAdditionalOptions(bool stopAfterFirstDiff)
65 m_pOptions->m_bStopAfterFirstDiff = stopAfterFirstDiff;
69 * @brief Set Abortable-interface.
70 * @param [in] piAbortable Pointer to abortable interface.
72 void ByteCompare::SetAbortable(const IAbortable * piAbortable)
74 m_piAbortable = const_cast<IAbortable*>(piAbortable);
78 * @brief Set filedata.
79 * @param [in] items Count of filedata items to set.
80 * @param [in] data File data.
81 * @note Path names are set by SetPaths() function.
83 void ByteCompare::SetFileData(int items, file_data *data)
85 // We support only two files currently!
92 * @brief Compare two specified files, byte-by-byte
93 * @param [in] bStopAfterFirstDiff Stop compare after we find first difference?
94 * @param [in] piAbortable Interface allowing to abort compare
97 int ByteCompare::CompareFiles(FileLocation *location)
100 // Right now, we assume files are in 8-bit encoding
101 // because transform code converted any UCS-2 files to UTF-8
102 // We could compare directly in UCS-2LE here, as an optimization, in that case
103 char buff[2][WMCMPBUFF]; // buffered access to files
105 unsigned diffcode = 0;
107 // area of buffer currently holding data
108 int64_t bfstart[2]; // offset into buff[i] where current data resides
109 int64_t bfend[2]; // past-the-end pointer into buff[i], giving end of current data
110 // buff[0] has bytes to process from buff[0][bfstart[0]] to buff[0][bfend[0]-1]
112 bool eof[2]; // if we've finished file
114 // initialize our buffer pointers and end of file flags
115 for (i = 0; i < 2; ++i)
117 bfstart[i] = bfend[i] = 0;
121 ByteComparator comparator(m_pOptions.get());
124 // we handle the files in WMCMPBUFF sized buffers (variable buff[][])
125 // That is, we do one buffer full at a time
126 // or even less, as we process until one side buffer is empty, then reload that one
128 while (!eof[0] || !eof[1])
130 if (m_piAbortable != nullptr && m_piAbortable->ShouldAbort())
131 return DIFFCODE::CMPABORT;
133 // load or update buffers as appropriate
134 for (i = 0; i < 2; ++i)
136 if (!eof[i] && bfstart[i] == sizeof(buff[i])/sizeof(buff[i][0]))
138 bfstart[i] = bfend[i] = 0;
140 if (!eof[i] && bfend[i] < sizeof(buff[i])/sizeof(buff[i][0]) - 1)
142 // Assume our blocks are in range of int
143 int space = sizeof(buff[i])/sizeof(buff[i][0]) - (int) bfend[i];
144 int rtn = _read(m_inf[i].desc, &buff[i][bfend[i]], (unsigned)space);
146 return DIFFCODE::CMPERR;
150 if (m_inf[0].desc == m_inf[1].desc)
152 bfstart[1] = bfstart[0];
155 location[1] = location[0];
156 memcpy(&buff[1][bfend[1] - rtn], &buff[0][bfend[0] - rtn], rtn);
161 // where to start comparing right now
162 const char* ptr0 = &buff[0][bfstart[0]];
163 const char* ptr1 = &buff[1][bfstart[1]];
165 // remember where we started
166 const char* orig0 = ptr0;
167 const char* orig1 = ptr1;
169 // how far can we go right now?
170 const char* end0 = &buff[0][bfend[0]];
171 const char* end1 = &buff[1][bfend[1]];
173 int64_t offset0 = (ptr0 - &buff[0][0]);
174 int64_t offset1 = (ptr1 - &buff[1][0]);
176 // are these two buffers the same?
177 int result = comparator.CompareBuffers(m_textStats[0], m_textStats[1],
178 ptr0, ptr1, end0, end1, eof[0], eof[1], offset0, offset1);
179 if (result == ByteComparator::RESULT_DIFF)
181 if (m_pOptions->m_bStopAfterFirstDiff)
183 // By bailing out here
184 // we leave our text statistics incomplete
185 return diffcode | DIFFCODE::DIFF;
189 diffcode |= DIFFCODE::DIFF;
192 // move our current pointers over what we just compared
193 assert(ptr0 >= orig0);
194 assert(ptr1 >= orig1);
195 bfstart[0] += ptr0 - orig0;
196 bfstart[1] += ptr1 - orig1;
199 else if (result == ByteComparator::NEED_MORE_0)
201 const int m = (int)(ptr0 - &buff[0][0]);
202 const int l = (int)(end0 - ptr0);
203 //move uncompared data to begin of buff0
204 memcpy(&buff[0][0], &buff[0][m], l);
206 bfstart[1] += ptr1 - orig1;
209 else if (result == ByteComparator::NEED_MORE_1)
211 const int m = (int)(ptr1 - &buff[1][0]);
212 const int l = (int)(end1 - ptr1);
213 //move uncompared data to begin of buff1
214 memcpy(&buff[1][0], &buff[1][m], l);
216 bfstart[0] += ptr0 - orig0;
219 else if (result == ByteComparator::NEED_MORE_BOTH)
221 if ((end0 == ptr0) && (end1 == ptr1))
223 bfstart[0] += ptr0 - orig0;
225 bfstart[1] += ptr1 - orig1;
232 const int m = (int)(ptr0 - orig0);
233 const int l = (int)(end0 - ptr0);
234 //move uncompared data to begin of buff0
235 memcpy(&buff[0][0], &buff[0][m], l);
241 const int m = (int)(ptr1 - orig1);
242 const int l = (int)(end1 - ptr1);
243 //move uncompared data to begin of buff1
244 memcpy(&buff[1][0], &buff[1][ m], l);
252 assert(result == ByteComparator::RESULT_SAME);
255 // Did we finish both files?
256 // We set the text/binary status only for fully compared files. Only
257 // then the result is reliable.
258 if (eof[0] && eof[1])
260 bool bBin0 = (m_textStats[0].nzeros > 0);
261 bool bBin1 = (m_textStats[1].nzeros > 0);
264 diffcode |= DIFFCODE::BIN | DIFFCODE::BINSIDE1 | DIFFCODE::BINSIDE2;
266 diffcode |= DIFFCODE::BIN | DIFFCODE::BINSIDE1;
268 diffcode |= DIFFCODE::BIN | DIFFCODE::BINSIDE2;
270 diffcode |= DIFFCODE::TEXT;
272 // If either unfinished, they differ
273 if (ptr0 != end0 || ptr1 != end1)
274 diffcode = (diffcode & DIFFCODE::DIFF);
275 if (diffcode & DIFFCODE::DIFF)
276 return diffcode | DIFFCODE::DIFF;
278 return diffcode | DIFFCODE::SAME;
285 * @brief Copy text stat results from diffutils back into the FileTextStats structure
287 static void CopyTextStats(const FileTextStats * stats, FileTextStats * myTextStats)
289 myTextStats->ncrlfs = stats->ncrlfs;
290 myTextStats->ncrs = stats->ncrs;
291 myTextStats->nlfs = stats->nlfs;
292 myTextStats->nzeros = stats->nzeros;
296 * @brief Return text statistics for last compare.
297 * @param [in] side For which file to return statistics.
298 * @param [out] stats Stats as asked.
300 void ByteCompare::GetTextStats(int side, FileTextStats *stats) const
302 CopyTextStats(&m_textStats[side], stats);
305 } // namespace CompareEngines