OSDN Git Service

Reduce compilation time (2)
[winmerge-jp/winmerge-jp.git] / Src / CompareEngines / ByteCompare.cpp
1 /**
2  * @file  ByteCompare.cpp
3  *
4  * @brief Implementation file for ByteCompare
5  */
6
7 #include "pch.h"
8 #include "ByteCompare.h"
9 #include <cassert>
10 #include <io.h>
11 #include "FileLocation.h"
12 #include "UnicodeString.h"
13 #include "IAbortable.h"
14 #include "CompareOptions.h"
15 #include "DiffContext.h"
16 #include "diff.h"
17 #include "ByteComparator.h"
18
19 namespace CompareEngines
20 {
21
22 static const int KILO = 1024; // Kilo(byte)
23
24 /** @brief Quick contents compare's file buffer size. */
25 static const int WMCMPBUFF = 32 * KILO;
26
27 static void CopyTextStats(const FileTextStats * stats, FileTextStats * myTextStats);
28
29 /**
30  * @brief Default constructor.
31  */
32 ByteCompare::ByteCompare()
33                 : m_pOptions(nullptr)
34                 , m_piAbortable(nullptr)
35                 , m_inf(nullptr)
36 {
37 }
38
39 /**
40  * @brief Default destructor.
41  */
42 ByteCompare::~ByteCompare()
43 {
44 }
45
46 /**
47  * @brief Set compare options from general compare options.
48  * @param [in ]options General compare options.
49  * @return true if succeeded, false otherwise.
50  */
51 bool ByteCompare::SetCompareOptions(const CompareOptions & options)
52 {
53         m_pOptions.reset(new QuickCompareOptions(options));
54         if (m_pOptions.get() == nullptr)
55                 return false;
56         return true;
57 }
58
59 /**
60  * @brief Set compare-type specific options.
61  * @param [in] stopAfterFirstDiff Do we stop compare after first found diff.
62  */
63 void ByteCompare::SetAdditionalOptions(bool stopAfterFirstDiff)
64 {
65         m_pOptions->m_bStopAfterFirstDiff = stopAfterFirstDiff;
66 }
67
68 /**
69  * @brief Set Abortable-interface.
70  * @param [in] piAbortable Pointer to abortable interface.
71  */
72 void ByteCompare::SetAbortable(const IAbortable * piAbortable)
73 {
74         m_piAbortable = const_cast<IAbortable*>(piAbortable);
75 }
76
77 /**
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.
82  */
83 void ByteCompare::SetFileData(int items, file_data *data)
84 {
85         // We support only two files currently!
86         assert(items == 2);
87         m_inf = data;
88 }
89
90
91 /**
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
95  * @return DIFFCODE
96  */
97 int ByteCompare::CompareFiles(FileLocation *location)
98 {
99         // TODO
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
104         int i;
105         unsigned diffcode = 0;
106
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]
111
112         bool eof[2]; // if we've finished file
113
114         // initialize our buffer pointers and end of file flags
115         for (i = 0; i < 2; ++i)
116         {
117                 bfstart[i] = bfend[i] = 0;
118                 eof[i] = false;
119         }
120
121         ByteComparator comparator(m_pOptions.get());
122
123         // Begin loop
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
127         // and continue
128         while (!eof[0] || !eof[1])
129         {
130                 if (m_piAbortable != nullptr && m_piAbortable->ShouldAbort())
131                         return DIFFCODE::CMPABORT;
132
133                 // load or update buffers as appropriate
134                 for (i = 0; i < 2; ++i)
135                 {
136                         if (!eof[i] && bfstart[i] == sizeof(buff[i])/sizeof(buff[i][0]))
137                         {
138                                 bfstart[i] = bfend[i] = 0;
139                         }
140                         if (!eof[i] && bfend[i] < sizeof(buff[i])/sizeof(buff[i][0]) - 1)
141                         {
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);
145                                 if (rtn == -1)
146                                         return DIFFCODE::CMPERR;
147                                 if (rtn < space)
148                                         eof[i] = true;
149                                 bfend[i] += rtn;
150                                 if (m_inf[0].desc == m_inf[1].desc)
151                                 {
152                                         bfstart[1] = bfstart[0];
153                                         bfend[1] = bfend[0];
154                                         eof[1] = eof[0];
155                                         location[1] = location[0];
156                                         memcpy(&buff[1][bfend[1] - rtn], &buff[0][bfend[0] - rtn], rtn);
157                                         break;
158                                 }
159                         }
160                 }
161                 // where to start comparing right now
162                 const char* ptr0 = &buff[0][bfstart[0]];
163                 const char* ptr1 = &buff[1][bfstart[1]];
164
165                 // remember where we started
166                 const char* orig0 = ptr0;
167                 const char* orig1 = ptr1;
168
169                 // how far can we go right now?
170                 const char* end0 = &buff[0][bfend[0]];
171                 const char* end1 = &buff[1][bfend[1]];
172
173                 int64_t offset0 = (ptr0 - &buff[0][0]);
174                 int64_t offset1 = (ptr1 - &buff[1][0]);
175
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)
180                 {
181                         if (m_pOptions->m_bStopAfterFirstDiff)
182                         {
183                                 // By bailing out here
184                                 // we leave our text statistics incomplete
185                                 return diffcode | DIFFCODE::DIFF;
186                         }
187                         else
188                         {
189                                 diffcode |= DIFFCODE::DIFF;
190                                 ptr0 = end0;
191                                 ptr1 = end1;
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;
197                         }
198                 }
199                 else if (result == ByteComparator::NEED_MORE_0)
200                 {
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);
205                         bfstart[0] = 0;
206                         bfstart[1] += ptr1 - orig1;
207                         bfend[0] = l;
208                 }
209                 else if (result == ByteComparator::NEED_MORE_1)
210                 {
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);
215                         bfstart[1] = 0;
216                         bfstart[0] += ptr0 - orig0;
217                         bfend[1] = l;
218                 }
219                 else if (result == ByteComparator::NEED_MORE_BOTH)
220                 {
221                         if ((end0 == ptr0) && (end1 == ptr1))
222                         {
223                                 bfstart[0] += ptr0 - orig0;
224                                 bfend[0] = 0;
225                                 bfstart[1] += ptr1 - orig1;
226                                 bfend[1] = 0;
227                         }
228                         else
229                         {
230                                 if (ptr0 < end0)
231                                 {
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);
236                                         bfstart[0] = 0;
237                                         bfend[0] += l;
238                                 }
239                                 if (ptr1 < end1)
240                                 {
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);
245                                         bfstart[1] = 0;
246                                         bfend[1] += l;
247                                 }
248                         }
249                 }
250                 else
251                 {
252                         assert(result == ByteComparator::RESULT_SAME);
253                 }
254
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])
259                 {
260                         bool bBin0 = (m_textStats[0].nzeros > 0);
261                         bool bBin1 = (m_textStats[1].nzeros > 0);
262
263                         if (bBin0 && bBin1)
264                                 diffcode |= DIFFCODE::BIN | DIFFCODE::BINSIDE1 | DIFFCODE::BINSIDE2;
265                         else if (bBin0)
266                                 diffcode |= DIFFCODE::BIN | DIFFCODE::BINSIDE1;
267                         else if (bBin1)
268                                 diffcode |= DIFFCODE::BIN | DIFFCODE::BINSIDE2;
269                         else
270                                 diffcode |= DIFFCODE::TEXT;
271
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;
277                         else
278                                 return diffcode | DIFFCODE::SAME;
279                 }
280         }
281         return diffcode;
282 }
283
284 /**
285  * @brief Copy text stat results from diffutils back into the FileTextStats structure
286  */
287 static void CopyTextStats(const FileTextStats * stats, FileTextStats * myTextStats)
288 {
289         myTextStats->ncrlfs = stats->ncrlfs;
290         myTextStats->ncrs = stats->ncrs;
291         myTextStats->nlfs = stats->nlfs;
292         myTextStats->nzeros = stats->nzeros;
293 }
294
295 /**
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.
299  */
300 void ByteCompare::GetTextStats(int side, FileTextStats *stats) const
301 {
302         CopyTextStats(&m_textStats[side], stats);
303 }
304
305 } // namespace CompareEngines