28 enum { minLengthToMatch = 3,
29 maxComplexity = 16 * 1024 * 1024 };
37 : text (t), start (s), length (len) {}
39 void incrementStart()
noexcept { ++text; ++start; --length; }
54 static void addDeletion (
TextDiff&
td,
int index,
int length)
62 static void diffSkippingCommonStart (TextDiff&
td, StringRegion a, StringRegion b)
76 diffRecursively (
td, a, b);
79 static void diffRecursively (TextDiff&
td, StringRegion a, StringRegion b)
81 int indexA = 0, indexB = 0;
82 auto len = findLongestCommonSubstring (a.text, a.length, indexA,
83 b.text, b.length, indexB);
85 if (len >= minLengthToMatch)
87 if (indexA > 0 && indexB > 0)
88 diffSkippingCommonStart (
td, StringRegion (a.text, a.start, indexA),
89 StringRegion (b.text, b.start, indexB));
91 addDeletion (
td, b.start, indexA);
93 addInsertion (
td, b.text, b.start, indexB);
95 diffRecursively (
td, StringRegion (a.text + (indexA + len), a.start + indexA + len, a.length - indexA - len),
96 StringRegion (b.text + (indexB + len), b.start + indexB + len, b.length - indexB - len));
100 if (a.length > 0) addDeletion (
td, b.start, a.length);
101 if (b.length > 0) addInsertion (
td, b.text, b.start, b.length);
119 JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6255)
121 JUCE_END_IGNORE_WARNINGS_MSVC
142 for (
int i = 0; i <
lenA; ++i)
144 auto ca = a.getAndAdvance();
149 if (
ca !=
b2.getAndAdvance())
155 auto len =
l0[
j] + 1;
186 while (length <
lenA && length <
lenB && *a == *b)
201 TextDiffHelpers::diffSkippingCommonStart (*
this,
original, target);
206 for (
auto& c : changes)
214 return insertedText.isEmpty();
219 return text.replaceSection (start, length, insertedText);
231 :
UnitTest (
"TextDiff class", UnitTestCategories::text)
238 for (
int i = r.
nextInt (numElementsInArray (buffer) - 1); --i >= 0;)
244 buffer[i] = (juce_wchar) (1 + r.
nextInt (0x10ffff - 1));
246 while (! CharPointer_UTF16::canRepresent (buffer[i]));
252 return CharPointer_UTF32 (buffer);
255 void testDiff (
const String& a,
const String& b)
257 TextDiff diff (a, b);
258 auto result = diff.appliedTo (a);
259 expectEquals (result, b);
262 void runTest()
override
264 beginTest (
"TextDiff");
266 auto r = getRandom();
268 testDiff (String(), String());
269 testDiff (
"x", String());
270 testDiff (String(),
"x");
273 testDiff (
"xxx",
"x");
274 testDiff (
"x",
"xxx");
276 for (
int i = 1000; --i >= 0;)
278 auto s = createString (r);
279 testDiff (s, createString (r));
280 testDiff (s + createString (r), s + createString (r));
Wraps a pointer to a null-terminated UTF-8 character string, and provides various methods to operate ...
A random number generator.
int nextInt() noexcept
Returns the next random 32 bit integer.
CharPointerType getCharPointer() const noexcept
Returns the character pointer currently being used to store this string.
int length() const noexcept
Returns the number of characters in the string.
CharPointer_UTF8 CharPointerType
This is the character encoding type used internally to store the string.
Calculates and applies a sequence of changes to convert one text string into another.
TextDiff(const String &original, const String &target)
Creates a set of diffs for converting the original string into the target.
String appliedTo(String text) const
Applies this sequence of changes to the original string, producing the target string that was specifi...
This is a base class for classes that perform a unit test.
wchar_t juce_wchar
A platform-independent 32-bit unicode character type.
Type unalignedPointerCast(void *ptr) noexcept
Casts a pointer to another type via void*, which suppresses the cast-align warning which sometimes ar...
void zeromem(void *memory, size_t numBytes) noexcept
Fills a block of memory with zeros.
Describes a change, which can be either an insertion or deletion.
String appliedTo(const String &original) const noexcept
Returns the result of applying this change to a string.
int length
If this change is a deletion, this specifies the number of characters to delete.
int start
Specifies the character index in a string at which text should be inserted or deleted.
String insertedText
If this change is a deletion, this string will be empty; otherwise, it'll be the text that should be ...
bool isDeletion() const noexcept
Returns true if this change is a deletion, or false for an insertion.