diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaWindow.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaWindow.java index 3ff0a6c1c..2eb1daf74 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaWindow.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaWindow.java @@ -65,8 +65,6 @@ final class DeltaWindow { private final ObjectReader reader; private final ProgressMonitor monitor; - private final DeltaWindowEntry[] window; - /** Maximum number of bytes to admit to the window at once. */ private final long maxMemory; @@ -74,9 +72,7 @@ final class DeltaWindow { private final int maxDepth; private final ObjectToPack[] toSearch; - private int cur; - private int end; /** Amount of memory we have loaded right now. */ @@ -84,9 +80,6 @@ final class DeltaWindow { // The object we are currently considering needs a lot of state: - /** Position of {@link #res} within {@link #window} array. */ - private int resSlot; - /** * Maximum delta chain depth the current object can have. *

@@ -100,8 +93,8 @@ final class DeltaWindow { /** If we have a delta for {@link #res}, this is the shortest found yet. */ private TemporaryBuffer.Heap bestDelta; - /** If we have {@link #bestDelta}, the window position it was created by. */ - private int bestSlot; + /** If we have {@link #bestDelta}, the window entry it was created from. */ + private DeltaWindowEntry bestBase; /** Used to compress cached deltas. */ private Deflater deflater; @@ -117,23 +110,9 @@ final class DeltaWindow { cur = beginIndex; end = endIndex; - // C Git increases the window size supplied by the user by 1. - // We don't know why it does this, but if the user asks for - // window=10, it actually processes with window=11. Because - // the window size has the largest direct impact on the final - // pack file size, we match this odd behavior here to give us - // a better chance of producing a similar sized pack as C Git. - // - // We would prefer to directly honor the user's request since - // PackWriter has a minimum of 2 for the window size, but then - // users might complain that JGit is creating a bigger pack file. - // - window = new DeltaWindowEntry[config.getDeltaSearchWindowSize() + 1]; - for (int i = 0; i < window.length; i++) - window[i] = new DeltaWindowEntry(); - maxMemory = Math.max(0, config.getDeltaSearchMemoryLimit()); maxDepth = config.getMaxDeltaDepth(); + res = DeltaWindowEntry.createWindow(config.getDeltaSearchWindowSize()); } synchronized int remaining() { @@ -167,15 +146,12 @@ final class DeltaWindow { break; next = toSearch[cur++]; } - res = window[resSlot]; if (maxMemory != 0) { clear(res); - int tail = next(resSlot); final long need = estimateSize(next); - while (maxMemory < loaded + need && tail != resSlot) { - clear(window[tail]); - tail = next(tail); - } + DeltaWindowEntry n = res.next; + for (; maxMemory < loaded + need && n != res; n = n.next) + clear(n); } res.set(next); @@ -231,11 +207,10 @@ final class DeltaWindow { // Loop through the window backwards, considering every entry. // This lets us look at the bigger objects that came before. // - for (int srcSlot = prior(resSlot); srcSlot != resSlot; srcSlot = prior(srcSlot)) { - DeltaWindowEntry src = window[srcSlot]; + for (DeltaWindowEntry src = res.prev; src != res; src = src.prev) { if (src.empty()) break; - if (delta(src, srcSlot) /* == NEXT_SRC */) + if (delta(src) /* == NEXT_SRC */) continue; bestDelta = null; return; @@ -251,7 +226,7 @@ final class DeltaWindow { // Select this best matching delta as the base for the object. // - ObjectToPack srcObj = window[bestSlot].object; + ObjectToPack srcObj = bestBase.object; ObjectToPack resObj = res.object; if (srcObj.isEdge()) { // The source (the delta base) is an edge object outside of the @@ -285,7 +260,7 @@ final class DeltaWindow { keepInWindow(); } - private boolean delta(final DeltaWindowEntry src, final int srcSlot) + private boolean delta(final DeltaWindowEntry src) throws IOException { // Objects must use only the same type as their delta base. // If we are looking at something where that isn't true we @@ -319,12 +294,12 @@ final class DeltaWindow { srcIndex = index(src); } catch (LargeObjectException tooBig) { // If the source is too big to work on, skip it. - dropFromWindow(srcSlot); + dropFromWindow(src); return NEXT_SRC; } catch (IOException notAvailable) { if (src.object.isEdge()) { // This is an edge that is suddenly not available. - dropFromWindow(srcSlot); + dropFromWindow(src); return NEXT_SRC; } else { throw notAvailable; @@ -355,7 +330,7 @@ final class DeltaWindow { if (isBetterDelta(src, delta)) { bestDelta = delta; - bestSlot = srcSlot; + bestBase = src; } return NEXT_SRC; @@ -390,39 +365,17 @@ final class DeltaWindow { } private void shuffleBaseUpInPriority() { - // Shuffle the entire window so that the best match we just used - // is at our current index, and our current object is at the index - // before it. Slide any entries in between to make space. - // - window[resSlot] = window[bestSlot]; - - DeltaWindowEntry next = res; - int slot = prior(resSlot); - for (; slot != bestSlot; slot = prior(slot)) { - DeltaWindowEntry e = window[slot]; - window[slot] = next; - next = e; - } - window[slot] = next; + // Reorder the window so that the best match we just used + // is the current one, and the now current object is before. + res.makeNext(bestBase); + res = bestBase; } private void keepInWindow() { - resSlot = next(resSlot); - } - - private int next(int slot) { - if (++slot == window.length) - return 0; - return slot; - } - - private int prior(int slot) { - if (slot == 0) - return window.length - 1; - return slot - 1; + res = res.next; } - private void dropFromWindow(@SuppressWarnings("unused") int srcSlot) { + private void dropFromWindow(@SuppressWarnings("unused") DeltaWindowEntry src) { // We should drop the current source entry from the window, // it is somehow invalid for us to work with. } @@ -437,7 +390,7 @@ final class DeltaWindow { // to access during reads. // if (resDelta.length() == bestDelta.length()) - return src.depth() < window[bestSlot].depth(); + return src.depth() < bestBase.depth(); return resDelta.length() < bestDelta.length(); } @@ -501,14 +454,12 @@ final class DeltaWindow { if (maxMemory == 0) return; - int tail = next(resSlot); - while (maxMemory < loaded + need) { - DeltaWindowEntry cur = window[tail]; - clear(cur); - if (cur == ent) + DeltaWindowEntry n = res.next; + for (; maxMemory < loaded + need; n = n.next) { + clear(n); + if (n == ent) throw new LargeObjectException.ExceedsLimit( maxMemory, loaded + need); - tail = next(tail); } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaWindowEntry.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaWindowEntry.java index 0b8c7ab81..958bae187 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaWindowEntry.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaWindowEntry.java @@ -44,6 +44,8 @@ package org.eclipse.jgit.internal.storage.pack; final class DeltaWindowEntry { + DeltaWindowEntry prev; + DeltaWindowEntry next; ObjectToPack object; /** Complete contents of this object. Lazily loaded. */ @@ -77,4 +79,40 @@ final class DeltaWindowEntry { final boolean empty() { return object == null; } + + final void makeNext(DeltaWindowEntry e) { + // Disconnect e from the chain. + e.prev.next = e.next; + e.next.prev = e.prev; + + // Insert e after this. + e.next = next; + e.prev = this; + next.prev = e; + next = e; + } + + static DeltaWindowEntry createWindow(int cnt) { + // C Git increases the window size supplied by the user by 1. + // We don't know why it does this, but if the user asks for + // window=10, it actually processes with window=11. Because + // the window size has the largest direct impact on the final + // pack file size, we match this odd behavior here to give us + // a better chance of producing a similar sized pack as C Git. + // + // We would prefer to directly honor the user's request since + // PackWriter has a minimum of 2 for the window size, but then + // users might complain that JGit is creating a bigger pack file. + DeltaWindowEntry res = new DeltaWindowEntry(); + DeltaWindowEntry p = res; + for (int i = 0; i < cnt; i++) { + DeltaWindowEntry e = new DeltaWindowEntry(); + e.prev = p; + p.next = e; + p = e; + } + p.next = res; + res.prev = p; + return res; + } }