@@ -6,7 +6,42 @@ import 'package:localizer_app_main/data/models/app_settings.dart';
66/// Progress callback for reporting diff calculation progress.
77typedef DiffProgressCallback = void Function (int processed, int total);
88
9+ /// Cache for string similarity calculations to avoid redundant computation.
10+ /// This is especially important for Smart Match mode which can have O(n²) comparisons.
11+ class _SimilarityCache {
12+ final Map <String , double > _cache = {};
13+ static const int _maxSize = 50000 ;
14+
15+ String _makeKey (String a, String b) {
16+ // Ensure consistent key regardless of order
17+ return a.hashCode < b.hashCode ? '$a \n ---\n $b ' : '$b \n ---\n $a ' ;
18+ }
19+
20+ double ? get (String a, String b) => _cache[_makeKey (a, b)];
21+
22+ void set (String a, String b, double similarity) {
23+ if (_cache.length >= _maxSize) {
24+ // Clear half the cache when full
25+ final keysToRemove = _cache.keys.take (_maxSize ~ / 2 ).toList ();
26+ for (final key in keysToRemove) {
27+ _cache.remove (key);
28+ }
29+ }
30+ _cache[_makeKey (a, b)] = similarity;
31+ }
32+
33+ void clear () => _cache.clear ();
34+ }
35+
936class DiffCalculator {
37+ // Similarity cache for avoiding redundant calculations
38+ static final _similarityCache = _SimilarityCache ();
39+
40+ /// Clears the similarity cache. Call when starting a new comparison.
41+ static void clearCache () {
42+ _similarityCache.clear ();
43+ }
44+
1045 /// Calculates diff between two data maps based on the comparison mode.
1146 ///
1247 /// - `Key-based` : Matches entries by key name (traditional approach).
@@ -20,6 +55,9 @@ class DiffCalculator {
2055 required AppSettings settings,
2156 DiffProgressCallback ? onProgress,
2257 }) {
58+ // Clear cache at start of new comparison
59+ _similarityCache.clear ();
60+
2361 final mode = settings.comparisonMode;
2462
2563 switch (mode) {
@@ -214,6 +252,7 @@ class DiffCalculator {
214252 .toList ();
215253
216254 // Phase 3: Try to match by value similarity (detect renames)
255+ // Uses cached similarity to avoid O(n²) redundant calculations
217256 final usedKeys2 = < String > {};
218257 for (final key1 in unmatchedKeys1) {
219258 String ? value1 =
@@ -227,7 +266,8 @@ class DiffCalculator {
227266 _normalizeValue (targetData[key2], ignoreCase, ignoreWhitespace);
228267
229268 if (value1 != null && value2 != null ) {
230- final similarity = value1.similarityTo (value2);
269+ // Use cached similarity calculation
270+ final similarity = _getCachedSimilarity (value1, value2);
231271 if (similarity >= similarityThreshold &&
232272 similarity > bestSimilarity) {
233273 bestSimilarity = similarity;
@@ -292,10 +332,24 @@ class DiffCalculator {
292332 } else if (value1 != null && value2 == null ) {
293333 return ComparisonStatusDetail .removed ();
294334 } else if (value1 != value2) {
295- final similarityScore = value1! . similarityTo ( value2! );
335+ final similarityScore = _getCachedSimilarity ( value1! , value2! );
296336 return ComparisonStatusDetail .modified (similarityScore);
297337 } else {
298338 return ComparisonStatusDetail .identical ();
299339 }
300340 }
341+
342+ /// Gets similarity score with caching to avoid redundant calculations.
343+ static double _getCachedSimilarity (String value1, String value2) {
344+ // Check cache first
345+ final cached = _similarityCache.get (value1, value2);
346+ if (cached != null ) {
347+ return cached;
348+ }
349+
350+ // Calculate and cache
351+ final similarity = value1.similarityTo (value2);
352+ _similarityCache.set (value1, value2, similarity);
353+ return similarity;
354+ }
301355}
0 commit comments