2 * @file stringdiffs.cpp
4 * @brief Implementation file for sd_ComputeWordDiffs (q.v.)
8 #include "stringdiffs.h"
16 #include "CompareOptions.h"
17 #include "stringdiffsi.h"
22 static bool Initialized;
23 static bool CustomChars;
24 static TCHAR *BreakChars;
25 static TCHAR BreakCharDefaults[] = _T(",.;:");
27 static bool isSafeWhitespace(TCHAR ch);
28 static bool isWordBreak(int breakType, const TCHAR *str, int index);
32 BreakChars = &BreakCharDefaults[0];
47 void sd_SetBreakChars(const TCHAR *breakChars)
55 BreakChars = _tcsdup(breakChars);
59 sd_ComputeWordDiffs(const String& str1, const String& str2,
60 bool case_sensitive, int whitespace, int breakType, bool byte_level,
61 std::vector<wdiff> * pDiffs)
63 String strs[3] = {str1, str2, _T("")};
64 sd_ComputeWordDiffs(2, strs, case_sensitive, whitespace, breakType, byte_level, pDiffs);
69 Comp02Functor(const String *strs, bool case_sensitive) :
70 strs_(strs), case_sensitive_(case_sensitive)
73 bool operator()(const wdiff &wd3)
75 size_t wlen0 = wd3.end[0] - wd3.begin[0] + 1;
76 size_t wlen2 = wd3.end[2] - wd3.begin[2] + 1;
81 if (memcmp(&strs_[0][wd3.begin[0]], &strs_[2][wd3.begin[2]], wlen0 * sizeof(TCHAR)) != 0)
86 if (_tcsnicmp(&strs_[0][wd3.begin[0]], &strs_[2][wd3.begin[2]], wlen0) != 0)
96 * @brief Construct our worker object and tell it to do the work
99 sd_ComputeWordDiffs(int nFiles, const String str[3],
100 bool case_sensitive, int whitespace, int breakType, bool byte_level,
101 std::vector<wdiff> * pDiffs)
105 stringdiffs sdiffs(str[0], str[1], case_sensitive, whitespace, breakType, pDiffs);
106 // Hash all words in both lines and then compare them word by word
107 // storing differences into m_wdiffs
108 sdiffs.BuildWordDiffList();
111 sdiffs.wordLevelToByteLevel();
113 // Now copy m_wdiffs into caller-supplied m_pDiffs (coalescing adjacents if possible)
114 sdiffs.PopulateDiffs();
121 stringdiffs sdiffs(str[1], str[2], case_sensitive, whitespace, breakType, pDiffs);
122 sdiffs.BuildWordDiffList();
124 sdiffs.wordLevelToByteLevel();
125 sdiffs.PopulateDiffs();
126 for (size_t i = 0; i < pDiffs->size(); i++)
128 wdiff& diff = (*pDiffs)[i];
129 diff.begin[2] = diff.begin[1];
130 diff.begin[1] = diff.begin[0];
132 diff.end[2] = diff.end[1];
133 diff.end[1] = diff.end[0];
137 else if (str[1].empty())
139 stringdiffs sdiffs(str[0], str[2], case_sensitive, whitespace, breakType, pDiffs);
140 sdiffs.BuildWordDiffList();
142 sdiffs.wordLevelToByteLevel();
143 sdiffs.PopulateDiffs();
144 for (size_t i = 0; i < pDiffs->size(); i++)
146 wdiff& diff = (*pDiffs)[i];
147 diff.begin[2] = diff.begin[1];
148 //diff.begin[0] = diff.begin[0];
150 diff.end[2] = diff.end[1];
151 //diff.end[0] = diff.end[0];
155 else if (str[2].empty())
157 stringdiffs sdiffs(str[0], str[1], case_sensitive, whitespace, breakType, pDiffs);
158 sdiffs.BuildWordDiffList();
160 sdiffs.wordLevelToByteLevel();
161 sdiffs.PopulateDiffs();
162 for (size_t i = 0; i < pDiffs->size(); i++)
164 wdiff& diff = (*pDiffs)[i];
165 //diff.begin[1] = diff.begin[1];
166 //diff.begin[0] = diff.begin[0];
168 //diff.end[1] = diff.end[1];
169 //diff.end[0] = diff.end[0];
175 std::vector<wdiff> diffs10, diffs12;
176 stringdiffs sdiffs10(str[1], str[0], case_sensitive, whitespace, breakType, &diffs10);
177 stringdiffs sdiffs12(str[1], str[2], case_sensitive, whitespace, breakType, &diffs12);
178 // Hash all words in both lines and then compare them word by word
179 // storing differences into m_wdiffs
180 sdiffs10.BuildWordDiffList();
181 sdiffs12.BuildWordDiffList();
184 sdiffs10.wordLevelToByteLevel();
185 sdiffs12.wordLevelToByteLevel();
187 // Now copy m_wdiffs into caller-supplied m_pDiffs (coalescing adjacents if possible)
188 sdiffs10.PopulateDiffs();
189 sdiffs12.PopulateDiffs();
191 Make3wayDiff(*pDiffs, diffs10, diffs12,
192 Comp02Functor(str, case_sensitive), false);
198 * @brief stringdiffs constructor simply loads all members from arguments
200 stringdiffs::stringdiffs(const String & str1, const String & str2,
201 bool case_sensitive, int whitespace, int breakType,
202 std::vector<wdiff> * pDiffs)
205 , m_case_sensitive(case_sensitive)
206 , m_whitespace(whitespace)
207 , m_breakType(breakType)
209 , m_matchblock(true) // Change to false to get word to word compare
215 * The destructor frees all diffs added to the vectors.
217 stringdiffs::~stringdiffs()
221 #ifdef STRINGDIFF_LOGGING
223 stringdiffs::debugoutput()
225 for (size_t i = 0; i < m_wdiffs.size(); i++)
230 int s1 = m_wdiffs[i].begin[0];
231 int e1 = m_wdiffs[i].end[0];
232 int s2 = m_wdiffs[i].begin[1];
233 int e2 = m_wdiffs[i].end[1];
235 int len1 = e1 - s1 + 1;
236 int len2 = e2 - s2 + 1;
239 str1 = m_str1.substr(s1 ,e1 - s1 + 1);
241 str1 = m_str1.substr(s1, 50);
244 str2 = m_str2.substr(s2, e2- s2 + 1);
246 str2 = m_str2.substr(s2, 50);
248 wsprintf(buf, _T("left= %s, %d,%d,\nright= %s, %d,%d \n"),
249 str1.c_str(), s1, e1, str2.c_str(), s2, e2);
250 OutputDebugString(buf);
256 stringdiffs::BuildWordDiffList_DP()
258 std::vector<char> edscript;
260 //if (dp(edscript) <= 0)
265 for (size_t k = 0; k < edscript.size(); k++)
268 if (edscript[k] == '-')
270 if (m_whitespace == WHITESPACE_IGNORE_ALL)
272 if (IsSpace(m_words1[i]))
279 s1 = m_words1[i].start;
280 e1 = m_words1[i].end;
281 s2 = m_words2[j-1].end+1;
283 m_wdiffs.push_back(wdiff(s1, e1, s2, e2));
286 else if (edscript[k] == '+')
288 if (m_whitespace == WHITESPACE_IGNORE_ALL)
290 if (IsSpace(m_words2[j]))
297 s1 = m_words1[i-1].end+1;
299 s2 = m_words2[j].start;
300 e2 = m_words2[j].end;
301 m_wdiffs.push_back(wdiff(s1, e1, s2, e2));
304 else if (edscript[k] == '!')
306 if (m_whitespace == WHITESPACE_IGNORE_CHANGE || m_whitespace == WHITESPACE_IGNORE_ALL)
308 if (IsSpace(m_words1[i]) && IsSpace(m_words2[j]))
315 s1 = m_words1[i].start;
316 e1 = m_words1[i].end;
317 s2 = m_words2[j].start;
318 e2 = m_words2[j].end ;
319 m_wdiffs.push_back(wdiff(s1, e1, s2, e2));
327 #ifdef STRINGDIFF_LOGGING
334 * @brief Add all different elements between lines to the wdiff list
337 stringdiffs::BuildWordDiffList()
339 BuildWordsArray(m_str1, m_words1);
340 BuildWordsArray(m_str2, m_words2);
343 if (m_words1.size() > 20480 || m_words2.size() > 20480)
345 if (m_words1.size() > 2048 || m_words2.size() > 2048)
348 int s1 = m_words1[0].start;
349 int e1 = m_words1[m_words1.size() - 1].end;
350 int s2 = m_words2[0].start;
351 int e2 = m_words2[m_words2.size() - 1].end;
352 m_wdiffs.push_back(wdiff(s1, e1, s2, e2));
356 BuildWordDiffList_DP();
360 * @brief Break line into constituent words
363 stringdiffs::BuildWordsArray(const String & str, std::vector<word>& words)
368 words.push_back(word(0, -1, 0, 0));
370 // state when we are looking for next word
372 if (isSafeWhitespace(str[i]))
379 // just finished a word
380 // e is first word character (space or at end)
383 words.push_back(word(begin, e, dlspace, Hash(str, begin, e, 0)));
385 if (i == str.length())
390 // state when we are inside a word
393 if (i == str.length() || ((atspace = isSafeWhitespace(str[i])) != 0) || isWordBreak(m_breakType, str.c_str(), i))
397 // just finished a word
398 // e is first non-word character (space or at end)
401 words.push_back(word(begin, e, dlword, Hash(str, begin, e, 0)));
403 if (i == str.length())
414 // start a new word because we hit a non-whitespace word break (eg, a comma)
415 // but, we have to put each word break character into its own word
416 words.push_back(word(i, i, dlbreak, Hash(str, i, i, 0)));
423 goto inword; // safe even if we're at the end or no longer in a word
427 * @brief Populate m_pDiffs from m_wdiffs (combining adjacent diffs)
429 * Doing the combining of adjacent diffs here keeps some complexity out of BuildWordsArray.
432 stringdiffs::PopulateDiffs()
434 for (int i=0; i< (int)m_wdiffs.size(); ++i)
437 // combine it with next ?
438 if (i+1< (int)m_wdiffs.size())
440 if (m_wdiffs[i].end[0] + 1 == m_wdiffs[i+1].begin[0]
441 && m_wdiffs[i].end[1] + 1 == m_wdiffs[i+1].begin[1])
443 // diff[i] and diff[i+1] are contiguous
444 // so combine them into diff[i+1] and ignore diff[i]
445 m_wdiffs[i+1].begin[0] = m_wdiffs[i].begin[0];
446 m_wdiffs[i+1].begin[1] = m_wdiffs[i].begin[1];
452 // Should never have a pair where both are missing
453 assert(m_wdiffs[i].begin[0]>=0 || m_wdiffs[i].begin[1]>=0);
455 // Store the diff[i] in the caller list (m_pDiffs)
456 m_pDiffs->push_back(wdiff(m_wdiffs[i]));
463 /* Rotate a value n bits to the left. */
464 #define UINT_BIT (sizeof (unsigned) * CHAR_BIT)
465 #define ROL(v, n) ((v) << (n) | (v) >> (UINT_BIT - (n)))
466 /* Given a hash value and a new character, return a new hash value. */
467 #define HASH(h, c) ((c) + ROL (h, 7))
470 stringdiffs::Hash(const String & str, int begin, int end, unsigned h) const
472 for (int i = begin; i <= end; ++i)
474 unsigned ch = static_cast<unsigned>(str[i]);
475 if (m_case_sensitive)
481 ch = static_cast<unsigned>(_totupper(ch));
490 * @brief Compare two words (by reference to original strings)
493 stringdiffs::AreWordsSame(const word & word1, const word & word2) const
495 if (this->m_whitespace != WHITESPACE_COMPARE_ALL)
497 if (IsSpace(word1) && IsSpace(word2))
500 if (word1.hash != word2.hash)
502 if (word1.length() != word2.length())
504 for (int i=0; i<word1.length(); ++i)
506 if (!caseMatch(m_str1[word1.start+i], m_str2[word2.start+i]))
513 * @brief Return true if characters match
516 stringdiffs::caseMatch(TCHAR ch1, TCHAR ch2) const
518 if (m_case_sensitive)
521 return _totupper(ch1)==_totupper(ch2);
525 * @ brief An O(NP) Sequence Comparison Algorithm. Sun Wu, Udi Manber, Gene Myers
528 stringdiffs::onp(std::vector<char> &edscript)
530 int M = m_words1.size() - 1;
531 int N = m_words2.size() - 1;
532 bool exchanged = false;
535 M = m_words2.size() - 1;
536 N = m_words1.size() - 1;
539 int *fp = (new int[(M+1) + 1 + (N+1)]) + (M+1);
540 std::vector<char> *es = (new std::vector<char>[(M+1) + 1 + (N+1)]) + (M+1);
544 for (k = -(M+1); k <= (N+1); k++)
550 for (k = -p; k <= DELTA-1; k++)
552 fp[k] = snake(k, std::max(fp[k-1] + 1, fp[k+1]), exchanged);
554 es[k] = fp[k-1] + 1 > fp[k+1] ? es[k-1] : es[k+1];
555 es[k].push_back(fp[k-1] + 1 > fp[k+1] ? '+' : '-');
556 es[k].resize(es[k].size() + fp[k] - std::max(fp[k-1] + 1, fp[k+1]), '=');
558 for (k = DELTA + p; k >= DELTA+1; k--)
560 fp[k] = snake(k, std::max(fp[k-1] + 1, fp[k+1]), exchanged);
562 es[k] = fp[k-1] + 1 > fp[k+1] ? es[k-1] : es[k+1];
563 es[k].push_back(fp[k-1] + 1 > fp[k+1] ? '+' : '-');
564 es[k].resize(es[k].size() + fp[k] - std::max(fp[k-1] + 1, fp[k+1]), '=');
567 fp[k] = snake(k, std::max(fp[k-1] + 1, fp[k+1]), exchanged);
569 es[k] = fp[k-1] + 1 > fp[k+1] ? es[k-1] : es[k+1];
570 es[k].push_back(fp[k-1] + 1 > fp[k+1] ? '+' : '-');
571 es[k].resize(es[k].size() + fp[k] - std::max(fp[k-1] + 1, fp[k+1]), '=');
572 } while (fp[k] != N);
574 std::vector<char> &ses = es[DELTA]; // Shortest edit script
578 for (size_t i = 1; i < ses.size(); i++)
583 if (i + 1 < ses.size() && ses[i + 1] == '-')
585 edscript.push_back('!');
591 edscript.push_back(exchanged ? '-' : '+');
596 if (i + 1 < ses.size() && ses[i + 1] == '+')
598 edscript.push_back('!');
604 edscript.push_back(exchanged ? '+' : '-');
609 edscript.push_back('=');
613 delete [] (es - (M+1));
614 delete [] (fp - (M+1));
620 stringdiffs::snake(int k, int y, bool exchanged)
623 int M = exchanged ? m_words2.size() - 1 : m_words1.size() - 1;
624 int N = exchanged ? m_words1.size() - 1 : m_words2.size() - 1;
626 while (x < M && y < N && (exchanged ? AreWordsSame(m_words1[y + 1], m_words2[x + 1]) : AreWordsSame(m_words1[x + 1], m_words2[y + 1]))) {
627 x = x + 1; y = y + 1;
633 * @brief Return true if chars match
635 * Caller must not call this for lead bytes
638 matchchar(TCHAR ch1, TCHAR ch2, bool casitive)
643 return _totupper(ch1)==_totupper(ch2);
647 /** Does character introduce a multicharacter character? */
648 static inline bool IsLeadByte(TCHAR ch)
653 return _getmbcp() && IsDBCSLeadByte(ch);
658 * @brief Is it whitespace (excludes all lead & trail bytes)?
661 isSafeWhitespace(TCHAR ch)
663 return _istspace((unsigned)ch) && !IsLeadByte(ch);
667 * @brief Is it a non-whitespace wordbreak character (ie, punctuation)?
670 isWordBreak(int breakType, const TCHAR *str, int index)
672 TCHAR ch = str[index];
673 // breakType==1 means break also on punctuation
675 if ((ch & 0xff00) == 0)
677 // TCHAR nextCh = str[index + 1];
678 // breakType==0 means whitespace only
681 return _tcschr(BreakChars, ch) != 0;
686 // ch==0xff0c/* Fullwidth Full Stop */ ||
687 // ch==0xff0e/* Fullwidth Comma */ ||
688 // ch==0xff1b/* Fullwidth Semicolon */ ||
689 // ch==0xff1a/* Fullwidth Colon */ ||
690 // ch==0x3002/* Ideographic Full Stop */ ||
691 // ch==0x3001/* Ideographic Comma */
694 // WORD wCharType, wCharTypeNext;
695 // GetStringTypeW(CT_CTYPE3, &ch, 1, &wCharType);
696 // TCHAR nextCh = str[index + 1];
697 // GetStringTypeW(CT_CTYPE3, &nextCh, 1, &wCharTypeNext);
698 // return (wCharType != wCharTypeNext);
703 // breakType==0 means whitespace only
706 return _tcschr(BreakChars, ch) != 0;
712 * @brief Return pointer to last character of specified string (handle MBCS)
714 * If the last byte is a broken multibyte (ie, a solo lead byte), this returns previous char
717 LastChar(LPCTSTR psz, int len)
719 if (!len) return psz;
721 if (!_getmbcp()) return psz+len-1;
723 LPCTSTR lastValid = psz+len-1;
726 while (psz<lastValid)
733 if (psz==lastValid && !IsLeadByte(*psz))
735 else // last character was multibyte or broken multibyte
740 * @brief advance current pointer over whitespace, until not whitespace or beyond end
741 * @param pcurrent [in,out] current location (to be advanced)
742 * @param end [in] last valid position (only go one beyond this)
745 AdvanceOverWhitespace(LPCTSTR * pcurrent, LPCTSTR end)
747 // advance over whitespace
748 while (*pcurrent <= end && isSafeWhitespace(**pcurrent))
749 ++(*pcurrent); // DBCS safe because of isSafeWhitespace above
753 * @brief Compute begin1,begin2,end1,end2 to display byte difference between strings str1 & str2
754 * @param casitive [in] true for case-sensitive, false for case-insensitive
755 * @param xwhite [in] This governs whether we handle whitespace specially (see WHITESPACE_COMPARE_ALL, WHITESPACE_IGNORE_CHANGE, WHITESPACE_IGNORE_ALL)
756 * @param [out] begin return -1 if not found or pos of equal
757 * @param [out] end return -1 if not found or pos of equal valid if begin1 >=0
758 * @param [in] equal false surch for a diff, true surch for equal
761 * Assumes whitespace is never leadbyte or trailbyte!
764 sd_ComputeByteDiff(String & str1, String & str2,
765 bool casitive, int xwhite,
766 int begin[2], int end[2], bool equal)
768 // Set to sane values
769 // Also this way can distinguish if we set begin[0] to -1 for no diff in line
770 begin[0] = end[0] = begin[1] = end[1] = 0;
772 int len1 = str1.length();
773 int len2 = str2.length();
775 LPCTSTR pbeg1 = str1.c_str();
776 LPCTSTR pbeg2 = str2.c_str();
778 if (len1 == 0 || len2 == 0)
790 // cursors from front, which we advance to beginning of difference
794 // pen1,pen2 point to the last valid character (broken multibyte lead chars don't count)
795 LPCTSTR pen1 = LastChar(py1, len1);
796 LPCTSTR pen2 = LastChar(py2, len2);
798 if (xwhite != WHITESPACE_COMPARE_ALL)
800 // Ignore leading and trailing whitespace
801 // by advancing py1 and py2
802 // and retreating pen1 and pen2
803 while (py1 < pen1 && isSafeWhitespace(*py1))
804 ++py1; // DBCS safe because of isSafeWhitespace above
805 while (py2 < pen2 && isSafeWhitespace(*py2))
806 ++py2; // DBCS safe because of isSafeWhitespace above
807 if ((pen1 < pbeg1 + len1 - 1 || pen2 < pbeg2 + len2 -1)
808 && (!len1 || !len2 || pbeg1[len1] != pbeg2[len2]))
810 // mismatched broken multibyte ends
814 while (pen1 > py1 && isSafeWhitespace(*pen1))
815 pen1 = CharPrev(py1, pen1);
816 while (pen2 > py2 && isSafeWhitespace(*pen2))
817 pen2 = CharPrev(py2, pen2);
820 //check for excaption of empty string on one side
821 //In that case display all as a diff
822 if (!equal && (((py1 == pen1) && isSafeWhitespace(*pen1)) ||
823 ((py2 == pen2) && isSafeWhitespace(*pen2))))
831 // Advance over matching beginnings of lines
832 // Advance py1 & py2 from beginning until find difference or end
835 // Potential difference extends from py1 to pen1 and py2 to pen2
837 // Check if either side finished
838 if (py1 > pen1 && py2 > pen2)
840 begin[0] = end[0] = begin[1] = end[1] = -1;
843 if (py1 > pen1 || py2 > pen2)
848 // handle all the whitespace logic (due to WinMerge whitespace settings)
849 if (xwhite && py1 < pen1 && isSafeWhitespace(*py1))
851 if (xwhite==WHITESPACE_IGNORE_CHANGE && !isSafeWhitespace(*py2))
853 // py1 is white but py2 is not
854 // in WHITESPACE_IGNORE_CHANGE mode,
855 // this doesn't qualify as skippable whitespace
856 break; // done with forward search
858 // gobble up all whitespace in current area
859 AdvanceOverWhitespace(&py1, pen1); // will go beyond end
860 AdvanceOverWhitespace(&py2, pen2); // will go beyond end
864 if (xwhite && py2 < pen2 && isSafeWhitespace(*py2))
866 if (xwhite==WHITESPACE_IGNORE_CHANGE && !isSafeWhitespace(*py1))
868 // py2 is white but py1 is not
869 // in WHITESPACE_IGNORE_CHANGE mode,
870 // this doesn't qualify as skippable whitespace
871 break; // done with forward search
873 // gobble up all whitespace in current area
874 AdvanceOverWhitespace(&py1, pen1); // will go beyond end
875 AdvanceOverWhitespace(&py2, pen2); // will go beyond end
879 // Now do real character match
880 if (IsLeadByte(*py1))
882 if (!IsLeadByte(*py2))
883 break; // done with forward search
884 // DBCS (we assume if a lead byte, then character is 2-byte)
885 if (!(py1[0] == py2[0] && py1[1] == py2[1]))
886 break; // done with forward search
887 py1 += 2; // DBCS specific
888 py2 += 2; // DBCS specific
892 if (IsLeadByte(*py2))
893 break; // done with forward search
894 if (!matchchar(py1[0], py2[0], casitive))
895 break; // done with forward search
896 ++py1; // DBCS safe b/c we checked above
897 ++py2; // DBCS safe b/c we checked above
901 // Potential difference extends from py1 to pen1 and py2 to pen2
903 // Store results of advance into return variables (begin[0] & begin[1])
904 // -1 in a begin variable means no visible diff area
905 begin[0] = py1 - pbeg1;
906 begin[1] = py2 - pbeg2;
911 // Retreat over matching ends of lines
912 // Retreat pz1 & pz2 from end until find difference or beginning
915 // Check if either side finished
916 if (pz1 < py1 && pz2 < py2)
918 begin[0] = end[0] = begin[1] = end[1] = -1;
921 if (pz1 < py1 || pz2 < py2)
926 // handle all the whitespace logic (due to WinMerge whitespace settings)
927 if (xwhite && pz1 > py1 && isSafeWhitespace(*pz1))
929 if (xwhite==1 && !isSafeWhitespace(*pz2))
930 break; // done with reverse search
931 // gobble up all whitespace in current area
932 while (pz1 > py1 && isSafeWhitespace(*pz1))
933 pz1 = CharPrev(py1, pz1);
934 while (pz2 > py2 && isSafeWhitespace(*pz2))
935 pz2 = CharPrev(py2, pz2);
939 if (xwhite && pz2 > py2 && isSafeWhitespace(*pz2))
942 break; // done with reverse search
943 while (pz2 > py2 && isSafeWhitespace(*pz2))
944 pz2 = CharPrev(py2, pz2);
948 // Now do real character match
949 if (IsLeadByte(*pz1))
951 if (!IsLeadByte(*pz2))
952 break; // done with forward search
953 // DBCS (we assume if a lead byte, then character is 2-byte)
954 if (!(pz1[0] == pz2[0] && pz1[1] == pz2[1]))
955 break; // done with forward search
959 if (IsLeadByte(*pz2))
960 break; // done with forward search
961 if (!matchchar(pz1[0], pz2[0], casitive))
962 break; // done with forward search
964 pz1 = (pz1 > pbeg1) ? CharPrev(pbeg1, pz1) : pz1 - 1;
965 pz2 = (pz2 > pbeg2) ? CharPrev(pbeg2, pz2) : pz2 - 1;
968 /* if (*pz1 == '\r' && *(pz1+1) == '\n')
973 else if (*pz2 == '\r' && *(pz2+1) == '\n')
978 if (*(pbeg1-1) == '\r' && *pbeg1 == '\n')
983 else if (*(pbeg2-1) == '\r' && *pbeg2 == '\n')
989 // Store results of advance into return variables (end[0] & end[1])
990 end[0] = pz1 - pbeg1;
991 end[1] = pz2 - pbeg2;
993 // Check if difference region was empty
994 if (begin[0] == end[0] + 1 && begin[1] == end[1] + 1)
995 begin[0] = -1; // no diff
999 * @brief adjust the range of the specified word diffs down to byte(char) level.
1000 * @param str1, str2 [in] line to be compared
1001 * @param casitive [in] true for case-sensitive, false for case-insensitive
1002 * @param xwhite [in] This governs whether we handle whitespace specially
1003 * (see WHITESPACE_COMPARE_ALL, WHITESPACE_IGNORE_CHANGE, WHITESPACE_IGNORE_ALL)
1005 void stringdiffs::wordLevelToByteLevel()
1007 for (size_t i = 0; i < m_wdiffs.size(); i++)
1009 int begin[3], end[3];
1010 wdiff& diff = m_wdiffs[i];
1011 String str1_2, str2_2;
1012 str1_2 = m_str1.substr(diff.begin[0], diff.end[0] - diff.begin[0] + 1);
1013 str2_2 = m_str2.substr(diff.begin[1], diff.end[1] - diff.begin[1] + 1);
1014 sd_ComputeByteDiff(str1_2, str2_2, m_case_sensitive, m_whitespace, begin, end, false);
1017 // no visible diff on side1
1018 diff.end[0] = diff.begin[0] - 1;
1022 diff.end[0] = diff.begin[0] + end[0];
1023 diff.begin[0] += begin[0];
1027 // no visible diff on side2
1028 diff.end[1] = diff.begin[1] - 1;
1032 diff.end[1] = diff.begin[1] + end[1];
1033 diff.begin[1] += begin[1];