ILIAS  trunk Revision v12.0_alpha-377-g3641b37b9db
_DiffEngine Class Reference
+ Collaboration diagram for _DiffEngine:

Public Member Functions

 diff ($from_lines, $to_lines)
 
 _line_hash ($line)
 Returns the whole line if it's small enough, or the MD5 hash otherwise. More...
 
 _diag ($xoff, $xlim, $yoff, $ylim, $nchunks)
 
 _lcs_pos ($ypos)
 
 _compareseq ($xoff, $xlim, $yoff, $ylim)
 
 _shift_boundaries ($lines, &$changed, $other_changed)
 

Data Fields

const MAX_XREF_LENGTH = 10000
 

Private Attributes

array $xchanged
 
array $xv
 
array $xind
 
array $ychanged
 
array $yv
 
array $yind
 
int $lcs
 
array $in_seq
 
array $seq = []
 

Detailed Description

Definition at line 146 of file class.WordLevelDiff.php.

Member Function Documentation

◆ _compareseq()

_DiffEngine::_compareseq (   $xoff,
  $xlim,
  $yoff,
  $ylim 
)

Definition at line 420 of file class.WordLevelDiff.php.

421 {
422 $fname = '_DiffEngine::_compareseq';
423 //wfProfileIn( $fname );
424
425 // Slide down the bottom initial diagonal.
426 while ($xoff < $xlim && $yoff < $ylim
427 && $this->xv[$xoff] == $this->yv[$yoff]) {
428 ++$xoff;
429 ++$yoff;
430 }
431
432 // Slide up the top initial diagonal.
433 while ($xlim > $xoff && $ylim > $yoff
434 && $this->xv[$xlim - 1] == $this->yv[$ylim - 1]) {
435 --$xlim;
436 --$ylim;
437 }
438
439 if ($xoff == $xlim || $yoff == $ylim) {
440 $lcs = 0;
441 } else {
442 // This is ad hoc but seems to work well.
443 //$nchunks = sqrt(min($xlim - $xoff, $ylim - $yoff) / 2.5);
444 //$nchunks = max(2,min(8,(int)$nchunks));
445 $nchunks = min(7, $xlim - $xoff, $ylim - $yoff) + 1;
446 list($lcs, $seps)
447 = $this->_diag($xoff, $xlim, $yoff, $ylim, $nchunks);
448 }
449
450 if ($lcs == 0) {
451 // X and Y sequences have no common subsequence:
452 // mark all changed.
453 while ($yoff < $ylim) {
454 $this->ychanged[$this->yind[$yoff++]] = 1;
455 }
456 while ($xoff < $xlim) {
457 $this->xchanged[$this->xind[$xoff++]] = 1;
458 }
459 } else {
460 // Use the partitions to split this problem into subproblems.
461 reset($seps);
462 $pt1 = $seps[0];
463 while ($pt2 = next($seps)) {
464 $this->_compareseq($pt1[0], $pt2[0], $pt1[1], $pt2[1]);
465 $pt1 = $pt2;
466 }
467 }
468 //wfProfileOut( $fname );
469 }
_diag($xoff, $xlim, $yoff, $ylim, $nchunks)
_compareseq($xoff, $xlim, $yoff, $ylim)

References $lcs, _compareseq(), _diag(), and ILIAS\User\Profile\ChangeMail\next.

Referenced by _compareseq(), and diff().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ _diag()

_DiffEngine::_diag (   $xoff,
  $xlim,
  $yoff,
  $ylim,
  $nchunks 
)

Definition at line 291 of file class.WordLevelDiff.php.

292 {
293 $fname = '_DiffEngine::_diag';
294 //wfProfileIn( $fname );
295 $flip = false;
296
297 if ($xlim - $xoff > $ylim - $yoff) {
298 // Things seems faster (I'm not sure I understand why)
299 // when the shortest sequence in X.
300 $flip = true;
301 list($xoff, $xlim, $yoff, $ylim)
302 = array( $yoff, $ylim, $xoff, $xlim);
303 }
304
305 if ($flip) {
306 for ($i = $ylim - 1; $i >= $yoff; $i--) {
307 $ymatches[$this->xv[$i]][] = $i;
308 }
309 } else {
310 for ($i = $ylim - 1; $i >= $yoff; $i--) {
311 $ymatches[$this->yv[$i]][] = $i;
312 }
313 }
314
315 $this->lcs = 0;
316 $this->seq[0] = $yoff - 1;
317 $this->in_seq = array();
318 $ymids[0] = array();
319
320 $numer = $xlim - $xoff + $nchunks - 1;
321 $x = $xoff;
322 for ($chunk = 0; $chunk < $nchunks; $chunk++) {
323 //wfProfileIn( "$fname-chunk" );
324 if ($chunk > 0) {
325 for ($i = 0; $i <= $this->lcs; $i++) {
326 $ymids[$i][$chunk - 1] = $this->seq[$i];
327 }
328 }
329
330 $x1 = $xoff + (int) (($numer + ($xlim - $xoff) * $chunk) / $nchunks);
331 for (; $x < $x1; $x++) {
332 $line = $flip ? $this->yv[$x] : $this->xv[$x];
333 if (empty($ymatches[$line])) {
334 continue;
335 }
336 $matches = $ymatches[$line];
337 reset($matches);
338 foreach ($matches as $junk => $y) {
339 if (empty($this->in_seq[$y])) {
340 $k = $this->_lcs_pos($y);
341 USE_ASSERTS && assert($k > 0);
342 $ymids[$k] = $ymids[$k - 1];
343 break;
344 }
345 }
346 foreach ($matches as $y) {
347 if ($y > $this->seq[$k - 1]) {
348 USE_ASSERTS && assert($y < $this->seq[$k]);
349 // Optimization: this is a common case:
350 // next match is just replacing previous match.
351 $this->in_seq[$this->seq[$k]] = false;
352 $this->seq[$k] = $y;
353 $this->in_seq[$y] = 1;
354 } elseif (empty($this->in_seq[$y])) {
355 $k = $this->_lcs_pos($y);
356 USE_ASSERTS && assert($k > 0);
357 $ymids[$k] = $ymids[$k - 1];
358 }
359 }
360 }
361 //wfProfileOut( "$fname-chunk" );
362 }
363
364 $seps[] = $flip ? array($yoff, $xoff) : array($xoff, $yoff);
365 $ymid = $ymids[$this->lcs];
366 for ($n = 0; $n < $nchunks - 1; $n++) {
367 $x1 = $xoff + (int) (($numer + ($xlim - $xoff) * $n) / $nchunks);
368 $y1 = $ymid[$n] + 1;
369 $seps[] = $flip ? array($y1, $x1) : array($x1, $y1);
370 }
371 $seps[] = $flip ? array($ylim, $xlim) : array($xlim, $ylim);
372
373 //wfProfileOut( $fname );
374 return array($this->lcs, $seps);
375 }
const USE_ASSERTS

References $lcs, _lcs_pos(), ILIAS\Repository\int(), and USE_ASSERTS.

Referenced by _compareseq().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ _lcs_pos()

_DiffEngine::_lcs_pos (   $ypos)

Definition at line 377 of file class.WordLevelDiff.php.

378 {
379 $fname = '_DiffEngine::_lcs_pos';
380 //wfProfileIn( $fname );
381
382 $end = $this->lcs;
383 if ($end == 0 || $ypos > $this->seq[$end]) {
384 $this->seq[++$this->lcs] = $ypos;
385 $this->in_seq[$ypos] = 1;
386 //wfProfileOut( $fname );
387 return $this->lcs;
388 }
389
390 $beg = 1;
391 while ($beg < $end) {
392 $mid = (int) (($beg + $end) / 2);
393 if ($ypos > $this->seq[$mid]) {
394 $beg = $mid + 1;
395 } else {
396 $end = $mid;
397 }
398 }
399
400 USE_ASSERTS && assert($ypos != $this->seq[$end]);
401
402 $this->in_seq[$this->seq[$end]] = false;
403 $this->seq[$end] = $ypos;
404 $this->in_seq[$ypos] = 1;
405 //wfProfileOut( $fname );
406 return $end;
407 }

References $lcs, ILIAS\Repository\int(), and USE_ASSERTS.

Referenced by _diag().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ _line_hash()

_DiffEngine::_line_hash (   $line)

Returns the whole line if it's small enough, or the MD5 hash otherwise.

Definition at line 265 of file class.WordLevelDiff.php.

266 {
267 if (strlen($line) > self::MAX_XREF_LENGTH) {
268 return md5($line);
269 } else {
270 return $line;
271 }
272 }

Referenced by diff().

+ Here is the caller graph for this function:

◆ _shift_boundaries()

_DiffEngine::_shift_boundaries (   $lines,
$changed,
  $other_changed 
)

Definition at line 483 of file class.WordLevelDiff.php.

484 {
485 $fname = '_DiffEngine::_shift_boundaries';
486 //wfProfileIn( $fname );
487 $i = 0;
488 $j = 0;
489
490 USE_ASSERTS && assert(sizeof($lines) == sizeof($changed));
491 $len = sizeof($lines);
492 $other_len = sizeof($other_changed);
493
494 while (1) {
495 /*
496 * Scan forwards to find beginning of another run of changes.
497 * Also keep track of the corresponding point in the other file.
498 *
499 * Throughout this code, $i and $j are adjusted together so that
500 * the first $i elements of $changed and the first $j elements
501 * of $other_changed both contain the same number of zeros
502 * (unchanged lines).
503 * Furthermore, $j is always kept so that $j == $other_len or
504 * $other_changed[$j] == false.
505 */
506 while ($j < $other_len && $other_changed[$j]) {
507 $j++;
508 }
509
510 while ($i < $len && !$changed[$i]) {
511 USE_ASSERTS && assert($j < $other_len && !$other_changed[$j]);
512 $i++;
513 $j++;
514 while ($j < $other_len && $other_changed[$j]) {
515 $j++;
516 }
517 }
518
519 if ($i == $len) {
520 break;
521 }
522
523 $start = $i;
524
525 // Find the end of this run of changes.
526 while (++$i < $len && $changed[$i]) {
527 }
528
529 do {
530 /*
531 * Record the length of this run of changes, so that
532 * we can later determine whether the run has grown.
533 */
534 $runlength = $i - $start;
535
536 /*
537 * Move the changed region back, so long as the
538 * previous unchanged line matches the last changed one.
539 * This merges with previous changed regions.
540 */
541 while ($start > 0 && $lines[$start - 1] == $lines[$i - 1]) {
542 $changed[--$start] = 1;
543 $changed[--$i] = false;
544 while ($start > 0 && $changed[$start - 1]) {
545 $start--;
546 }
547 USE_ASSERTS && assert($j > 0);
548 while ($other_changed[--$j]) {
549 }
550 USE_ASSERTS && assert($j >= 0 && !$other_changed[$j]);
551 }
552
553 /*
554 * Set CORRESPONDING to the end of the changed run, at the last
555 * point where it corresponds to a changed run in the other file.
556 * CORRESPONDING == LEN means no such point has been found.
557 */
558 $corresponding = $j < $other_len ? $i : $len;
559
560 /*
561 * Move the changed region forward, so long as the
562 * first changed line matches the following unchanged one.
563 * This merges with following changed regions.
564 * Do this second, so that if there are no merges,
565 * the changed region is moved forward as far as possible.
566 */
567 while ($i < $len && $lines[$start] == $lines[$i]) {
568 $changed[$start++] = false;
569 $changed[$i++] = 1;
570 while ($i < $len && $changed[$i]) {
571 $i++;
572 }
573
574 USE_ASSERTS && assert($j < $other_len && !$other_changed[$j]);
575 $j++;
576 if ($j < $other_len && $other_changed[$j]) {
577 $corresponding = $i;
578 while ($j < $other_len && $other_changed[$j]) {
579 $j++;
580 }
581 }
582 }
583 } while ($runlength != $i - $start);
584
585 /*
586 * If possible, move the fully-merged run of changes
587 * back to a corresponding run in the other file.
588 */
589 while ($corresponding < $i) {
590 $changed[--$start] = 1;
591 $changed[--$i] = 0;
592 USE_ASSERTS && assert($j > 0);
593 while ($other_changed[--$j]) {
594 }
595 USE_ASSERTS && assert($j >= 0 && !$other_changed[$j]);
596 }
597 }
598 //wfProfileOut( $fname );
599 }

References USE_ASSERTS.

Referenced by diff().

+ Here is the caller graph for this function:

◆ diff()

_DiffEngine::diff (   $from_lines,
  $to_lines 
)

Definition at line 159 of file class.WordLevelDiff.php.

160 {
161 $fname = '_DiffEngine::diff';
162 //wfProfileIn( $fname );
163
164 $n_from = sizeof($from_lines);
165 $n_to = sizeof($to_lines);
166
167 $this->xchanged = $this->ychanged = array();
168 $this->xv = $this->yv = array();
169 $this->xind = $this->yind = array();
170 unset($this->seq);
171 unset($this->in_seq);
172 unset($this->lcs);
173
174 // Skip leading common lines.
175 for ($skip = 0; $skip < $n_from && $skip < $n_to; $skip++) {
176 if ($from_lines[$skip] !== $to_lines[$skip]) {
177 break;
178 }
179 $this->xchanged[$skip] = $this->ychanged[$skip] = false;
180 }
181 // Skip trailing common lines.
182 $xi = $n_from;
183 $yi = $n_to;
184 for ($endskip = 0; --$xi > $skip && --$yi > $skip; $endskip++) {
185 if ($from_lines[$xi] !== $to_lines[$yi]) {
186 break;
187 }
188 $this->xchanged[$xi] = $this->ychanged[$yi] = false;
189 }
190
191 // Ignore lines which do not exist in both files.
192 for ($xi = $skip; $xi < $n_from - $endskip; $xi++) {
193 $xhash[$this->_line_hash($from_lines[$xi])] = 1;
194 }
195
196 for ($yi = $skip; $yi < $n_to - $endskip; $yi++) {
197 $line = $to_lines[$yi];
198 if (($this->ychanged[$yi] = empty($xhash[$this->_line_hash($line)]))) {
199 continue;
200 }
201 $yhash[$this->_line_hash($line)] = 1;
202 $this->yv[] = $line;
203 $this->yind[] = $yi;
204 }
205 for ($xi = $skip; $xi < $n_from - $endskip; $xi++) {
206 $line = $from_lines[$xi];
207 if (($this->xchanged[$xi] = empty($yhash[$this->_line_hash($line)]))) {
208 continue;
209 }
210 $this->xv[] = $line;
211 $this->xind[] = $xi;
212 }
213
214 // Find the LCS.
215 $this->_compareseq(0, sizeof($this->xv), 0, sizeof($this->yv));
216
217 // Merge edits when possible
218 $this->_shift_boundaries($from_lines, $this->xchanged, $this->ychanged);
219 $this->_shift_boundaries($to_lines, $this->ychanged, $this->xchanged);
220
221 // Compute the edit operations.
222 $edits = array();
223 $xi = $yi = 0;
224 while ($xi < $n_from || $yi < $n_to) {
225 USE_ASSERTS && assert($yi < $n_to || $this->xchanged[$xi]);
226 USE_ASSERTS && assert($xi < $n_from || $this->ychanged[$yi]);
227
228 // Skip matching "snake".
229 $copy = array();
230 while ($xi < $n_from && $yi < $n_to
231 && !$this->xchanged[$xi] && !$this->ychanged[$yi]) {
232 $copy[] = $from_lines[$xi++];
233 ++$yi;
234 }
235 if ($copy) {
236 $edits[] = new _DiffOp_Copy($copy);
237 }
238
239 // Find deletes & adds.
240 $delete = array();
241 while ($xi < $n_from && $this->xchanged[$xi]) {
242 $delete[] = $from_lines[$xi++];
243 }
244
245 $add = array();
246 while ($yi < $n_to && $this->ychanged[$yi]) {
247 $add[] = $to_lines[$yi++];
248 }
249
250 if ($delete && $add) {
251 $edits[] = new _DiffOp_Change($delete, $add);
252 } elseif ($delete) {
253 $edits[] = new _DiffOp_Delete($delete);
254 } elseif ($add) {
255 $edits[] = new _DiffOp_Add($add);
256 }
257 }
258 //wfProfileOut( $fname );
259 return $edits;
260 }
_shift_boundaries($lines, &$changed, $other_changed)
_line_hash($line)
Returns the whole line if it's small enough, or the MD5 hash otherwise.

References _compareseq(), _line_hash(), _shift_boundaries(), and USE_ASSERTS.

+ Here is the call graph for this function:

Field Documentation

◆ $in_seq

array _DiffEngine::$in_seq
private

Definition at line 156 of file class.WordLevelDiff.php.

◆ $lcs

int _DiffEngine::$lcs
private

Definition at line 155 of file class.WordLevelDiff.php.

Referenced by _compareseq(), _diag(), and _lcs_pos().

◆ $seq

array _DiffEngine::$seq = []
private

Definition at line 157 of file class.WordLevelDiff.php.

◆ $xchanged

array _DiffEngine::$xchanged
private

Definition at line 149 of file class.WordLevelDiff.php.

◆ $xind

array _DiffEngine::$xind
private

Definition at line 151 of file class.WordLevelDiff.php.

◆ $xv

array _DiffEngine::$xv
private

Definition at line 150 of file class.WordLevelDiff.php.

◆ $ychanged

array _DiffEngine::$ychanged
private

Definition at line 152 of file class.WordLevelDiff.php.

◆ $yind

array _DiffEngine::$yind
private

Definition at line 154 of file class.WordLevelDiff.php.

◆ $yv

array _DiffEngine::$yv
private

Definition at line 153 of file class.WordLevelDiff.php.

◆ MAX_XREF_LENGTH

const _DiffEngine::MAX_XREF_LENGTH = 10000

Definition at line 148 of file class.WordLevelDiff.php.


The documentation for this class was generated from the following file: