]> source.dussan.org Git - jgit.git/commitdiff
Make type and size lazy for large delta objects 42/1042/2
authorShawn O. Pearce <spearce@spearce.org>
Fri, 2 Jul 2010 20:12:06 +0000 (13:12 -0700)
committerShawn O. Pearce <spearce@spearce.org>
Sat, 3 Jul 2010 17:54:29 +0000 (10:54 -0700)
Callers don't necessarily need the getSize() result from a large
delta.  They instead should be always using openStream() or copyTo()
for blobs going to local files, or they should be checking the
result of the constant-time isLarge() method to determine the type
of access they can use on the ObjectLoader.  Avoid inflating the
delta instruction stream twice by delaying the decoding of the size
until after we have created the DeltaStream and decoded the header.

Likewise with the type, callers don't necessarily always need it
to be present in an ObjectLoader.  Delay looking at it as late as
we can, thereby avoiding an ugly O(N^2) loop looking up the type
for every single object in the entire delta chain.

Change-Id: I6487b75b52a5d201d811a8baed2fb4fcd6431320
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
org.eclipse.jgit/src/org/eclipse/jgit/storage/file/LargePackedDeltaObject.java
org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackFile.java

index a6d7d8946e781376898ba27d82a0735a3209baf0..53a0e617fc7136ac6b509d73d83d2144400d8f39 100644 (file)
@@ -46,20 +46,25 @@ package org.eclipse.jgit.storage.file;
 import java.io.BufferedInputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.zip.DataFormatException;
 import java.util.zip.InflaterInputStream;
 
 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
 import org.eclipse.jgit.errors.LargeObjectException;
 import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectLoader;
 import org.eclipse.jgit.lib.ObjectStream;
+import org.eclipse.jgit.storage.pack.BinaryDelta;
 import org.eclipse.jgit.storage.pack.DeltaStream;
 
 class LargePackedDeltaObject extends ObjectLoader {
-       private final int type;
+       private static final long SIZE_UNKNOWN = -1;
 
-       private final long size;
+       private int type;
+
+       private long size;
 
        private final long objectOffset;
 
@@ -71,11 +76,11 @@ class LargePackedDeltaObject extends ObjectLoader {
 
        private final FileObjectDatabase db;
 
-       LargePackedDeltaObject(int type, long size, long objectOffset,
+       LargePackedDeltaObject(long objectOffset,
                        long baseOffset, int headerLength, PackFile pack,
                        FileObjectDatabase db) {
-               this.type = type;
-               this.size = size;
+               this.type = Constants.OBJ_BAD;
+               this.size = SIZE_UNKNOWN;
                this.objectOffset = objectOffset;
                this.baseOffset = baseOffset;
                this.headerLength = headerLength;
@@ -85,11 +90,58 @@ class LargePackedDeltaObject extends ObjectLoader {
 
        @Override
        public int getType() {
+               if (type == Constants.OBJ_BAD) {
+                       WindowCursor wc = new WindowCursor(db);
+                       try {
+                               type = pack.getObjectType(wc, objectOffset);
+                       } catch (IOException packGone) {
+                               // If the pack file cannot be pinned into the cursor, it
+                               // probably was repacked recently. Go find the object
+                               // again and get the type from that location instead.
+                               //
+                               try {
+                                       type = wc.open(getObjectId()).getType();
+                               } catch (IOException packGone2) {
+                                       // "He's dead, Jim." We just can't discover the type
+                                       // and the interface isn't supposed to be lazy here.
+                                       // Report an invalid type code instead, callers will
+                                       // wind up bailing out with an error at some point.
+                               }
+                       } finally {
+                               wc.release();
+                       }
+               }
                return type;
        }
 
        @Override
        public long getSize() {
+               if (size == SIZE_UNKNOWN) {
+                       WindowCursor wc = new WindowCursor(db);
+                       try {
+                               byte[] b = pack.getDeltaHeader(wc, objectOffset + headerLength);
+                               size = BinaryDelta.getResultSize(b);
+                       } catch (DataFormatException objectCorrupt) {
+                               // The zlib stream for the delta is corrupt. We probably
+                               // cannot access the object. Keep the size negative and
+                               // report that bogus result to the caller.
+                       } catch (IOException packGone) {
+                               // If the pack file cannot be pinned into the cursor, it
+                               // probably was repacked recently. Go find the object
+                               // again and get the size from that location instead.
+                               //
+                               try {
+                                       size = wc.open(getObjectId()).getSize();
+                               } catch (IOException packGone2) {
+                                       // "He's dead, Jim." We just can't discover the size
+                                       // and the interface isn't supposed to be lazy here.
+                                       // Report an invalid type code instead, callers will
+                                       // wind up bailing out with an error at some point.
+                               }
+                       } finally {
+                               wc.release();
+                       }
+               }
                return size;
        }
 
@@ -112,7 +164,7 @@ class LargePackedDeltaObject extends ObjectLoader {
                final WindowCursor wc = new WindowCursor(db);
                InputStream in = open(wc);
                in = new BufferedInputStream(in, 8192);
-               return new ObjectStream.Filter(type, size, in) {
+               return new ObjectStream.Filter(getType(), size, in) {
                        @Override
                        public void close() throws IOException {
                                wc.release();
@@ -132,24 +184,44 @@ class LargePackedDeltaObject extends ObjectLoader {
                        // probably was repacked recently. Go find the object
                        // again and open the stream from that location instead.
                        //
-                       return wc.open(getObjectId(), type).openStream();
+                       return wc.open(getObjectId()).openStream();
                }
                delta = new InflaterInputStream(delta);
 
                final ObjectLoader base = pack.load(wc, baseOffset);
-               return new DeltaStream(delta) {
+               DeltaStream ds = new DeltaStream(delta) {
+                       private long baseSize = SIZE_UNKNOWN;
+
                        @Override
                        protected InputStream openBase() throws IOException {
+                               InputStream in;
                                if (base instanceof LargePackedDeltaObject)
-                                       return ((LargePackedDeltaObject) base).open(wc);
-                               return base.openStream();
+                                       in = ((LargePackedDeltaObject) base).open(wc);
+                               else
+                                       in = base.openStream();
+                               if (baseSize == SIZE_UNKNOWN) {
+                                       if (in instanceof DeltaStream)
+                                               baseSize = ((DeltaStream) in).getSize();
+                                       else if (in instanceof ObjectStream)
+                                               baseSize = ((ObjectStream) in).getSize();
+                               }
+                               return in;
                        }
 
                        @Override
                        protected long getBaseSize() throws IOException {
-                               return base.getSize();
+                               if (baseSize == SIZE_UNKNOWN) {
+                                       // This code path should never be used as DeltaStream
+                                       // is supposed to open the stream first, which would
+                                       // initialize the size for us directly from the stream.
+                                       baseSize = base.getSize();
+                               }
+                               return baseSize;
                        }
                };
+               if (size == SIZE_UNKNOWN)
+                       size = ds.getSize();
+               return ds;
        }
 
        private ObjectId getObjectId() throws IOException {
index ec68b8600a10d7d800f0c833a30b926b827a3325..dc1fff4bc5669e99bf042a7993e2978c06bc0363 100644 (file)
@@ -688,10 +688,8 @@ public class PackFile implements Iterable<PackIndex.MutableEntry> {
                        // that implies the resulting object is going to be massive.
                        // Use only the large delta format here.
                        //
-                       byte[] hdr = getDeltaHeader(posSelf + hdrLen, curs);
-                       return new LargePackedDeltaObject(getObjectType(curs, posBase), //
-                                       BinaryDelta.getResultSize(hdr), //
-                                       posSelf, posBase, hdrLen, this, curs.db);
+                       return new LargePackedDeltaObject(posSelf, posBase, hdrLen, //
+                                       this, curs.db);
                }
 
                byte[] data;
@@ -707,10 +705,8 @@ public class PackFile implements Iterable<PackIndex.MutableEntry> {
                                // The base itself is large. We have to produce a large
                                // delta stream as we don't want to build the whole base.
                                //
-                               byte[] hdr = getDeltaHeader(posSelf + hdrLen, curs);
-                               return new LargePackedDeltaObject(getObjectType(curs, posBase),
-                                               BinaryDelta.getResultSize(hdr), //
-                                               posSelf, posBase, hdrLen, this, curs.db);
+                               return new LargePackedDeltaObject(posSelf, posBase, hdrLen,
+                                               this, curs.db);
                        }
                        data = p.getCachedBytes();
                        type = p.getType();
@@ -727,7 +723,7 @@ public class PackFile implements Iterable<PackIndex.MutableEntry> {
                return new ObjectLoader.SmallObject(type, data);
        }
 
-       private byte[] getDeltaHeader(long pos, WindowCursor wc)
+       byte[] getDeltaHeader(WindowCursor wc, long pos)
                        throws IOException, DataFormatException {
                // The delta stream starts as two variable length integers. If we
                // assume they are 64 bits each, we need 16 bytes to encode them,
@@ -739,8 +735,7 @@ public class PackFile implements Iterable<PackIndex.MutableEntry> {
                return hdr;
        }
 
-       private int getObjectType(final WindowCursor curs, long pos)
-                       throws IOException {
+       int getObjectType(final WindowCursor curs, long pos) throws IOException {
                final byte[] ib = curs.tempId;
                for (;;) {
                        readFully(pos, ib, 0, 20, curs);