From 42691339476af7c68aac3afc31abfec0ad450d95 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 30 Nov 2006 01:42:41 -0500 Subject: [PATCH] Completely rewrote object data access to improve performance. According to the profiling that I was able to do we were spending a lot of our time in the object decompression/delta application portions of jgit when we were walking a large number of commit objects. This is what I feared, which was that our way-too-flexible stack of InputStreams was simply costing us too much in overhead to be realistic. So I've written pretty much all of that code and taking the approach that core Git takes. We allocate entire byte[] for data anytime we need to work with something, rather than attempting to stream it from an InputStream. I've also pushed the object inflation code down as close to the ByteWindow as possible, so that the byte window itself can be offered up to the active Inflater as the input. This actually worked out well as it maintains a nice clean abstraction between the code that needs the decompressed data and the windows which hold the compressed data. So at this point once the data is in memory we are able to access it without copying, except during decompression. But if we are using a mmapped region and Java won't let us get access to the underlying byte[] (because its mapped MapMode.READ_ONLY) then we are forced to copy the data into a temporary buffer before we can hand it over to the deflate routine. This version of the code shows a roughly a 10x speed improvement for the case of walking along a chain of 50,000 commits (from the Mozilla repository). Unfortunately this version also contains a broken commit parser, an untested but heavily modified tree tree parser, and a lightly tested but also heavily modified binary delta apply routine. More work still needs to be done on those. Signed-off-by: Shawn O. Pearce --- .../src/org/spearce/jgit/lib/BinaryDelta.java | 102 ++++++++++ .../src/org/spearce/jgit/lib/ByteArrayWindow.java | 17 ++ .../src/org/spearce/jgit/lib/ByteBufferWindow.java | 20 ++ .../src/org/spearce/jgit/lib/ByteWindow.java | 32 +++ .../src/org/spearce/jgit/lib/CheckoutTree.java | 42 ++-- .../src/org/spearce/jgit/lib/Commit.java | 84 ++++---- .../jgit/lib/DeltaOfsPackedObjectLoader.java | 18 ++ .../jgit/lib/DeltaOfsPackedObjectReader.java | 18 -- .../spearce/jgit/lib/DeltaPackedObjectLoader.java | 47 +++++ .../spearce/jgit/lib/DeltaPackedObjectReader.java | 46 ----- ...Reader.java => DeltaRefPackedObjectLoader.java} | 12 +- .../src/org/spearce/jgit/lib/FileTreeEntry.java | 2 +- .../src/org/spearce/jgit/lib/ObjectId.java | 27 +++ .../lib/{ObjectReader.java => ObjectLoader.java} | 38 +--- .../src/org/spearce/jgit/lib/PackFile.java | 68 ++++--- ...teBufferWindow.java => PackedObjectLoader.java} | 37 ++-- .../org/spearce/jgit/lib/PackedObjectReader.java | 101 ---------- .../src/org/spearce/jgit/lib/PatchDeltaStream.java | 215 --------------------- .../src/org/spearce/jgit/lib/Repository.java | 99 +++------- .../src/org/spearce/jgit/lib/Tree.java | 76 +++----- .../org/spearce/jgit/lib/UnpackedObjectLoader.java | 191 ++++++++++++++++++ .../org/spearce/jgit/lib/UnpackedObjectReader.java | 162 ---------------- .../spearce/jgit/lib/WholePackedObjectLoader.java | 27 +++ .../spearce/jgit/lib/WholePackedObjectReader.java | 11 -- .../src/org/spearce/jgit/lib/WindowedFile.java | 25 +++ .../src/org/spearce/jgit/lib/XInputStream.java | 154 --------------- .../tst/org/spearce/jgit/lib/T0004_PackReader.java | 10 +- .../tst/org/spearce/jgit/lib/XInputStream.java | 62 ++++++ 28 files changed, 745 insertions(+), 998 deletions(-) create mode 100644 org.spearce.jgit/src/org/spearce/jgit/lib/BinaryDelta.java create mode 100644 org.spearce.jgit/src/org/spearce/jgit/lib/DeltaOfsPackedObjectLoader.java delete mode 100644 org.spearce.jgit/src/org/spearce/jgit/lib/DeltaOfsPackedObjectReader.java create mode 100644 org.spearce.jgit/src/org/spearce/jgit/lib/DeltaPackedObjectLoader.java delete mode 100644 org.spearce.jgit/src/org/spearce/jgit/lib/DeltaPackedObjectReader.java rename org.spearce.jgit/src/org/spearce/jgit/lib/{DeltaRefPackedObjectReader.java => DeltaRefPackedObjectLoader.java} (52%) rename org.spearce.jgit/src/org/spearce/jgit/lib/{ObjectReader.java => ObjectLoader.java} (59%) copy org.spearce.jgit/src/org/spearce/jgit/lib/{ByteBufferWindow.java => PackedObjectLoader.java} (60%) delete mode 100644 org.spearce.jgit/src/org/spearce/jgit/lib/PackedObjectReader.java delete mode 100644 org.spearce.jgit/src/org/spearce/jgit/lib/PatchDeltaStream.java create mode 100644 org.spearce.jgit/src/org/spearce/jgit/lib/UnpackedObjectLoader.java delete mode 100644 org.spearce.jgit/src/org/spearce/jgit/lib/UnpackedObjectReader.java create mode 100644 org.spearce.jgit/src/org/spearce/jgit/lib/WholePackedObjectLoader.java delete mode 100644 org.spearce.jgit/src/org/spearce/jgit/lib/WholePackedObjectReader.java delete mode 100644 org.spearce.jgit/src/org/spearce/jgit/lib/XInputStream.java create mode 100644 org.spearce.jgit/tst/org/spearce/jgit/lib/XInputStream.java diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/BinaryDelta.java b/org.spearce.jgit/src/org/spearce/jgit/lib/BinaryDelta.java new file mode 100644 index 00000000..24a1ab23 --- /dev/null +++ b/org.spearce.jgit/src/org/spearce/jgit/lib/BinaryDelta.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2006 Shawn Pearce + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License, version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + */ +package org.spearce.jgit.lib; + + +/** + * Recreate a stream from a base stream and a GIT pack delta. + *

+ * This entire class is heavily cribbed from patch-delta.c in the + * GIT project. The original delta patching code was written by Nicolas Pitre + * (<nico@cam.org>). + *

+ */ +public class BinaryDelta { + public static final byte[] apply(final byte[] base, final byte[] delta) { + int deltaPtr = 0; + + // Length of the base object (a variable length int). + // + int baseLen = 0; + int c, shift = 0; + do { + c = delta[deltaPtr++] & 0xff; + baseLen |= (c & 0x7f) << shift; + shift += 7; + } while ((c & 0x80) != 0); + if (base.length != baseLen) + throw new IllegalArgumentException("base length incorrect"); + + // Length of the resulting object (a variable length int). + // + int resLen = 0; + shift = 0; + do { + c = delta[deltaPtr++] & 0xff; + resLen |= (c & 0x7f) << shift; + shift += 7; + } while ((c & 0x80) != 0); + + final byte[] result = new byte[resLen]; + int resultPtr = 0; + while (deltaPtr < delta.length) { + final int cmd = delta[deltaPtr++] & 0xff; + if ((cmd & 0x80) != 0) { + // Determine the segment of the base which should + // be copied into the output. The segment is given + // as an offset and a length. + // + int copyOffset = 0; + if ((cmd & 0x01) != 0) + copyOffset = delta[deltaPtr++] & 0xff; + if ((cmd & 0x02) != 0) + copyOffset |= (delta[deltaPtr++] & 0xff) << 8; + if ((cmd & 0x04) != 0) + copyOffset |= (delta[deltaPtr++] & 0xff) << 16; + if ((cmd & 0x08) != 0) + copyOffset |= (delta[deltaPtr++] & 0xff) << 24; + + int copySize = 0; + if ((cmd & 0x10) != 0) + copySize = delta[deltaPtr++] & 0xff; + if ((cmd & 0x20) != 0) + copySize |= (delta[deltaPtr++] & 0xff) << 8; + if ((cmd & 0x40) != 0) + copySize |= (delta[deltaPtr++] & 0xff) << 16; + if (copySize == 0) + copySize = 0x10000; + + System.arraycopy(base, copyOffset, result, resultPtr, copySize); + resultPtr += copySize; + } else if (cmd != 0) { + // Anything else the data is literal within the delta + // itself. + // + System.arraycopy(delta, deltaPtr, result, resultPtr, cmd); + deltaPtr += cmd; + resultPtr += cmd; + } else { + // cmd == 0 has been reserved for future encoding but + // for now its not acceptable. + // + throw new IllegalArgumentException("unsupported command 0"); + } + } + + return result; + } +} diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/ByteArrayWindow.java b/org.spearce.jgit/src/org/spearce/jgit/lib/ByteArrayWindow.java index 99c3b014..e2f98fbc 100644 --- a/org.spearce.jgit/src/org/spearce/jgit/lib/ByteArrayWindow.java +++ b/org.spearce.jgit/src/org/spearce/jgit/lib/ByteArrayWindow.java @@ -16,6 +16,9 @@ */ package org.spearce.jgit.lib; +import java.util.zip.DataFormatException; +import java.util.zip.Inflater; + public final class ByteArrayWindow extends ByteWindow { private final byte[] array; @@ -30,6 +33,20 @@ public final class ByteArrayWindow extends ByteWindow { return n; } + public int inflate(final int pos, final byte[] b, int o, final Inflater inf) + throws DataFormatException { + while (!inf.finished()) { + if (inf.needsInput()) { + inf.setInput(array, pos, array.length - pos); + break; + } + o += inf.inflate(b, o, b.length - o); + } + while (!inf.finished() && !inf.needsInput()) + o += inf.inflate(b, o, b.length - o); + return o; + } + public int size() { return array.length; } diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/ByteBufferWindow.java b/org.spearce.jgit/src/org/spearce/jgit/lib/ByteBufferWindow.java index 9bf6ca22..0faacbb6 100644 --- a/org.spearce.jgit/src/org/spearce/jgit/lib/ByteBufferWindow.java +++ b/org.spearce.jgit/src/org/spearce/jgit/lib/ByteBufferWindow.java @@ -17,6 +17,8 @@ package org.spearce.jgit.lib; import java.nio.ByteBuffer; +import java.util.zip.DataFormatException; +import java.util.zip.Inflater; public final class ByteBufferWindow extends ByteWindow { private final ByteBuffer buffer; @@ -35,6 +37,24 @@ public final class ByteBufferWindow extends ByteWindow { return n; } + public int inflate(final int pos, final byte[] b, int o, final Inflater inf) + throws DataFormatException { + final byte[] tmp = new byte[512]; + final ByteBuffer s = buffer.slice(); + s.position(pos); + while (s.remaining() > 0 && !inf.finished()) { + if (inf.needsInput()) { + final int n = Math.min(s.remaining(), tmp.length); + s.get(tmp, 0, n); + inf.setInput(tmp, 0, n); + } + o += inf.inflate(b, o, b.length - o); + } + while (!inf.finished() && !inf.needsInput()) + o += inf.inflate(b, o, b.length - o); + return o; + } + public int size() { return buffer.capacity(); } diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/ByteWindow.java b/org.spearce.jgit/src/org/spearce/jgit/lib/ByteWindow.java index 81faae98..9a1dab37 100644 --- a/org.spearce.jgit/src/org/spearce/jgit/lib/ByteWindow.java +++ b/org.spearce.jgit/src/org/spearce/jgit/lib/ByteWindow.java @@ -16,6 +16,9 @@ */ package org.spearce.jgit.lib; +import java.util.zip.DataFormatException; +import java.util.zip.Inflater; + /** * A window of data currently stored within a cache. *

@@ -58,6 +61,35 @@ public abstract class ByteWindow { public abstract int copy(int pos, byte[] dstbuf, int dstoff, int cnt); /** + * Pump bytes into the supplied inflater as input. + * + * @param pos + * offset within the window to start supplying input + * from. + * @param dstbuf + * destination buffer the inflater should output + * decompressed data to. + * @param dstoff + * current offset within dstbuf to inflate + * into. + * @param inf + * the inflater to feed input to. The caller is + * responsible for initializing the inflater as multiple + * windows may need to supply data to the same inflater + * to completely decompress something. + * @return updated dstoff based on the number of bytes + * successfully copied into dstbuf by + * inf. If the inflater is not yet finished then + * another window's data must still be supplied as input to + * finish decompression. + * @throws DataFormatException + * the inflater encounted an invalid chunk of data. Data + * stream corruption is likely. + */ + public abstract int inflate(int pos, byte[] dstbuf, int dstoff, Inflater inf) + throws DataFormatException; + + /** * Get the total size of this window. * * @return number of bytes in this window. diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/CheckoutTree.java b/org.spearce.jgit/src/org/spearce/jgit/lib/CheckoutTree.java index 53be2194..7a3d72f8 100644 --- a/org.spearce.jgit/src/org/spearce/jgit/lib/CheckoutTree.java +++ b/org.spearce.jgit/src/org/spearce/jgit/lib/CheckoutTree.java @@ -19,44 +19,30 @@ package org.spearce.jgit.lib; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; -import java.io.InputStream; +import org.spearce.jgit.errors.IncorrectObjectTypeException; import org.spearce.jgit.errors.MissingObjectException; public class CheckoutTree extends TreeVisitorWithCurrentDirectory { - private final byte[] copyBuffer; + private static final String TYPE_BLOB = Constants.TYPE_BLOB; public CheckoutTree(final File root) { super(root); - copyBuffer = new byte[8192]; } - public void visitFile(final FileTreeEntry f) throws IOException { - final File destFile = new File(getCurrentDirectory(), f.getName()); - final ObjectReader or = f.openReader(); - - if (or == null) { - throw new MissingObjectException(f.getId(), Constants.TYPE_BLOB); - } - + public void visitFile(final FileTreeEntry fte) throws IOException { + final File destFile = new File(getCurrentDirectory(), fte.getName()); + final ObjectLoader loader = fte.openReader(); + if (loader == null) + throw new MissingObjectException(fte.getId(), TYPE_BLOB); + final byte[] data = loader.getBytes(); + if (!TYPE_BLOB.equals(loader.getType())) + throw new IncorrectObjectTypeException(fte.getId(), TYPE_BLOB); + final FileOutputStream fos = new FileOutputStream(destFile); try { - final InputStream is = or.getInputStream(); - try { - final FileOutputStream fos = new FileOutputStream(destFile); - try { - int r; - while ((r = is.read(copyBuffer)) > 0) { - fos.write(copyBuffer, 0, r); - } - } finally { - fos.close(); - } - } finally { - or.close(); - } - } catch (IOException ioe) { - destFile.delete(); - throw ioe; + fos.write(data); + } finally { + fos.close(); } } diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/Commit.java b/org.spearce.jgit/src/org/spearce/jgit/lib/Commit.java index b3abb051..4e03a5af 100644 --- a/org.spearce.jgit/src/org/spearce/jgit/lib/Commit.java +++ b/org.spearce.jgit/src/org/spearce/jgit/lib/Commit.java @@ -16,12 +16,10 @@ */ package org.spearce.jgit.lib; -import java.io.BufferedReader; import java.io.IOException; import java.util.ArrayList; import java.util.List; -import org.spearce.jgit.errors.CorruptObjectException; import org.spearce.jgit.errors.MissingObjectException; public class Commit implements Treeish { @@ -31,7 +29,7 @@ public class Commit implements Treeish { private ObjectId treeId; - private final List parentIds; + private List parentIds; private PersonIdent author; @@ -46,57 +44,43 @@ public class Commit implements Treeish { parentIds = new ArrayList(2); } - public Commit(final Repository db, final ObjectId id, - final BufferedReader br) throws IOException { + public Commit(final Repository db, final ObjectId id, final byte[] raw) + throws IOException { objdb = db; commitId = id; - - final StringBuffer tempMessage; - final char[] readBuf; - int readLen; - String n; - - n = br.readLine(); - if (n == null || !n.startsWith("tree ")) { - throw new CorruptObjectException(commitId, "no tree"); - } - treeId = new ObjectId(n.substring("tree ".length())); - + treeId = ObjectId.fromString(raw, 5); parentIds = new ArrayList(2); + int rawPtr = 46; for (;;) { - n = br.readLine(); - if (n == null) { - throw new CorruptObjectException(commitId, "no parent(s)"); - } - if (n.startsWith("parent ")) { - parentIds.add(new ObjectId(n.substring("parent ".length()))); - } else { + if (raw[rawPtr] != 'p') break; - } - } - - if (n == null || !n.startsWith("author ")) { - throw new CorruptObjectException(commitId, "no author"); - } - author = new PersonIdent(n.substring("author ".length())); - - n = br.readLine(); - if (n == null || !n.startsWith("committer ")) { - throw new CorruptObjectException(commitId, "no committer"); + parentIds.add(ObjectId.fromString(raw, rawPtr + 7)); + rawPtr += 48; } - committer = new PersonIdent(n.substring("committer ".length())); - n = br.readLine(); - if (n == null || !n.equals("")) { - throw new CorruptObjectException(commitId, "malformed header"); - } - - tempMessage = new StringBuffer(); - readBuf = new char[128]; - while ((readLen = br.read(readBuf)) > 0) { - tempMessage.append(readBuf, 0, readLen); - } - message = tempMessage.toString(); + // + // if (n == null || !n.startsWith("author ")) { + // throw new CorruptObjectException(commitId, "no author"); + // } + // author = new PersonIdent(n.substring("author ".length())); + // + // n = br.readLine(); + // if (n == null || !n.startsWith("committer ")) { + // throw new CorruptObjectException(commitId, "no committer"); + // } + // committer = new PersonIdent(n.substring("committer ".length())); + // + // n = br.readLine(); + // if (n == null || !n.equals("")) { + // throw new CorruptObjectException(commitId, "malformed header"); + // } + // + // tempMessage = new StringBuffer(); + // readBuf = new char[128]; + // while ((readLen = br.read(readBuf)) > 0) { + // tempMessage.append(readBuf, 0, readLen); + // } + // message = tempMessage.toString(); } public ObjectId getCommitId() { @@ -163,10 +147,8 @@ public class Commit implements Treeish { } public void commit() throws IOException { - if (getCommitId() != null) { - throw new IllegalStateException("Commit already made: " - + getCommitId()); - } + if (getCommitId() != null) + throw new IllegalStateException("exists " + getCommitId()); setCommitId(new ObjectWriter(objdb).writeCommit(this)); } diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/DeltaOfsPackedObjectLoader.java b/org.spearce.jgit/src/org/spearce/jgit/lib/DeltaOfsPackedObjectLoader.java new file mode 100644 index 00000000..5f479f8e --- /dev/null +++ b/org.spearce.jgit/src/org/spearce/jgit/lib/DeltaOfsPackedObjectLoader.java @@ -0,0 +1,18 @@ +package org.spearce.jgit.lib; + +import java.io.IOException; + +/** Reads a deltafied object which uses an offset to find its base. */ +class DeltaOfsPackedObjectLoader extends DeltaPackedObjectLoader { + private final long deltaBase; + + public DeltaOfsPackedObjectLoader(final PackFile pr, final long offset, + final int deltaSz, final long base) { + super(pr, offset, deltaSz); + deltaBase = base; + } + + protected ObjectLoader getBaseLoader() throws IOException { + return pack.resolveBase(deltaBase); + } +} diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/DeltaOfsPackedObjectReader.java b/org.spearce.jgit/src/org/spearce/jgit/lib/DeltaOfsPackedObjectReader.java deleted file mode 100644 index b71ce394..00000000 --- a/org.spearce.jgit/src/org/spearce/jgit/lib/DeltaOfsPackedObjectReader.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.spearce.jgit.lib; - -import java.io.IOException; - -/** Reads a deltafied object which uses an offset to find its base. */ -public class DeltaOfsPackedObjectReader extends DeltaPackedObjectReader { - private final long deltaBase; - - public DeltaOfsPackedObjectReader(final PackFile pr, final long offset, - final long base) { - super(pr, offset); - deltaBase = base; - } - - protected ObjectReader baseReader() throws IOException { - return pack.resolveBase(deltaBase); - } -} diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/DeltaPackedObjectLoader.java b/org.spearce.jgit/src/org/spearce/jgit/lib/DeltaPackedObjectLoader.java new file mode 100644 index 00000000..b776dff9 --- /dev/null +++ b/org.spearce.jgit/src/org/spearce/jgit/lib/DeltaPackedObjectLoader.java @@ -0,0 +1,47 @@ +package org.spearce.jgit.lib; + +import java.io.IOException; +import java.util.zip.DataFormatException; + +import org.spearce.jgit.errors.CorruptObjectException; + +/** Reader for a deltafied object stored in a pack file. */ +abstract class DeltaPackedObjectLoader extends PackedObjectLoader { + private final int deltaSize; + + DeltaPackedObjectLoader(final PackFile pr, final long offset, + final int deltaSz) { + super(pr, offset); + deltaSize = deltaSz; + } + + public String getType() throws IOException { + if (objectType == null) + getBytes(); + return objectType; + } + + public long getSize() throws IOException { + if (objectType == null) + getBytes(); + return objectSize; + } + + public byte[] getBytes() throws IOException { + try { + final ObjectLoader baseLoader = getBaseLoader(); + final byte[] data = BinaryDelta.apply(baseLoader.getBytes(), pack + .decompress(dataOffset, deltaSize)); + objectType = baseLoader.getType(); + objectSize = data.length; + return data; + } catch (DataFormatException dfe) { + final CorruptObjectException coe; + coe = new CorruptObjectException(getId(), "bad stream"); + coe.initCause(dfe); + throw coe; + } + } + + protected abstract ObjectLoader getBaseLoader() throws IOException; +} diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/DeltaPackedObjectReader.java b/org.spearce.jgit/src/org/spearce/jgit/lib/DeltaPackedObjectReader.java deleted file mode 100644 index 5ec037b5..00000000 --- a/org.spearce.jgit/src/org/spearce/jgit/lib/DeltaPackedObjectReader.java +++ /dev/null @@ -1,46 +0,0 @@ -package org.spearce.jgit.lib; - -import java.io.IOException; -import java.io.InputStream; - -/** Reader for a deltafied object stored in a pack file. */ -abstract class DeltaPackedObjectReader extends PackedObjectReader { - DeltaPackedObjectReader(final PackFile pr, final long offset) { - super(pr, offset); - objectSize = -1; - } - - public String getType() throws IOException { - if (objectType == null) { - final ObjectReader b = baseReader(); - try { - objectType = b.getType(); - } finally { - b.close(); - } - } - return objectType; - } - - public long getSize() throws IOException { - if (objectSize == -1) { - final PatchDeltaStream p; - p = new PatchDeltaStream(packStream(), null); - objectSize = p.getResultLength(); - p.close(); - } - return objectSize; - } - - public InputStream getInputStream() throws IOException { - final ObjectReader b = baseReader(); - final PatchDeltaStream p = new PatchDeltaStream(packStream(), b); - if (objectSize == -1) - objectSize = p.getResultLength(); - if (objectType == null) - objectType = b.getType(); - return p; - } - - protected abstract ObjectReader baseReader() throws IOException; -} diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/DeltaRefPackedObjectReader.java b/org.spearce.jgit/src/org/spearce/jgit/lib/DeltaRefPackedObjectLoader.java similarity index 52% rename from org.spearce.jgit/src/org/spearce/jgit/lib/DeltaRefPackedObjectReader.java rename to org.spearce.jgit/src/org/spearce/jgit/lib/DeltaRefPackedObjectLoader.java index 074ac6ca..72b73e32 100644 --- a/org.spearce.jgit/src/org/spearce/jgit/lib/DeltaRefPackedObjectReader.java +++ b/org.spearce.jgit/src/org/spearce/jgit/lib/DeltaRefPackedObjectLoader.java @@ -5,17 +5,17 @@ import java.io.IOException; import org.spearce.jgit.errors.MissingObjectException; /** Reads a deltaified object which uses an {@link ObjectId} to find its base. */ -class DeltaRefPackedObjectReader extends DeltaPackedObjectReader { +class DeltaRefPackedObjectLoader extends DeltaPackedObjectLoader { private final ObjectId deltaBase; - public DeltaRefPackedObjectReader(final PackFile pr, final long offset, - final ObjectId base) { - super(pr, offset); + DeltaRefPackedObjectLoader(final PackFile pr, final long offset, + final int deltaSz, final ObjectId base) { + super(pr, offset, deltaSz); deltaBase = base; } - protected ObjectReader baseReader() throws IOException { - final ObjectReader or = pack.resolveBase(deltaBase); + protected ObjectLoader getBaseLoader() throws IOException { + final ObjectLoader or = pack.resolveBase(deltaBase); if (or == null) throw new MissingObjectException(deltaBase, "delta base"); return or; diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/FileTreeEntry.java b/org.spearce.jgit/src/org/spearce/jgit/lib/FileTreeEntry.java index e6ea0b2d..d425c68b 100644 --- a/org.spearce.jgit/src/org/spearce/jgit/lib/FileTreeEntry.java +++ b/org.spearce.jgit/src/org/spearce/jgit/lib/FileTreeEntry.java @@ -39,7 +39,7 @@ public class FileTreeEntry extends TreeEntry { mode = execute ? FileMode.EXECUTABLE_FILE : FileMode.REGULAR_FILE; } - public ObjectReader openReader() throws IOException { + public ObjectLoader openReader() throws IOException { return getRepository().openBlob(getId()); } diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/ObjectId.java b/org.spearce.jgit/src/org/spearce/jgit/lib/ObjectId.java index c0f36c47..c5eb95ac 100644 --- a/org.spearce.jgit/src/org/spearce/jgit/lib/ObjectId.java +++ b/org.spearce.jgit/src/org/spearce/jgit/lib/ObjectId.java @@ -52,6 +52,33 @@ public class ObjectId implements Comparable { return i != null ? i.toString() : ZEROID_STR; } + public static ObjectId fromString(final byte[] i, int offset) { + final byte[] id = new byte[Constants.OBJECT_ID_LENGTH]; + for (int k = 0; k < Constants.OBJECT_ID_LENGTH; k++) { + final byte c1 = i[offset++]; + final byte c2 = i[offset++]; + int b; + + if ('0' <= c1 && c1 <= '9') + b = c1 - '0'; + else if ('a' <= c1 && c1 <= 'f') + b = c1 - 'a' + 10; + else + throw new IllegalArgumentException("Invalid id: " + (char) c1); + + b <<= 4; + + if ('0' <= c2 && c2 <= '9') + b |= c2 - '0'; + else if ('a' <= c2 && c2 <= 'f') + b |= c2 - 'a' + 10; + else + throw new IllegalArgumentException("Invalid id: " + (char) c2); + id[k] = (byte) b; + } + return new ObjectId(id); + } + private static int compare(final byte[] a, final byte[] b) { for (int k = 0; k < a.length && k < b.length; k++) { final int ak = a[k] & 0xff; diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/ObjectReader.java b/org.spearce.jgit/src/org/spearce/jgit/lib/ObjectLoader.java similarity index 59% rename from org.spearce.jgit/src/org/spearce/jgit/lib/ObjectReader.java rename to org.spearce.jgit/src/org/spearce/jgit/lib/ObjectLoader.java index b5753145..c817bf0d 100644 --- a/org.spearce.jgit/src/org/spearce/jgit/lib/ObjectReader.java +++ b/org.spearce.jgit/src/org/spearce/jgit/lib/ObjectLoader.java @@ -16,56 +16,34 @@ */ package org.spearce.jgit.lib; -import java.io.BufferedReader; import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.UnsupportedEncodingException; import java.security.MessageDigest; -public abstract class ObjectReader { +public abstract class ObjectLoader { private ObjectId objectId; public ObjectId getId() throws IOException { if (objectId == null) { final MessageDigest md = Constants.newMessageDigest(); - final InputStream is = getInputStream(); - try { - final byte[] buf = new byte[2048]; - int r; - md.update(Constants.encodeASCII(getType())); - md.update((byte) ' '); - md.update(Constants.encodeASCII(getSize())); - md.update((byte) 0); - while ((r = is.read(buf)) > 0) { - md.update(buf, 0, r); - } - } finally { - is.close(); - } + md.update(Constants.encodeASCII(getType())); + md.update((byte) ' '); + md.update(Constants.encodeASCII(getSize())); + md.update((byte) 0); + md.update(getBytes()); objectId = new ObjectId(md.digest()); } return objectId; } protected void setId(final ObjectId id) { - if (objectId != null) { + if (objectId != null) throw new IllegalStateException("Id already set."); - } objectId = id; } - public BufferedReader getBufferedReader() - throws UnsupportedEncodingException, IOException { - return new BufferedReader(new InputStreamReader(getInputStream(), - Constants.CHARACTER_ENCODING)); - } - public abstract String getType() throws IOException; public abstract long getSize() throws IOException; - public abstract InputStream getInputStream() throws IOException; - - public abstract void close() throws IOException; + public abstract byte[] getBytes() throws IOException; } diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/PackFile.java b/org.spearce.jgit/src/org/spearce/jgit/lib/PackFile.java index 7230d15e..fa06af4e 100644 --- a/org.spearce.jgit/src/org/spearce/jgit/lib/PackFile.java +++ b/org.spearce.jgit/src/org/spearce/jgit/lib/PackFile.java @@ -18,6 +18,8 @@ package org.spearce.jgit.lib; import java.io.File; import java.io.IOException; +import java.nio.channels.FileChannel.MapMode; +import java.util.zip.DataFormatException; import org.spearce.jgit.errors.CorruptObjectException; @@ -41,7 +43,7 @@ public class PackFile { repo = parentRepo; // FIXME window size and mmap type should be configurable pack = new WindowedFile(repo.getWindowCache(), packFile, - 16 * 1024 * 1024, null); + 64 * 1024 * 1024, MapMode.READ_WRITE); try { readPackHeader(); @@ -52,7 +54,7 @@ public class PackFile { + ".idx"); // FIXME window size and mmap type should be configurable idx = new WindowedFile(repo.getWindowCache(), idxFile, - 16 * 1024 * 1024, null); + 64 * 1024 * 1024, MapMode.READ_WRITE); try { idxHeader = readIndexHeader(); } catch (IOException ioe) { @@ -71,20 +73,20 @@ public class PackFile { } } - ObjectReader resolveBase(final ObjectId id) throws IOException { + ObjectLoader resolveBase(final ObjectId id) throws IOException { return get(id); } - ObjectReader resolveBase(final long ofs) throws IOException { + ObjectLoader resolveBase(final long ofs) throws IOException { return reader(ofs); } - public synchronized PackedObjectReader get(final ObjectId id) + public synchronized PackedObjectLoader get(final ObjectId id) throws IOException { final long offset = findOffset(id); if (offset == -1) return null; - final PackedObjectReader objReader = reader(offset); + final PackedObjectLoader objReader = reader(offset); objReader.setId(id); return objReader; } @@ -94,9 +96,11 @@ public class PackFile { idx.close(); } - final int read(final long pos, final byte[] dst, final int off, final int n) - throws IOException { - return pack.read(pos, dst, off, n); + byte[] decompress(final long position, final int totalSize) + throws DataFormatException, IOException { + final byte[] dstbuf = new byte[totalSize]; + pack.readCompressed(position, dstbuf); + return dstbuf; } private void readPackHeader() throws IOException { @@ -132,61 +136,61 @@ public class PackFile { return idxHeader; } - private PackedObjectReader reader(final long objOffset) throws IOException { + private PackedObjectLoader reader(final long objOffset) throws IOException { final byte[] ib = new byte[Constants.OBJECT_ID_LENGTH]; long pos = objOffset; - int c; + int p = 0; pack.readFully(pos, ib); - c = ib[(int) (pos++ - objOffset)] & 0xff; + int c = ib[p++] & 0xff; final int typeCode = (c >> 4) & 7; - long size = c & 15; + long dataSize = c & 15; int shift = 4; while ((c & 0x80) != 0) { - c = ib[(int) (pos++ - objOffset)] & 0xff; - size += (c & 0x7f) << shift; + c = ib[p++] & 0xff; + dataSize += (c & 0x7f) << shift; shift += 7; } + pos += p; switch (typeCode) { - case Constants.OBJ_EXT: - throw new IOException("Extended object types not supported."); case Constants.OBJ_COMMIT: - return new WholePackedObjectReader(this, pos, - Constants.TYPE_COMMIT, size); + return whole(Constants.TYPE_COMMIT, pos, dataSize); case Constants.OBJ_TREE: - return new WholePackedObjectReader(this, pos, Constants.TYPE_TREE, - size); + return whole(Constants.TYPE_TREE, pos, dataSize); case Constants.OBJ_BLOB: - return new WholePackedObjectReader(this, pos, Constants.TYPE_BLOB, - size); + return whole(Constants.TYPE_BLOB, pos, dataSize); case Constants.OBJ_TAG: - return new WholePackedObjectReader(this, pos, Constants.TYPE_TAG, - size); - case Constants.OBJ_TYPE_5: - throw new IOException("Object type 5 not supported."); + return whole(Constants.TYPE_TAG, pos, dataSize); case Constants.OBJ_OFS_DELTA: { pack.readFully(pos, ib); - c = ib[(int) (pos++ - objOffset)] & 0xff; + p = 0; + c = ib[p++] & 0xff; long ofs = c & 127; while ((c & 128) != 0) { ofs += 1; - c = ib[(int) (pos++ - objOffset)] & 0xff; + c = ib[p++] & 0xff; ofs <<= 7; ofs += (c & 127); } - return new DeltaOfsPackedObjectReader(this, pos, objOffset - ofs); + return new DeltaOfsPackedObjectLoader(this, pos + p, + (int) dataSize, objOffset - ofs); } case Constants.OBJ_REF_DELTA: { pack.readFully(pos, ib); - pos += ib.length; - return new DeltaRefPackedObjectReader(this, pos, new ObjectId(ib)); + return new DeltaRefPackedObjectLoader(this, pos + ib.length, + (int) dataSize, new ObjectId(ib)); } default: throw new IOException("Unknown object type " + typeCode + "."); } } + private final WholePackedObjectLoader whole(final String type, + final long pos, final long size) { + return new WholePackedObjectLoader(this, pos, type, (int) size); + } + private long findOffset(final ObjectId objId) throws IOException { final int levelOne = objId.getFirstByte(); final byte[] intbuf = new byte[4]; diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/ByteBufferWindow.java b/org.spearce.jgit/src/org/spearce/jgit/lib/PackedObjectLoader.java similarity index 60% copy from org.spearce.jgit/src/org/spearce/jgit/lib/ByteBufferWindow.java copy to org.spearce.jgit/src/org/spearce/jgit/lib/PackedObjectLoader.java index 9bf6ca22..52c75795 100644 --- a/org.spearce.jgit/src/org/spearce/jgit/lib/ByteBufferWindow.java +++ b/org.spearce.jgit/src/org/spearce/jgit/lib/PackedObjectLoader.java @@ -16,26 +16,31 @@ */ package org.spearce.jgit.lib; -import java.nio.ByteBuffer; +import java.io.IOException; -public final class ByteBufferWindow extends ByteWindow { - private final ByteBuffer buffer; +abstract class PackedObjectLoader extends ObjectLoader { + protected final PackFile pack; - public ByteBufferWindow(final WindowProvider o, final int d, - final ByteBuffer b) { - super(o, d); - buffer = b; + protected final long dataOffset; + + protected String objectType; + + protected int objectSize; + + protected PackedObjectLoader(final PackFile pr, final long offset) { + pack = pr; + dataOffset = offset; + } + + public String getType() throws IOException { + return objectType; } - public final int copy(final int p, final byte[] b, final int o, int n) { - final ByteBuffer s = buffer.slice(); - s.position(p); - n = Math.min(s.remaining(), n); - s.get(b, o, n); - return n; + public long getSize() throws IOException { + return objectSize; } - public int size() { - return buffer.capacity(); + public long getDataOffset() { + return dataOffset; } -} \ No newline at end of file +} diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/PackedObjectReader.java b/org.spearce.jgit/src/org/spearce/jgit/lib/PackedObjectReader.java deleted file mode 100644 index 8104e065..00000000 --- a/org.spearce.jgit/src/org/spearce/jgit/lib/PackedObjectReader.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (C) 2006 Shawn Pearce - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License, version 2.1, as published by the Free Software Foundation. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - */ -package org.spearce.jgit.lib; - -import java.io.BufferedInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.zip.DataFormatException; -import java.util.zip.Inflater; - -abstract class PackedObjectReader extends ObjectReader { - protected final PackFile pack; - - protected final long dataOffset; - - protected String objectType; - - protected long objectSize; - - protected PackedObjectReader(final PackFile pr, final long offset) { - pack = pr; - dataOffset = offset; - } - - public String getType() throws IOException { - return objectType; - } - - public long getSize() throws IOException { - return objectSize; - } - - public long getDataOffset() { - return dataOffset; - } - - public InputStream getInputStream() throws IOException { - return packStream(); - } - - public void close() throws IOException { - } - - protected BufferedInputStream packStream() { - return new BufferedInputStream(new PackStream()); - } - - private class PackStream extends InputStream { - private Inflater inf = new Inflater(false); - - private byte[] in = new byte[2048]; - - private long offset = getDataOffset(); - - public int read() throws IOException { - final byte[] sbb = new byte[1]; - return read(sbb, 0, 1) == 1 ? sbb[0] & 0xff : -1; - } - - public int read(final byte[] b, final int off, final int len) - throws IOException { - if (inf.finished()) - return -1; - if (inf.needsInput()) { - final int n = pack.read(offset, in, 0, in.length); - inf.setInput(in, 0, n); - offset += n; - } - - try { - return inf.inflate(b, off, len); - } catch (DataFormatException dfe) { - final IOException e = new IOException("Corrupt ZIP stream."); - e.initCause(dfe); - throw e; - } - } - - public void close() { - if (inf != null) { - inf.end(); - inf = null; - in = null; - } - } - } -} diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/PatchDeltaStream.java b/org.spearce.jgit/src/org/spearce/jgit/lib/PatchDeltaStream.java deleted file mode 100644 index 92c3d0b7..00000000 --- a/org.spearce.jgit/src/org/spearce/jgit/lib/PatchDeltaStream.java +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Copyright (C) 2006 Shawn Pearce - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License, version 2.1, as published by the Free Software Foundation. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - */ -package org.spearce.jgit.lib; - -import java.io.IOException; -import java.io.InputStream; - -import org.spearce.jgit.errors.CorruptObjectException; - -/** - * Recreate a stream from a base stream and a GIT pack delta. - *

- * This entire class is heavily cribbed from patch-delta.c in the - * GIT project. The original delta patching code was written by Nicolas Pitre - * (<nico@cam.org>). - *

- */ -public class PatchDeltaStream extends InputStream { - private final InputStream deltaStream; - - private byte[] base; - - private long resLen; - - private int cmd; - - private int copyOffset; - - private int copySize; - - public PatchDeltaStream(final InputStream delta, - final ObjectReader baseReader) throws IOException { - deltaStream = delta; - cmd = -1; - - // Length of the base object (a variable length int). - // - long baseLen = 0; - int c, shift = 0; - do { - c = readDelta(); - baseLen |= (c & 0x7f) << shift; - shift += 7; - } while ((c & 0x80) != 0); - - // Length of the resulting object (a variable length int). - // - shift = 0; - do { - c = readDelta(); - resLen |= (c & 0x7f) << shift; - shift += 7; - } while ((c & 0x80) != 0); - - // We need to address parts of the base at random so pull the - // entire base into a byte array to permit random accessing. - // - if (baseReader != null) { - final InputStream i = baseReader.getInputStream(); - try { - int expBaseLen = (int) baseReader.getSize(); - if (baseLen != expBaseLen) - throw new CorruptObjectException("Base length from delta (" - + baseLen - + ") does not equal actual length from base (" - + expBaseLen + ")."); - - shift = 0; - base = new byte[expBaseLen]; - while (expBaseLen > 0) { - final int n = i.read(base, shift, expBaseLen); - if (n < 0) - throw new CorruptObjectException("Can't completely" - + " load delta base for patching."); - shift += n; - expBaseLen -= n; - } - } finally { - i.close(); - baseReader.close(); - } - } - } - - public long getResultLength() { - return resLen; - } - - public int read() throws IOException { - final byte[] b = new byte[1]; - return read(b, 0, 1) == 1 ? b[0] : -1; - } - - public int read(final byte[] b, int off, int len) throws IOException { - if (base == null) - return -1; - - int r = 0; - while (len > 0) { - if (cmd == -1) { - // We don't have a valid command ready so read the next - // one from the delta stream. - // - cmd = deltaStream.read(); - if (cmd < 0) { - base = null; - return r; - } - - if ((cmd & 0x80) != 0) { - // Determine the segment of the base which should - // be copied into the output. The segment is given - // as an offset and a length. - // - copyOffset = 0; - copySize = 0; - - if ((cmd & 0x01) != 0) - copyOffset = readDelta(); - if ((cmd & 0x02) != 0) - copyOffset |= readDelta() << 8; - if ((cmd & 0x04) != 0) - copyOffset |= readDelta() << 16; - if ((cmd & 0x08) != 0) - copyOffset |= readDelta() << 24; - - if ((cmd & 0x10) != 0) - copySize = readDelta(); - if ((cmd & 0x20) != 0) - copySize |= readDelta() << 8; - if ((cmd & 0x40) != 0) - copySize |= readDelta() << 16; - if (copySize == 0) - copySize = 0x10000; - } else if (cmd != 0) { - // Anything else the data is literal within the delta - // itself. - // - copySize = cmd; - } else { - // cmd == 0 has been reserved for future encoding but - // for now its not acceptable. - // - throw new CorruptObjectException("Delta is corrupt."); - } - } - - if ((cmd & 0x80) != 0) { - // Copy the segment copyOffset through copyOffset+copySize - // from the base to the output. - // - final int n = Math.min(len, copySize); - System.arraycopy(base, copyOffset, b, off, n); - r += n; - off += n; - len -= n; - copySize -= n; - if (copySize == 0) - cmd = -1; - else - copyOffset += n; - } else if (cmd != 0) { - // Literally copy from the delta to the output. - // - final int n = Math.min(len, copySize); - xreadDelta(b, off, n); - r += n; - off += n; - len -= n; - copySize -= n; - if (copySize == 0) - cmd = -1; - } - } - - return r; - } - - private int readDelta() throws IOException { - final int r = deltaStream.read(); - if (r < 0) - throw new CorruptObjectException("Unexpected end of delta."); - return r; - } - - private void xreadDelta(final byte[] buf, int o, int len) - throws IOException { - int r; - while ((r = deltaStream.read(buf, o, len)) > 0) { - o += r; - len -= r; - } - if (len > 0) - throw new CorruptObjectException("Unexpected end of delta."); - } - - public void close() throws IOException { - deltaStream.close(); - base = null; - } -} diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java b/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java index 33e5cdd5..bfdcd3e0 100644 --- a/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java +++ b/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java @@ -19,7 +19,6 @@ package org.spearce.jgit.lib; import java.io.BufferedReader; import java.io.File; import java.io.FileFilter; -import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; @@ -93,7 +92,7 @@ public class Repository { private void initializeWindowCache() { // FIXME these should be configurable... - windows = new WindowCache(512 * 1024 * 1024, 32); + windows = new WindowCache(256 * 1024 * 1024, 4); } public File getDirectory() { @@ -122,60 +121,35 @@ public class Repository { while (i.hasNext()) { final PackFile p = (PackFile) i.next(); try { - final ObjectReader o = p.get(objectId); - if (o != null) { - o.close(); + if (p.get(objectId) != null) return true; - } } catch (IOException ioe) { - // This shouldn't happen unless the pack was corrupted after we - // opened it. We'll ignore the error as though the object does - // not exist in this pack. + // This shouldn't happen unless the pack was corrupted + // after we opened it. We'll ignore the error as though + // the object does not exist in this pack. // } } return toFile(objectId).isFile(); } - public ObjectReader openObject(final ObjectId id) throws IOException { - final ObjectReader packed = objectInPack(id); + public ObjectLoader openObject(final ObjectId id) throws IOException { + final ObjectLoader packed = objectInPack(id); if (packed != null) return packed; - - final XInputStream fis = openObjectStream(id); - if (fis == null) - return null; - try { - return new UnpackedObjectReader(id, fis); - } catch (IOException ioe) { - fis.close(); - throw ioe; + return new UnpackedObjectLoader(this, id); + } catch (FileNotFoundException fnfe) { + return null; } } - public ObjectReader openBlob(final ObjectId id) throws IOException { - final ObjectReader or = openObject(id); - if (or == null) { - return null; - } else if (Constants.TYPE_BLOB.equals(or.getType())) { - return or; - } else { - or.close(); - throw new IncorrectObjectTypeException(id, Constants.TYPE_BLOB); - } + public ObjectLoader openBlob(final ObjectId id) throws IOException { + return openObject(id); } - public ObjectReader openTree(final ObjectId id) throws IOException { - final ObjectReader or = openObject(id); - if (or == null) { - return null; - } else if (Constants.TYPE_TREE.equals(or.getType())) { - return or; - } else { - or.close(); - throw new IncorrectObjectTypeException(id, Constants.TYPE_TREE); - } + public ObjectLoader openTree(final ObjectId id) throws IOException { + return openObject(id); } public Commit mapCommit(final String revstr) throws IOException { @@ -184,15 +158,13 @@ public class Repository { } public Commit mapCommit(final ObjectId id) throws IOException { - final ObjectReader or = openObject(id); - if (or == null) { + final ObjectLoader or = openObject(id); + if (or == null) return null; - } else if (Constants.TYPE_COMMIT.equals(or.getType())) { - return new Commit(this, id, or.getBufferedReader()); - } else { - or.close(); - throw new IncorrectObjectTypeException(id, Constants.TYPE_COMMIT); - } + final byte[] raw = or.getBytes(); + if (Constants.TYPE_COMMIT.equals(or.getType())) + return new Commit(this, id, raw); + throw new IncorrectObjectTypeException(id, Constants.TYPE_COMMIT); } public Tree mapTree(final String revstr) throws IOException { @@ -201,17 +173,15 @@ public class Repository { } public Tree mapTree(final ObjectId id) throws IOException { - final ObjectReader or = openObject(id); - if (or == null) { + final ObjectLoader or = openObject(id); + if (or == null) return null; - } else if (Constants.TYPE_TREE.equals(or.getType())) { - return new Tree(this, id, or.getInputStream()); - } else if (Constants.TYPE_COMMIT.equals(or.getType())) { - return new Commit(this, id, or.getBufferedReader()).getTree(); - } else { - or.close(); - throw new IncorrectObjectTypeException(id, Constants.TYPE_TREE); - } + final byte[] raw = or.getBytes(); + if (Constants.TYPE_COMMIT.equals(or.getType())) + return new Tree(this, id, raw); + if (Constants.TYPE_COMMIT.equals(or.getType())) + return new Commit(this, id, raw).getTree(); + throw new IncorrectObjectTypeException(id, Constants.TYPE_TREE); } public RefLock lockRef(final String ref) throws IOException { @@ -273,21 +243,12 @@ public class Repository { } } - private XInputStream openObjectStream(final ObjectId objectId) - throws IOException { - try { - return new XInputStream(new FileInputStream(toFile(objectId))); - } catch (FileNotFoundException fnfe) { - return null; - } - } - - private ObjectReader objectInPack(final ObjectId objectId) { + private ObjectLoader objectInPack(final ObjectId objectId) { final Iterator i = packs.iterator(); while (i.hasNext()) { final PackFile p = (PackFile) i.next(); try { - final ObjectReader o = p.get(objectId); + final ObjectLoader o = p.get(objectId); if (o != null) { return o; } diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/Tree.java b/org.spearce.jgit/src/org/spearce/jgit/lib/Tree.java index 6bef6e87..7047e44d 100644 --- a/org.spearce.jgit/src/org/spearce/jgit/lib/Tree.java +++ b/org.spearce.jgit/src/org/spearce/jgit/lib/Tree.java @@ -16,9 +16,7 @@ */ package org.spearce.jgit.lib; -import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.InputStream; import java.util.Arrays; import org.spearce.jgit.errors.CorruptObjectException; @@ -91,11 +89,11 @@ public class Tree extends TreeEntry implements Treeish { contents = EMPTY_TREE; } - public Tree(final Repository repo, final ObjectId myId, final InputStream is) + public Tree(final Repository repo, final ObjectId myId, final byte[] raw) throws IOException { super(null, myId, null); db = repo; - readTree(is); + readTree(raw); } private Tree(final Tree parent, final byte[] nameUTF8) { @@ -104,11 +102,6 @@ public class Tree extends TreeEntry implements Treeish { contents = EMPTY_TREE; } - public Tree(final Repository r, final ObjectId id, final byte[] nameUTF8) { - super(null, id, nameUTF8); - db = r; - } - public Tree(final Tree parent, final ObjectId id, final byte[] nameUTF8) { super(parent, id, nameUTF8); db = parent.getRepository(); @@ -319,68 +312,47 @@ public class Tree extends TreeEntry implements Treeish { private void ensureLoaded() throws IOException, MissingObjectException { if (!isLoaded()) { - final ObjectReader or = db.openTree(getId()); + final ObjectLoader or = db.openTree(getId()); if (or == null) throw new MissingObjectException(getId(), Constants.TYPE_TREE); - try { - readTree(or.getInputStream()); - } finally { - or.close(); - } + readTree(or.getBytes()); } } - private void readTree(final InputStream is) throws IOException { + private void readTree(final byte[] raw) throws IOException { + int rawPtr = 0; TreeEntry[] temp = new TreeEntry[64]; int nextIndex = 0; boolean resort = false; - for (;;) { - int c; - int mode; - final ByteArrayOutputStream nameBuf; - final byte[] entId; - final byte[] name; - final ObjectId id; - final TreeEntry ent; - int entIdLen; - - c = is.read(); - if (c == -1) - break; - else if (c < '0' || c > '7') + while (rawPtr < raw.length) { + int c = raw[rawPtr++] & 0xff; + if (c < '0' || c > '7') throw new CorruptObjectException(getId(), "invalid entry mode"); - mode = c - '0'; + int mode = c - '0'; for (;;) { - c = is.read(); + c = raw[rawPtr++] & 0xff; if (' ' == c) break; else if (c < '0' || c > '7') throw new CorruptObjectException(getId(), "invalid mode"); - mode *= 8; + mode <<= 3; mode += c - '0'; } - nameBuf = new ByteArrayOutputStream(128); - for (;;) { - c = is.read(); - if (c == -1) - throw new CorruptObjectException(getId(), "unexpected eof"); - else if (0 == c) - break; - nameBuf.write(c); - } + int nameLen = 0; + while ((raw[rawPtr + nameLen] & 0xff) != 0) + nameLen++; + final byte[] name = new byte[nameLen]; + System.arraycopy(raw, rawPtr, name, 0, nameLen); + rawPtr += nameLen + 1; - entId = new byte[Constants.OBJECT_ID_LENGTH]; - entIdLen = 0; - while ((c = is.read(entId, entIdLen, entId.length - entIdLen)) > 0) - entIdLen += c; - if (entIdLen != entId.length) - throw new CorruptObjectException(getId(), "missing hash"); - - id = new ObjectId(entId); - name = nameBuf.toByteArray(); + final byte[] entId = new byte[Constants.OBJECT_ID_LENGTH]; + System.arraycopy(raw, rawPtr, entId, 0, Constants.OBJECT_ID_LENGTH); + final ObjectId id = new ObjectId(entId); + rawPtr += Constants.OBJECT_ID_LENGTH; + final TreeEntry ent; if (FileMode.REGULAR_FILE.equals(mode)) ent = new FileTreeEntry(this, id, name, false); else if (FileMode.EXECUTABLE_FILE.equals(mode)) @@ -413,7 +385,7 @@ public class Tree extends TreeEntry implements Treeish { contents = n; } - // Resort contents using our internal sorting order. GIT sorts + // Resort contents using our internal sorting order. Git sorts // subtrees as though their names end in '/' but that's not how // we sort them in memory. Its all the fault of the index... // diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/UnpackedObjectLoader.java b/org.spearce.jgit/src/org/spearce/jgit/lib/UnpackedObjectLoader.java new file mode 100644 index 00000000..c508914b --- /dev/null +++ b/org.spearce.jgit/src/org/spearce/jgit/lib/UnpackedObjectLoader.java @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2006 Shawn Pearce + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License, version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + */ +package org.spearce.jgit.lib; + +import java.io.FileInputStream; +import java.io.IOException; +import java.util.zip.DataFormatException; +import java.util.zip.Inflater; + +import org.spearce.jgit.errors.CorruptObjectException; + +public class UnpackedObjectLoader extends ObjectLoader { + private final String objectType; + + private final int objectSize; + + private final byte[] bytes; + + public UnpackedObjectLoader(final Repository db, final ObjectId id) + throws IOException { + final FileInputStream objStream = new FileInputStream(db.toFile(id)); + final byte[] compressed; + try { + compressed = new byte[objStream.available()]; + int off = 0; + while (off < compressed.length) + off += objStream.read(compressed, off, compressed.length - off); + } finally { + objStream.close(); + } + setId(id); + + // Try to determine if this is a legacy format loose object or + // a new style loose object. The legacy format was completely + // compressed with zlib so the first byte must be 0x78 (15-bit + // window size, deflated) and the first 16 bit word must be + // evenly divisible by 31. Otherwise its a new style loose + // object. + // + final int fb = compressed[0] & 0xff; + if (fb == 0x78 && (((fb << 8) | compressed[1] & 0xff) % 31) == 0) { + final Inflater inflater = new Inflater(false); + inflater.setInput(compressed); + final byte[] hdr = new byte[64]; + int avail = 0; + while (!inflater.finished() && avail < hdr.length) + try { + avail += inflater.inflate(hdr, avail, hdr.length - avail); + } catch (DataFormatException dfe) { + final CorruptObjectException coe; + coe = new CorruptObjectException(getId(), "bad stream"); + coe.initCause(dfe); + inflater.end(); + throw coe; + } + if (avail < 5) + throw new CorruptObjectException(id, "no header"); + + int pos; + switch (hdr[0]) { + case 'b': + if (hdr[1] != 'l' || hdr[2] != 'o' || hdr[3] != 'b' + || hdr[4] != ' ') + throw new CorruptObjectException(id, "invalid type"); + objectType = Constants.TYPE_BLOB; + pos = 5; + break; + case 'c': + if (avail < 7 || hdr[1] != 'o' || hdr[2] != 'm' + || hdr[3] != 'm' || hdr[4] != 'i' || hdr[5] != 't' + || hdr[6] != ' ') + throw new CorruptObjectException(id, "invalid type"); + objectType = Constants.TYPE_COMMIT; + pos = 7; + break; + case 't': + switch (hdr[1]) { + case 'a': + if (hdr[2] != 'g' || hdr[3] != ' ') + throw new CorruptObjectException(id, "invalid type"); + objectType = Constants.TYPE_TAG; + pos = 4; + break; + case 'r': + if (hdr[2] != 'e' || hdr[3] != 'e' || hdr[4] != ' ') + throw new CorruptObjectException(id, "invalid type"); + objectType = Constants.TYPE_TREE; + pos = 5; + break; + default: + throw new CorruptObjectException(id, "invalid type"); + } + break; + default: + throw new CorruptObjectException(id, "invalid type"); + } + + int tempSize = 0; + while (pos < avail) { + final int c = hdr[pos++]; + if (0 == c) + break; + else if (c < '0' || c > '9') + throw new CorruptObjectException(id, "invalid length"); + tempSize *= 10; + tempSize += c - '0'; + } + objectSize = tempSize; + bytes = new byte[objectSize]; + if (pos < avail) + System.arraycopy(hdr, pos, bytes, 0, avail - pos); + decompress(inflater, pos); + } else { + int p = 0; + int c = compressed[p++] & 0xff; + final int typeCode = (c >> 4) & 7; + int size = c & 15; + int shift = 4; + while ((c & 0x80) != 0) { + c = compressed[p++] & 0xff; + size += (c & 0x7f) << shift; + shift += 7; + } + + switch (typeCode) { + case Constants.OBJ_COMMIT: + objectType = Constants.TYPE_COMMIT; + break; + case Constants.OBJ_TREE: + objectType = Constants.TYPE_TREE; + break; + case Constants.OBJ_BLOB: + objectType = Constants.TYPE_BLOB; + break; + case Constants.OBJ_TAG: + objectType = Constants.TYPE_TAG; + break; + default: + throw new CorruptObjectException(id, "invalid type"); + } + + objectSize = size; + bytes = new byte[objectSize]; + final Inflater inflater = new Inflater(false); + inflater.setInput(compressed, p, compressed.length - p); + decompress(inflater, 0); + } + } + + private void decompress(final Inflater inf, int p) throws IOException { + try { + while (!inf.finished()) + p += inf.inflate(bytes, p, objectSize - p); + } catch (DataFormatException dfe) { + final CorruptObjectException coe; + coe = new CorruptObjectException(getId(), "bad stream"); + coe.initCause(dfe); + throw coe; + } finally { + inf.end(); + } + if (p != objectSize) + new CorruptObjectException(getId(), "incorrect length"); + } + + public String getType() { + return objectType; + } + + public long getSize() { + return objectSize; + } + + public byte[] getBytes() { + return bytes; + } +} diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/UnpackedObjectReader.java b/org.spearce.jgit/src/org/spearce/jgit/lib/UnpackedObjectReader.java deleted file mode 100644 index 524c1f3c..00000000 --- a/org.spearce.jgit/src/org/spearce/jgit/lib/UnpackedObjectReader.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright (C) 2006 Shawn Pearce - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License, version 2.1, as published by the Free Software Foundation. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - */ -package org.spearce.jgit.lib; - -import java.io.IOException; -import java.io.InputStream; -import java.util.zip.InflaterInputStream; - -import org.spearce.jgit.errors.CorruptObjectException; - -public class UnpackedObjectReader extends ObjectReader { - private static final int MAX_TYPE_LEN = 16; - - private final String objectType; - - private final long objectSize; - - private InflaterInputStream inflater; - - public UnpackedObjectReader(final ObjectId id, final XInputStream src) - throws IOException { - long tempSize = 0; - int byte1, byte2, word1; - - setId(id); - - // Try to determine if this is a legacy format loose object or - // a new style loose object. The legacy format was completely - // compressed with zlib so the first byte must be 0x78 (15-bit - // window size, deflated) and the first 16 bit word must be - // evenly divisible by 31. Otherwise its a new style loose object. - // - src.mark(2); - byte1 = src.readUInt8(); - byte2 = src.readUInt8(); - word1 = (byte1 << 8) | byte2; - if (byte1 == 0x78 && (word1 % 31) == 0) { - final StringBuffer typeBuf = new StringBuffer(MAX_TYPE_LEN); - final String typeStr; - - src.reset(); - inflater = new InflaterInputStream(src); - - for (;;) { - final int c = inflater.read(); - if (' ' == c) { - break; - } else if (c < 'a' || c > 'z' - || typeBuf.length() >= MAX_TYPE_LEN) { - throw new CorruptObjectException(id, "bad type in header"); - } - typeBuf.append((char) c); - } - - typeStr = typeBuf.toString(); - if (Constants.TYPE_BLOB.equals(typeStr)) { - objectType = Constants.TYPE_BLOB; - } else if (Constants.TYPE_TREE.equals(typeStr)) { - objectType = Constants.TYPE_TREE; - } else if (Constants.TYPE_COMMIT.equals(typeStr)) { - objectType = Constants.TYPE_COMMIT; - } else if (Constants.TYPE_TAG.equals(typeStr)) { - objectType = Constants.TYPE_TAG; - } else { - throw new CorruptObjectException(id, "invalid type: " + typeStr); - } - - for (;;) { - final int c = inflater.read(); - if (0 == c) { - break; - } else if (c < '0' || c > '9') { - throw new CorruptObjectException(id, "bad length in header"); - } - tempSize *= 10; - tempSize += c - '0'; - } - } else { - int typeCode, c, shift; - long size; - - src.reset(); - c = src.readUInt8(); - typeCode = (c >> 4) & 7; - size = c & 15; - shift = 4; - while ((c & 0x80) != 0) { - c = src.readUInt8(); - size += (c & 0x7f) << shift; - shift += 7; - } - - switch (typeCode) { - case Constants.OBJ_EXT: - throw new CorruptObjectException(id, - "Extended object types not supported."); - case Constants.OBJ_COMMIT: - objectType = Constants.TYPE_COMMIT; - break; - case Constants.OBJ_TREE: - objectType = Constants.TYPE_TREE; - break; - case Constants.OBJ_BLOB: - objectType = Constants.TYPE_BLOB; - break; - case Constants.OBJ_TAG: - objectType = Constants.TYPE_TAG; - break; - case Constants.OBJ_TYPE_5: - throw new CorruptObjectException(id, - "Object type 5 not supported."); - case Constants.OBJ_OFS_DELTA: - case Constants.OBJ_REF_DELTA: - throw new CorruptObjectException(id, - "Delta in loose object not supported."); - default: - throw new CorruptObjectException(id, "Unknown object type " - + typeCode + "."); - } - - inflater = new InflaterInputStream(src); - } - - objectSize = tempSize; - } - - public String getType() { - return objectType; - } - - public long getSize() { - return objectSize; - } - - public InputStream getInputStream() { - if (inflater == null) { - throw new IllegalStateException("Already closed."); - } - return inflater; - } - - public void close() throws IOException { - if (inflater != null) { - inflater.close(); - inflater = null; - } - } -} diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/WholePackedObjectLoader.java b/org.spearce.jgit/src/org/spearce/jgit/lib/WholePackedObjectLoader.java new file mode 100644 index 00000000..9e2d6ea3 --- /dev/null +++ b/org.spearce.jgit/src/org/spearce/jgit/lib/WholePackedObjectLoader.java @@ -0,0 +1,27 @@ +package org.spearce.jgit.lib; + +import java.io.IOException; +import java.util.zip.DataFormatException; + +import org.spearce.jgit.errors.CorruptObjectException; + +/** Reader for a non-delta (just deflated) object in a pack file. */ +class WholePackedObjectLoader extends PackedObjectLoader { + WholePackedObjectLoader(final PackFile pr, final long offset, + final String type, final int size) { + super(pr, offset); + objectType = type; + objectSize = size; + } + + public byte[] getBytes() throws IOException { + try { + return pack.decompress(dataOffset, objectSize); + } catch (DataFormatException dfe) { + final CorruptObjectException coe; + coe = new CorruptObjectException(getId(), "bad stream"); + coe.initCause(dfe); + throw coe; + } + } +} diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/WholePackedObjectReader.java b/org.spearce.jgit/src/org/spearce/jgit/lib/WholePackedObjectReader.java deleted file mode 100644 index 877d0128..00000000 --- a/org.spearce.jgit/src/org/spearce/jgit/lib/WholePackedObjectReader.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.spearce.jgit.lib; - -/** Reader for a non-delta (just deflated) object in a pack file. */ -class WholePackedObjectReader extends PackedObjectReader { - WholePackedObjectReader(final PackFile pr, final long offset, - final String type, final long size) { - super(pr, offset); - objectType = type; - objectSize = size; - } -} diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/WindowedFile.java b/org.spearce.jgit/src/org/spearce/jgit/lib/WindowedFile.java index 136321b0..3b189bdd 100644 --- a/org.spearce.jgit/src/org/spearce/jgit/lib/WindowedFile.java +++ b/org.spearce.jgit/src/org/spearce/jgit/lib/WindowedFile.java @@ -22,6 +22,8 @@ import java.io.IOException; import java.io.RandomAccessFile; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel.MapMode; +import java.util.zip.DataFormatException; +import java.util.zip.Inflater; /** * Read-only cached file access. @@ -224,6 +226,29 @@ public class WindowedFile { throw new EOFException(); } + public void readCompressed(final long position, final byte[] dstbuf) + throws IOException, DataFormatException { + final Inflater inf = new Inflater(false); + try { + readCompressed(position, dstbuf, inf); + } finally { + inf.end(); + } + } + + public void readCompressed(long pos, final byte[] dstbuf, final Inflater inf) + throws IOException, DataFormatException { + int dstoff = 0; + dstoff = cache.get(wp, (int) (pos >> szb)).inflate(((int) pos) & szm, + dstbuf, dstoff, inf); + pos >>= szb; + while (!inf.finished()) { + dstoff = cache.get(wp, (int) ++pos).inflate(0, dstbuf, dstoff, inf); + } + if (dstoff != dstbuf.length) + throw new EOFException(); + } + /** * Reads a 32 bit unsigned integer in network byte order. * diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/XInputStream.java b/org.spearce.jgit/src/org/spearce/jgit/lib/XInputStream.java deleted file mode 100644 index e6e572fb..00000000 --- a/org.spearce.jgit/src/org/spearce/jgit/lib/XInputStream.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright (C) 2006 Shawn Pearce - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License, version 2.1, as published by the Free Software Foundation. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - */ -package org.spearce.jgit.lib; - -import java.io.BufferedInputStream; -import java.io.EOFException; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.nio.channels.FileChannel; - -public class XInputStream extends BufferedInputStream { - private final byte[] intbuf = new byte[8]; - - private FileChannel fc; - - private long offset; - - public XInputStream(final FileInputStream s) { - super(s); - fc = s.getChannel(); - } - - public XInputStream(final InputStream s) { - super(s); - } - - public synchronized int read() throws IOException { - final int n = super.read(); - if (n >= 0) { - offset++; - } - return n; - } - - public synchronized int read(final byte[] b, final int off, final int len) - throws IOException { - final int n = super.read(b, off, len); - if (n >= 0) { - offset += n; - } - return n; - } - - public synchronized void reset() throws IOException { - final int p = pos; - super.reset(); - offset -= p - pos; - } - - public synchronized long skip(final long n) throws IOException { - // BufferedInputStream doesn't skip until its buffer is empty so we - // might need to invoke skip more than once until all underlying - // InputStreams have advanced by the amount requested. - // - long r = 0; - while (r < n) { - final long i = super.skip(n - r); - if (i <= 0) { - break; - } - r += i; - } - offset += r; - return r; - } - - public synchronized long position() throws IOException { - return offset; - } - - public synchronized void position(final long p) throws IOException { - if (p < offset) { - // Rewind target isn't in the buffer; we need to rewind the - // stream and clear the buffer. We can only do this if it has - // a FileChannel as otherwise we have no way to position it. - // - if (fc == null) { - throw new IOException("Stream is not reverse seekable."); - } - count = 0; - offset = p; - fc.position(p); - } else if (p > offset) { - // Fast-foward is simply a skip and most streams are skippable - // even - // if skipping would require consuming all data to actually - // perform - // the skip. Verify the skip took place because if it didn't - // then - // things didn't work as we had planned (the stream isn't - // seekable - // or we are trying to position past the end in an effor to - // create a - // hole, which we don't really support doing here). - // - skip(p - offset); - if (offset != p) { - throw new IOException("Stream is not forward seekable."); - } - } - } - - public synchronized byte[] readFully(final int len) throws IOException { - final byte[] buf = new byte[len]; - readFully(buf, 0, len); - return buf; - } - - public synchronized void readFully(final byte[] buf, int o, int len) - throws IOException { - int r; - while (len > 0 && (r = read(buf, o, len)) > 0) { - o += r; - len -= r; - } - if (len > 0) { - throw new EOFException(); - } - } - - public int readUInt8() throws IOException { - final int r = read(); - if (r < 0) { - throw new EOFException(); - } - return r; - } - - public long readUInt32() throws IOException { - readFully(intbuf, 0, 4); - return (intbuf[0] & 0xff) << 24 | (intbuf[1] & 0xff) << 16 - | (intbuf[2] & 0xff) << 8 | (intbuf[3] & 0xff); - } - - public synchronized void close() throws IOException { - fc = null; - super.close(); - } -} diff --git a/org.spearce.jgit/tst/org/spearce/jgit/lib/T0004_PackReader.java b/org.spearce.jgit/tst/org/spearce/jgit/lib/T0004_PackReader.java index e6ff3ce1..2d51e5d7 100644 --- a/org.spearce.jgit/tst/org/spearce/jgit/lib/T0004_PackReader.java +++ b/org.spearce.jgit/tst/org/spearce/jgit/lib/T0004_PackReader.java @@ -26,7 +26,7 @@ public class T0004_PackReader extends RepositoryTestCase { public void test003_lookupCompressedObject() throws IOException { final PackFile pr; final ObjectId id; - final PackedObjectReader or; + final PackedObjectLoader or; id = new ObjectId("902d5476fa249b7abc9d84c611577a81381f0327"); pr = new PackFile(db, TEST_PACK); @@ -36,23 +36,21 @@ public class T0004_PackReader extends RepositoryTestCase { assertEquals(Constants.TYPE_TREE, or.getType()); assertEquals(35, or.getSize()); assertEquals(7738, or.getDataOffset()); - or.close(); pr.close(); } public void test004_lookupDeltifiedObject() throws IOException { final ObjectId id; - final ObjectReader or; + final ObjectLoader or; id = new ObjectId("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259"); or = db.openObject(id); assertNotNull(or); - assertTrue(or instanceof PackedObjectReader); + assertTrue(or instanceof PackedObjectLoader); assertEquals(id, or.getId()); assertEquals(Constants.TYPE_BLOB, or.getType()); assertEquals(18009, or.getSize()); - assertEquals(537, ((PackedObjectReader) or).getDataOffset()); - or.close(); + assertEquals(537, ((PackedObjectLoader) or).getDataOffset()); } public void test005_todopack() throws IOException { diff --git a/org.spearce.jgit/tst/org/spearce/jgit/lib/XInputStream.java b/org.spearce.jgit/tst/org/spearce/jgit/lib/XInputStream.java new file mode 100644 index 00000000..cf8255f6 --- /dev/null +++ b/org.spearce.jgit/tst/org/spearce/jgit/lib/XInputStream.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2006 Shawn Pearce + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License, version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + */ +package org.spearce.jgit.lib; + +import java.io.BufferedInputStream; +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; + +class XInputStream extends BufferedInputStream { + private final byte[] intbuf = new byte[8]; + + public XInputStream(final InputStream s) { + super(s); + } + + public synchronized byte[] readFully(final int len) throws IOException { + final byte[] buf = new byte[len]; + readFully(buf, 0, len); + return buf; + } + + public synchronized void readFully(final byte[] buf, int o, int len) + throws IOException { + int r; + while (len > 0 && (r = read(buf, o, len)) > 0) { + o += r; + len -= r; + } + if (len > 0) { + throw new EOFException(); + } + } + + public int readUInt8() throws IOException { + final int r = read(); + if (r < 0) { + throw new EOFException(); + } + return r; + } + + public long readUInt32() throws IOException { + readFully(intbuf, 0, 4); + return (intbuf[0] & 0xff) << 24 | (intbuf[1] & 0xff) << 16 + | (intbuf[2] & 0xff) << 8 | (intbuf[3] & 0xff); + } +} -- 2.11.4.GIT