From aa2cd6f4d49ea9d684e1486bcd49546a623ded23 Mon Sep 17 00:00:00 2001 From: Joe Baker-Malone Date: Thu, 4 Feb 2016 12:58:00 -0800 Subject: [PATCH] Eliminating tail recursion in JBDiff#split Previously, on large-ish files, JBDiff#split would sometimes cause StackOverflowExceptions due to the use of tail recursion. Changed to use stacks to maintain state during iteration, and eliminated the recursion. Also updating variable names to be more readable. (cherry picked from commit 59aee5d) --- updater/src/ie/wombat/jbdiff/JBDiff.java | 167 ++++++++++++----------- 1 file changed, 90 insertions(+), 77 deletions(-) diff --git a/updater/src/ie/wombat/jbdiff/JBDiff.java b/updater/src/ie/wombat/jbdiff/JBDiff.java index f75f36584de2..2713602b814f 100644 --- a/updater/src/ie/wombat/jbdiff/JBDiff.java +++ b/updater/src/ie/wombat/jbdiff/JBDiff.java @@ -32,6 +32,7 @@ import com.intellij.updater.Utils.OpenByteArrayOutputStream; import java.io.*; import java.nio.charset.StandardCharsets; +import java.util.Stack; import java.util.zip.GZIPOutputStream; /** @@ -51,105 +52,117 @@ public class JBDiff { private static final String VERSION = "jbdiff-0.1.1"; - private static final int min(int x, int y) { + private static int min(int x, int y) { return x < y ? x : y; } - private final static void split(int[] I, int[] V, int start, int len, int h) { - - int i, j, k, x, tmp, jj, kk; - - if (len < 16) { - for (k = start; k < start + len; k += j) { - j = 1; - x = V[I[k] + h]; - for (i = 1; k + i < start + len; i++) { - if (V[I[k + i] + h] < x) { - x = V[I[k + i] + h]; - j = 0; - } - - if (V[I[k + i] + h] == x) { - tmp = I[k + j]; - I[k + j] = I[k + i]; - I[k + i] = tmp; - j++; - } + private static void selectSplit(int[] I, int[] V, int start, int len, int h) { + int j; + for (int k = start; k < start + len; k += j) { + j = 1; + int x = V[I[k] + h]; + for (int i = 1; k + i < start + len; i++) { + if (V[I[k + i] + h] < x) { + x = V[I[k + i] + h]; + j = 0; } - for (i = 0; i < j; i++) { - V[I[k + i]] = k + j - 1; - } - if (j == 1) { - I[k] = -1; + if (V[I[k + i] + h] == x) { + int tmp = I[k + j]; + I[k + j] = I[k + i]; + I[k + i] = tmp; + j++; } } - return; - } - - x = V[I[start + len / 2] + h]; - jj = 0; - kk = 0; - for (i = start; i < start + len; i++) { - if (V[I[i] + h] < x) { - jj++; + for (int i = 0; i < j; i++) { + V[I[k + i]] = k + j - 1; } - if (V[I[i] + h] == x) { - kk++; + if (j == 1) { + I[k] = -1; } } + } - jj += start; - kk += jj; + private static void split(int[] I, int[] V, int initstart, int initlen, int h) { + Stack startStack = new Stack(); + Stack lenStack = new Stack(); + startStack.push(initstart); + lenStack.push(initlen); + while (!startStack.isEmpty()) { + int start = startStack.pop(); + int len = lenStack.pop(); - i = start; - j = 0; - k = 0; - while (i < jj) { - if (V[I[i] + h] < x) { - i++; + if (len < 16) { + selectSplit(I, V, start, len, h); + continue; } - else if (V[I[i] + h] == x) { - tmp = I[i]; - I[i] = I[jj + j]; - I[jj + j] = tmp; - j++; + + int pivot = V[I[start + len / 2] + h]; + int endLessThanIndex = 0; + int endLessOrEqualIndex = 0; + for (int i = start; i < start + len; i++) { + if (V[I[i] + h] < pivot) { + endLessThanIndex++; + } + if (V[I[i] + h] == pivot) { + endLessOrEqualIndex++; + } } - else { - tmp = I[i]; - I[i] = I[kk + k]; - I[kk + k] = tmp; - k++; + + endLessThanIndex += start; + endLessOrEqualIndex += endLessThanIndex; + + int i = start; + int currentEqualIndex = 0; + int currentGreaterIndex = 0; + while (i < endLessThanIndex) { + if (V[I[i] + h] < pivot) { + i++; + } + else if (V[I[i] + h] == pivot) { + int tmp = I[i]; + I[i] = I[endLessThanIndex + currentEqualIndex]; + I[endLessThanIndex + currentEqualIndex] = tmp; + currentEqualIndex++; + } + else { + int tmp = I[i]; + I[i] = I[endLessOrEqualIndex + currentGreaterIndex]; + I[endLessOrEqualIndex + currentGreaterIndex] = tmp; + currentGreaterIndex++; + } } - } - while (jj + j < kk) { - if (V[I[jj + j] + h] == x) { - j++; + while (endLessThanIndex + currentEqualIndex < endLessOrEqualIndex) { + if (V[I[endLessThanIndex + currentEqualIndex] + h] == pivot) { + currentEqualIndex++; + } + else { + int tmp = I[endLessThanIndex + currentEqualIndex]; + I[endLessThanIndex + currentEqualIndex] = I[endLessOrEqualIndex + currentGreaterIndex]; + I[endLessOrEqualIndex + currentGreaterIndex] = tmp; + currentGreaterIndex++; + } } - else { - tmp = I[jj + j]; - I[jj + j] = I[kk + k]; - I[kk + k] = tmp; - k++; + + for (i = endLessThanIndex; i < endLessOrEqualIndex; i++) { + V[I[i]] = endLessOrEqualIndex - 1; } - } - if (jj > start) { - split(I, V, start, jj - start, h); - } + if (endLessThanIndex == endLessOrEqualIndex - 1) { + I[endLessThanIndex] = -1; + } - for (i = 0; i < kk - jj; i++) { - V[I[jj + i]] = kk - 1; - } + if (endLessThanIndex > start) { + startStack.push(start); + lenStack.push(endLessThanIndex - start); + } - if (jj == kk - 1) { - I[jj] = -1; - } - - if (start + len > kk) { - split(I, V, kk, start + len - kk, h); + if (start + len > endLessOrEqualIndex) { + startStack.push(endLessOrEqualIndex); + lenStack.push(start + len - endLessOrEqualIndex); + } } }