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;
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;
@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;
}
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();
// 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 {
// 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;
// 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();
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,
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);