source: trunk/Diff.pas

Last change on this file was 120, checked in by chronos, 2 years ago
  • Added: Partially implemented contacts compare.
File size: 25.2 KB
Line 
1unit Diff;
2
3(*******************************************************************************
4* Component TDiff *
5* Version: 3.1 *
6* Date: 7 November 2009 *
7* Compilers: Delphi 7 - Delphi2009 *
8* Author: Angus Johnson - angusj-AT-myrealbox-DOT-com *
9* Copyright: © 2001-200( Angus Johnson *
10* *
11* Licence to use, terms and conditions: *
12* The code in the TDiff component is released as freeware *
13* provided you agree to the following terms & conditions: *
14* 1. the copyright notice, terms and conditions are *
15* left unchanged *
16* 2. modifications to the code by other authors must be *
17* clearly documented and accompanied by the modifier's name. *
18* 3. the TDiff component may be freely compiled into binary *
19* format and no acknowledgement is required. However, a *
20* discrete acknowledgement would be appreciated (eg. in a *
21* program's 'About Box'). *
22* *
23* Description: Component to list differences between two integer arrays *
24* using a "longest common subsequence" algorithm. *
25* Typically, this component is used to diff 2 text files *
26* once their individuals lines have been hashed. *
27* *
28* Acknowledgements: The key algorithm in this component is based on: *
29* "An O(ND) Difference Algorithm and its Variations" *
30* By E Myers - Algorithmica Vol. 1 No. 2, 1986, pp. 251-266 *
31* http://www.cs.arizona.edu/people/gene/ *
32* http://www.cs.arizona.edu/people/gene/PAPERS/diff.ps *
33* *
34*******************************************************************************)
35
36
37(*******************************************************************************
38* History: *
39* 13 December 2001 - Original Release *
40* 22 April 2008 - Complete rewrite to greatly improve the code and *
41* provide a much simpler view of differences through a new *
42* 'Compares' property. *
43* 7 November 2009 - Updated so now compiles in newer versions of Delphi. *
44*******************************************************************************)
45
46interface
47
48uses
49 LCLIntf, SysUtils, Classes, Math, Forms;
50
51const
52 //Maximum realistic deviation from centre diagonal vector ...
53 MAX_DIAGONAL = $FFFFFF; //~16 million
54
55type
56
57{$IFDEF UNICODE}
58 P8Bits = PByte;
59{$ELSE}
60 P8Bits = PAnsiChar;
61{$ENDIF}
62
63 PDiags = ^TDiags;
64 TDiags = array [-MAX_DIAGONAL .. MAX_DIAGONAL] of integer;
65
66 PIntArray = ^TIntArray;
67 TIntArray = array[0 .. MAXINT div sizeof(integer) -1] of Integer;
68 PChrArray = ^TChrArray;
69 TChrArray = array[0 .. MAXINT div sizeof(char) -1] of Char;
70
71 TChangeKind = (ckNone, ckAdd, ckDelete, ckModify);
72
73 PCompareRec = ^TCompareRec;
74 TCompareRec = record
75 Kind : TChangeKind;
76 oldIndex1,
77 oldIndex2 : integer;
78 case boolean of
79 false : (chr1, chr2 : Char);
80 true : (int1, int2 : integer);
81 end;
82
83 TDiffStats = record
84 matches : integer;
85 adds : integer;
86 deletes : integer;
87 modifies : integer;
88 end;
89
90 TDiff = class(TComponent)
91 private
92 fCompareList: TList;
93 fCancelled: boolean;
94 fExecuting: boolean;
95 fDiagBuffer, bDiagBuffer: pointer;
96 Chrs1, Chrs2: PChrArray;
97 Ints1, Ints2: PIntArray;
98 LastCompareRec: TCompareRec;
99 fDiag, bDiag: PDiags;
100 fDiffStats: TDiffStats;
101 procedure InitDiagArrays(MaxOscill, len1, len2: integer);
102 //nb: To optimize speed, separate functions are called for either
103 //integer or character compares ...
104 procedure RecursiveDiffChr(offset1, offset2, len1, len2: integer);
105 procedure AddChangeChrs(offset1, range: integer; ChangeKind: TChangeKind);
106 procedure RecursiveDiffInt(offset1, offset2, len1, len2: integer);
107 procedure AddChangeInts(offset1, range: integer; ChangeKind: TChangeKind);
108
109 function GetCompareCount: integer;
110 function GetCompare(index: integer): TCompareRec;
111 public
112 constructor Create(aOwner: TComponent); override;
113 destructor Destroy; override;
114
115 //compare either and array of characters or an array of integers ...
116 function Execute(pints1, pints2: PInteger; len1, len2: integer): boolean; overload;
117 function Execute(pchrs1, pchrs2: PChar; len1, len2: integer): boolean; overload;
118
119 //Cancel allows interrupting excessively prolonged comparisons
120 procedure Cancel;
121 procedure Clear;
122
123 property Cancelled: boolean read fCancelled;
124 property Count: integer read GetCompareCount;
125 property Compares[index: integer]: TCompareRec read GetCompare; default;
126 property DiffStats: TDiffStats read fDiffStats;
127 end;
128
129procedure Register;
130
131implementation
132
133procedure Register;
134begin
135 RegisterComponents('Samples', [TDiff]);
136end;
137
138constructor TDiff.Create(aOwner: TComponent);
139begin
140 inherited;
141 fCompareList := TList.create;
142end;
143//------------------------------------------------------------------------------
144
145destructor TDiff.Destroy;
146begin
147 Clear;
148 fCompareList.free;
149 inherited;
150end;
151//------------------------------------------------------------------------------
152
153function TDiff.Execute(pchrs1, pchrs2: PChar; len1, len2: integer): boolean;
154var
155 maxOscill, x1,x2, savedLen: integer;
156 compareRec: PCompareRec;
157begin
158 result := not fExecuting;
159 if not result then exit;
160 fExecuting := true;
161 fCancelled := false;
162 try
163 Clear;
164
165 //save first string length for later (ie for any trailing matches) ...
166 savedLen := len1-1;
167
168 //setup the character arrays ...
169 Chrs1 := pointer(pchrs1);
170 Chrs2 := pointer(pchrs2);
171
172 //ignore top matches ...
173 x1:= 0; x2 := 0;
174 while (len1 > 0) and (len2 > 0) and (Chrs1[len1-1] = Chrs2[len2-1]) do
175 begin
176 dec(len1); dec(len2);
177 end;
178
179 //if something doesn't match ...
180 if (len1 <> 0) or (len2 <> 0) then
181 begin
182 //ignore bottom of matches too ...
183 while (len1 > 0) and (len2 > 0) and (Chrs1[x1] = Chrs2[x2]) do
184 begin
185 dec(len1); dec(len2);
186 inc(x1); inc(x2);
187 end;
188
189 maxOscill := min(max(len1,len2), MAX_DIAGONAL);
190 fCompareList.Capacity := len1 + len2;
191
192 //nb: the Diag arrays are extended by 1 at each end to avoid testing
193 //for array limits. Hence '+3' because will also includes Diag[0] ...
194 GetMem(fDiagBuffer, sizeof(integer)*(maxOscill*2+3));
195 GetMem(bDiagBuffer, sizeof(integer)*(maxOscill*2+3));
196 try
197 RecursiveDiffChr(x1, x2, len1, len2);
198 finally
199 freeMem(fDiagBuffer);
200 freeMem(bDiagBuffer);
201 end;
202 end;
203
204 if fCancelled then
205 begin
206 result := false;
207 Clear;
208 exit;
209 end;
210
211 //finally, append any trailing matches onto compareList ...
212 while (LastCompareRec.oldIndex1 < savedLen) do
213 begin
214 with LastCompareRec do
215 begin
216 Kind := ckNone;
217 inc(oldIndex1);
218 inc(oldIndex2);
219 chr1 := Chrs1[oldIndex1];
220 chr2 := Chrs2[oldIndex2];
221 end;
222 New(compareRec);
223 compareRec^ := LastCompareRec;
224 fCompareList.Add(compareRec);
225 inc(fDiffStats.matches);
226 end;
227 finally
228 fExecuting := false;
229 end;
230
231end;
232//------------------------------------------------------------------------------
233
234function TDiff.Execute(pints1, pints2: PInteger; len1, len2: integer): boolean;
235var
236 maxOscill, x1,x2, savedLen: integer;
237 compareRec: PCompareRec;
238begin
239 result := not fExecuting;
240 if not result then exit;
241 fExecuting := true;
242 fCancelled := false;
243 try
244 Clear;
245
246 //setup the character arrays ...
247 Ints1 := pointer(pints1);
248 Ints2 := pointer(pints2);
249
250 //save first string length for later (ie for any trailing matches) ...
251 savedLen := len1-1;
252
253 //ignore top matches ...
254 x1:= 0; x2 := 0;
255 while (len1 > 0) and (len2 > 0) and (Ints1[len1-1] = Ints2[len2-1]) do
256 begin
257 dec(len1); dec(len2);
258 end;
259
260 //if something doesn't match ...
261 if (len1 <> 0) or (len2 <> 0) then
262 begin
263
264 //ignore bottom of matches too ...
265 while (len1 > 0) and (len2 > 0) and (Ints1[x1] = Ints2[x2]) do
266 begin
267 dec(len1); dec(len2);
268 inc(x1); inc(x2);
269 end;
270
271 maxOscill := min(max(len1,len2), MAX_DIAGONAL);
272 fCompareList.Capacity := len1 + len2;
273
274 //nb: the Diag arrays are extended by 1 at each end to avoid testing
275 //for array limits. Hence '+3' because will also includes Diag[0] ...
276 GetMem(fDiagBuffer, sizeof(integer)*(maxOscill*2+3));
277 GetMem(bDiagBuffer, sizeof(integer)*(maxOscill*2+3));
278 try
279 RecursiveDiffInt(x1, x2, len1, len2);
280 finally
281 freeMem(fDiagBuffer);
282 freeMem(bDiagBuffer);
283 end;
284 end;
285
286 if fCancelled then
287 begin
288 result := false;
289 Clear;
290 exit;
291 end;
292
293 //finally, append any trailing matches onto compareList ...
294 while (LastCompareRec.oldIndex1 < savedLen) do
295 begin
296 with LastCompareRec do
297 begin
298 Kind := ckNone;
299 inc(oldIndex1);
300 inc(oldIndex2);
301 int1 := Ints1[oldIndex1];
302 int2 := Ints2[oldIndex2];
303 end;
304 New(compareRec);
305 compareRec^ := LastCompareRec;
306 fCompareList.Add(compareRec);
307 inc(fDiffStats.matches);
308 end;
309 finally
310 fExecuting := false;
311 end;
312
313end;
314//------------------------------------------------------------------------------
315
316procedure TDiff.InitDiagArrays(MaxOscill, len1, len2: integer);
317var
318 diag: integer;
319begin
320 inc(maxOscill); //for the extra diag at each end of the arrays ...
321 P8Bits(fDiag) := P8Bits(fDiagBuffer) - sizeof(integer)*(MAX_DIAGONAL-maxOscill);
322 P8Bits(bDiag) := P8Bits(bDiagBuffer) - sizeof(integer)*(MAX_DIAGONAL-maxOscill);
323 //initialize Diag arrays (assumes 0 based arrays) ...
324 for diag := - maxOscill to maxOscill do fDiag[diag] := -MAXINT;
325 fDiag[0] := -1;
326 for diag := - maxOscill to maxOscill do bDiag[diag] := MAXINT;
327 bDiag[len1 - len2] := len1-1;
328end;
329//------------------------------------------------------------------------------
330
331procedure TDiff.RecursiveDiffChr(offset1, offset2, len1, len2: integer);
332var
333 diag, lenDelta, Oscill, maxOscill, x1, x2: integer;
334begin
335 //nb: the possible depth of recursion here is most unlikely to cause
336 // problems with stack overflows.
337 application.processmessages;
338 if fCancelled then exit;
339
340 if (len1 = 0) then
341 begin
342 AddChangeChrs(offset1, len2, ckAdd);
343 exit;
344 end
345 else if (len2 = 0) then
346 begin
347 AddChangeChrs(offset1, len1, ckDelete);
348 exit;
349 end
350 else if (len1 = 1) and (len2 = 1) then
351 begin
352 AddChangeChrs(offset1, 1, ckDelete);
353 AddChangeChrs(offset1, 1, ckAdd);
354 exit;
355 end;
356
357 maxOscill := min(max(len1,len2), MAX_DIAGONAL);
358 InitDiagArrays(MaxOscill, len1, len2);
359 lenDelta := len1 -len2;
360
361 Oscill := 1; //ie assumes prior filter of top and bottom matches
362 while Oscill <= maxOscill do
363 begin
364
365 if (Oscill mod 200) = 0 then
366 begin
367 application.processmessages;
368 if fCancelled then exit;
369 end;
370
371 //do forward oscillation (keeping diag within assigned grid)...
372 diag := Oscill;
373 while diag > len1 do dec(diag,2);
374 while diag >= max(- Oscill, -len2) do
375 begin
376 if fDiag[diag-1] < fDiag[diag+1] then
377 x1 := fDiag[diag+1] else
378 x1 := fDiag[diag-1]+1;
379 x2 := x1 - diag;
380 while (x1 < len1-1) and (x2 < len2-1) and
381 (Chrs1[offset1+x1+1] = Chrs2[offset2+x2+1]) do
382 begin
383 inc(x1); inc(x2);
384 end;
385 fDiag[diag] := x1;
386
387 //nb: (fDiag[diag] is always < bDiag[diag]) here when NOT odd(lenDelta) ...
388 if odd(lenDelta) and (fDiag[diag] >= bDiag[diag]) then
389 begin
390 inc(x1);inc(x2);
391 //save x1 & x2 for second recursive_diff() call by reusing no longer
392 //needed variables (ie minimize variable allocation in recursive fn) ...
393 diag := x1; Oscill := x2;
394 while (x1 > 0) and (x2 > 0) and (Chrs1[offset1+x1-1] = Chrs2[offset2+x2-1]) do
395 begin
396 dec(x1); dec(x2);
397 end;
398 RecursiveDiffChr(offset1, offset2, x1, x2);
399 x1 := diag; x2 := Oscill;
400 RecursiveDiffChr(offset1+x1, offset2+x2, len1-x1, len2-x2);
401 exit; //ALL DONE
402 end;
403 dec(diag,2);
404 end;
405
406 //do backward oscillation (keeping diag within assigned grid)...
407 diag := lenDelta + Oscill;
408 while diag > len1 do dec(diag,2);
409 while diag >= max(lenDelta - Oscill, -len2) do
410 begin
411 if bDiag[diag-1] < bDiag[diag+1] then
412 x1 := bDiag[diag-1] else
413 x1 := bDiag[diag+1]-1;
414 x2 := x1 - diag;
415 while (x1 > -1) and (x2 > -1) and (Chrs1[offset1+x1] = Chrs2[offset2+x2]) do
416 begin
417 dec(x1); dec(x2);
418 end;
419 bDiag[diag] := x1;
420
421 if bDiag[diag] <= fDiag[diag] then
422 begin
423 //flag return value then ...
424 inc(x1);inc(x2);
425 RecursiveDiffChr(offset1, offset2, x1, x2);
426 while (x1 < len1) and (x2 < len2) and
427 (Chrs1[offset1+x1] = Chrs2[offset2+x2]) do
428 begin
429 inc(x1); inc(x2);
430 end;
431 RecursiveDiffChr(offset1+x1, offset2+x2, len1-x1, len2-x2);
432 exit; //ALL DONE
433 end;
434 dec(diag,2);
435 end;
436
437 inc(Oscill);
438 end; //while Oscill <= maxOscill
439
440 raise Exception.create('oops - error in RecursiveDiffChr()');
441end;
442//------------------------------------------------------------------------------
443
444procedure TDiff.RecursiveDiffInt(offset1, offset2, len1, len2: integer);
445var
446 diag, lenDelta, Oscill, maxOscill, x1, x2: integer;
447begin
448 //nb: the possible depth of recursion here is most unlikely to cause
449 // problems with stack overflows.
450 application.processmessages;
451 if fCancelled then exit;
452
453 if (len1 = 0) then
454 begin
455 assert(len2 > 0,'oops!');
456 AddChangeInts(offset1, len2, ckAdd);
457 exit;
458 end
459 else if (len2 = 0) then
460 begin
461 AddChangeInts(offset1, len1, ckDelete);
462 exit;
463 end
464 else if (len1 = 1) and (len2 = 1) then
465 begin
466 assert(Ints1[offset1] <> Ints2[offset2],'oops!');
467 AddChangeInts(offset1, 1, ckDelete);
468 AddChangeInts(offset1, 1, ckAdd);
469 exit;
470 end;
471
472 maxOscill := min(max(len1,len2), MAX_DIAGONAL);
473 InitDiagArrays(MaxOscill, len1, len2);
474 lenDelta := len1 -len2;
475
476 Oscill := 1; //ie assumes prior filter of top and bottom matches
477 while Oscill <= maxOscill do
478 begin
479
480 if (Oscill mod 200) = 0 then
481 begin
482 application.processmessages;
483 if fCancelled then exit;
484 end;
485
486 //do forward oscillation (keeping diag within assigned grid)...
487 diag := Oscill;
488 while diag > len1 do dec(diag,2);
489 while diag >= max(- Oscill, -len2) do
490 begin
491 if fDiag[diag-1] < fDiag[diag+1] then
492 x1 := fDiag[diag+1] else
493 x1 := fDiag[diag-1]+1;
494 x2 := x1 - diag;
495 while (x1 < len1-1) and (x2 < len2-1) and
496 (Ints1[offset1+x1+1] = Ints2[offset2+x2+1]) do
497 begin
498 inc(x1); inc(x2);
499 end;
500 fDiag[diag] := x1;
501
502 //nb: (fDiag[diag] is always < bDiag[diag]) here when NOT odd(lenDelta) ...
503 if odd(lenDelta) and (fDiag[diag] >= bDiag[diag]) then
504 begin
505 inc(x1);inc(x2);
506 //save x1 & x2 for second recursive_diff() call by reusing no longer
507 //needed variables (ie minimize variable allocation in recursive fn) ...
508 diag := x1; Oscill := x2;
509 while (x1 > 0) and (x2 > 0) and (Ints1[offset1+x1-1] = Ints2[offset2+x2-1]) do
510 begin
511 dec(x1); dec(x2);
512 end;
513 RecursiveDiffInt(offset1, offset2, x1, x2);
514 x1 := diag; x2 := Oscill;
515 RecursiveDiffInt(offset1+x1, offset2+x2, len1-x1, len2-x2);
516 exit; //ALL DONE
517 end;
518 dec(diag,2);
519 end;
520
521 //do backward oscillation (keeping diag within assigned grid)...
522 diag := lenDelta + Oscill;
523 while diag > len1 do dec(diag,2);
524 while diag >= max(lenDelta - Oscill, -len2) do
525 begin
526 if bDiag[diag-1] < bDiag[diag+1] then
527 x1 := bDiag[diag-1] else
528 x1 := bDiag[diag+1]-1;
529 x2 := x1 - diag;
530 while (x1 > -1) and (x2 > -1) and (Ints1[offset1+x1] = Ints2[offset2+x2]) do
531 begin
532 dec(x1); dec(x2);
533 end;
534 bDiag[diag] := x1;
535
536 if bDiag[diag] <= fDiag[diag] then
537 begin
538 //flag return value then ...
539 inc(x1);inc(x2);
540 RecursiveDiffInt(offset1, offset2, x1, x2);
541 while (x1 < len1) and (x2 < len2) and
542 (Ints1[offset1+x1] = Ints2[offset2+x2]) do
543 begin
544 inc(x1); inc(x2);
545 end;
546 RecursiveDiffInt(offset1+x1, offset2+x2, len1-x1, len2-x2);
547 exit; //ALL DONE
548 end;
549 dec(diag,2);
550 end;
551
552 inc(Oscill);
553 end; //while Oscill <= maxOscill
554
555 raise Exception.create('oops - error in RecursiveDiffInt()');
556end;
557//------------------------------------------------------------------------------
558
559procedure TDiff.Clear;
560var
561 i: integer;
562begin
563 for i := 0 to fCompareList.Count-1 do
564 dispose(PCompareRec(fCompareList[i]));
565 fCompareList.clear;
566 LastCompareRec.Kind := ckNone;
567 LastCompareRec.oldIndex1 := -1;
568 LastCompareRec.oldIndex2 := -1;
569 fDiffStats.matches := 0;
570 fDiffStats.adds := 0;
571 fDiffStats.deletes :=0;
572 fDiffStats.modifies :=0;
573 Chrs1 := nil; Chrs2 := nil; Ints1 := nil; Ints2 := nil;
574end;
575//------------------------------------------------------------------------------
576
577function TDiff.GetCompareCount: integer;
578begin
579 result := fCompareList.count;
580end;
581//------------------------------------------------------------------------------
582
583function TDiff.GetCompare(index: integer): TCompareRec;
584begin
585 result := PCompareRec(fCompareList[index])^;
586end;
587//------------------------------------------------------------------------------
588
589procedure TDiff.AddChangeChrs(offset1, range: integer; ChangeKind: TChangeKind);
590var
591 i,j: integer;
592 compareRec: PCompareRec;
593begin
594 //first, add any unchanged items into this list ...
595 while (LastCompareRec.oldIndex1 < offset1 -1) do
596 begin
597 with LastCompareRec do
598 begin
599 Kind := ckNone;
600 inc(oldIndex1);
601 inc(oldIndex2);
602 chr1 := Chrs1[oldIndex1];
603 chr2 := Chrs2[oldIndex2];
604 end;
605 New(compareRec);
606 compareRec^ := LastCompareRec;
607 fCompareList.Add(compareRec);
608 inc(fDiffStats.matches);
609 end;
610
611 case ChangeKind of
612 ckAdd :
613 begin
614 for i := 1 to range do
615 begin
616 with LastCompareRec do
617 begin
618
619 //check if a range of adds are following a range of deletes
620 //and convert them to modifies ...
621 if Kind = ckDelete then
622 begin
623 j := fCompareList.Count -1;
624 while (j > 0) and (PCompareRec(fCompareList[j-1]).Kind = ckDelete) do
625 dec(j);
626 PCompareRec(fCompareList[j]).Kind := ckModify;
627 dec(fDiffStats.deletes);
628 inc(fDiffStats.modifies);
629 inc(LastCompareRec.oldIndex2);
630 PCompareRec(fCompareList[j]).oldIndex2 := LastCompareRec.oldIndex2;
631 PCompareRec(fCompareList[j]).chr2 := Chrs2[oldIndex2];
632 if j = fCompareList.Count-1 then LastCompareRec.Kind := ckModify;
633 continue;
634 end;
635
636 Kind := ckAdd;
637 chr1 := #0;
638 inc(oldIndex2);
639 chr2 := Chrs2[oldIndex2]; //ie what we added
640 end;
641 New(compareRec);
642 compareRec^ := LastCompareRec;
643 fCompareList.Add(compareRec);
644 inc(fDiffStats.adds);
645 end;
646 end;
647 ckDelete :
648 begin
649 for i := 1 to range do
650 begin
651 with LastCompareRec do
652 begin
653
654 //check if a range of deletes are following a range of adds
655 //and convert them to modifies ...
656 if Kind = ckAdd then
657 begin
658 j := fCompareList.Count -1;
659 while (j > 0) and (PCompareRec(fCompareList[j-1]).Kind = ckAdd) do
660 dec(j);
661 PCompareRec(fCompareList[j]).Kind := ckModify;
662 dec(fDiffStats.adds);
663 inc(fDiffStats.modifies);
664 inc(LastCompareRec.oldIndex1);
665 PCompareRec(fCompareList[j]).oldIndex1 := LastCompareRec.oldIndex1;
666 PCompareRec(fCompareList[j]).chr1 := Chrs1[oldIndex1];
667 if j = fCompareList.Count-1 then LastCompareRec.Kind := ckModify;
668 continue;
669 end;
670
671 Kind := ckDelete;
672 chr2 := #0;
673 inc(oldIndex1);
674 chr1 := Chrs1[oldIndex1]; //ie what we deleted
675 end;
676 New(compareRec);
677 compareRec^ := LastCompareRec;
678 fCompareList.Add(compareRec);
679 inc(fDiffStats.deletes);
680 end;
681 end;
682 end;
683end;
684//------------------------------------------------------------------------------
685
686procedure TDiff.AddChangeInts(offset1, range: integer; ChangeKind: TChangeKind);
687var
688 i,j: integer;
689 compareRec: PCompareRec;
690begin
691 //first, add any unchanged items into this list ...
692 while (LastCompareRec.oldIndex1 < offset1 -1) do
693 begin
694 with LastCompareRec do
695 begin
696 Kind := ckNone;
697 inc(oldIndex1);
698 inc(oldIndex2);
699 int1 := Ints1[oldIndex1];
700 int2 := Ints2[oldIndex2];
701 end;
702 New(compareRec);
703 compareRec^ := LastCompareRec;
704 fCompareList.Add(compareRec);
705 inc(fDiffStats.matches);
706 end;
707
708 case ChangeKind of
709 ckAdd :
710 begin
711 for i := 1 to range do
712 begin
713 with LastCompareRec do
714 begin
715
716 //check if a range of adds are following a range of deletes
717 //and convert them to modifies ...
718 if Kind = ckDelete then
719 begin
720 j := fCompareList.Count -1;
721 while (j > 0) and (PCompareRec(fCompareList[j-1]).Kind = ckDelete) do
722 dec(j);
723 PCompareRec(fCompareList[j]).Kind := ckModify;
724 dec(fDiffStats.deletes);
725 inc(fDiffStats.modifies);
726 inc(LastCompareRec.oldIndex2);
727 PCompareRec(fCompareList[j]).oldIndex2 := LastCompareRec.oldIndex2;
728 PCompareRec(fCompareList[j]).int2 := Ints2[oldIndex2];
729 if j = fCompareList.Count-1 then LastCompareRec.Kind := ckModify;
730 continue;
731 end;
732
733 Kind := ckAdd;
734 int1 := $0;
735 inc(oldIndex2);
736 int2 := Ints2[oldIndex2]; //ie what we added
737 end;
738 New(compareRec);
739 compareRec^ := LastCompareRec;
740 fCompareList.Add(compareRec);
741 inc(fDiffStats.adds);
742 end;
743 end;
744 ckDelete :
745 begin
746 for i := 1 to range do
747 begin
748 with LastCompareRec do
749 begin
750
751 //check if a range of deletes are following a range of adds
752 //and convert them to modifies ...
753 if Kind = ckAdd then
754 begin
755 j := fCompareList.Count -1;
756 while (j > 0) and (PCompareRec(fCompareList[j-1]).Kind = ckAdd) do
757 dec(j);
758 PCompareRec(fCompareList[j]).Kind := ckModify;
759 dec(fDiffStats.adds);
760 inc(fDiffStats.modifies);
761 inc(LastCompareRec.oldIndex1);
762 PCompareRec(fCompareList[j]).oldIndex1 := LastCompareRec.oldIndex1;
763 PCompareRec(fCompareList[j]).int1 := Ints1[oldIndex1];
764 if j = fCompareList.Count-1 then LastCompareRec.Kind := ckModify;
765 continue;
766 end;
767
768 Kind := ckDelete;
769 int2 := $0;
770 inc(oldIndex1);
771 int1 := Ints1[oldIndex1]; //ie what we deleted
772 end;
773 New(compareRec);
774 compareRec^ := LastCompareRec;
775 fCompareList.Add(compareRec);
776 inc(fDiffStats.deletes);
777 end;
778 end;
779 end;
780end;
781//------------------------------------------------------------------------------
782
783procedure TDiff.Cancel;
784begin
785 fCancelled := true;
786end;
787//------------------------------------------------------------------------------
788
789end.
Note: See TracBrowser for help on using the repository browser.