diff --git a/modules/juce_core/text/juce_TextDiff.cpp b/modules/juce_core/text/juce_TextDiff.cpp index 8a03318b74..91c0e43397 100644 --- a/modules/juce_core/text/juce_TextDiff.cpp +++ b/modules/juce_core/text/juce_TextDiff.cpp @@ -28,7 +28,8 @@ struct TextDiffHelpers { - enum { minLengthToMatch = 3 }; + enum { minLengthToMatch = 3, + maxLengthToScan = 128 * 1024 }; struct StringRegion { @@ -38,6 +39,8 @@ struct TextDiffHelpers StringRegion (const String::CharPointerType t, int s, int len) noexcept : text (t), start (s), length (len) {} + void incrementStart() noexcept { ++text; ++start; --length; } + String::CharPointerType text; int start, length; }; @@ -61,27 +64,26 @@ struct TextDiffHelpers static void diffSkippingCommonStart (TextDiff& td, StringRegion a, StringRegion b) { - String::CharPointerType sa (a.text); - String::CharPointerType sb (b.text); - const int maxLen = jmax (a.length, b.length); - - for (int i = 0; i < maxLen; ++i, ++sa, ++sb) + for (;;) { - if (*sa != *sb) - { - diffRecursively (td, StringRegion (sa, a.start + i, a.length - i), - StringRegion (sb, b.start + i, b.length - i)); + const juce_wchar ca = *a.text; + const juce_wchar cb = *b.text; + + if (ca != cb || ca == 0) break; - } + + a.incrementStart(); + b.incrementStart(); } + + diffRecursively (td, a, b); } static void diffRecursively (TextDiff& td, StringRegion a, StringRegion b) { - int indexA, indexB; - const int len = findLongestCommonSubstring (a.text, a.length, - b.text, b.length, - indexA, indexB); + int indexA = 0, indexB = 0; + const int len = findLongestCommonSubstring (a.text, a.length, indexA, + b.text, b.length, indexB); if (len >= minLengthToMatch) { @@ -103,22 +105,25 @@ struct TextDiffHelpers } } - static int findLongestCommonSubstring (String::CharPointerType a, const int lenA, - const String::CharPointerType b, const int lenB, - int& indexInA, int& indexInB) + static int findLongestCommonSubstring (String::CharPointerType a, const int lenA, int& indexInA, + String::CharPointerType b, const int lenB, int& indexInB) noexcept { if (lenA == 0 || lenB == 0) return 0; - HeapBlock lines; - lines.calloc (2 + 2 * (size_t) lenB); + if (lenA > maxLengthToScan || lenB > maxLengthToScan) + return findCommonSuffix (a, lenA, indexInA, + b, lenB, indexInB); + + const size_t scratchSpace = sizeof (int) * (2 + 2 * (size_t) lenB); + int* const lines = (int*) alloca (scratchSpace); + zeromem (lines, scratchSpace); int* l0 = lines; int* l1 = l0 + lenB + 1; int loopsWithoutImprovement = 0; int bestLength = 0; - indexInA = indexInB = 0; for (int i = 0; i < lenA; ++i) { @@ -156,6 +161,25 @@ struct TextDiffHelpers indexInB -= bestLength - 1; return bestLength; } + + static int findCommonSuffix (String::CharPointerType a, const int lenA, int& indexInA, + String::CharPointerType b, const int lenB, int& indexInB) noexcept + { + int length = 0; + a += lenA - 1; + b += lenB - 1; + + while (length < lenA && length < lenB && *a == *b) + { + --a; + --b; + ++length; + } + + indexInA = lenA - length; + indexInB = lenB - length; + return length; + } }; TextDiff::TextDiff (const String& original, const String& target)