From d11ca1b0845987f1e17c284ea86a264b45bf52d7 Mon Sep 17 00:00:00 2001 From: Shawn Pearce Date: Fri, 25 Jul 2014 08:17:27 -0700 Subject: [PATCH] HistogramDiff: Convert stack recursion to heap managed queue Each time the longest common substring is found the diff algorithm recurses to reprocess the regions before and after the common string. Large files with many edits can trigger StackOverflowError as the algorithm attempts to process a deeply split tree of regions. This is especially prone to happen in servers where the Java stack size may have been limited to 1M or even 256K. To keep edits produced in order a queue is used to process edits in a depth-first strategy. Change-Id: Iae7260c6934efdffac7c7bee4d3633a8208924f7 --- .../org/eclipse/jgit/diff/HistogramDiff.java | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/HistogramDiff.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/HistogramDiff.java index 0979db1e7..e57faaf85 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/HistogramDiff.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/HistogramDiff.java @@ -43,6 +43,9 @@ package org.eclipse.jgit.diff; +import java.util.ArrayList; +import java.util.List; + /** * An extended form of Bram Cohen's patience diff algorithm. *

@@ -130,15 +133,14 @@ public class HistogramDiff extends LowLevelDiffAlgorithm { public void diffNonCommon(EditList edits, HashedSequenceComparator cmp, HashedSequence a, HashedSequence b, Edit region) { - new State(edits, cmp, a, b).diffReplace(region); + new State(edits, cmp, a, b).diffRegion(region); } private class State { private final HashedSequenceComparator cmp; - private final HashedSequence a; - private final HashedSequence b; + private final List queue = new ArrayList(); /** Result edits we have determined that must be made to convert a to b. */ final EditList edits; @@ -151,7 +153,13 @@ public class HistogramDiff extends LowLevelDiffAlgorithm { this.edits = edits; } - void diffReplace(Edit r) { + void diffRegion(Edit r) { + diffReplace(r); + while (!queue.isEmpty()) + diff(queue.remove(queue.size() - 1)); + } + + private void diffReplace(Edit r) { Edit lcs = new HistogramDiffIndex(maxChainLength, cmp, a, b, r) .findLongestCommonSequence(); if (lcs != null) { @@ -163,8 +171,8 @@ public class HistogramDiff extends LowLevelDiffAlgorithm { // edits.add(r); } else { - diff(r.before(lcs)); - diff(r.after(lcs)); + queue.add(r.after(lcs)); + queue.add(r.before(lcs)); } } else if (fallback instanceof LowLevelDiffAlgorithm) {