/** * Look through the patches and break up any which are longer than the * maximum limit of the match algorithm. * Intended to be called only from within apply(). * Modifies $patches. TODO try to fix it! * * @param PatchObject[] $patches Array of PatchObjects. */ public function splitMax(&$patches) { $patchSize = $this->getMatch()->getMaxBits(); if ($patchSize == 0) { // TODO PHP has fixed size int, so this case isn't relevant. return; } for ($i = 0; $i < count($patches); $i++) { if ($patches[$i]->getLength1() <= $patchSize) { continue; } $bigPatch = $patches[$i]; // Remove the big old patch. array_splice($patches, $i, 1); $i--; $start1 = $bigPatch->getStart1(); $start2 = $bigPatch->getStart2(); $preContext = ''; $bigPatchDiffs = $bigPatch->getChanges(); while (count($bigPatchDiffs)) { // Create one of several smaller patches. $empty = true; $patch = new PatchObject(); $preContextLen = mb_strlen($preContext); $patch->setStart1($start1 - $preContextLen); $patch->setStart2($start2 - $preContextLen); if ($preContext != '') { $patch->setLength1($preContextLen); $patch->setLength2($preContextLen); $patch->appendChanges(array(Diff::EQUAL, $preContext)); } while (count($bigPatchDiffs) && $patch->getLength1() < $patchSize - $this->getMargin()) { list($diffType, $diffText) = $bigPatchDiffs[0]; $diffTextLen = mb_strlen($diffText); if ($diffType == Diff::INSERT) { // Insertions are harmless. $patch->setLength2($patch->getLength2() + $diffTextLen); $start2 += $diffTextLen; $patch->appendChanges(array_shift($bigPatchDiffs)); $empty = false; } elseif ($diffType == Diff::DELETE && ($patchDiffs = $patch->getChanges()) && count($patchDiffs) == 1 && $patchDiffs[0][0] == Diff::EQUAL && 2 * $patchSize < $diffTextLen) { // This is a large deletion. Let it pass in one chunk. $patch->setLength1($patch->getLength1() + $diffTextLen); $start1 += $diffTextLen; array_shift($bigPatchDiffs); $patch->appendChanges(array($diffType, $diffText)); $empty = false; } else { // Deletion or equality. Only take as much as we can stomach. $diffText = mb_substr($diffText, 0, $patchSize - $patch->getLength1() - $this->getMargin()); $diffTextLen = mb_strlen($diffText); $patch->setLength1($patch->getLength1() + $diffTextLen); $start1 += $diffTextLen; if ($diffType == Diff::EQUAL) { $patch->setLength2($patch->getLength2() + $diffTextLen); $start2 += $diffTextLen; } else { $empty = false; } if ($diffText == $bigPatchDiffs[0][1]) { array_shift($bigPatchDiffs); } else { $bigPatchDiffs[0][1] = mb_substr($bigPatchDiffs[0][1], $diffTextLen); } $patch->appendChanges(array($diffType, $diffText)); } } // Compute the head context for the next patch. $diff = $this->getDiff(); $diff->setChanges($patch->getChanges()); $preContext = $diff->text2(); $preContext = mb_substr($preContext, -$this->getMargin()); // Append the end context for this patch. $diff->setChanges($bigPatchDiffs); $postContext = $diff->text1(); $postContext = mb_substr($postContext, 0, $this->getMargin()); if ($postContext != '') { $patch->setLength1($patch->getLength1() + mb_strlen($postContext)); $patch->setLength2($patch->getLength2() + mb_strlen($postContext)); if (($patchDiffs = $patch->getChanges()) && count($patchDiffs) && $patchDiffs[count($patchDiffs) - 1][0] == Diff::EQUAL) { $patchDiffs[count($patchDiffs) - 1][1] .= $postContext; $patch->setChanges($patchDiffs); } else { $patch->appendChanges(array(Diff::EQUAL, $postContext)); } } if (!$empty) { $i++; array_splice($patches, $i, 0, array($patch)); } } } }