diff --git a/org.eclipse.jgit.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.test/META-INF/MANIFEST.MF index 4de17bdcb..dbdf0c9cd 100644 --- a/org.eclipse.jgit.test/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.test/META-INF/MANIFEST.MF @@ -7,7 +7,8 @@ Bundle-Localization: plugin Bundle-Vendor: %provider_name Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: J2SE-1.5 -Import-Package: org.eclipse.jgit.api;version="[2.4.0,2.5.0)", +Import-Package: javaewah;version="0.5.6", + org.eclipse.jgit.api;version="[2.4.0,2.5.0)", org.eclipse.jgit.api.errors;version="[2.4.0,2.5.0)", org.eclipse.jgit.awtui;version="[2.4.0,2.5.0)", org.eclipse.jgit.blame;version="[2.4.0,2.5.0)", diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/InflatingBitSetTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/InflatingBitSetTest.java new file mode 100644 index 000000000..811096e3d --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/InflatingBitSetTest.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2012, Google Inc. + * 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 static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import javaewah.EWAHCompressedBitmap; + +import org.junit.Test; + +public class InflatingBitSetTest { + + @Test + public void testMaybeContains() { + EWAHCompressedBitmap ecb = new EWAHCompressedBitmap(); + ecb.set(63); + ecb.set(64); + ecb.set(128); + + InflatingBitSet ibs = new InflatingBitSet(ecb); + assertTrue(ibs.maybeContains(0)); + assertFalse(ibs.contains(0)); // Advance + assertFalse(ibs.maybeContains(0)); + assertTrue(ibs.maybeContains(63)); + assertTrue(ibs.maybeContains(64)); + assertTrue(ibs.maybeContains(65)); + assertFalse(ibs.maybeContains(129)); + } + + @Test + public void testContainsMany() { + EWAHCompressedBitmap ecb = new EWAHCompressedBitmap(); + ecb.set(64); + ecb.set(65); + ecb.set(1024); + + InflatingBitSet ibs = new InflatingBitSet(ecb); + assertFalse(ibs.contains(0)); + assertTrue(ibs.contains(64)); + assertTrue(ibs.contains(65)); + assertFalse(ibs.contains(66)); + assertTrue(ibs.contains(1024)); + assertFalse(ibs.contains(1025)); + } + + @Test + public void testContainsOne() { + EWAHCompressedBitmap ecb = new EWAHCompressedBitmap(); + ecb.set(64); + + InflatingBitSet ibs = new InflatingBitSet(ecb); + assertTrue(ibs.contains(64)); + assertTrue(ibs.contains(64)); + assertFalse(ibs.contains(65)); + assertFalse(ibs.contains(63)); + } + + @Test + public void testContainsEmpty() { + InflatingBitSet ibs = new InflatingBitSet(new EWAHCompressedBitmap()); + assertFalse(ibs.maybeContains(0)); + assertFalse(ibs.contains(0)); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/StoredBitmapTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/StoredBitmapTest.java new file mode 100644 index 000000000..9170e6e3c --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/StoredBitmapTest.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2012, Google Inc. + * 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 static org.junit.Assert.*; + +import javaewah.EWAHCompressedBitmap; + +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.storage.file.BasePackBitmapIndex.StoredBitmap; +import org.junit.Test; + +public class StoredBitmapTest { + + @Test + public void testGetBitmapWithoutXor() { + EWAHCompressedBitmap b = bitmapOf(100); + StoredBitmap sb = newStoredBitmap(bitmapOf(100)); + assertEquals(b, sb.getBitmap()); + } + + @Test + public void testGetBitmapWithOneXor() { + StoredBitmap sb = newStoredBitmap(bitmapOf(100), bitmapOf(100, 101)); + assertEquals(bitmapOf(101), sb.getBitmap()); + } + + @Test + public void testGetBitmapWithThreeXor() { + StoredBitmap sb = newStoredBitmap( + bitmapOf(100), + bitmapOf(90, 101), + bitmapOf(100, 101), + bitmapOf(50)); + assertEquals(bitmapOf(50, 90), sb.getBitmap()); + assertEquals(bitmapOf(50, 90), sb.getBitmap()); + } + + private static final StoredBitmap newStoredBitmap( + EWAHCompressedBitmap... bitmaps) { + StoredBitmap sb = null; + for (EWAHCompressedBitmap bitmap : bitmaps) + sb = new StoredBitmap(ObjectId.zeroId(), bitmap, sb, 0); + return sb; + } + + private static final EWAHCompressedBitmap bitmapOf(int... bits) { + EWAHCompressedBitmap b = new EWAHCompressedBitmap(); + for (int bit : bits) + b.set(bit); + return b; + } +} diff --git a/org.eclipse.jgit/META-INF/MANIFEST.MF b/org.eclipse.jgit/META-INF/MANIFEST.MF index 30f2be1cf..4249377ad 100644 --- a/org.eclipse.jgit/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit/META-INF/MANIFEST.MF @@ -36,7 +36,8 @@ Export-Package: org.eclipse.jgit.api;version="2.4.0", Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: J2SE-1.5 Require-Bundle: com.jcraft.jsch;bundle-version="[0.1.37,0.2.0)" -Import-Package: javax.crypto, +Import-Package: javaewah;version="0.5.6", + javax.crypto, javax.net.ssl, org.xml.sax, org.xml.sax.helpers diff --git a/org.eclipse.jgit/pom.xml b/org.eclipse.jgit/pom.xml index a4167292d..b9775419b 100644 --- a/org.eclipse.jgit/pom.xml +++ b/org.eclipse.jgit/pom.xml @@ -73,6 +73,11 @@ com.jcraft jsch + + + com.googlecode.javaewah + JavaEWAH + diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties index cc0b76623..28e0a4cc6 100644 --- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties +++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties @@ -196,6 +196,7 @@ expectedBooleanStringValue=Expected boolean string value expectedCharacterEncodingGuesses=Expected {0} character encoding guesses expectedEOFReceived=expected EOF; received ''{0}'' instead expectedGot=expected ''{0}'', got ''{1}'' +expectedLessThanGot=expected less than ''{0}'', got ''{1}'' expectedPktLineWithService=expected pkt-line with '# service=-', got ''{0}'' expectedReceivedContentType=expected Content-Type {0}; received Content-Type {1} expectedReportForRefNotReceived={0}: expected report for ref {1} not received @@ -336,6 +337,7 @@ objectAtHasBadZlibStream=Object at {0} in {1} has bad zlib stream objectAtPathDoesNotHaveId=Object at path "{0}" does not have an id assigned. All object ids must be assigned prior to writing a tree. objectIsCorrupt=Object {0} is corrupt: {1} objectIsNotA=Object {0} is not a {1}. +objectNotFound=Object {0} not found. objectNotFoundIn=Object {0} not found in {1}. obtainingCommitsForCherryPick=Obtaining commits that need to be cherry-picked offsetWrittenDeltaBaseForObjectNotFoundInAPack=Offset-written delta base for object not found in a pack diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java index f8ea5ff93..388e0d325 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java @@ -258,6 +258,7 @@ public static JGitText get() { /***/ public String expectedCharacterEncodingGuesses; /***/ public String expectedEOFReceived; /***/ public String expectedGot; + /***/ public String expectedLessThanGot; /***/ public String expectedPktLineWithService; /***/ public String expectedReceivedContentType; /***/ public String expectedReportForRefNotReceived; @@ -398,6 +399,7 @@ public static JGitText get() { /***/ public String objectAtPathDoesNotHaveId; /***/ public String objectIsCorrupt; /***/ public String objectIsNotA; + /***/ public String objectNotFound; /***/ public String objectNotFoundIn; /***/ public String obtainingCommitsForCherryPick; /***/ public String offsetWrittenDeltaBaseForObjectNotFoundInAPack; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BitmapIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BitmapIndex.java new file mode 100644 index 000000000..0905980dc --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BitmapIndex.java @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2012, Google Inc. + * 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.lib; + +import java.util.Iterator; + +import org.eclipse.jgit.storage.file.PackBitmapIndex; + +/** A compressed bitmap representation of the entire object graph. */ +public interface BitmapIndex { + /** + * Get the bitmap for the id. The returned bitmap is immutable and the + * bitwise operations return the result of the operation in a new Bitmap. + * + * @param objectId + * the object ID + * @return the Bitmap for the objectId or null, if one does not exist. + */ + Bitmap getBitmap(AnyObjectId objectId); + + /** @return a new {@code BitmapBuilder} based on the values in the index. */ + BitmapBuilder newBitmapBuilder(); + + /** + * A bitmap representation of ObjectIds that can be iterated to return the + * underlying {@code ObjectId}s or operated on with other {@code Bitmap}s. + */ + public interface Bitmap extends Iterable { + /** + * Bitwise-OR the current bitmap with the value from the other + * bitmap. + * + * @param other + * the other bitmap + * @return a bitmap that is the bitwise-OR. + */ + Bitmap or(Bitmap other); + + /** + * Bitwise-AND-NOT the current bitmap with the value from the other + * bitmap. + * + * @param other + * the other bitmap + * @return a bitmap that is the bitwise-AND-NOT. + */ + Bitmap andNot(Bitmap other); + + /** + * Bitwise-XOR the current bitmap with the value from the other + * bitmap. + * + * @param other + * the other bitmap + * @return a bitmap that is the bitwise-XOR. + */ + Bitmap xor(Bitmap other); + + /** + * Returns an iterator over a set of elements of type BitmapObject. The + * BitmapObject instance is reused across calls to + * {@link Iterator#next()} for performance reasons. + * + * @return an Iterator. + */ + Iterator iterator(); + } + + /** + * A builder for a bitmap. The bitwise operations update the builder and + * return a reference to the current builder. + */ + public interface BitmapBuilder extends Bitmap { + /** + * Adds the id and the existing bitmap for the id, if one exists, to the + * bitmap. + * + * @param objectId + * the object ID + * @param type + * the Git object type. See {@link Constants}. + * @return true if the value was not contained or able to be loaded. + */ + boolean add(AnyObjectId objectId, int type); + + /** + * Whether the bitmap has the id set. + * + * @param objectId + * the object ID + * @return whether the bitmap currently contains the object ID + */ + boolean contains(AnyObjectId objectId); + + /** + * Remove the id from the bitmap. + * + * @param objectId + * the object ID + */ + void remove(AnyObjectId objectId); + + /** + * Bitwise-OR the current bitmap with the value from the other bitmap. + * + * @param other + * the other bitmap + * @return the current builder. + */ + BitmapBuilder or(Bitmap other); + + /** + * Bitwise-AND-NOT the current bitmap with the value from the other + * bitmap. + * + * @param other + * the other bitmap + * @return the current builder. + */ + BitmapBuilder andNot(Bitmap other); + + /** + * Bitwise-XOR the current bitmap with the value from the other bitmap. + * + * @param other + * the other bitmap + * @return the current builder. + */ + BitmapBuilder xor(Bitmap other); + + /** @return the fully built immutable bitmap */ + Bitmap build(); + + /** + * Determines if the entire bitmap index is contained in the bitmap. If + * it is, the matching bits are removed from the bitmap and true is + * returned. If the bitmap index is null, false is returned. + * + * @param bitmapIndex + * the bitmap index to check if it is completely contained + * inside of the current bitmap. + * @return {@code true} if the bitmap index was a complete match. + */ + boolean removeAllOrNone(PackBitmapIndex bitmapIndex); + + /** @return the number of elements in the bitmap. */ + int cardinality(); + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BitmapObject.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BitmapObject.java new file mode 100644 index 000000000..ba7ed648d --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BitmapObject.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2012, Google Inc. + * 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.lib; + +/** Base object type accessed during bitmap expansion. */ +public abstract class BitmapObject { + /** + * Get Git object type. See {@link Constants}. + * + * @return object type + */ + public abstract int getType(); + + /** + * Get the name of this object. + * + * @return unique hash of this object. + */ + public abstract ObjectId getObjectId(); +} \ No newline at end of file diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java index ee4f3d9e0..68fee612f 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java @@ -436,6 +436,17 @@ public void walkAdviceEnd() { // Do nothing by default, most readers don't want or need advice. } + /** + * An index that can be used to speed up ObjectWalks. + * + * @return the index or null if one does not exist. + * @throws IOException + * when the index fails to load + */ + public BitmapIndex getBitmapIndex() throws IOException { + return null; + } + /** * Release any resources used by this reader. *

diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsPackDescription.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsPackDescription.java index a793d3c82..7aa3b5093 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsPackDescription.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsPackDescription.java @@ -69,7 +69,7 @@ public class DfsPackDescription implements Comparable { private long lastModified; - private Map sizeMap; + private final Map sizeMap; private long objectCount; @@ -79,6 +79,8 @@ public class DfsPackDescription implements Comparable { private PackWriter.Statistics stats; + private int extensions; + /** * Initialize a description by pack name and repository. *

@@ -98,7 +100,7 @@ public DfsPackDescription(DfsRepositoryDescription repoDesc, String name) { this.repoDesc = repoDesc; int dot = name.lastIndexOf('.'); this.packName = (dot < 0) ? name : name.substring(0, dot); - this.sizeMap = new HashMap(5); + this.sizeMap = new HashMap(PackExt.values().length * 2); } /** @return description of the repository. */ @@ -106,11 +108,30 @@ public DfsRepositoryDescription getRepositoryDescription() { return repoDesc; } + /** + * Adds the pack file extension to the known list. + * + * @param ext + * the file extension + */ + public void addFileExt(PackExt ext) { + extensions |= ext.getBit(); + } + + /** + * @param ext + * the file extension + * @return whether the pack file extensions is known to exist. + */ + public boolean hasFileExt(PackExt ext) { + return (extensions & ext.getBit()) != 0; + } + /** * @param ext * the file extension * @return name of the file. - * */ + */ public String getFileName(PackExt ext) { return packName + '.' + ext.getExtension(); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsPackFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsPackFile.java index ca58f8df2..735e36670 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsPackFile.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsPackFile.java @@ -45,6 +45,7 @@ package org.eclipse.jgit.storage.dfs; +import static org.eclipse.jgit.storage.pack.PackExt.BITMAP_INDEX; import static org.eclipse.jgit.storage.pack.PackExt.PACK; import static org.eclipse.jgit.storage.pack.PackExt.INDEX; @@ -71,9 +72,11 @@ import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectLoader; import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.storage.file.PackBitmapIndex; import org.eclipse.jgit.storage.file.PackIndex; import org.eclipse.jgit.storage.file.PackReverseIndex; import org.eclipse.jgit.storage.pack.BinaryDelta; +import org.eclipse.jgit.storage.pack.PackExt; import org.eclipse.jgit.storage.pack.PackOutputStream; import org.eclipse.jgit.storage.pack.StoredObjectRepresentation; import org.eclipse.jgit.util.IO; @@ -99,6 +102,9 @@ public final class DfsPackFile { /** Offset used to cache {@link #reverseIndex}. See {@link #POS_INDEX}. */ private static final long POS_REVERSE_INDEX = -2; + /** Offset used to cache {@link #bitmapIndex}. See {@link #POS_INDEX}. */ + private static final long POS_BITMAP_INDEX = -3; + /** Cache that owns this pack file and its data. */ private final DfsBlockCache cache; @@ -141,6 +147,9 @@ public final class DfsPackFile { /** Reverse version of {@link #index} mapping position to {@link ObjectId}. */ private volatile DfsBlockCache.Ref reverseIndex; + /** Index of compressed bitmap mapping entire object graph. */ + private volatile DfsBlockCache.Ref bitmapIndex; + /** * Objects we have tried to read, and discovered to be corrupt. *

@@ -267,6 +276,64 @@ else if (bs <= 0) } } + PackBitmapIndex getPackBitmapIndex(DfsReader ctx) throws IOException { + DfsBlockCache.Ref idxref = bitmapIndex; + if (idxref != null) { + PackBitmapIndex idx = idxref.get(); + if (idx != null) + return idx; + } + + if (!packDesc.hasFileExt(PackExt.BITMAP_INDEX)) + return null; + + synchronized (initLock) { + idxref = bitmapIndex; + if (idxref != null) { + PackBitmapIndex idx = idxref.get(); + if (idx != null) + return idx; + } + + long size; + PackBitmapIndex idx; + try { + ReadableChannel rc = ctx.db.openFile(packDesc, BITMAP_INDEX); + try { + InputStream in = Channels.newInputStream(rc); + int wantSize = 8192; + int bs = rc.blockSize(); + if (0 < bs && bs < wantSize) + bs = (wantSize / bs) * bs; + else if (bs <= 0) + bs = wantSize; + in = new BufferedInputStream(in, bs); + idx = PackBitmapIndex.read( + in, idx(ctx), getReverseIdx(ctx)); + } finally { + size = rc.position(); + rc.close(); + } + } catch (EOFException e) { + IOException e2 = new IOException(MessageFormat.format( + DfsText.get().shortReadOfIndex, + packDesc.getFileName(BITMAP_INDEX))); + e2.initCause(e); + throw e2; + } catch (IOException e) { + IOException e2 = new IOException(MessageFormat.format( + DfsText.get().cannotReadIndex, + packDesc.getFileName(BITMAP_INDEX))); + e2.initCause(e); + throw e2; + } + + bitmapIndex = cache.put(key, POS_BITMAP_INDEX, + (int) Math.min(size, Integer.MAX_VALUE), idx); + return idx; + } + } + private PackReverseIndex getReverseIdx(DfsReader ctx) throws IOException { DfsBlockCache.Ref revref = reverseIndex; if (revref != null) { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsReader.java index e22e13eaf..455f1bbd9 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsReader.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsReader.java @@ -75,6 +75,7 @@ import org.eclipse.jgit.lib.AnyObjectId; import org.eclipse.jgit.lib.AsyncObjectLoaderQueue; import org.eclipse.jgit.lib.AsyncObjectSizeQueue; +import org.eclipse.jgit.lib.BitmapIndex; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.InflaterCache; import org.eclipse.jgit.lib.ObjectId; @@ -84,6 +85,8 @@ import org.eclipse.jgit.revwalk.ObjectWalk; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevWalk; +import org.eclipse.jgit.storage.file.BitmapIndexImpl; +import org.eclipse.jgit.storage.file.PackBitmapIndex; import org.eclipse.jgit.storage.pack.CachedPack; import org.eclipse.jgit.storage.pack.ObjectReuseAsIs; import org.eclipse.jgit.storage.pack.ObjectToPack; @@ -139,6 +142,16 @@ public ObjectReader newReader() { return new DfsReader(db); } + @Override + public BitmapIndex getBitmapIndex() throws IOException { + for (DfsPackFile pack : db.getPacks()) { + PackBitmapIndex bitmapIndex = pack.getPackBitmapIndex(this); + if (bitmapIndex != null) + return new BitmapIndexImpl(bitmapIndex); + } + return null; + } + @Override public Collection resolve(AbbreviatedObjectId id) throws IOException { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/BasePackBitmapIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/BasePackBitmapIndex.java new file mode 100644 index 000000000..eb5b49fdd --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/BasePackBitmapIndex.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2012, Google Inc. + * 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 javaewah.EWAHCompressedBitmap; + +import org.eclipse.jgit.lib.AnyObjectId; +import org.eclipse.jgit.lib.ObjectIdOwnerMap; + +/** + * Base implementation of the PackBitmapIndex. + */ +abstract class BasePackBitmapIndex extends PackBitmapIndex { + private final ObjectIdOwnerMap bitmaps; + + BasePackBitmapIndex(ObjectIdOwnerMap bitmaps) { + this.bitmaps = bitmaps; + } + + public EWAHCompressedBitmap getBitmap(AnyObjectId objectId) { + StoredBitmap sb = bitmaps.get(objectId); + return sb != null ? sb.getBitmap() : null; + } + + ObjectIdOwnerMap getBitmaps() { + return bitmaps; + } + + /** + * Data representation of the bitmap entry restored from a pack index. The + * commit of the bitmap is the map key. + */ + static final class StoredBitmap extends ObjectIdOwnerMap.Entry { + private volatile Object bitmapContainer; + private final int flags; + + StoredBitmap(AnyObjectId objectId, EWAHCompressedBitmap bitmap, + StoredBitmap xorBitmap, int flags) { + super(objectId); + this.bitmapContainer = xorBitmap == null + ? bitmap + : new XorCompressedBitmap(bitmap, xorBitmap); + this.flags = flags; + } + + /** + * Computes and returns the full bitmap. + * + * @return the full bitmap + */ + EWAHCompressedBitmap getBitmap() { + // Fast path to immediately return the expanded result. + Object r = bitmapContainer; + if (r instanceof EWAHCompressedBitmap) + return (EWAHCompressedBitmap) r; + + // Expand the bitmap and cache the result. + XorCompressedBitmap xb = (XorCompressedBitmap) r; + EWAHCompressedBitmap out = xb.bitmap; + for (;;) { + r = xb.xorBitmap.bitmapContainer; + if (r instanceof EWAHCompressedBitmap) { + out = out.xor((EWAHCompressedBitmap) r); + bitmapContainer = out; + return out; + } + xb = (XorCompressedBitmap) r; + out = out.xor(xb.bitmap); + } + } + + /** @return the flags associated with the bitmap */ + int getFlags() { + return flags; + } + } + + private static final class XorCompressedBitmap { + final EWAHCompressedBitmap bitmap; + final StoredBitmap xorBitmap; + + XorCompressedBitmap(EWAHCompressedBitmap b, StoredBitmap xb) { + bitmap = b; + xorBitmap = xb; + } + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/BitSet.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/BitSet.java new file mode 100644 index 000000000..c79eda20a --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/BitSet.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2012, Google Inc. + * 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.util.Arrays; + +import javaewah.EWAHCompressedBitmap; + +/** + * A random access BitSet to supports efficient conversions to + * EWAHCompressedBitmap. + */ +final class BitSet { + + private long[] words; + + BitSet(int initialCapacity) { + words = new long[block(initialCapacity) + 1]; + } + + final void clear() { + Arrays.fill(words, 0); + } + + final void set(int position) { + int block = block(position); + if (block >= words.length) { + long[] buf = new long[2 * block(position)]; + System.arraycopy(words, 0, buf, 0, words.length); + words = buf; + } + words[block] |= mask(position); + } + + final void clear(int position) { + int block = block(position); + if (block < words.length) + words[block] &= ~mask(position); + } + + final boolean get(int position) { + int block = block(position); + return block < words.length && (words[block] & mask(position)) != 0; + } + + final EWAHCompressedBitmap toEWAHCompressedBitmap() { + EWAHCompressedBitmap compressed = new EWAHCompressedBitmap( + words.length); + int runningEmptyWords = 0; + long lastNonEmptyWord = 0; + for (long word : words) { + if (word == 0) { + runningEmptyWords++; + continue; + } + + if (lastNonEmptyWord != 0) + compressed.add(lastNonEmptyWord); + + if (runningEmptyWords > 0) { + compressed.addStreamOfEmptyWords(false, runningEmptyWords); + runningEmptyWords = 0; + } + + lastNonEmptyWord = word; + } + int bitsThatMatter = 64 - Long.numberOfLeadingZeros(lastNonEmptyWord); + if (bitsThatMatter > 0) + compressed.add(lastNonEmptyWord, bitsThatMatter); + return compressed; + } + + private static final int block(int position) { + return position >> 6; + } + + private static final long mask(int position) { + return 1L << position; + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/BitmapIndexImpl.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/BitmapIndexImpl.java new file mode 100644 index 000000000..0b97d2b4d --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/BitmapIndexImpl.java @@ -0,0 +1,484 @@ +/* + * Copyright (C) 2012, Google Inc. + * 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.text.MessageFormat; +import java.util.Iterator; +import java.util.NoSuchElementException; + +import javaewah.EWAHCompressedBitmap; +import javaewah.IntIterator; + +import org.eclipse.jgit.internal.JGitText; +import org.eclipse.jgit.lib.AnyObjectId; +import org.eclipse.jgit.lib.BitmapIndex; +import org.eclipse.jgit.lib.BitmapObject; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.ObjectIdOwnerMap; +import org.eclipse.jgit.util.BlockList; + +/** A compressed bitmap representation of the entire object graph. */ +public class BitmapIndexImpl implements BitmapIndex { + private static final int EXTRA_BITS = 10 * 1024; + + private final PackBitmapIndex packIndex; + + private final MutableBitmapIndex mutableIndex; + + private final int indexObjectCount; + + /** + * Creates a BitmapIndex that is back by Compressed bitmaps. + * + * @param packIndex + * the bitmap index for the pack. + */ + public BitmapIndexImpl(PackBitmapIndex packIndex) { + this.packIndex = packIndex; + mutableIndex = new MutableBitmapIndex(); + indexObjectCount = packIndex.getObjectCount(); + } + + PackBitmapIndex getPackBitmapIndex() { + return packIndex; + } + + public CompressedBitmap getBitmap(AnyObjectId objectId) { + EWAHCompressedBitmap compressed = packIndex.getBitmap(objectId); + if (compressed == null) + return null; + return new CompressedBitmap(compressed); + } + + public CompressedBitmapBuilder newBitmapBuilder() { + return new CompressedBitmapBuilder(); + } + + private int findPosition(AnyObjectId objectId) { + int position = packIndex.findPosition(objectId); + if (position < 0) { + position = mutableIndex.findPosition(objectId); + if (position >= 0) + position += indexObjectCount; + } + return position; + } + + private int addObject(AnyObjectId objectId, int type) { + int position = findPosition(objectId); + if (position < 0) { + position = mutableIndex.addObject(objectId, type); + position += indexObjectCount; + } + return position; + } + + private static final class ComboBitset { + private InflatingBitSet inflatingBitmap; + + private BitSet toAdd; + + private BitSet toRemove; + + private ComboBitset() { + this(new EWAHCompressedBitmap()); + } + + private ComboBitset(EWAHCompressedBitmap bitmap) { + this.inflatingBitmap = new InflatingBitSet(bitmap); + } + + EWAHCompressedBitmap combine() { + EWAHCompressedBitmap toAddCompressed = null; + if (toAdd != null) { + toAddCompressed = toAdd.toEWAHCompressedBitmap(); + toAdd = null; + } + + EWAHCompressedBitmap toRemoveCompressed = null; + if (toRemove != null) { + toRemoveCompressed = toRemove.toEWAHCompressedBitmap(); + toRemove = null; + } + + if (toAddCompressed != null) + or(toAddCompressed); + if (toRemoveCompressed != null) + andNot(toRemoveCompressed); + return inflatingBitmap.getBitmap(); + } + + void or(EWAHCompressedBitmap inbits) { + if (toRemove != null) + combine(); + inflatingBitmap = inflatingBitmap.or(inbits); + } + + void andNot(EWAHCompressedBitmap inbits) { + if (toAdd != null || toRemove != null) + combine(); + inflatingBitmap = inflatingBitmap.andNot(inbits); + } + + void xor(EWAHCompressedBitmap inbits) { + if (toAdd != null || toRemove != null) + combine(); + inflatingBitmap = inflatingBitmap.xor(inbits); + } + + boolean contains(int position) { + if (toRemove != null && toRemove.get(position)) + return false; + if (toAdd != null && toAdd.get(position)) + return true; + return inflatingBitmap.contains(position); + } + + void remove(int position) { + if (toAdd != null) + toAdd.clear(position); + + if (inflatingBitmap.maybeContains(position)) { + if (toRemove == null) + toRemove = new BitSet(position + EXTRA_BITS); + toRemove.set(position); + } + } + + void set(int position) { + if (toRemove != null) + toRemove.clear(position); + + if (toAdd == null) + toAdd = new BitSet(position + EXTRA_BITS); + toAdd.set(position); + } + } + + private final class CompressedBitmapBuilder implements BitmapBuilder { + private ComboBitset bitset = new ComboBitset(); + + public boolean add(AnyObjectId objectId, int type) { + int position = addObject(objectId, type); + if (bitset.contains(position)) + return false; + + Bitmap entry = getBitmap(objectId); + if (entry != null) { + or(entry); + return false; + } + + bitset.set(position); + return true; + } + + public boolean contains(AnyObjectId objectId) { + int position = findPosition(objectId); + return 0 <= position && bitset.contains(position); + } + + public void remove(AnyObjectId objectId) { + int position = findPosition(objectId); + if (0 <= position) + bitset.remove(position); + } + + public CompressedBitmapBuilder or(Bitmap other) { + if (isSameCompressedBitmap(other)) { + bitset.or(((CompressedBitmap) other).bitmap); + } else if (isSameCompressedBitmapBuilder(other)) { + CompressedBitmapBuilder b = (CompressedBitmapBuilder) other; + bitset.or(b.bitset.combine()); + } else { + throw new IllegalArgumentException(); + } + return this; + } + + public CompressedBitmapBuilder andNot(Bitmap other) { + if (isSameCompressedBitmap(other)) { + bitset.andNot(((CompressedBitmap) other).bitmap); + } else if (isSameCompressedBitmapBuilder(other)) { + CompressedBitmapBuilder b = (CompressedBitmapBuilder) other; + bitset.andNot(b.bitset.combine()); + } else { + throw new IllegalArgumentException(); + } + return this; + } + + public CompressedBitmapBuilder xor(Bitmap other) { + if (isSameCompressedBitmap(other)) { + bitset.xor(((CompressedBitmap) other).bitmap); + } else if (isSameCompressedBitmapBuilder(other)) { + CompressedBitmapBuilder b = (CompressedBitmapBuilder) other; + bitset.xor(b.bitset.combine()); + } else { + throw new IllegalArgumentException(); + } + return this; + } + + /** @return the fully built immutable bitmap */ + public CompressedBitmap build() { + return new CompressedBitmap(bitset.combine()); + } + + public Iterator iterator() { + return build().iterator(); + } + + public int cardinality() { + return bitset.combine().cardinality(); + } + + public boolean removeAllOrNone(PackBitmapIndex index) { + if (!packIndex.equals(index)) + return false; + + EWAHCompressedBitmap curr = bitset.combine() + .xor(ones(indexObjectCount)); + + IntIterator ii = curr.intIterator(); + if (ii.hasNext() && ii.next() < indexObjectCount) + return false; + bitset = new ComboBitset(curr); + return true; + } + + private BitmapIndexImpl getBitmapIndex() { + return BitmapIndexImpl.this; + } + } + + final class CompressedBitmap implements Bitmap { + private final EWAHCompressedBitmap bitmap; + + private CompressedBitmap(EWAHCompressedBitmap bitmap) { + this.bitmap = bitmap; + } + + public CompressedBitmap or(Bitmap other) { + return new CompressedBitmap(bitmap.or(bitmapOf(other))); + } + + public CompressedBitmap andNot(Bitmap other) { + return new CompressedBitmap(bitmap.andNot(bitmapOf(other))); + } + + public CompressedBitmap xor(Bitmap other) { + return new CompressedBitmap(bitmap.xor(bitmapOf(other))); + } + + private EWAHCompressedBitmap bitmapOf(Bitmap other) { + if (isSameCompressedBitmap(other)) + return ((CompressedBitmap) other).bitmap; + if (isSameCompressedBitmapBuilder(other)) + return ((CompressedBitmapBuilder) other).build().bitmap; + CompressedBitmapBuilder builder = newBitmapBuilder(); + builder.or(other); + return builder.build().bitmap; + } + + private final IntIterator ofObjectType(int type) { + return packIndex.ofObjectType(bitmap, type).intIterator(); + } + + public Iterator iterator() { + final IntIterator dynamic = bitmap.andNot(ones(indexObjectCount)) + .intIterator(); + final IntIterator commits = ofObjectType(Constants.OBJ_COMMIT); + final IntIterator trees = ofObjectType(Constants.OBJ_TREE); + final IntIterator blobs = ofObjectType(Constants.OBJ_BLOB); + final IntIterator tags = ofObjectType(Constants.OBJ_TAG); + return new Iterator() { + private final BitmapObjectImpl out = new BitmapObjectImpl(); + private int type; + private IntIterator cached = dynamic; + + public boolean hasNext() { + if (!cached.hasNext()) { + if (commits.hasNext()) { + type = Constants.OBJ_COMMIT; + cached = commits; + } else if (trees.hasNext()) { + type = Constants.OBJ_TREE; + cached = trees; + } else if (blobs.hasNext()) { + type = Constants.OBJ_BLOB; + cached = blobs; + } else if (tags.hasNext()) { + type = Constants.OBJ_TAG; + cached = tags; + } else { + return false; + } + } + return true; + } + + public BitmapObject next() { + if (!hasNext()) + throw new NoSuchElementException(); + + int position = cached.next(); + if (position < indexObjectCount) { + out.type = type; + out.objectId = packIndex.getObject(position); + } else { + position -= indexObjectCount; + MutableEntry entry = mutableIndex.getObject(position); + out.type = entry.type; + out.objectId = entry; + } + return out; + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + EWAHCompressedBitmap getEwahCompressedBitmap() { + return bitmap; + } + + private BitmapIndexImpl getPackBitmapIndex() { + return BitmapIndexImpl.this; + } + } + + private static final class MutableBitmapIndex { + private final ObjectIdOwnerMap + revMap = new ObjectIdOwnerMap(); + + private final BlockList + revList = new BlockList(); + + int findPosition(AnyObjectId objectId) { + MutableEntry entry = revMap.get(objectId); + if (entry == null) + return -1; + return entry.position; + } + + MutableEntry getObject(int position) { + try { + MutableEntry entry = revList.get(position); + if (entry == null) + throw new IllegalArgumentException(MessageFormat.format( + JGitText.get().objectNotFound, + String.valueOf(position))); + return entry; + } catch (IndexOutOfBoundsException ex) { + throw new IllegalArgumentException(ex); + } + } + + int addObject(AnyObjectId objectId, int type) { + MutableEntry entry = new MutableEntry( + objectId, type, revList.size()); + revList.add(entry); + revMap.add(entry); + return entry.position; + } + } + + private static final class MutableEntry extends ObjectIdOwnerMap.Entry { + private final int type; + + private final int position; + + MutableEntry(AnyObjectId objectId, int type, int position) { + super(objectId); + this.type = type; + this.position = position; + } + } + + private static final class BitmapObjectImpl extends BitmapObject { + private ObjectId objectId; + + private int type; + + @Override + public ObjectId getObjectId() { + return objectId; + } + + @Override + public int getType() { + return type; + } + } + + private boolean isSameCompressedBitmap(Bitmap other) { + if (other instanceof CompressedBitmap) { + CompressedBitmap b = (CompressedBitmap) other; + return this == b.getPackBitmapIndex(); + } + return false; + } + + private boolean isSameCompressedBitmapBuilder(Bitmap other) { + if (other instanceof CompressedBitmapBuilder) { + CompressedBitmapBuilder b = (CompressedBitmapBuilder) other; + return this == b.getBitmapIndex(); + } + return false; + } + + private static final EWAHCompressedBitmap ones(int sizeInBits) { + EWAHCompressedBitmap mask = new EWAHCompressedBitmap(); + mask.addStreamOfEmptyWords( + true, sizeInBits / EWAHCompressedBitmap.wordinbits); + int remaining = sizeInBits % EWAHCompressedBitmap.wordinbits; + if (remaining > 0) + mask.add((1L << remaining) - 1, remaining); + return mask; + } +} 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 0bbb3ffe9..939b9cf3c 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 @@ -262,6 +262,11 @@ void selectObjectRepresentation(PackWriter packer, ObjectToPack otp, wrapped.selectObjectRepresentation(packer, otp, curs); } + @Override + Collection getPacks() { + return wrapped.getPacks(); + } + private static class UnpackedObjectId extends ObjectIdOwnerMap.Entry { UnpackedObjectId(AnyObjectId id) { super(id); 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 6c8c1f6af..ed0577f80 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 @@ -292,6 +292,8 @@ abstract InsertLooseObjectResult insertUnpackedObject(File tmp, abstract FileObjectDatabase newCachedFileObjectDatabase(); + abstract Collection getPacks(); + static class AlternateHandle { final FileObjectDatabase db; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/InflatingBitSet.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/InflatingBitSet.java new file mode 100644 index 000000000..32b33b16c --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/InflatingBitSet.java @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2012, Google Inc. + * 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 javaewah.EWAHCompressedBitmap; +import javaewah.IntIterator; + +/** + * A wrapper around the EWAHCompressedBitmap optimized for the contains + * operation. + */ +final class InflatingBitSet { + private static final long[] EMPTY = new long[0]; + + private final EWAHCompressedBitmap bitmap; + + private IntIterator iterator; + + private long[] inflated; + + private int nextPosition = -1; + + private final int sizeInBits; + + InflatingBitSet(EWAHCompressedBitmap bitmap) { + this(bitmap, EMPTY); + } + + private InflatingBitSet(EWAHCompressedBitmap orBitmap, long[] inflated) { + this.bitmap = orBitmap; + this.inflated = inflated; + this.sizeInBits = bitmap.sizeInBits(); + } + + final boolean maybeContains(int position) { + if (get(position)) + return true; + return nextPosition <= position && position < sizeInBits; + } + + final boolean contains(int position) { + if (get(position)) + return true; + if (position <= nextPosition || position >= sizeInBits) + return position == nextPosition; + + if (iterator == null) { + iterator = bitmap.intIterator(); + if (iterator.hasNext()) + nextPosition = iterator.next(); + else + return false; + } else if (!iterator.hasNext()) + return false; + + int positionBlock = block(position); + if (positionBlock >= inflated.length) { + long[] tmp = new long[block(sizeInBits) + 1]; + System.arraycopy(inflated, 0, tmp, 0, inflated.length); + inflated = tmp; + } + + int block = block(nextPosition); + long word = mask(nextPosition); + int end = Math.max(nextPosition, position) | 63; + while (iterator.hasNext()) { + nextPosition = iterator.next(); + if (end < nextPosition) + break; + + int b = block(nextPosition); + long m = mask(nextPosition); + if (block == b) { + word |= m; + } else { + inflated[block] = word; + block = b; + word = m; + } + } + inflated[block] = word; + return block == positionBlock && (word & mask(position)) != 0; + } + + private final boolean get(int position) { + int b = block(position); + return b < inflated.length && (inflated[b] & mask(position)) != 0; + } + + private static final int block(int position) { + return position >> 6; + } + + private static final long mask(int position) { + return 1L << position; + } + + private final boolean isEmpty() { + return sizeInBits == 0; + } + + final InflatingBitSet or(EWAHCompressedBitmap other) { + if (other.sizeInBits() == 0) + return this; + return new InflatingBitSet(bitmap.or(other), inflated); + } + + final InflatingBitSet andNot(EWAHCompressedBitmap other) { + if (isEmpty()) + return this; + return new InflatingBitSet(bitmap.andNot(other)); + } + + final InflatingBitSet xor(EWAHCompressedBitmap other) { + if (isEmpty()) { + if (other.sizeInBits() == 0) + return this; + return new InflatingBitSet(other); + } + return new InflatingBitSet(bitmap.xor(other)); + } + + final EWAHCompressedBitmap getBitmap() { + return bitmap; + } +} \ No newline at end of file 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 7574f2aa8..99ceeb74b 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 @@ -241,6 +241,7 @@ public File fileFor(final AnyObjectId objectId) { * containing objects referenced by commits further back in the * history of the repository. */ + @Override public Collection getPacks() { PackList list = packList.get(); if (list == NO_PACKS) diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackBitmapIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackBitmapIndex.java new file mode 100644 index 000000000..525018e1f --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackBitmapIndex.java @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2012, Google Inc. + * 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.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.text.MessageFormat; + +import javaewah.EWAHCompressedBitmap; + +import org.eclipse.jgit.errors.CorruptObjectException; +import org.eclipse.jgit.internal.JGitText; +import org.eclipse.jgit.lib.AnyObjectId; +import org.eclipse.jgit.lib.ObjectId; + +/** + * Logical representation of the bitmap data stored in the pack index. + * {@link ObjectId}s are encoded as a single integer in the range [0, + * {@link #getObjectCount()}). Compressed bitmaps are available at certain + * {@code ObjectId}s, which represent all of the objects reachable from that + * {@code ObjectId} (include the {@code ObjectId} itself). The meaning of the + * positions in the bitmaps can be decoded using {@link #getObject(int)} and + * {@link #ofObjectType(EWAHCompressedBitmap, int)}. Furthermore, + * {@link #findPosition(AnyObjectId)} can be used to build other bitmaps that a + * compatible with the encoded bitmaps available from the index. + */ +public abstract class PackBitmapIndex { + /** Flag bit denoting the bitmap should be reused during index creation. */ + public static final int FLAG_REUSE = 1; + + /** + * Read an existing pack bitmap index file from a buffered stream. + *

+ * The format of the file will be automatically detected and a proper access + * implementation for that format will be constructed and returned to the + * caller. The file may or may not be held open by the returned instance. + * + * @param idxFile + * existing pack .bitmap to read. + * @param packIndex + * the pack index for the corresponding pack file. + * @param reverseIndex + * the pack reverse index for the corresponding pack file. + * @return a copy of the index in-memory. + * @throws IOException + * the stream cannot be read. + * @throws CorruptObjectException + * the stream does not contain a valid pack bitmap index. + */ + public static PackBitmapIndex open( + File idxFile, PackIndex packIndex, PackReverseIndex reverseIndex) + throws IOException { + final FileInputStream fd = new FileInputStream(idxFile); + try { + return read(fd, packIndex, reverseIndex); + } catch (IOException ioe) { + final String path = idxFile.getAbsolutePath(); + final IOException err; + err = new IOException(MessageFormat.format( + JGitText.get().unreadablePackIndex, path)); + err.initCause(ioe); + throw err; + } finally { + try { + fd.close(); + } catch (IOException err2) { + // ignore + } + } + } + + /** + * Read an existing pack bitmap index file from a buffered stream. + *

+ * The format of the file will be automatically detected and a proper access + * implementation for that format will be constructed and returned to the + * caller. The file may or may not be held open by the returned instance. + * + * @param fd + * stream to read the bitmap index file from. The stream must be + * buffered as some small IOs are performed against the stream. + * The caller is responsible for closing the stream. + * @param packIndex + * the pack index for the corresponding pack file. + * @param reverseIndex + * the pack reverse index for the corresponding pack file. + * @return a copy of the index in-memory. + * @throws IOException + * the stream cannot be read. + * @throws CorruptObjectException + * the stream does not contain a valid pack bitmap index. + */ + public static PackBitmapIndex read( + InputStream fd, PackIndex packIndex, PackReverseIndex reverseIndex) + throws IOException { + return new PackBitmapIndexV1(fd, packIndex, reverseIndex); + } + + /** Footer checksum applied on the bottom of the pack file. */ + byte[] packChecksum; + + /** + * Finds the position in the bitmap of the object. + * + * @param objectId + * the id for which the bitmap position will be found. + * @return the bitmap id or -1 if the object was not found. + */ + public abstract int findPosition(AnyObjectId objectId); + + /** + * Get the object at the bitmap position. + * + * @param position + * the id for which the object will be found. + * @return the ObjectId. + * @throws IllegalArgumentException + * when the item is not found. + */ + public abstract ObjectId getObject(int position) throws IllegalArgumentException; + + /** + * Returns a bitmap containing positions for objects that have the given Git + * type. + * + * @param bitmap + * the object bitmap. + * @param type + * the Git type. + * @return the object bitmap with only objects of the Git type. + */ + public abstract EWAHCompressedBitmap ofObjectType( + EWAHCompressedBitmap bitmap, int type); + + /** + * Returns the previously constructed bitmap for the object. + * + * @param objectId + * the id for which the bitmap will be found. + * @return the bitmap or null if the object was not found. + */ + public abstract EWAHCompressedBitmap getBitmap(AnyObjectId objectId); + + /** + * Obtain the total number of objects described by this index. + * {@code getObjectCount() - 1} is the largest bit that will be set in a + * bitmap. + * + * @return number of objects in this index, and likewise in the associated + * pack that this index was generated from. + */ + public abstract int getObjectCount(); +} \ No newline at end of file diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackBitmapIndexBuilder.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackBitmapIndexBuilder.java new file mode 100644 index 000000000..6b32c8bc4 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackBitmapIndexBuilder.java @@ -0,0 +1,349 @@ +/* + * Copyright (C) 2012, Google Inc. + * 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.text.MessageFormat; +import java.util.Arrays; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; + +import javaewah.EWAHCompressedBitmap; + +import org.eclipse.jgit.internal.JGitText; +import org.eclipse.jgit.lib.AnyObjectId; +import org.eclipse.jgit.lib.BitmapIndex.Bitmap; +import org.eclipse.jgit.lib.BitmapIndex.BitmapBuilder; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.ObjectIdOwnerMap; +import org.eclipse.jgit.storage.file.BitmapIndexImpl.CompressedBitmap; +import org.eclipse.jgit.storage.pack.ObjectToPack; +import org.eclipse.jgit.util.BlockList; + +/** + * Helper for constructing {@link PackBitmapIndex}es. + */ +public class PackBitmapIndexBuilder extends BasePackBitmapIndex { + private static final int MAX_XOR_OFFSET_SEARCH = 10; + + private final EWAHCompressedBitmap commits; + private final EWAHCompressedBitmap trees; + private final EWAHCompressedBitmap blobs; + private final EWAHCompressedBitmap tags; + private final ObjectToPack[] byOffset; + private final BlockList + byAddOrder = new BlockList(); + private final ObjectIdOwnerMap + positionEntries = new ObjectIdOwnerMap(); + + /** + * Creates a PackBitmapIndex used for building the contents of an index + * file. + * + * @param byName + * objects sorted by name. + */ + public PackBitmapIndexBuilder(List byName) { + super(new ObjectIdOwnerMap()); + byOffset = sortByOffset(byName); + + int sizeInWords = Math.max(byOffset.length / 64, 4); + commits = new EWAHCompressedBitmap(sizeInWords); + trees = new EWAHCompressedBitmap(sizeInWords); + blobs = new EWAHCompressedBitmap(sizeInWords); + tags = new EWAHCompressedBitmap(sizeInWords); + for (int i = 0; i < byOffset.length; i++) { + int type = byOffset[i].getType(); + switch (type) { + case Constants.OBJ_COMMIT: + commits.set(i); + break; + case Constants.OBJ_TREE: + trees.set(i); + break; + case Constants.OBJ_BLOB: + blobs.set(i); + break; + case Constants.OBJ_TAG: + tags.set(i); + break; + default: + throw new IllegalArgumentException(MessageFormat.format( + JGitText.get().badObjectType, String.valueOf(type))); + } + } + } + + private ObjectToPack[] sortByOffset(List entries) { + ObjectToPack[] result = new ObjectToPack[entries.size()]; + for (int i = 0; i < result.length; i++) { + result[i] = entries.get(i); + positionEntries.add(new PositionEntry(result[i], i)); + } + Arrays.sort(result, new Comparator() { + public int compare(ObjectToPack a, ObjectToPack b) { + return Long.signum(a.getOffset() - b.getOffset()); + } + }); + for (int i = 0; i < result.length; i++) + positionEntries.get(result[i]).offsetPosition = i; + return result; + } + + /** + * Stores the bitmap for the objectId. + * + * @param objectId + * the object id key for the bitmap. + * @param bitmap + * the bitmap + * @param flags + * the flags to be stored with the bitmap + */ + public void addBitmap(AnyObjectId objectId, Bitmap bitmap, int flags) { + if (bitmap instanceof BitmapBuilder) + bitmap = ((BitmapBuilder) bitmap).build(); + + EWAHCompressedBitmap compressed; + if (bitmap instanceof CompressedBitmap) + compressed = ((CompressedBitmap) bitmap).getEwahCompressedBitmap(); + else + throw new IllegalArgumentException(bitmap.getClass().toString()); + + addBitmap(objectId, compressed, flags); + } + + /** + * Stores the bitmap for the objectId. + * + * @param objectId + * the object id key for the bitmap. + * @param bitmap + * the bitmap + * @param flags + * the flags to be stored with the bitmap + */ + public void addBitmap( + AnyObjectId objectId, EWAHCompressedBitmap bitmap, int flags) { + StoredBitmap result = new StoredBitmap(objectId, bitmap, null, flags); + getBitmaps().add(result); + byAddOrder.add(result); + } + + @Override + public EWAHCompressedBitmap ofObjectType( + EWAHCompressedBitmap bitmap, int type) { + switch (type) { + case Constants.OBJ_BLOB: + return getBlobs().and(bitmap); + case Constants.OBJ_TREE: + return getTrees().and(bitmap); + case Constants.OBJ_COMMIT: + return getCommits().and(bitmap); + case Constants.OBJ_TAG: + return getTags().and(bitmap); + } + throw new IllegalArgumentException(); + } + + @Override + public int findPosition(AnyObjectId objectId) { + PositionEntry entry = positionEntries.get(objectId); + if (entry == null) + return -1; + return entry.offsetPosition; + } + + @Override + public ObjectId getObject(int position) throws IllegalArgumentException { + ObjectId objectId = byOffset[position]; + if (objectId == null) + throw new IllegalArgumentException(); + return objectId; + } + + /** @return the commit object bitmap. */ + public EWAHCompressedBitmap getCommits() { + return commits; + } + + /** @return the tree object bitmap. */ + public EWAHCompressedBitmap getTrees() { + return trees; + } + + /** @return the blob object bitmap. */ + public EWAHCompressedBitmap getBlobs() { + return blobs; + } + + /** @return the tag object bitmap. */ + public EWAHCompressedBitmap getTags() { + return tags; + } + + /** @return the index storage options. */ + public int getOptions() { + return PackBitmapIndexV1.OPT_FULL; + } + + /** @return the number of bitmaps. */ + public int getBitmapCount() { + return getBitmaps().size(); + } + + /** Removes all the bitmaps entries added. */ + public void clearBitmaps() { + byAddOrder.clear(); + getBitmaps().clear(); + } + + @Override + public int getObjectCount() { + return byOffset.length; + } + + /** @return an iterator over the xor compressed entries. */ + public Iterable getCompressedBitmaps() { + // Add order is from oldest to newest. The reverse add order is the + // output order. + return new Iterable() { + public Iterator iterator() { + return new Iterator() { + private int index = byAddOrder.size() - 1; + + public boolean hasNext() { + return index >= 0; + } + + public StoredEntry next() { + if (!hasNext()) + throw new NoSuchElementException(); + StoredBitmap item = byAddOrder.get(index); + int bestXorOffset = 0; + EWAHCompressedBitmap bestBitmap = item.getBitmap(); + + // Attempt to compress the bitmap with an XOR of the + // previously written entries. + for (int i = 1; i <= MAX_XOR_OFFSET_SEARCH; i++) { + int curr = i + index; + if (curr >= byAddOrder.size()) + break; + + StoredBitmap other = byAddOrder.get(curr); + EWAHCompressedBitmap bitmap = other.getBitmap() + .xor(item.getBitmap()); + + if (bitmap.sizeInBytes() + < bestBitmap.sizeInBytes()) { + bestBitmap = bitmap; + bestXorOffset = i; + } + } + index--; + + PositionEntry entry = positionEntries.get(item); + if (entry == null) + throw new IllegalStateException(); + return new StoredEntry(entry.namePosition, bestBitmap, + bestXorOffset, item.getFlags()); + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + }; + } + + /** Data object for the on disk representation of a bitmap entry. */ + public static final class StoredEntry { + private final long objectId; + private final EWAHCompressedBitmap bitmap; + private final int xorOffset; + private final int flags; + + private StoredEntry(long objectId, EWAHCompressedBitmap bitmap, + int xorOffset, int flags) { + this.objectId = objectId; + this.bitmap = bitmap; + this.xorOffset = xorOffset; + this.flags = flags; + } + + /** @return the bitmap */ + public EWAHCompressedBitmap getBitmap() { + return bitmap; + } + + /** @return the xorOffset */ + public int getXorOffset() { + return xorOffset; + } + + /** @return the flags */ + public int getFlags() { + return flags; + } + + /** @return the ObjectId */ + public long getObjectId() { + return objectId; + } + } + + private static final class PositionEntry extends ObjectIdOwnerMap.Entry { + private final int namePosition; + + private int offsetPosition; + + private PositionEntry(AnyObjectId objectId, int namePosition) { + super(objectId); + this.namePosition = namePosition; + } + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackBitmapIndexV1.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackBitmapIndexV1.java new file mode 100644 index 000000000..ca7e0cae8 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackBitmapIndexV1.java @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2012, Google Inc. + * 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.io.DataInput; +import java.io.IOException; +import java.io.InputStream; +import java.text.MessageFormat; +import java.util.Arrays; + +import javaewah.EWAHCompressedBitmap; + +import org.eclipse.jgit.internal.JGitText; +import org.eclipse.jgit.lib.AnyObjectId; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.ObjectIdOwnerMap; +import org.eclipse.jgit.util.IO; +import org.eclipse.jgit.util.NB; + +/** + * Support for the pack bitmap index v1 format, which contains experimental + * support for bitmaps. + * + * @see PackBitmapIndex + */ +class PackBitmapIndexV1 extends BasePackBitmapIndex { + static final byte[] MAGIC = { 'B', 'I', 'T', 'M' }; + static final int OPT_FULL = 1; + + private static final int MAX_XOR_OFFSET = 126; + + private final PackIndex packIndex; + private final PackReverseIndex reverseIndex; + private final EWAHCompressedBitmap commits; + private final EWAHCompressedBitmap trees; + private final EWAHCompressedBitmap blobs; + private final EWAHCompressedBitmap tags; + + private final ObjectIdOwnerMap bitmaps; + + PackBitmapIndexV1(final InputStream fd, PackIndex packIndex, + PackReverseIndex reverseIndex) throws IOException { + super(new ObjectIdOwnerMap()); + this.packIndex = packIndex; + this.reverseIndex = reverseIndex; + this.bitmaps = getBitmaps(); + + final byte[] scratch = new byte[32]; + IO.readFully(fd, scratch, 0, scratch.length); + + // Check the magic bytes + for (int i = 0; i < MAGIC.length; i++) { + if (scratch[i] != MAGIC[i]) { + byte[] actual = new byte[MAGIC.length]; + System.arraycopy(scratch, 0, actual, 0, MAGIC.length); + throw new IOException(MessageFormat.format( + JGitText.get().expectedGot, Arrays.toString(MAGIC), + Arrays.toString(actual))); + } + } + + // Read the version (2 bytes) + final int version = NB.decodeUInt16(scratch, 4); + if (version != 1) + throw new IOException(MessageFormat.format( + JGitText.get().unsupportedPackIndexVersion, + Integer.valueOf(version))); + + // Read the options (2 bytes) + final int opts = NB.decodeUInt16(scratch, 6); + switch (opts) { + case OPT_FULL: + // Bitmaps are self contained within this file. + break; + default: + throw new IOException(MessageFormat.format( + JGitText.get().expectedGot, Integer.valueOf(OPT_FULL), + Integer.valueOf(opts))); + } + + // Read the number of entries (1 int32) + long numEntries = NB.decodeUInt32(scratch, 8); + if (numEntries > Integer.MAX_VALUE) + throw new IOException(JGitText.get().indexFileIsTooLargeForJgit); + + // Checksum applied on the bottom of the corresponding pack file. + this.packChecksum = new byte[20]; + System.arraycopy(scratch, 12, packChecksum, 0, packChecksum.length); + + // Read the bitmaps for the Git types + SimpleDataInput dataInput = new SimpleDataInput(fd); + this.commits = readBitmap(dataInput); + this.trees = readBitmap(dataInput); + this.blobs = readBitmap(dataInput); + this.tags = readBitmap(dataInput); + + // An entry is object id, xor offset, flag byte, and a length encoded + // bitmap. The object id is an int32 of the nth position sorted by name. + // The xor offset is a single byte offset back in the list of entries. + StoredBitmap[] recentBitmaps = new StoredBitmap[MAX_XOR_OFFSET]; + for (int i = 0; i < (int) numEntries; i++) { + IO.readFully(fd, scratch, 0, 6); + int nthObjectId = NB.decodeInt32(scratch, 0); + int xorOffset = scratch[4]; + int flags = scratch[5]; + EWAHCompressedBitmap bitmap = readBitmap(dataInput); + + if (nthObjectId < 0) + throw new IOException(MessageFormat.format( + JGitText.get().invalidId, String.valueOf(nthObjectId))); + if (xorOffset < 0) + throw new IOException(MessageFormat.format( + JGitText.get().invalidId, String.valueOf(xorOffset))); + if (xorOffset > MAX_XOR_OFFSET) + throw new IOException(MessageFormat.format( + JGitText.get().expectedLessThanGot, + String.valueOf(MAX_XOR_OFFSET), + String.valueOf(xorOffset))); + if (xorOffset > i) + throw new IOException(MessageFormat.format( + JGitText.get().expectedLessThanGot, String.valueOf(i), + String.valueOf(xorOffset))); + + ObjectId objectId = packIndex.getObjectId(nthObjectId); + StoredBitmap xorBitmap = null; + if (xorOffset > 0) { + int index = (i - xorOffset); + xorBitmap = recentBitmaps[index % recentBitmaps.length]; + if (xorBitmap == null) + throw new IOException(MessageFormat.format( + JGitText.get().invalidId, + String.valueOf(xorOffset))); + } + + StoredBitmap sb = new StoredBitmap( + objectId, bitmap, xorBitmap, flags); + bitmaps.add(sb); + recentBitmaps[i % recentBitmaps.length] = sb; + } + } + + @Override + public int findPosition(AnyObjectId objectId) { + long offset = packIndex.findOffset(objectId); + if (offset == -1) + return -1; + return reverseIndex.findPostion(offset); + } + + @Override + public ObjectId getObject(int position) throws IllegalArgumentException { + ObjectId objectId = reverseIndex.findObjectByPosition(position); + if (objectId == null) + throw new IllegalArgumentException(); + return objectId; + } + + @Override + public int getObjectCount() { + return (int) packIndex.getObjectCount(); + } + + @Override + public EWAHCompressedBitmap ofObjectType( + EWAHCompressedBitmap bitmap, int type) { + switch (type) { + case Constants.OBJ_BLOB: + return blobs.and(bitmap); + case Constants.OBJ_TREE: + return trees.and(bitmap); + case Constants.OBJ_COMMIT: + return commits.and(bitmap); + case Constants.OBJ_TAG: + return tags.and(bitmap); + } + throw new IllegalArgumentException(); + } + + @Override + public boolean equals(Object o) { + // TODO(cranger): compare the pack checksum? + if (o instanceof PackBitmapIndexV1) + return getPackIndex() == ((PackBitmapIndexV1) o).getPackIndex(); + return false; + } + + @Override + public int hashCode() { + return getPackIndex().hashCode(); + } + + PackIndex getPackIndex() { + return packIndex; + } + + private static EWAHCompressedBitmap readBitmap(DataInput dataInput) + throws IOException { + EWAHCompressedBitmap bitmap = new EWAHCompressedBitmap(); + bitmap.deserialize(dataInput); + return bitmap; + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackBitmapIndexWriterV1.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackBitmapIndexWriterV1.java new file mode 100644 index 000000000..8636eb315 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackBitmapIndexWriterV1.java @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2012, Google Inc. + * 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.io.BufferedOutputStream; +import java.io.DataOutput; +import java.io.IOException; +import java.io.OutputStream; +import java.security.DigestOutputStream; +import java.text.MessageFormat; + +import javaewah.EWAHCompressedBitmap; + +import org.eclipse.jgit.internal.JGitText; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.storage.file.PackBitmapIndexBuilder.StoredEntry; +import org.eclipse.jgit.util.io.SafeBufferedOutputStream; + +/** + * Creates the version 1 pack bitmap index files. + * + * @see PackBitmapIndexV1 + */ +class PackBitmapIndexWriterV1 { + private final DigestOutputStream out; + private final DataOutput dataOutput; + + PackBitmapIndexWriterV1(final OutputStream dst) { + out = new DigestOutputStream(dst instanceof BufferedOutputStream ? dst + : new SafeBufferedOutputStream(dst), + Constants.newMessageDigest()); + dataOutput = new SimpleDataOutput(out); + } + + /** + * Write all object entries to the index stream. + *

+ * After writing the stream passed to the factory is flushed but remains + * open. Callers are always responsible for closing the output stream. + * + * @param bitmaps + * the index data for the bitmaps + * @param packDataChecksum + * checksum signature of the entire pack data content. This is + * traditionally the last 20 bytes of the pack file's own stream. + * @throws IOException + * an error occurred while writing to the output stream, or this + * index format cannot store the object data supplied. + */ + public void write(PackBitmapIndexBuilder bitmaps, byte[] packDataChecksum) + throws IOException { + if (bitmaps == null || packDataChecksum.length != 20) + throw new IllegalStateException(); + + writeHeader(bitmaps.getOptions(), bitmaps.getBitmapCount(), + packDataChecksum); + writeBody(bitmaps); + writeFooter(); + + out.flush(); + } + + private void writeHeader( + int options, int bitmapCount, byte[] packDataChecksum) + throws IOException { + out.write(PackBitmapIndexV1.MAGIC); + dataOutput.writeShort(1); + dataOutput.writeShort(options); + dataOutput.writeInt(bitmapCount); + out.write(packDataChecksum); + } + + private void writeBody(PackBitmapIndexBuilder bitmaps) throws IOException { + writeBitmap(bitmaps.getCommits()); + writeBitmap(bitmaps.getTrees()); + writeBitmap(bitmaps.getBlobs()); + writeBitmap(bitmaps.getTags()); + writeBitmaps(bitmaps); + } + + private void writeBitmap(EWAHCompressedBitmap bitmap) throws IOException { + bitmap.serialize(dataOutput); + } + + private void writeBitmaps(PackBitmapIndexBuilder bitmaps) + throws IOException { + int bitmapCount = 0; + for (StoredEntry entry : bitmaps.getCompressedBitmaps()) { + writeBitmapEntry(entry); + bitmapCount++; + } + + int expectedBitmapCount = bitmaps.getBitmapCount(); + if (expectedBitmapCount != bitmapCount) + throw new IOException(MessageFormat.format( + JGitText.get().expectedGot, + String.valueOf(expectedBitmapCount), + String.valueOf(bitmapCount))); + } + + private void writeBitmapEntry(StoredEntry entry) throws IOException { + // Write object, xor offset, and bitmap + dataOutput.writeInt((int) entry.getObjectId()); + out.write(entry.getXorOffset()); + out.write(entry.getFlags()); + writeBitmap(entry.getBitmap()); + } + + private void writeFooter() throws IOException { + out.on(false); + out.write(out.getMessageDigest().digest()); + } +} 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 17dfa1f1a..31de38172 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 @@ -45,6 +45,7 @@ package org.eclipse.jgit.storage.file; +import static org.eclipse.jgit.storage.pack.PackExt.BITMAP_INDEX; import static org.eclipse.jgit.storage.pack.PackExt.INDEX; import java.io.EOFException; @@ -97,6 +98,8 @@ public int compare(final PackFile a, final PackFile b) { private final File packFile; + private final int extensions; + private File keepFile; private volatile String packName; @@ -124,6 +127,8 @@ public int compare(final PackFile a, final PackFile b) { private PackReverseIndex reverseIdx; + private PackBitmapIndex bitmapIdx; + /** * Objects we have tried to read, and discovered to be corrupt. *

@@ -144,6 +149,7 @@ public int compare(final PackFile a, final PackFile b) { public PackFile(final File packFile, int extensions) { this.packFile = packFile; this.packLastModified = (int) (packFile.lastModified() >> 10); + this.extensions = extensions; // Multiply by 31 here so we can more directly combine with another // value in WindowCache.hash(), without doing the multiply there. @@ -1050,6 +1056,22 @@ private long findEndOffset(final long startOffset) return getReverseIdx().findNextOffset(startOffset, maxOffset); } + synchronized PackBitmapIndex getBitmapIndex() throws IOException { + if (bitmapIdx == null && hasExt(BITMAP_INDEX)) { + final PackBitmapIndex idx = PackBitmapIndex.open( + extFile(BITMAP_INDEX), idx(), getReverseIdx()); + + if (packChecksum == null) + packChecksum = idx.packChecksum; + else if (!Arrays.equals(packChecksum, idx.packChecksum)) + throw new PackMismatchException( + JGitText.get().packChecksumMismatch); + + bitmapIdx = idx; + } + return bitmapIdx; + } + private synchronized PackReverseIndex getReverseIdx() throws IOException { if (reverseIdx == null) reverseIdx = new PackReverseIndex(idx()); @@ -1087,4 +1109,8 @@ private File extFile(PackExt ext) { String b = (dot < 0) ? p : p.substring(0, dot); return new File(packFile.getParentFile(), b + '.' + ext.getExtension()); } + + private boolean hasExt(PackExt ext) { + return (extensions & ext.getBit()) != 0; + } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackReverseIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackReverseIndex.java index 7eeb02833..7361eace4 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackReverseIndex.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/PackReverseIndex.java @@ -193,4 +193,25 @@ public long findNextOffset(final long offset, final long maxOffset) return offsets64[i64 + 1]; } } + + int findPostion(long offset) { + if (offset <= Integer.MAX_VALUE) { + final int i32 = Arrays.binarySearch(offsets32, (int) offset); + if (i32 < 0) + return -1; + return i32; + } else { + final int i64 = Arrays.binarySearch(offsets64, offset); + if (i64 < 0) + return -1; + return nth32.length + i64; + } + } + + ObjectId findObjectByPosition(int nthPosition) { + if (nthPosition < nth32.length) + return index.getObjectId(nth32[nthPosition]); + final int i64 = nthPosition - nth32.length; + return index.getObjectId(nth64[i64]); + } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/SimpleDataInput.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/SimpleDataInput.java new file mode 100644 index 000000000..d95060fed --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/SimpleDataInput.java @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2012, Google Inc. + * 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.io.DataInput; +import java.io.IOException; +import java.io.InputStream; + +import org.eclipse.jgit.util.IO; +import org.eclipse.jgit.util.NB; + +/** + * An implementation of DataInput that only handles readInt() and readLong() + * using the Git conversion utilities for network byte order handling. This is + * needed to read {@link javaewah.EWAHCompressedBitmap}s. + */ +class SimpleDataInput implements DataInput { + private final InputStream fd; + + private final byte[] buf = new byte[8]; + + SimpleDataInput(InputStream fd) { + this.fd = fd; + } + + public int readInt() throws IOException { + readFully(buf, 0, 4); + return NB.decodeInt32(buf, 0); + } + + public long readLong() throws IOException { + readFully(buf, 0, 8); + return NB.decodeInt64(buf, 0); + } + + public long readUnsignedInt() throws IOException { + readFully(buf, 0, 4); + return NB.decodeUInt32(buf, 0); + } + + public void readFully(byte[] b) throws IOException { + readFully(b, 0, b.length); + } + + public void readFully(byte[] b, int off, int len) throws IOException { + IO.readFully(fd, b, off, len); + } + + public int skipBytes(int n) throws IOException { + throw new UnsupportedOperationException(); + } + + public boolean readBoolean() throws IOException { + throw new UnsupportedOperationException(); + } + + public byte readByte() throws IOException { + throw new UnsupportedOperationException(); + } + + public int readUnsignedByte() throws IOException { + throw new UnsupportedOperationException(); + } + + public short readShort() throws IOException { + throw new UnsupportedOperationException(); + } + + public int readUnsignedShort() throws IOException { + throw new UnsupportedOperationException(); + } + + public char readChar() throws IOException { + throw new UnsupportedOperationException(); + } + + public float readFloat() throws IOException { + throw new UnsupportedOperationException(); + } + + public double readDouble() throws IOException { + throw new UnsupportedOperationException(); + } + + public String readLine() throws IOException { + throw new UnsupportedOperationException(); + } + + public String readUTF() throws IOException { + throw new UnsupportedOperationException(); + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/SimpleDataOutput.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/SimpleDataOutput.java new file mode 100644 index 000000000..17c49daff --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/SimpleDataOutput.java @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2012, Google Inc. + * 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.io.DataOutput; +import java.io.IOException; +import java.io.OutputStream; + +import org.eclipse.jgit.util.NB; + +/** + * An implementation of {@link DataOutput} that only handles + * {@link #writeInt(int)} and {@link #writeLong(long)} using the Git conversion + * utilities for network byte order handling. This is needed to write + * {@link javaewah.EWAHCompressedBitmap}s. + */ +class SimpleDataOutput implements DataOutput { + private final OutputStream fd; + + private final byte[] buf = new byte[8]; + + SimpleDataOutput(OutputStream fd) { + this.fd = fd; + } + + public void writeShort(int v) throws IOException { + NB.encodeInt16(buf, 0, v); + fd.write(buf, 0, 2); + } + + public void writeInt(int v) throws IOException { + NB.encodeInt32(buf, 0, v); + fd.write(buf, 0, 4); + } + + public void writeLong(long v) throws IOException { + NB.encodeInt64(buf, 0, v); + fd.write(buf, 0, 8); + } + + public void write(int b) throws IOException { + throw new UnsupportedOperationException(); + } + + public void write(byte[] b) throws IOException { + throw new UnsupportedOperationException(); + } + + public void write(byte[] b, int off, int len) throws IOException { + throw new UnsupportedOperationException(); + } + + public void writeBoolean(boolean v) throws IOException { + throw new UnsupportedOperationException(); + } + + public void writeByte(int v) throws IOException { + throw new UnsupportedOperationException(); + } + + public void writeChar(int v) throws IOException { + throw new UnsupportedOperationException(); + } + + public void writeFloat(float v) throws IOException { + throw new UnsupportedOperationException(); + } + + public void writeDouble(double v) throws IOException { + throw new UnsupportedOperationException(); + } + + public void writeBytes(String s) throws IOException { + throw new UnsupportedOperationException(); + } + + public void writeChars(String s) throws IOException { + throw new UnsupportedOperationException(); + } + + public void writeUTF(String s) throws IOException { + throw new UnsupportedOperationException(); + } +} \ No newline at end of file diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCursor.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCursor.java index 350f981b2..2af48edcc 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCursor.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCursor.java @@ -62,6 +62,7 @@ import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.lib.AbbreviatedObjectId; import org.eclipse.jgit.lib.AnyObjectId; +import org.eclipse.jgit.lib.BitmapIndex; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.InflaterCache; import org.eclipse.jgit.lib.ObjectId; @@ -102,6 +103,16 @@ public ObjectReader newReader() { return new WindowCursor(db); } + @Override + public BitmapIndex getBitmapIndex() throws IOException { + for (PackFile pack : db.getPacks()) { + PackBitmapIndex index = pack.getBitmapIndex(); + if (index != null) + return new BitmapIndexImpl(index); + } + return null; + } + @Override public Collection resolve(AbbreviatedObjectId id) throws IOException { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackExt.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackExt.java index 61a660930..7766800e4 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackExt.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackExt.java @@ -53,6 +53,9 @@ public class PackExt { /** A pack index file extension. */ public static final PackExt INDEX = newPackExt("idx"); //$NON-NLS-1$ + /** A pack bitmap index file extension. */ + public static final PackExt BITMAP_INDEX = newPackExt("bitmap"); //$NON-NLS-1$ + /** @return all of the PackExt values. */ public static PackExt[] values() { return VALUES; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/NB.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/NB.java index 07f7990b7..5a56a0621 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/NB.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/NB.java @@ -102,6 +102,39 @@ public static int decodeInt32(final byte[] intbuf, final int offset) { return (r << 8) | (intbuf[offset + 3] & 0xff); } + /** + * Convert sequence of 8 bytes (network byte order) into signed value. + * + * @param intbuf + * buffer to acquire the 8 bytes of data from. + * @param offset + * position within the buffer to begin reading from. This + * position and the next 7 bytes after it (for a total of 8 + * bytes) will be read. + * @return signed integer value that matches the 64 bits read. + */ + public static long decodeInt64(final byte[] intbuf, final int offset) { + long r = intbuf[offset] << 8; + + r |= intbuf[offset + 1] & 0xff; + r <<= 8; + + r |= intbuf[offset + 2] & 0xff; + r <<= 8; + + r |= intbuf[offset + 3] & 0xff; + r <<= 8; + + r |= intbuf[offset + 4] & 0xff; + r <<= 8; + + r |= intbuf[offset + 5] & 0xff; + r <<= 8; + + r |= intbuf[offset + 6] & 0xff; + return (r << 8) | (intbuf[offset + 7] & 0xff); + } + /** * Convert sequence of 4 bytes (network byte order) into unsigned value. * @@ -186,7 +219,7 @@ public static void encodeInt32(final byte[] intbuf, final int offset, int v) { * Write a 64 bit integer as a sequence of 8 bytes (network byte order). * * @param intbuf - * buffer to write the 48bytes of data into. + * buffer to write the 8 bytes of data into. * @param offset * position within the buffer to begin writing to. This position * and the next 7 bytes after it (for a total of 8 bytes) will be diff --git a/pom.xml b/pom.xml index 44cf70319..f875b1283 100644 --- a/pom.xml +++ b/pom.xml @@ -177,6 +177,7 @@ 2.3.1.201302201838-r 0.1.46 + 0.5.6 4.5 2.0.12 @@ -405,6 +406,12 @@ ${jsch-version} + + com.googlecode.javaewah + JavaEWAH + ${javaewah-version} + + args4j args4j