ILIAS  release_7 Revision v7.30-3-g800a261c036
_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
 

Detailed Description

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

Member Function Documentation

◆ _compareseq()

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

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

1110 {
1111 $fname = '_DiffEngine::_compareseq';
1112 //wfProfileIn( $fname );
1113
1114 // Slide down the bottom initial diagonal.
1115 while ($xoff < $xlim && $yoff < $ylim
1116 && $this->xv[$xoff] == $this->yv[$yoff]) {
1117 ++$xoff;
1118 ++$yoff;
1119 }
1120
1121 // Slide up the top initial diagonal.
1122 while ($xlim > $xoff && $ylim > $yoff
1123 && $this->xv[$xlim - 1] == $this->yv[$ylim - 1]) {
1124 --$xlim;
1125 --$ylim;
1126 }
1127
1128 if ($xoff == $xlim || $yoff == $ylim) {
1129 $lcs = 0;
1130 } else {
1131 // This is ad hoc but seems to work well.
1132 //$nchunks = sqrt(min($xlim - $xoff, $ylim - $yoff) / 2.5);
1133 //$nchunks = max(2,min(8,(int)$nchunks));
1134 $nchunks = min(7, $xlim - $xoff, $ylim - $yoff) + 1;
1135 list($lcs, $seps)
1136 = $this->_diag($xoff, $xlim, $yoff, $ylim, $nchunks);
1137 }
1138
1139 if ($lcs == 0) {
1140 // X and Y sequences have no common subsequence:
1141 // mark all changed.
1142 while ($yoff < $ylim) {
1143 $this->ychanged[$this->yind[$yoff++]] = 1;
1144 }
1145 while ($xoff < $xlim) {
1146 $this->xchanged[$this->xind[$xoff++]] = 1;
1147 }
1148 } else {
1149 // Use the partitions to split this problem into subproblems.
1150 reset($seps);
1151 $pt1 = $seps[0];
1152 while ($pt2 = next($seps)) {
1153 $this->_compareseq($pt1[0], $pt2[0], $pt1[1], $pt2[1]);
1154 $pt1 = $pt2;
1155 }
1156 }
1157 //wfProfileOut( $fname );
1158 }
_diag($xoff, $xlim, $yoff, $ylim, $nchunks)
_compareseq($xoff, $xlim, $yoff, $ylim)

References _compareseq(), and _diag().

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 980 of file class.WordLevelDiff.php.

981 {
982 $fname = '_DiffEngine::_diag';
983 //wfProfileIn( $fname );
984 $flip = false;
985
986 if ($xlim - $xoff > $ylim - $yoff) {
987 // Things seems faster (I'm not sure I understand why)
988 // when the shortest sequence in X.
989 $flip = true;
990 list($xoff, $xlim, $yoff, $ylim)
991 = array( $yoff, $ylim, $xoff, $xlim);
992 }
993
994 if ($flip) {
995 for ($i = $ylim - 1; $i >= $yoff; $i--) {
996 $ymatches[$this->xv[$i]][] = $i;
997 }
998 } else {
999 for ($i = $ylim - 1; $i >= $yoff; $i--) {
1000 $ymatches[$this->yv[$i]][] = $i;
1001 }
1002 }
1003
1004 $this->lcs = 0;
1005 $this->seq[0] = $yoff - 1;
1006 $this->in_seq = array();
1007 $ymids[0] = array();
1008
1009 $numer = $xlim - $xoff + $nchunks - 1;
1010 $x = $xoff;
1011 for ($chunk = 0; $chunk < $nchunks; $chunk++) {
1012 //wfProfileIn( "$fname-chunk" );
1013 if ($chunk > 0) {
1014 for ($i = 0; $i <= $this->lcs; $i++) {
1015 $ymids[$i][$chunk - 1] = $this->seq[$i];
1016 }
1017 }
1018
1019 $x1 = $xoff + (int) (($numer + ($xlim - $xoff) * $chunk) / $nchunks);
1020 for (; $x < $x1; $x++) {
1021 $line = $flip ? $this->yv[$x] : $this->xv[$x];
1022 if (empty($ymatches[$line])) {
1023 continue;
1024 }
1025 $matches = $ymatches[$line];
1026 reset($matches);
1027 foreach ($matches as $junk => $y) {
1028 if (empty($this->in_seq[$y])) {
1029 $k = $this->_lcs_pos($y);
1030 USE_ASSERTS && assert($k > 0);
1031 $ymids[$k] = $ymids[$k - 1];
1032 break;
1033 }
1034 }
1035 foreach ($matches as $y) {
1036 if ($y > $this->seq[$k - 1]) {
1037 USE_ASSERTS && assert($y < $this->seq[$k]);
1038 // Optimization: this is a common case:
1039 // next match is just replacing previous match.
1040 $this->in_seq[$this->seq[$k]] = false;
1041 $this->seq[$k] = $y;
1042 $this->in_seq[$y] = 1;
1043 } elseif (empty($this->in_seq[$y])) {
1044 $k = $this->_lcs_pos($y);
1045 USE_ASSERTS && assert($k > 0);
1046 $ymids[$k] = $ymids[$k - 1];
1047 }
1048 }
1049 }
1050 //wfProfileOut( "$fname-chunk" );
1051 }
1052
1053 $seps[] = $flip ? array($yoff, $xoff) : array($xoff, $yoff);
1054 $ymid = $ymids[$this->lcs];
1055 for ($n = 0; $n < $nchunks - 1; $n++) {
1056 $x1 = $xoff + (int) (($numer + ($xlim - $xoff) * $n) / $nchunks);
1057 $y1 = $ymid[$n] + 1;
1058 $seps[] = $flip ? array($y1, $x1) : array($x1, $y1);
1059 }
1060 $seps[] = $flip ? array($ylim, $xlim) : array($xlim, $ylim);
1061
1062 //wfProfileOut( $fname );
1063 return array($this->lcs, $seps);
1064 }
$n
Definition: RandomTest.php:85
const USE_ASSERTS
$i
Definition: metadata.php:24

References $i, $n, _lcs_pos(), 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 1066 of file class.WordLevelDiff.php.

1067 {
1068 $fname = '_DiffEngine::_lcs_pos';
1069 //wfProfileIn( $fname );
1070
1071 $end = $this->lcs;
1072 if ($end == 0 || $ypos > $this->seq[$end]) {
1073 $this->seq[++$this->lcs] = $ypos;
1074 $this->in_seq[$ypos] = 1;
1075 //wfProfileOut( $fname );
1076 return $this->lcs;
1077 }
1078
1079 $beg = 1;
1080 while ($beg < $end) {
1081 $mid = (int) (($beg + $end) / 2);
1082 if ($ypos > $this->seq[$mid]) {
1083 $beg = $mid + 1;
1084 } else {
1085 $end = $mid;
1086 }
1087 }
1088
1089 USE_ASSERTS && assert($ypos != $this->seq[$end]);
1090
1091 $this->in_seq[$this->seq[$end]] = false;
1092 $this->seq[$end] = $ypos;
1093 $this->in_seq[$ypos] = 1;
1094 //wfProfileOut( $fname );
1095 return $end;
1096 }

References USE_ASSERTS.

Referenced by _diag().

+ 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 954 of file class.WordLevelDiff.php.

955 {
956 if (strlen($line) > self::MAX_XREF_LENGTH) {
957 return md5($line);
958 } else {
959 return $line;
960 }
961 }

Referenced by diff().

+ Here is the caller graph for this function:

◆ _shift_boundaries()

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

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

1173 {
1174 $fname = '_DiffEngine::_shift_boundaries';
1175 //wfProfileIn( $fname );
1176 $i = 0;
1177 $j = 0;
1178
1179 USE_ASSERTS && assert(sizeof($lines) == sizeof($changed));
1180 $len = sizeof($lines);
1181 $other_len = sizeof($other_changed);
1182
1183 while (1) {
1184 /*
1185 * Scan forwards to find beginning of another run of changes.
1186 * Also keep track of the corresponding point in the other file.
1187 *
1188 * Throughout this code, $i and $j are adjusted together so that
1189 * the first $i elements of $changed and the first $j elements
1190 * of $other_changed both contain the same number of zeros
1191 * (unchanged lines).
1192 * Furthermore, $j is always kept so that $j == $other_len or
1193 * $other_changed[$j] == false.
1194 */
1195 while ($j < $other_len && $other_changed[$j]) {
1196 $j++;
1197 }
1198
1199 while ($i < $len && !$changed[$i]) {
1200 USE_ASSERTS && assert($j < $other_len && !$other_changed[$j]);
1201 $i++;
1202 $j++;
1203 while ($j < $other_len && $other_changed[$j]) {
1204 $j++;
1205 }
1206 }
1207
1208 if ($i == $len) {
1209 break;
1210 }
1211
1212 $start = $i;
1213
1214 // Find the end of this run of changes.
1215 while (++$i < $len && $changed[$i]) {
1216 continue;
1217 }
1218
1219 do {
1220 /*
1221 * Record the length of this run of changes, so that
1222 * we can later determine whether the run has grown.
1223 */
1224 $runlength = $i - $start;
1225
1226 /*
1227 * Move the changed region back, so long as the
1228 * previous unchanged line matches the last changed one.
1229 * This merges with previous changed regions.
1230 */
1231 while ($start > 0 && $lines[$start - 1] == $lines[$i - 1]) {
1232 $changed[--$start] = 1;
1233 $changed[--$i] = false;
1234 while ($start > 0 && $changed[$start - 1]) {
1235 $start--;
1236 }
1237 USE_ASSERTS && assert($j > 0);
1238 while ($other_changed[--$j]) {
1239 continue;
1240 }
1241 USE_ASSERTS && assert($j >= 0 && !$other_changed[$j]);
1242 }
1243
1244 /*
1245 * Set CORRESPONDING to the end of the changed run, at the last
1246 * point where it corresponds to a changed run in the other file.
1247 * CORRESPONDING == LEN means no such point has been found.
1248 */
1249 $corresponding = $j < $other_len ? $i : $len;
1250
1251 /*
1252 * Move the changed region forward, so long as the
1253 * first changed line matches the following unchanged one.
1254 * This merges with following changed regions.
1255 * Do this second, so that if there are no merges,
1256 * the changed region is moved forward as far as possible.
1257 */
1258 while ($i < $len && $lines[$start] == $lines[$i]) {
1259 $changed[$start++] = false;
1260 $changed[$i++] = 1;
1261 while ($i < $len && $changed[$i]) {
1262 $i++;
1263 }
1264
1265 USE_ASSERTS && assert($j < $other_len && !$other_changed[$j]);
1266 $j++;
1267 if ($j < $other_len && $other_changed[$j]) {
1268 $corresponding = $i;
1269 while ($j < $other_len && $other_changed[$j]) {
1270 $j++;
1271 }
1272 }
1273 }
1274 } while ($runlength != $i - $start);
1275
1276 /*
1277 * If possible, move the fully-merged run of changes
1278 * back to a corresponding run in the other file.
1279 */
1280 while ($corresponding < $i) {
1281 $changed[--$start] = 1;
1282 $changed[--$i] = 0;
1283 USE_ASSERTS && assert($j > 0);
1284 while ($other_changed[--$j]) {
1285 continue;
1286 }
1287 USE_ASSERTS && assert($j >= 0 && !$other_changed[$j]);
1288 }
1289 }
1290 //wfProfileOut( $fname );
1291 }

References $changed, $i, and USE_ASSERTS.

Referenced by diff().

+ Here is the caller graph for this function:

◆ diff()

_DiffEngine::diff (   $from_lines,
  $to_lines 
)

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

849 {
850 $fname = '_DiffEngine::diff';
851 //wfProfileIn( $fname );
852
853 $n_from = sizeof($from_lines);
854 $n_to = sizeof($to_lines);
855
856 $this->xchanged = $this->ychanged = array();
857 $this->xv = $this->yv = array();
858 $this->xind = $this->yind = array();
859 unset($this->seq);
860 unset($this->in_seq);
861 unset($this->lcs);
862
863 // Skip leading common lines.
864 for ($skip = 0; $skip < $n_from && $skip < $n_to; $skip++) {
865 if ($from_lines[$skip] !== $to_lines[$skip]) {
866 break;
867 }
868 $this->xchanged[$skip] = $this->ychanged[$skip] = false;
869 }
870 // Skip trailing common lines.
871 $xi = $n_from;
872 $yi = $n_to;
873 for ($endskip = 0; --$xi > $skip && --$yi > $skip; $endskip++) {
874 if ($from_lines[$xi] !== $to_lines[$yi]) {
875 break;
876 }
877 $this->xchanged[$xi] = $this->ychanged[$yi] = false;
878 }
879
880 // Ignore lines which do not exist in both files.
881 for ($xi = $skip; $xi < $n_from - $endskip; $xi++) {
882 $xhash[$this->_line_hash($from_lines[$xi])] = 1;
883 }
884
885 for ($yi = $skip; $yi < $n_to - $endskip; $yi++) {
886 $line = $to_lines[$yi];
887 if (($this->ychanged[$yi] = empty($xhash[$this->_line_hash($line)]))) {
888 continue;
889 }
890 $yhash[$this->_line_hash($line)] = 1;
891 $this->yv[] = $line;
892 $this->yind[] = $yi;
893 }
894 for ($xi = $skip; $xi < $n_from - $endskip; $xi++) {
895 $line = $from_lines[$xi];
896 if (($this->xchanged[$xi] = empty($yhash[$this->_line_hash($line)]))) {
897 continue;
898 }
899 $this->xv[] = $line;
900 $this->xind[] = $xi;
901 }
902
903 // Find the LCS.
904 $this->_compareseq(0, sizeof($this->xv), 0, sizeof($this->yv));
905
906 // Merge edits when possible
907 $this->_shift_boundaries($from_lines, $this->xchanged, $this->ychanged);
908 $this->_shift_boundaries($to_lines, $this->ychanged, $this->xchanged);
909
910 // Compute the edit operations.
911 $edits = array();
912 $xi = $yi = 0;
913 while ($xi < $n_from || $yi < $n_to) {
914 USE_ASSERTS && assert($yi < $n_to || $this->xchanged[$xi]);
915 USE_ASSERTS && assert($xi < $n_from || $this->ychanged[$yi]);
916
917 // Skip matching "snake".
918 $copy = array();
919 while ($xi < $n_from && $yi < $n_to
920 && !$this->xchanged[$xi] && !$this->ychanged[$yi]) {
921 $copy[] = $from_lines[$xi++];
922 ++$yi;
923 }
924 if ($copy) {
925 $edits[] = new _DiffOp_Copy($copy);
926 }
927
928 // Find deletes & adds.
929 $delete = array();
930 while ($xi < $n_from && $this->xchanged[$xi]) {
931 $delete[] = $from_lines[$xi++];
932 }
933
934 $add = array();
935 while ($yi < $n_to && $this->ychanged[$yi]) {
936 $add[] = $to_lines[$yi++];
937 }
938
939 if ($delete && $add) {
940 $edits[] = new _DiffOp_Change($delete, $add);
941 } elseif ($delete) {
942 $edits[] = new _DiffOp_Delete($delete);
943 } elseif ($add) {
944 $edits[] = new _DiffOp_Add($add);
945 }
946 }
947 //wfProfileOut( $fname );
948 return $edits;
949 }
_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.

Referenced by Diff\__construct().

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

Field Documentation

◆ MAX_XREF_LENGTH

const _DiffEngine::MAX_XREF_LENGTH = 10000

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


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