Merge changes I3668a396,I18f48321,I121f356c
* changes:
Unpack and cache large deltas as loose objects
Remember loose objects and fast-track their lookup
Correctly name DeltaBaseCache
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/CachedObjectDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/CachedObjectDirectory.java
index ca0f06f..f0159f6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/CachedObjectDirectory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/CachedObjectDirectory.java
@@ -54,7 +54,6 @@
import org.eclipse.jgit.lib.ObjectDatabase;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectIdSubclassMap;
-import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.storage.pack.ObjectToPack;
import org.eclipse.jgit.storage.pack.PackWriter;
@@ -113,7 +112,7 @@
}
@Override
- public ObjectInserter newInserter() {
+ public ObjectDirectoryInserter newInserter() {
return wrapped.newInserter();
}
@@ -214,6 +213,11 @@
}
@Override
+ boolean insertUnpackedObject(File tmp, ObjectId objectId, boolean force) {
+ return wrapped.insertUnpackedObject(tmp, objectId, force);
+ }
+
+ @Override
void selectObjectRepresentation(PackWriter packer, ObjectToPack otp,
WindowCursor curs) throws IOException {
wrapped.selectObjectRepresentation(packer, otp, curs);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/DeltaBaseCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/DeltaBaseCache.java
new file mode 100644
index 0000000..8b54824
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/DeltaBaseCache.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.storage.file;
+
+import java.lang.ref.SoftReference;
+
+class DeltaBaseCache {
+ private static final int CACHE_SZ = 1024;
+
+ private static final SoftReference<Entry> DEAD;
+
+ private static int hash(final long position) {
+ return (((int) position) << 22) >>> 22;
+ }
+
+ private static int maxByteCount;
+
+ private static final Slot[] cache;
+
+ private static Slot lruHead;
+
+ private static Slot lruTail;
+
+ private static int openByteCount;
+
+ static {
+ DEAD = new SoftReference<Entry>(null);
+ maxByteCount = new WindowCacheConfig().getDeltaBaseCacheLimit();
+
+ cache = new Slot[CACHE_SZ];
+ for (int i = 0; i < CACHE_SZ; i++)
+ cache[i] = new Slot();
+ }
+
+ static synchronized void reconfigure(final WindowCacheConfig cfg) {
+ final int dbLimit = cfg.getDeltaBaseCacheLimit();
+ if (maxByteCount != dbLimit) {
+ maxByteCount = dbLimit;
+ releaseMemory();
+ }
+ }
+
+ static synchronized Entry get(final PackFile pack, final long position) {
+ final Slot e = cache[hash(position)];
+ if (e.provider == pack && e.position == position) {
+ final Entry buf = e.data.get();
+ if (buf != null) {
+ moveToHead(e);
+ return buf;
+ }
+ }
+ return null;
+ }
+
+ static synchronized void store(final PackFile pack, final long position,
+ final byte[] data, final int objectType) {
+ if (data.length > maxByteCount)
+ return; // Too large to cache.
+
+ final Slot e = cache[hash(position)];
+ clearEntry(e);
+
+ openByteCount += data.length;
+ releaseMemory();
+
+ e.provider = pack;
+ e.position = position;
+ e.sz = data.length;
+ e.data = new SoftReference<Entry>(new Entry(data, objectType));
+ moveToHead(e);
+ }
+
+ private static void releaseMemory() {
+ while (openByteCount > maxByteCount && lruTail != null) {
+ final Slot currOldest = lruTail;
+ final Slot nextOldest = currOldest.lruPrev;
+
+ clearEntry(currOldest);
+ currOldest.lruPrev = null;
+ currOldest.lruNext = null;
+
+ if (nextOldest == null)
+ lruHead = null;
+ else
+ nextOldest.lruNext = null;
+ lruTail = nextOldest;
+ }
+ }
+
+ static synchronized void purge(final PackFile file) {
+ for (final Slot e : cache) {
+ if (e.provider == file) {
+ clearEntry(e);
+ unlink(e);
+ }
+ }
+ }
+
+ private static void moveToHead(final Slot e) {
+ unlink(e);
+ e.lruPrev = null;
+ e.lruNext = lruHead;
+ if (lruHead != null)
+ lruHead.lruPrev = e;
+ else
+ lruTail = e;
+ lruHead = e;
+ }
+
+ private static void unlink(final Slot e) {
+ final Slot prev = e.lruPrev;
+ final Slot next = e.lruNext;
+ if (prev != null)
+ prev.lruNext = next;
+ if (next != null)
+ next.lruPrev = prev;
+ }
+
+ private static void clearEntry(final Slot e) {
+ openByteCount -= e.sz;
+ e.provider = null;
+ e.data = DEAD;
+ e.sz = 0;
+ }
+
+ private DeltaBaseCache() {
+ throw new UnsupportedOperationException();
+ }
+
+ static class Entry {
+ final byte[] data;
+
+ final int type;
+
+ Entry(final byte[] aData, final int aType) {
+ data = aData;
+ type = aType;
+ }
+ }
+
+ private static class Slot {
+ Slot lruPrev;
+
+ Slot lruNext;
+
+ PackFile provider;
+
+ long position;
+
+ int sz;
+
+ SoftReference<Entry> data = DEAD;
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileObjectDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileObjectDatabase.java
index da38887..29c7a25 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileObjectDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileObjectDatabase.java
@@ -62,6 +62,9 @@
return new WindowCursor(this);
}
+ @Override
+ public abstract ObjectDirectoryInserter newInserter();
+
/**
* Does the requested object exist in this database?
* <p>
@@ -246,6 +249,8 @@
abstract long getObjectSize2(WindowCursor curs, String objectName,
AnyObjectId objectId) throws IOException;
+ abstract boolean insertUnpackedObject(File tmp, ObjectId id, boolean force);
+
abstract FileObjectDatabase newCachedFileObjectDatabase();
static class AlternateHandle {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LargePackedDeltaObject.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LargePackedDeltaObject.java
index 2b98f10..8d15fcf 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LargePackedDeltaObject.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LargePackedDeltaObject.java
@@ -44,9 +44,12 @@
package org.eclipse.jgit.storage.file;
import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.DataFormatException;
+import java.util.zip.DeflaterOutputStream;
import java.util.zip.InflaterInputStream;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
@@ -58,7 +61,6 @@
import org.eclipse.jgit.lib.ObjectStream;
import org.eclipse.jgit.storage.pack.BinaryDelta;
import org.eclipse.jgit.storage.pack.DeltaStream;
-import org.eclipse.jgit.util.TemporaryBuffer;
import org.eclipse.jgit.util.io.TeeInputStream;
class LargePackedDeltaObject extends ObjectLoader {
@@ -165,14 +167,39 @@
@Override
public ObjectStream openStream() throws MissingObjectException, IOException {
+ // If the object was recently unpacked, its available loose.
+ // The loose format is going to be faster to access than a
+ // delta applied on top of a base. Use that whenever we can.
+ //
+ final ObjectId myId = getObjectId();
final WindowCursor wc = new WindowCursor(db);
+ ObjectLoader ldr = db.openObject2(wc, myId.name(), myId);
+ if (ldr != null)
+ return ldr.openStream();
+
InputStream in = open(wc);
in = new BufferedInputStream(in, 8192);
- return new ObjectStream.Filter(getType(), size, in) {
+
+ // While we inflate the object, also deflate it back as a loose
+ // object. This will later be cleaned up by a gc pass, but until
+ // then we will reuse the loose form by the above code path.
+ //
+ int myType = getType();
+ long mySize = getSize();
+ final ObjectDirectoryInserter odi = db.newInserter();
+ final File tmp = odi.newTempFile();
+ DeflaterOutputStream dOut = odi.compress(new FileOutputStream(tmp));
+ odi.writeHeader(dOut, myType, mySize);
+
+ in = new TeeInputStream(in, dOut);
+ return new ObjectStream.Filter(myType, mySize, in) {
@Override
public void close() throws IOException {
- wc.release();
super.close();
+
+ odi.release();
+ wc.release();
+ db.insertUnpackedObject(tmp, myId, true /* force creation */);
}
};
}
@@ -195,13 +222,9 @@
final ObjectLoader base = pack.load(wc, baseOffset);
DeltaStream ds = new DeltaStream(delta) {
private long baseSize = SIZE_UNKNOWN;
- private TemporaryBuffer.LocalFile buffer;
@Override
protected InputStream openBase() throws IOException {
- if (buffer != null)
- return buffer.openInputStream();
-
InputStream in;
if (base instanceof LargePackedDeltaObject)
in = ((LargePackedDeltaObject) base).open(wc);
@@ -213,9 +236,7 @@
else if (in instanceof ObjectStream)
baseSize = ((ObjectStream) in).getSize();
}
-
- buffer = new TemporaryBuffer.LocalFile(db.getDirectory());
- return new TeeInputStream(in, buffer);
+ return in;
}
@Override
@@ -228,14 +249,11 @@
}
return baseSize;
}
-
- @Override
- public void close() throws IOException {
- super.close();
- if (buffer != null)
- buffer.destroy();
- }
};
+ if (type == Constants.OBJ_BAD) {
+ if (!(base instanceof LargePackedDeltaObject))
+ type = base.getType();
+ }
if (size == SIZE_UNKNOWN)
size = ds.getSize();
return ds;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectory.java
index 2ad14c8..372a978 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectory.java
@@ -69,7 +69,6 @@
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectDatabase;
import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.lib.RepositoryCache;
import org.eclipse.jgit.lib.RepositoryCache.FileKey;
@@ -117,6 +116,8 @@
private final AtomicReference<AlternateHandle[]> alternates;
+ private final UnpackedObjectCache unpackedObjectCache;
+
/**
* Initialize a reference to an on-disk object directory.
*
@@ -140,6 +141,7 @@
packDirectory = new File(objects, "pack");
alternatesFile = new File(infoDirectory, "alternates");
packList = new AtomicReference<PackList>(NO_PACKS);
+ unpackedObjectCache = new UnpackedObjectCache();
this.fs = fs;
alternates = new AtomicReference<AlternateHandle[]>();
@@ -173,12 +175,14 @@
}
@Override
- public ObjectInserter newInserter() {
+ public ObjectDirectoryInserter newInserter() {
return new ObjectDirectoryInserter(this, config);
}
@Override
public void close() {
+ unpackedObjectCache.clear();
+
final PackList packs = packList.get();
packList.set(NO_PACKS);
for (final PackFile p : packs.packs)
@@ -255,6 +259,8 @@
}
boolean hasObject1(final AnyObjectId objectId) {
+ if (unpackedObjectCache.isUnpacked(objectId))
+ return true;
for (final PackFile p : packList.get().packs) {
try {
if (p.hasObject(objectId)) {
@@ -328,6 +334,14 @@
ObjectLoader openObject1(final WindowCursor curs,
final AnyObjectId objectId) throws IOException {
+ if (unpackedObjectCache.isUnpacked(objectId)) {
+ ObjectLoader ldr = openObject2(curs, objectId.name(), objectId);
+ if (ldr != null)
+ return ldr;
+ else
+ unpackedObjectCache.remove(objectId);
+ }
+
PackList pList = packList.get();
SEARCH: for (;;) {
for (final PackFile p : pList.packs) {
@@ -429,15 +443,61 @@
File path = fileFor(objectName);
FileInputStream in = new FileInputStream(path);
try {
+ unpackedObjectCache.add(objectId);
return UnpackedObject.open(in, path, objectId, curs);
} finally {
in.close();
}
} catch (FileNotFoundException noFile) {
+ unpackedObjectCache.remove(objectId);
return null;
}
}
+ @Override
+ boolean insertUnpackedObject(File tmp, ObjectId id, boolean force) {
+ if (!force && has(id)) {
+ // Object is already in the repository, remove temporary file.
+ //
+ tmp.delete();
+ return true;
+ }
+ tmp.setReadOnly();
+
+ final File dst = fileFor(id);
+ if (force && dst.exists()) {
+ tmp.delete();
+ return true;
+ }
+ if (tmp.renameTo(dst)) {
+ unpackedObjectCache.add(id);
+ return true;
+ }
+
+ // Maybe the directory doesn't exist yet as the object
+ // directories are always lazily created. Note that we
+ // try the rename first as the directory likely does exist.
+ //
+ dst.getParentFile().mkdir();
+ if (tmp.renameTo(dst)) {
+ unpackedObjectCache.add(id);
+ return true;
+ }
+
+ if (!force && has(id)) {
+ tmp.delete();
+ return true;
+ }
+
+ // The object failed to be renamed into its proper
+ // location and it doesn't exist in the repository
+ // either. We really don't know what went wrong, so
+ // fail.
+ //
+ tmp.delete();
+ return false;
+ }
+
boolean tryAgain1() {
final PackList old = packList.get();
if (old.tryAgain(packDirectory.lastModified()))
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectoryInserter.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectoryInserter.java
index 5016679..d92285d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectoryInserter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectoryInserter.java
@@ -83,36 +83,10 @@
final MessageDigest md = digest();
final File tmp = toTemp(md, type, len, is);
final ObjectId id = ObjectId.fromRaw(md.digest());
- if (db.has(id)) {
- // Object is already in the repository, remove temporary file.
- //
- tmp.delete();
+ if (db.insertUnpackedObject(tmp, id, false /* no duplicate */))
return id;
- }
final File dst = db.fileFor(id);
- if (tmp.renameTo(dst))
- return id;
-
- // Maybe the directory doesn't exist yet as the object
- // directories are always lazily created. Note that we
- // try the rename first as the directory likely does exist.
- //
- dst.getParentFile().mkdir();
- if (tmp.renameTo(dst))
- return id;
-
- if (db.has(id)) {
- tmp.delete();
- return id;
- }
-
- // The object failed to be renamed into its proper
- // location and it doesn't exist in the repository
- // either. We really don't know what went wrong, so
- // fail.
- //
- tmp.delete();
throw new ObjectWritingException("Unable to create new object: " + dst);
}
@@ -136,15 +110,12 @@
final InputStream is) throws IOException, FileNotFoundException,
Error {
boolean delete = true;
- File tmp = File.createTempFile("noz", null, db.getDirectory());
+ File tmp = newTempFile();
try {
DigestOutputStream dOut = new DigestOutputStream(
compress(new FileOutputStream(tmp)), md);
try {
- dOut.write(Constants.encodedTypeString(type));
- dOut.write((byte) ' ');
- dOut.write(Constants.encodeASCII(len));
- dOut.write((byte) 0);
+ writeHeader(dOut, type, len);
final byte[] buf = buffer();
while (len > 0) {
@@ -158,7 +129,6 @@
dOut.close();
}
- tmp.setReadOnly();
delete = false;
return tmp;
} finally {
@@ -167,7 +137,19 @@
}
}
- private DeflaterOutputStream compress(final OutputStream out) {
+ void writeHeader(OutputStream out, final int type, long len)
+ throws IOException {
+ out.write(Constants.encodedTypeString(type));
+ out.write((byte) ' ');
+ out.write(Constants.encodeASCII(len));
+ out.write((byte) 0);
+ }
+
+ File newTempFile() throws IOException {
+ return File.createTempFile("noz", null, db.getDirectory());
+ }
+
+ DeflaterOutputStream compress(final OutputStream out) {
if (deflate == null)
deflate = new Deflater(config.get(CoreConfig.KEY).getCompression());
else
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackFile.java
index ed159ef..5239111 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackFile.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackFile.java
@@ -220,7 +220,7 @@
* Close the resources utilized by this repository
*/
public void close() {
- UnpackedObjectCache.purge(this);
+ DeltaBaseCache.purge(this);
WindowCache.purge(this);
synchronized (this) {
loadedIdx = null;
@@ -274,14 +274,6 @@
return getReverseIdx().findObject(offset);
}
- private final UnpackedObjectCache.Entry readCache(final long position) {
- return UnpackedObjectCache.get(this, position);
- }
-
- private final void saveCache(final long position, final byte[] data, final int type) {
- UnpackedObjectCache.store(this, position, data, type);
- }
-
private final byte[] decompress(final long position, final long totalSize,
final WindowCursor curs) throws IOException, DataFormatException {
final byte[] dstbuf = new byte[(int) totalSize];
@@ -700,7 +692,7 @@
byte[] data;
int type;
- UnpackedObjectCache.Entry e = readCache(posBase);
+ DeltaBaseCache.Entry e = DeltaBaseCache.get(this, posBase);
if (e != null) {
data = e.data;
type = e.type;
@@ -715,7 +707,7 @@
}
data = p.getCachedBytes();
type = p.getType();
- saveCache(posBase, data, type);
+ DeltaBaseCache.store(this, posBase, data, type);
}
// At this point we have the base, and its small, and the delta
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/UnpackedObjectCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/UnpackedObjectCache.java
index 92f4824..4d05c6f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/UnpackedObjectCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/UnpackedObjectCache.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
+ * Copyright (C) 2010, Google Inc.
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -43,153 +43,107 @@
package org.eclipse.jgit.storage.file;
-import java.lang.ref.SoftReference;
+import java.util.concurrent.atomic.AtomicReferenceArray;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.ObjectId;
+
+/** Remembers objects that are currently unpacked. */
class UnpackedObjectCache {
- private static final int CACHE_SZ = 1024;
+ private static final int INITIAL_BITS = 5; // size = 32
- private static final SoftReference<Entry> DEAD;
+ private static final int MAX_BITS = 11; // size = 2048
- private static int hash(final long position) {
- return (((int) position) << 22) >>> 22;
+ private volatile Table table;
+
+ UnpackedObjectCache() {
+ table = new Table(INITIAL_BITS);
}
- private static int maxByteCount;
-
- private static final Slot[] cache;
-
- private static Slot lruHead;
-
- private static Slot lruTail;
-
- private static int openByteCount;
-
- static {
- DEAD = new SoftReference<Entry>(null);
- maxByteCount = new WindowCacheConfig().getDeltaBaseCacheLimit();
-
- cache = new Slot[CACHE_SZ];
- for (int i = 0; i < CACHE_SZ; i++)
- cache[i] = new Slot();
+ boolean isUnpacked(AnyObjectId objectId) {
+ return table.contains(objectId);
}
- static synchronized void reconfigure(final WindowCacheConfig cfg) {
- final int dbLimit = cfg.getDeltaBaseCacheLimit();
- if (maxByteCount != dbLimit) {
- maxByteCount = dbLimit;
- releaseMemory();
+ void add(AnyObjectId objectId) {
+ Table t = table;
+ if (t.add(objectId)) {
+ // The object either already exists in the table, or was
+ // successfully added. Either way leave the table alone.
+ //
+ } else {
+ // The object won't fit into the table. Implement a crude
+ // cache removal by just dropping the table away, but double
+ // it in size for the next incarnation.
+ //
+ Table n = new Table(Math.min(t.bits + 1, MAX_BITS));
+ n.add(objectId);
+ table = n;
}
}
- static synchronized Entry get(final PackFile pack, final long position) {
- final Slot e = cache[hash(position)];
- if (e.provider == pack && e.position == position) {
- final Entry buf = e.data.get();
- if (buf != null) {
- moveToHead(e);
- return buf;
+ void remove(AnyObjectId objectId) {
+ if (isUnpacked(objectId))
+ clear();
+ }
+
+ void clear() {
+ table = new Table(INITIAL_BITS);
+ }
+
+ private static class Table {
+ private static final int MAX_CHAIN = 8;
+
+ private final AtomicReferenceArray<ObjectId> ids;
+
+ private final int shift;
+
+ final int bits;
+
+ Table(int bits) {
+ this.ids = new AtomicReferenceArray<ObjectId>(1 << bits);
+ this.shift = 32 - bits;
+ this.bits = bits;
+ }
+
+ boolean contains(AnyObjectId toFind) {
+ int i = index(toFind);
+ for (int n = 0; n < MAX_CHAIN; n++) {
+ ObjectId obj = ids.get(i);
+ if (obj == null)
+ break;
+
+ if (AnyObjectId.equals(obj, toFind))
+ return true;
+
+ if (++i == ids.length())
+ i = 0;
}
+ return false;
}
- return null;
- }
- static synchronized void store(final PackFile pack, final long position,
- final byte[] data, final int objectType) {
- if (data.length > maxByteCount)
- return; // Too large to cache.
+ boolean add(AnyObjectId toAdd) {
+ int i = index(toAdd);
+ for (int n = 0; n < MAX_CHAIN;) {
+ ObjectId obj = ids.get(i);
+ if (obj == null) {
+ if (ids.compareAndSet(i, null, toAdd.copy()))
+ return true;
+ else
+ continue;
+ }
- final Slot e = cache[hash(position)];
- clearEntry(e);
+ if (AnyObjectId.equals(obj, toAdd))
+ return true;
- openByteCount += data.length;
- releaseMemory();
-
- e.provider = pack;
- e.position = position;
- e.sz = data.length;
- e.data = new SoftReference<Entry>(new Entry(data, objectType));
- moveToHead(e);
- }
-
- private static void releaseMemory() {
- while (openByteCount > maxByteCount && lruTail != null) {
- final Slot currOldest = lruTail;
- final Slot nextOldest = currOldest.lruPrev;
-
- clearEntry(currOldest);
- currOldest.lruPrev = null;
- currOldest.lruNext = null;
-
- if (nextOldest == null)
- lruHead = null;
- else
- nextOldest.lruNext = null;
- lruTail = nextOldest;
- }
- }
-
- static synchronized void purge(final PackFile file) {
- for (final Slot e : cache) {
- if (e.provider == file) {
- clearEntry(e);
- unlink(e);
+ if (++i == ids.length())
+ i = 0;
+ n++;
}
+ return false;
}
- }
- private static void moveToHead(final Slot e) {
- unlink(e);
- e.lruPrev = null;
- e.lruNext = lruHead;
- if (lruHead != null)
- lruHead.lruPrev = e;
- else
- lruTail = e;
- lruHead = e;
- }
-
- private static void unlink(final Slot e) {
- final Slot prev = e.lruPrev;
- final Slot next = e.lruNext;
- if (prev != null)
- prev.lruNext = next;
- if (next != null)
- next.lruPrev = prev;
- }
-
- private static void clearEntry(final Slot e) {
- openByteCount -= e.sz;
- e.provider = null;
- e.data = DEAD;
- e.sz = 0;
- }
-
- private UnpackedObjectCache() {
- throw new UnsupportedOperationException();
- }
-
- static class Entry {
- final byte[] data;
-
- final int type;
-
- Entry(final byte[] aData, final int aType) {
- data = aData;
- type = aType;
+ private int index(AnyObjectId id) {
+ return id.hashCode() >>> shift;
}
}
-
- private static class Slot {
- Slot lruPrev;
-
- Slot lruNext;
-
- PackFile provider;
-
- long position;
-
- int sz;
-
- SoftReference<Entry> data = DEAD;
- }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCache.java
index 68fa191..f533af4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCache.java
@@ -187,7 +187,7 @@
oc.removeAll();
cache = nc;
streamFileThreshold = cfg.getStreamFileThreshold();
- UnpackedObjectCache.reconfigure(cfg);
+ DeltaBaseCache.reconfigure(cfg);
}
static int getStreamFileThreshold() {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheConfig.java
index 90ea376..95ec6f7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheConfig.java
@@ -146,7 +146,7 @@
}
/**
- * @return maximum number of bytes to cache in {@link UnpackedObjectCache}
+ * @return maximum number of bytes to cache in {@link DeltaBaseCache}
* for inflated, recently accessed objects, without delta chains.
* <b>Default 10 MB.</b>
*/
@@ -157,7 +157,7 @@
/**
* @param newLimit
* maximum number of bytes to cache in
- * {@link UnpackedObjectCache} for inflated, recently accessed
+ * {@link DeltaBaseCache} for inflated, recently accessed
* objects, without delta chains.
*/
public void setDeltaBaseCacheLimit(final int newLimit) {