2 * @file stringdiffs.cpp
4 * @brief Implementation file for sd_ComputeWordDiffs (q.v.)
8 #include "stringdiffs.h"
15 #include "CompareOptions.h"
16 #include "stringdiffsi.h"
21 static bool Initialized;
22 static bool CustomChars;
23 static TCHAR *BreakChars;
24 static TCHAR BreakCharDefaults[] = _T(",.;:");
26 static bool isSafeWhitespace(TCHAR ch);
27 static bool isWordBreak(int breakType, const TCHAR *str, int index);
31 BreakChars = &BreakCharDefaults[0];
46 void sd_SetBreakChars(const TCHAR *breakChars)
54 BreakChars = _tcsdup(breakChars);
58 sd_ComputeWordDiffs(const String& str1, const String& str2,
59 bool case_sensitive, int whitespace, int breakType, bool byte_level,
60 std::vector<wdiff> * pDiffs)
62 String strs[3] = {str1, str2, _T("")};
63 sd_ComputeWordDiffs(2, strs, case_sensitive, whitespace, breakType, byte_level, pDiffs);
68 Comp02Functor(const String *strs, bool case_sensitive) :
69 strs_(strs), case_sensitive_(case_sensitive)
72 bool operator()(const wdiff &wd3)
74 size_t wlen0 = wd3.end[0] - wd3.begin[0] + 1;
75 size_t wlen2 = wd3.end[2] - wd3.begin[2] + 1;
80 if (memcmp(&strs_[0][wd3.begin[0]], &strs_[2][wd3.begin[2]], wlen0 * sizeof(TCHAR)) != 0)
85 if (_tcsnicmp(&strs_[0][wd3.begin[0]], &strs_[2][wd3.begin[2]], wlen0) != 0)
95 * @brief Construct our worker object and tell it to do the work
98 sd_ComputeWordDiffs(int nFiles, const String str[3],
99 bool case_sensitive, int whitespace, int breakType, bool byte_level,
100 std::vector<wdiff> * pDiffs)
104 stringdiffs sdiffs(str[0], str[1], case_sensitive, whitespace, breakType, pDiffs);
105 // Hash all words in both lines and then compare them word by word
106 // storing differences into m_wdiffs
107 sdiffs.BuildWordDiffList();
110 sdiffs.wordLevelToByteLevel();
112 // Now copy m_wdiffs into caller-supplied m_pDiffs (coalescing adjacents if possible)
113 sdiffs.PopulateDiffs();
120 stringdiffs sdiffs(str[1], str[2], case_sensitive, whitespace, breakType, pDiffs);
121 sdiffs.BuildWordDiffList();
123 sdiffs.wordLevelToByteLevel();
124 sdiffs.PopulateDiffs();
125 for (size_t i = 0; i < pDiffs->size(); i++)
127 wdiff& diff = (*pDiffs)[i];
128 diff.begin[2] = diff.begin[1];
129 diff.begin[1] = diff.begin[0];
131 diff.end[2] = diff.end[1];
132 diff.end[1] = diff.end[0];
136 else if (str[1].empty())
138 stringdiffs sdiffs(str[0], str[2], case_sensitive, whitespace, breakType, pDiffs);
139 sdiffs.BuildWordDiffList();
141 sdiffs.wordLevelToByteLevel();
142 sdiffs.PopulateDiffs();
143 for (size_t i = 0; i < pDiffs->size(); i++)
145 wdiff& diff = (*pDiffs)[i];
146 diff.begin[2] = diff.begin[1];
147 //diff.begin[0] = diff.begin[0];
149 diff.end[2] = diff.end[1];
150 //diff.end[0] = diff.end[0];
154 else if (str[2].empty())
156 stringdiffs sdiffs(str[0], str[1], case_sensitive, whitespace, breakType, pDiffs);
157 sdiffs.BuildWordDiffList();
159 sdiffs.wordLevelToByteLevel();
160 sdiffs.PopulateDiffs();
161 for (size_t i = 0; i < pDiffs->size(); i++)
163 wdiff& diff = (*pDiffs)[i];
164 //diff.begin[1] = diff.begin[1];
165 //diff.begin[0] = diff.begin[0];
167 //diff.end[1] = diff.end[1];
168 //diff.end[0] = diff.end[0];
174 std::vector<wdiff> diffs10, diffs12;
175 stringdiffs sdiffs10(str[1], str[0], case_sensitive, whitespace, breakType, &diffs10);
176 stringdiffs sdiffs12(str[1], str[2], case_sensitive, whitespace, breakType, &diffs12);
177 // Hash all words in both lines and then compare them word by word
178 // storing differences into m_wdiffs
179 sdiffs10.BuildWordDiffList();
180 sdiffs12.BuildWordDiffList();
183 sdiffs10.wordLevelToByteLevel();
184 sdiffs12.wordLevelToByteLevel();
186 // Now copy m_wdiffs into caller-supplied m_pDiffs (coalescing adjacents if possible)
187 sdiffs10.PopulateDiffs();
188 sdiffs12.PopulateDiffs();
190 Make3wayDiff(*pDiffs, diffs10, diffs12,
191 Comp02Functor(str, case_sensitive), false);
197 * @brief stringdiffs constructor simply loads all members from arguments
199 stringdiffs::stringdiffs(const String & str1, const String & str2,
200 bool case_sensitive, int whitespace, int breakType,
201 std::vector<wdiff> * pDiffs)
204 , m_case_sensitive(case_sensitive)
205 , m_whitespace(whitespace)
206 , m_breakType(breakType)
208 , m_matchblock(true) // Change to false to get word to word compare
214 * The destructor frees all diffs added to the vectors.
216 stringdiffs::~stringdiffs()
220 #ifdef STRINGDIFF_LOGGING
222 stringdiffs::debugoutput()
224 for (size_t i = 0; i < m_wdiffs.size(); i++)
229 int s1 = m_wdiffs[i].begin[0];
230 int e1 = m_wdiffs[i].end[0];
231 int s2 = m_wdiffs[i].begin[1];
232 int e2 = m_wdiffs[i].end[1];
234 int len1 = e1 - s1 + 1;
235 int len2 = e2 - s2 + 1;
238 str1 = m_str1.substr(s1 ,e1 - s1 + 1);
240 str1 = m_str1.substr(s1, 50);
243 str2 = m_str2.substr(s2, e2- s2 + 1);
245 str2 = m_str2.substr(s2, 50);
247 wsprintf(buf, _T("left= %s, %d,%d,\nright= %s, %d,%d \n"),
248 str1.c_str(), s1, e1, str2.c_str(), s2, e2);
249 OutputDebugString(buf);
255 stringdiffs::BuildWordDiffList_DP()
257 std::vector<char> edscript;
259 //if (dp(edscript) <= 0)
264 for (size_t k = 0; k < edscript.size(); k++)
267 if (edscript[k] == '-')
269 if (m_whitespace == WHITESPACE_IGNORE_ALL)
271 if (IsSpace(m_words1[i]))
278 s1 = m_words1[i].start;
279 e1 = m_words1[i].end;
280 s2 = m_words2[j-1].end+1;
282 m_wdiffs.push_back(wdiff(s1, e1, s2, e2));
285 else if (edscript[k] == '+')
287 if (m_whitespace == WHITESPACE_IGNORE_ALL)
289 if (IsSpace(m_words2[j]))
296 s1 = m_words1[i-1].end+1;
298 s2 = m_words2[j].start;
299 e2 = m_words2[j].end;
300 m_wdiffs.push_back(wdiff(s1, e1, s2, e2));
303 else if (edscript[k] == '!')
305 if (m_whitespace == WHITESPACE_IGNORE_CHANGE || m_whitespace == WHITESPACE_IGNORE_ALL)
307 if (IsSpace(m_words1[i]) && IsSpace(m_words2[j]))
314 s1 = m_words1[i].start;
315 e1 = m_words1[i].end;
316 s2 = m_words2[j].start;
317 e2 = m_words2[j].end ;
318 m_wdiffs.push_back(wdiff(s1, e1, s2, e2));
326 #ifdef STRINGDIFF_LOGGING
333 * @brief Add all different elements between lines to the wdiff list
336 stringdiffs::BuildWordDiffList()
338 BuildWordsArray(m_str1, m_words1);
339 BuildWordsArray(m_str2, m_words2);
342 if (m_words1.size() > 20480 || m_words2.size() > 20480)
344 if (m_words1.size() > 2048 || m_words2.size() > 2048)
347 int s1 = m_words1[0].start;
348 int e1 = m_words1[m_words1.size() - 1].end;
349 int s2 = m_words2[0].start;
350 int e2 = m_words2[m_words2.size() - 1].end;
351 m_wdiffs.push_back(wdiff(s1, e1, s2, e2));
355 BuildWordDiffList_DP();
359 * @brief Break line into constituent words
362 stringdiffs::BuildWordsArray(const String & str, std::vector<word>& words)
367 words.push_back(word(0, -1, 0, 0));
369 // state when we are looking for next word
371 if (isSafeWhitespace(str[i]))
378 // just finished a word
379 // e is first word character (space or at end)
382 words.push_back(word(begin, e, dlspace, Hash(str, begin, e, 0)));
384 if (i == str.length())
389 // state when we are inside a word
392 if (i == str.length() || ((atspace = isSafeWhitespace(str[i])) != 0) || isWordBreak(m_breakType, str.c_str(), i))
396 // just finished a word
397 // e is first non-word character (space or at end)
400 words.push_back(word(begin, e, dlword, Hash(str, begin, e, 0)));
402 if (i == str.length())
413 // start a new word because we hit a non-whitespace word break (eg, a comma)
414 // but, we have to put each word break character into its own word
415 words.push_back(word(i, i, dlbreak, Hash(str, i, i, 0)));
422 goto inword; // safe even if we're at the end or no longer in a word
426 * @brief Populate m_pDiffs from m_wdiffs (combining adjacent diffs)
428 * Doing the combining of adjacent diffs here keeps some complexity out of BuildWordsArray.
431 stringdiffs::PopulateDiffs()
433 for (int i=0; i< (int)m_wdiffs.size(); ++i)
436 // combine it with next ?
437 if (i+1< (int)m_wdiffs.size())
439 if (m_wdiffs[i].end[0] + 1 == m_wdiffs[i+1].begin[0]
440 && m_wdiffs[i].end[1] + 1 == m_wdiffs[i+1].begin[1])
442 // diff[i] and diff[i+1] are contiguous
443 // so combine them into diff[i+1] and ignore diff[i]
444 m_wdiffs[i+1].begin[0] = m_wdiffs[i].begin[0];
445 m_wdiffs[i+1].begin[1] = m_wdiffs[i].begin[1];
451 // Should never have a pair where both are missing
452 assert(m_wdiffs[i].begin[0]>=0 || m_wdiffs[i].begin[1]>=0);
454 // Store the diff[i] in the caller list (m_pDiffs)
455 m_pDiffs->push_back(wdiff(m_wdiffs[i]));
462 /* Rotate a value n bits to the left. */
463 #define UINT_BIT (sizeof (unsigned) * CHAR_BIT)
464 #define ROL(v, n) ((v) << (n) | (v) >> (UINT_BIT - (n)))
465 /* Given a hash value and a new character, return a new hash value. */
466 #define HASH(h, c) ((c) + ROL (h, 7))
469 stringdiffs::Hash(const String & str, int begin, int end, unsigned h) const
471 for (int i = begin; i <= end; ++i)
473 unsigned ch = static_cast<unsigned>(str[i]);
474 if (m_case_sensitive)
480 ch = static_cast<unsigned>(_totupper(ch));
489 * @brief Compare two words (by reference to original strings)
492 stringdiffs::AreWordsSame(const word & word1, const word & word2) const
494 if (this->m_whitespace != WHITESPACE_COMPARE_ALL)
496 if (IsSpace(word1) && IsSpace(word2))
499 if (word1.hash != word2.hash)
501 if (word1.length() != word2.length())
503 for (int i=0; i<word1.length(); ++i)
505 if (!caseMatch(m_str1[word1.start+i], m_str2[word2.start+i]))
512 * @brief Return true if characters match
515 stringdiffs::caseMatch(TCHAR ch1, TCHAR ch2) const
517 if (m_case_sensitive)
520 return _totupper(ch1)==_totupper(ch2);
524 * @ brief An O(NP) Sequence Comparison Algorithm. Sun Wu, Udi Manber, Gene Myers
527 stringdiffs::onp(std::vector<char> &edscript)
529 int M = m_words1.size() - 1;
530 int N = m_words2.size() - 1;
531 bool exchanged = false;
534 M = m_words2.size() - 1;
535 N = m_words1.size() - 1;
538 int *fp = (new int[(M+1) + 1 + (N+1)]) + (M+1);
539 std::vector<char> *es = (new std::vector<char>[(M+1) + 1 + (N+1)]) + (M+1);
543 for (k = -(M+1); k <= (N+1); k++)
549 for (k = -p; k <= DELTA-1; k++)
551 fp[k] = snake(k, std::max(fp[k-1] + 1, fp[k+1]), exchanged);
553 es[k] = fp[k-1] + 1 > fp[k+1] ? es[k-1] : es[k+1];
554 es[k].push_back(fp[k-1] + 1 > fp[k+1] ? '+' : '-');
555 es[k].resize(es[k].size() + fp[k] - std::max(fp[k-1] + 1, fp[k+1]), '=');
557 for (k = DELTA + p; k >= DELTA+1; k--)
559 fp[k] = snake(k, std::max(fp[k-1] + 1, fp[k+1]), exchanged);
561 es[k] = fp[k-1] + 1 > fp[k+1] ? es[k-1] : es[k+1];
562 es[k].push_back(fp[k-1] + 1 > fp[k+1] ? '+' : '-');
563 es[k].resize(es[k].size() + fp[k] - std::max(fp[k-1] + 1, fp[k+1]), '=');
566 fp[k] = snake(k, std::max(fp[k-1] + 1, fp[k+1]), exchanged);
568 es[k] = fp[k-1] + 1 > fp[k+1] ? es[k-1] : es[k+1];
569 es[k].push_back(fp[k-1] + 1 > fp[k+1] ? '+' : '-');
570 es[k].resize(es[k].size() + fp[k] - std::max(fp[k-1] + 1, fp[k+1]), '=');
571 } while (fp[k] != N);
573 std::vector<char> &ses = es[DELTA]; // Shortest edit script
577 for (size_t i = 1; i < ses.size(); i++)
582 if (i + 1 < ses.size() && ses[i + 1] == '-')
584 edscript.push_back('!');
590 edscript.push_back(exchanged ? '-' : '+');
595 if (i + 1 < ses.size() && ses[i + 1] == '+')
597 edscript.push_back('!');
603 edscript.push_back(exchanged ? '+' : '-');
608 edscript.push_back('=');
612 delete [] (es - (M+1));
613 delete [] (fp - (M+1));
619 stringdiffs::snake(int k, int y, bool exchanged)
622 int M = exchanged ? m_words2.size() - 1 : m_words1.size() - 1;
623 int N = exchanged ? m_words1.size() - 1 : m_words2.size() - 1;
625 while (x < M && y < N && (exchanged ? AreWordsSame(m_words1[y + 1], m_words2[x + 1]) : AreWordsSame(m_words1[x + 1], m_words2[y + 1]))) {
626 x = x + 1; y = y + 1;
632 * @brief Return true if chars match
634 * Caller must not call this for lead bytes
637 matchchar(TCHAR ch1, TCHAR ch2, bool casitive)
642 return _totupper(ch1)==_totupper(ch2);
646 /** Does character introduce a multicharacter character? */
647 static inline bool IsLeadByte(TCHAR ch)
652 return _getmbcp() && IsDBCSLeadByte(ch);
657 * @brief Is it whitespace (excludes all lead & trail bytes)?
660 isSafeWhitespace(TCHAR ch)
662 return _istspace((unsigned)ch) && !IsLeadByte(ch);
666 * @brief Is it a non-whitespace wordbreak character (ie, punctuation)?
669 isWordBreak(int breakType, const TCHAR *str, int index)
671 TCHAR ch = str[index];
672 // breakType==1 means break also on punctuation
674 if ((ch & 0xff00) == 0)
676 TCHAR nextCh = str[index + 1];
677 // breakType==0 means whitespace only
680 return _tcschr(BreakChars, ch) != 0;
685 // ch==0xff0c/* Fullwidth Full Stop */ ||
686 // ch==0xff0e/* Fullwidth Comma */ ||
687 // ch==0xff1b/* Fullwidth Semicolon */ ||
688 // ch==0xff1a/* Fullwidth Colon */ ||
689 // ch==0x3002/* Ideographic Full Stop */ ||
690 // ch==0x3001/* Ideographic Comma */
693 // WORD wCharType, wCharTypeNext;
694 // GetStringTypeW(CT_CTYPE3, &ch, 1, &wCharType);
695 // TCHAR nextCh = str[index + 1];
696 // GetStringTypeW(CT_CTYPE3, &nextCh, 1, &wCharTypeNext);
697 // return (wCharType != wCharTypeNext);
702 // breakType==0 means whitespace only
705 return _tcschr(BreakChars, ch) != 0;
711 * @brief Return pointer to last character of specified string (handle MBCS)
713 * If the last byte is a broken multibyte (ie, a solo lead byte), this returns previous char
716 LastChar(LPCTSTR psz, int len)
718 if (!len) return psz;
720 if (!_getmbcp()) return psz+len-1;
722 LPCTSTR lastValid = psz+len-1;
725 while (psz<lastValid)
732 if (psz==lastValid && !IsLeadByte(*psz))
734 else // last character was multibyte or broken multibyte
739 * @brief advance current pointer over whitespace, until not whitespace or beyond end
740 * @param pcurrent [in,out] current location (to be advanced)
741 * @param end [in] last valid position (only go one beyond this)
744 AdvanceOverWhitespace(LPCTSTR * pcurrent, LPCTSTR end)
746 // advance over whitespace
747 while (*pcurrent <= end && isSafeWhitespace(**pcurrent))
748 ++(*pcurrent); // DBCS safe because of isSafeWhitespace above
752 * @brief Compute begin1,begin2,end1,end2 to display byte difference between strings str1 & str2
753 * @param casitive [in] true for case-sensitive, false for case-insensitive
754 * @param xwhite [in] This governs whether we handle whitespace specially (see WHITESPACE_COMPARE_ALL, WHITESPACE_IGNORE_CHANGE, WHITESPACE_IGNORE_ALL)
755 * @param [out] begin return -1 if not found or pos of equal
756 * @param [out] end return -1 if not found or pos of equal valid if begin1 >=0
757 * @param [in] equal false surch for a diff, true surch for equal
760 * Assumes whitespace is never leadbyte or trailbyte!
763 sd_ComputeByteDiff(String & str1, String & str2,
764 bool casitive, int xwhite,
765 int begin[2], int end[2], bool equal)
767 // Set to sane values
768 // Also this way can distinguish if we set begin[0] to -1 for no diff in line
769 begin[0] = end[0] = begin[1] = end[1] = 0;
771 int len1 = str1.length();
772 int len2 = str2.length();
774 LPCTSTR pbeg1 = str1.c_str();
775 LPCTSTR pbeg2 = str2.c_str();
777 if (len1 == 0 || len2 == 0)
789 // cursors from front, which we advance to beginning of difference
793 // pen1,pen2 point to the last valid character (broken multibyte lead chars don't count)
794 LPCTSTR pen1 = LastChar(py1, len1);
795 LPCTSTR pen2 = LastChar(py2, len2);
797 if (xwhite != WHITESPACE_COMPARE_ALL)
799 // Ignore leading and trailing whitespace
800 // by advancing py1 and py2
801 // and retreating pen1 and pen2
802 while (py1 < pen1 && isSafeWhitespace(*py1))
803 ++py1; // DBCS safe because of isSafeWhitespace above
804 while (py2 < pen2 && isSafeWhitespace(*py2))
805 ++py2; // DBCS safe because of isSafeWhitespace above
806 if ((pen1 < pbeg1 + len1 - 1 || pen2 < pbeg2 + len2 -1)
807 && (!len1 || !len2 || pbeg1[len1] != pbeg2[len2]))
809 // mismatched broken multibyte ends
813 while (pen1 > py1 && isSafeWhitespace(*pen1))
814 pen1 = CharPrev(py1, pen1);
815 while (pen2 > py2 && isSafeWhitespace(*pen2))
816 pen2 = CharPrev(py2, pen2);
819 //check for excaption of empty string on one side
820 //In that case display all as a diff
821 if (!equal && (((py1 == pen1) && isSafeWhitespace(*pen1)) ||
822 ((py2 == pen2) && isSafeWhitespace(*pen2))))
830 // Advance over matching beginnings of lines
831 // Advance py1 & py2 from beginning until find difference or end
834 // Potential difference extends from py1 to pen1 and py2 to pen2
836 // Check if either side finished
837 if (py1 > pen1 && py2 > pen2)
839 begin[0] = end[0] = begin[1] = end[1] = -1;
842 if (py1 > pen1 || py2 > pen2)
847 // handle all the whitespace logic (due to WinMerge whitespace settings)
848 if (xwhite && py1 < pen1 && isSafeWhitespace(*py1))
850 if (xwhite==WHITESPACE_IGNORE_CHANGE && !isSafeWhitespace(*py2))
852 // py1 is white but py2 is not
853 // in WHITESPACE_IGNORE_CHANGE mode,
854 // this doesn't qualify as skippable whitespace
855 break; // done with forward search
857 // gobble up all whitespace in current area
858 AdvanceOverWhitespace(&py1, pen1); // will go beyond end
859 AdvanceOverWhitespace(&py2, pen2); // will go beyond end
863 if (xwhite && py2 < pen2 && isSafeWhitespace(*py2))
865 if (xwhite==WHITESPACE_IGNORE_CHANGE && !isSafeWhitespace(*py1))
867 // py2 is white but py1 is not
868 // in WHITESPACE_IGNORE_CHANGE mode,
869 // this doesn't qualify as skippable whitespace
870 break; // done with forward search
872 // gobble up all whitespace in current area
873 AdvanceOverWhitespace(&py1, pen1); // will go beyond end
874 AdvanceOverWhitespace(&py2, pen2); // will go beyond end
878 // Now do real character match
879 if (IsLeadByte(*py1))
881 if (!IsLeadByte(*py2))
882 break; // done with forward search
883 // DBCS (we assume if a lead byte, then character is 2-byte)
884 if (!(py1[0] == py2[0] && py1[1] == py2[1]))
885 break; // done with forward search
886 py1 += 2; // DBCS specific
887 py2 += 2; // DBCS specific
891 if (IsLeadByte(*py2))
892 break; // done with forward search
893 if (!matchchar(py1[0], py2[0], casitive))
894 break; // done with forward search
895 ++py1; // DBCS safe b/c we checked above
896 ++py2; // DBCS safe b/c we checked above
900 // Potential difference extends from py1 to pen1 and py2 to pen2
902 // Store results of advance into return variables (begin[0] & begin[1])
903 // -1 in a begin variable means no visible diff area
904 begin[0] = py1 - pbeg1;
905 begin[1] = py2 - pbeg2;
910 // Retreat over matching ends of lines
911 // Retreat pz1 & pz2 from end until find difference or beginning
914 // Check if either side finished
915 if (pz1 < py1 && pz2 < py2)
917 begin[0] = end[0] = begin[1] = end[1] = -1;
920 if (pz1 < py1 || pz2 < py2)
925 // handle all the whitespace logic (due to WinMerge whitespace settings)
926 if (xwhite && pz1 > py1 && isSafeWhitespace(*pz1))
928 if (xwhite==1 && !isSafeWhitespace(*pz2))
929 break; // done with reverse search
930 // gobble up all whitespace in current area
931 while (pz1 > py1 && isSafeWhitespace(*pz1))
932 pz1 = CharPrev(py1, pz1);
933 while (pz2 > py2 && isSafeWhitespace(*pz2))
934 pz2 = CharPrev(py2, pz2);
938 if (xwhite && pz2 > py2 && isSafeWhitespace(*pz2))
941 break; // done with reverse search
942 while (pz2 > py2 && isSafeWhitespace(*pz2))
943 pz2 = CharPrev(py2, pz2);
947 // Now do real character match
948 if (IsLeadByte(*pz1))
950 if (!IsLeadByte(*pz2))
951 break; // done with forward search
952 // DBCS (we assume if a lead byte, then character is 2-byte)
953 if (!(pz1[0] == pz2[0] && pz1[1] == pz2[1]))
954 break; // done with forward search
958 if (IsLeadByte(*pz2))
959 break; // done with forward search
960 if (!matchchar(pz1[0], pz2[0], casitive))
961 break; // done with forward search
963 pz1 = (pz1 > pbeg1) ? CharPrev(pbeg1, pz1) : pz1 - 1;
964 pz2 = (pz2 > pbeg2) ? CharPrev(pbeg2, pz2) : pz2 - 1;
967 /* if (*pz1 == '\r' && *(pz1+1) == '\n')
972 else if (*pz2 == '\r' && *(pz2+1) == '\n')
977 if (*(pbeg1-1) == '\r' && *pbeg1 == '\n')
982 else if (*(pbeg2-1) == '\r' && *pbeg2 == '\n')
988 // Store results of advance into return variables (end[0] & end[1])
989 end[0] = pz1 - pbeg1;
990 end[1] = pz2 - pbeg2;
992 // Check if difference region was empty
993 if (begin[0] == end[0] + 1 && begin[1] == end[1] + 1)
994 begin[0] = -1; // no diff
998 * @brief adjust the range of the specified word diffs down to byte(char) level.
999 * @param str1, str2 [in] line to be compared
1000 * @param casitive [in] true for case-sensitive, false for case-insensitive
1001 * @param xwhite [in] This governs whether we handle whitespace specially
1002 * (see WHITESPACE_COMPARE_ALL, WHITESPACE_IGNORE_CHANGE, WHITESPACE_IGNORE_ALL)
1004 void stringdiffs::wordLevelToByteLevel()
1006 for (size_t i = 0; i < m_wdiffs.size(); i++)
1008 int begin[3], end[3];
1009 wdiff& diff = m_wdiffs[i];
1010 String str1_2, str2_2;
1011 str1_2 = m_str1.substr(diff.begin[0], diff.end[0] - diff.begin[0] + 1);
1012 str2_2 = m_str2.substr(diff.begin[1], diff.end[1] - diff.begin[1] + 1);
1013 sd_ComputeByteDiff(str1_2, str2_2, m_case_sensitive, m_whitespace, begin, end, false);
1016 // no visible diff on side1
1017 diff.end[0] = diff.begin[0] - 1;
1021 diff.end[0] = diff.begin[0] + end[0];
1022 diff.begin[0] += begin[0];
1026 // no visible diff on side2
1027 diff.end[1] = diff.begin[1] - 1;
1031 diff.end[1] = diff.begin[1] + end[1];
1032 diff.begin[1] += begin[1];