BitmapIndexImpl.java

  1. /*
  2.  * Copyright (C) 2012, Google Inc.
  3.  * and other copyright owners as documented in the project's IP log.
  4.  *
  5.  * This program and the accompanying materials are made available
  6.  * under the terms of the Eclipse Distribution License v1.0 which
  7.  * accompanies this distribution, is reproduced below, and is
  8.  * available at http://www.eclipse.org/org/documents/edl-v10.php
  9.  *
  10.  * All rights reserved.
  11.  *
  12.  * Redistribution and use in source and binary forms, with or
  13.  * without modification, are permitted provided that the following
  14.  * conditions are met:
  15.  *
  16.  * - Redistributions of source code must retain the above copyright
  17.  *   notice, this list of conditions and the following disclaimer.
  18.  *
  19.  * - Redistributions in binary form must reproduce the above
  20.  *   copyright notice, this list of conditions and the following
  21.  *   disclaimer in the documentation and/or other materials provided
  22.  *   with the distribution.
  23.  *
  24.  * - Neither the name of the Eclipse Foundation, Inc. nor the
  25.  *   names of its contributors may be used to endorse or promote
  26.  *   products derived from this software without specific prior
  27.  *   written permission.
  28.  *
  29.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  30.  * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  31.  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  32.  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  33.  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  34.  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  35.  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  36.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  37.  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  38.  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  39.  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  40.  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  41.  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  42.  */

  43. package org.eclipse.jgit.internal.storage.file;

  44. import java.text.MessageFormat;
  45. import java.util.Iterator;
  46. import java.util.NoSuchElementException;

  47. import org.eclipse.jgit.internal.JGitText;
  48. import org.eclipse.jgit.lib.AnyObjectId;
  49. import org.eclipse.jgit.lib.BitmapIndex;
  50. import org.eclipse.jgit.lib.BitmapObject;
  51. import org.eclipse.jgit.lib.Constants;
  52. import org.eclipse.jgit.lib.ObjectId;
  53. import org.eclipse.jgit.lib.ObjectIdOwnerMap;
  54. import org.eclipse.jgit.util.BlockList;

  55. import com.googlecode.javaewah.EWAHCompressedBitmap;
  56. import com.googlecode.javaewah.IntIterator;

  57. /**
  58.  * A compressed bitmap representation of the entire object graph.
  59.  */
  60. public class BitmapIndexImpl implements BitmapIndex {
  61.     private static final int EXTRA_BITS = 10 * 1024;

  62.     final PackBitmapIndex packIndex;

  63.     final MutableBitmapIndex mutableIndex;

  64.     final int indexObjectCount;

  65.     /**
  66.      * Creates a BitmapIndex that is back by Compressed bitmaps.
  67.      *
  68.      * @param packIndex
  69.      *            the bitmap index for the pack.
  70.      */
  71.     public BitmapIndexImpl(PackBitmapIndex packIndex) {
  72.         this.packIndex = packIndex;
  73.         mutableIndex = new MutableBitmapIndex();
  74.         indexObjectCount = packIndex.getObjectCount();
  75.     }

  76.     PackBitmapIndex getPackBitmapIndex() {
  77.         return packIndex;
  78.     }

  79.     /** {@inheritDoc} */
  80.     @Override
  81.     public CompressedBitmap getBitmap(AnyObjectId objectId) {
  82.         EWAHCompressedBitmap compressed = packIndex.getBitmap(objectId);
  83.         if (compressed == null)
  84.             return null;
  85.         return new CompressedBitmap(compressed, this);
  86.     }

  87.     /** {@inheritDoc} */
  88.     @Override
  89.     public CompressedBitmapBuilder newBitmapBuilder() {
  90.         return new CompressedBitmapBuilder(this);
  91.     }

  92.     int findPosition(AnyObjectId objectId) {
  93.         int position = packIndex.findPosition(objectId);
  94.         if (position < 0) {
  95.             position = mutableIndex.findPosition(objectId);
  96.             if (position >= 0)
  97.                 position += indexObjectCount;
  98.         }
  99.         return position;
  100.     }

  101.     int findOrInsert(AnyObjectId objectId, int type) {
  102.         int position = findPosition(objectId);
  103.         if (position < 0) {
  104.             position = mutableIndex.findOrInsert(objectId, type);
  105.             position += indexObjectCount;
  106.         }
  107.         return position;
  108.     }

  109.     private static final class ComboBitset {
  110.         private InflatingBitSet inflatingBitmap;

  111.         private BitSet toAdd;

  112.         private BitSet toRemove;

  113.         ComboBitset() {
  114.             this(new EWAHCompressedBitmap());
  115.         }

  116.         ComboBitset(EWAHCompressedBitmap bitmap) {
  117.             this.inflatingBitmap = new InflatingBitSet(bitmap);
  118.         }

  119.         EWAHCompressedBitmap combine() {
  120.             EWAHCompressedBitmap toAddCompressed = null;
  121.             if (toAdd != null) {
  122.                 toAddCompressed = toAdd.toEWAHCompressedBitmap();
  123.                 toAdd = null;
  124.             }

  125.             EWAHCompressedBitmap toRemoveCompressed = null;
  126.             if (toRemove != null) {
  127.                 toRemoveCompressed = toRemove.toEWAHCompressedBitmap();
  128.                 toRemove = null;
  129.             }

  130.             if (toAddCompressed != null)
  131.                 or(toAddCompressed);
  132.             if (toRemoveCompressed != null)
  133.                 andNot(toRemoveCompressed);
  134.             return inflatingBitmap.getBitmap();
  135.         }

  136.         void or(EWAHCompressedBitmap inbits) {
  137.             if (toRemove != null)
  138.                 combine();
  139.             inflatingBitmap = inflatingBitmap.or(inbits);
  140.         }

  141.         void andNot(EWAHCompressedBitmap inbits) {
  142.             if (toAdd != null || toRemove != null)
  143.                 combine();
  144.             inflatingBitmap = inflatingBitmap.andNot(inbits);
  145.         }

  146.         void xor(EWAHCompressedBitmap inbits) {
  147.             if (toAdd != null || toRemove != null)
  148.                 combine();
  149.             inflatingBitmap = inflatingBitmap.xor(inbits);
  150.         }

  151.         boolean contains(int position) {
  152.             if (toRemove != null && toRemove.get(position))
  153.                 return false;
  154.             if (toAdd != null && toAdd.get(position))
  155.                 return true;
  156.             return inflatingBitmap.contains(position);
  157.         }

  158.         void remove(int position) {
  159.             if (toAdd != null)
  160.                 toAdd.clear(position);

  161.             if (inflatingBitmap.maybeContains(position)) {
  162.                 if (toRemove == null)
  163.                     toRemove = new BitSet(position + EXTRA_BITS);
  164.                 toRemove.set(position);
  165.             }
  166.         }

  167.         void set(int position) {
  168.             if (toRemove != null)
  169.                 toRemove.clear(position);

  170.             if (toAdd == null)
  171.                 toAdd = new BitSet(position + EXTRA_BITS);
  172.             toAdd.set(position);
  173.         }
  174.     }

  175.     private static final class CompressedBitmapBuilder implements BitmapBuilder {
  176.         private ComboBitset bitset;
  177.         private final BitmapIndexImpl bitmapIndex;

  178.         CompressedBitmapBuilder(BitmapIndexImpl bitmapIndex) {
  179.             this.bitset = new ComboBitset();
  180.             this.bitmapIndex = bitmapIndex;
  181.         }

  182.         @Override
  183.         public boolean contains(AnyObjectId objectId) {
  184.             int position = bitmapIndex.findPosition(objectId);
  185.             return 0 <= position && bitset.contains(position);
  186.         }

  187.         @Override
  188.         public BitmapBuilder addObject(AnyObjectId objectId, int type) {
  189.             bitset.set(bitmapIndex.findOrInsert(objectId, type));
  190.             return this;
  191.         }

  192.         @Override
  193.         public void remove(AnyObjectId objectId) {
  194.             int position = bitmapIndex.findPosition(objectId);
  195.             if (0 <= position)
  196.                 bitset.remove(position);
  197.         }

  198.         @Override
  199.         public CompressedBitmapBuilder or(Bitmap other) {
  200.             bitset.or(ewahBitmap(other));
  201.             return this;
  202.         }

  203.         @Override
  204.         public CompressedBitmapBuilder andNot(Bitmap other) {
  205.             bitset.andNot(ewahBitmap(other));
  206.             return this;
  207.         }

  208.         @Override
  209.         public CompressedBitmapBuilder xor(Bitmap other) {
  210.             bitset.xor(ewahBitmap(other));
  211.             return this;
  212.         }

  213.         /** @return the fully built immutable bitmap */
  214.         @Override
  215.         public CompressedBitmap build() {
  216.             return new CompressedBitmap(bitset.combine(), bitmapIndex);
  217.         }

  218.         @Override
  219.         public Iterator<BitmapObject> iterator() {
  220.             return build().iterator();
  221.         }

  222.         @Override
  223.         public int cardinality() {
  224.             return bitset.combine().cardinality();
  225.         }

  226.         @Override
  227.         public boolean removeAllOrNone(PackBitmapIndex index) {
  228.             if (!bitmapIndex.packIndex.equals(index))
  229.                 return false;

  230.             EWAHCompressedBitmap curr = bitset.combine()
  231.                     .xor(ones(bitmapIndex.indexObjectCount));

  232.             IntIterator ii = curr.intIterator();
  233.             if (ii.hasNext() && ii.next() < bitmapIndex.indexObjectCount)
  234.                 return false;
  235.             bitset = new ComboBitset(curr);
  236.             return true;
  237.         }

  238.         @Override
  239.         public BitmapIndexImpl getBitmapIndex() {
  240.             return bitmapIndex;
  241.         }

  242.         private EWAHCompressedBitmap ewahBitmap(Bitmap other) {
  243.             if (other instanceof CompressedBitmap) {
  244.                 CompressedBitmap b = (CompressedBitmap) other;
  245.                 if (b.bitmapIndex != bitmapIndex) {
  246.                     throw new IllegalArgumentException();
  247.                 }
  248.                 return b.bitmap;
  249.             }
  250.             if (other instanceof CompressedBitmapBuilder) {
  251.                 CompressedBitmapBuilder b = (CompressedBitmapBuilder) other;
  252.                 if (b.bitmapIndex != bitmapIndex) {
  253.                     throw new IllegalArgumentException();
  254.                 }
  255.                 return b.bitset.combine();
  256.             }
  257.             throw new IllegalArgumentException();
  258.         }
  259.     }

  260.     /**
  261.      * Wrapper for a {@link EWAHCompressedBitmap} and {@link PackBitmapIndex}.
  262.      * <p>
  263.      * For a EWAHCompressedBitmap {@code bitmap} representing a vector of
  264.      * bits, {@code new CompressedBitmap(bitmap, bitmapIndex)} represents the
  265.      * objects at those positions in {@code bitmapIndex.packIndex}.
  266.      */
  267.     public static final class CompressedBitmap implements Bitmap {
  268.         final EWAHCompressedBitmap bitmap;
  269.         final BitmapIndexImpl bitmapIndex;

  270.         /**
  271.          * Construct compressed bitmap for given bitmap and bitmap index
  272.          *
  273.          * @param bitmap
  274.          * @param bitmapIndex
  275.          */
  276.         public CompressedBitmap(EWAHCompressedBitmap bitmap, BitmapIndexImpl bitmapIndex) {
  277.             this.bitmap = bitmap;
  278.             this.bitmapIndex = bitmapIndex;
  279.         }

  280.         @Override
  281.         public CompressedBitmap or(Bitmap other) {
  282.             return new CompressedBitmap(bitmap.or(ewahBitmap(other)), bitmapIndex);
  283.         }

  284.         @Override
  285.         public CompressedBitmap andNot(Bitmap other) {
  286.             return new CompressedBitmap(bitmap.andNot(ewahBitmap(other)), bitmapIndex);
  287.         }

  288.         @Override
  289.         public CompressedBitmap xor(Bitmap other) {
  290.             return new CompressedBitmap(bitmap.xor(ewahBitmap(other)), bitmapIndex);
  291.         }

  292.         private final IntIterator ofObjectType(int type) {
  293.             return bitmapIndex.packIndex.ofObjectType(bitmap, type).intIterator();
  294.         }

  295.         @Override
  296.         public Iterator<BitmapObject> iterator() {
  297.             final IntIterator dynamic = bitmap.andNot(ones(bitmapIndex.indexObjectCount))
  298.                     .intIterator();
  299.             final IntIterator commits = ofObjectType(Constants.OBJ_COMMIT);
  300.             final IntIterator trees = ofObjectType(Constants.OBJ_TREE);
  301.             final IntIterator blobs = ofObjectType(Constants.OBJ_BLOB);
  302.             final IntIterator tags = ofObjectType(Constants.OBJ_TAG);
  303.             return new Iterator<BitmapObject>() {
  304.                 private final BitmapObjectImpl out = new BitmapObjectImpl();
  305.                 private int type;
  306.                 private IntIterator cached = dynamic;

  307.                 @Override
  308.                 public boolean hasNext() {
  309.                     if (!cached.hasNext()) {
  310.                         if (commits.hasNext()) {
  311.                             type = Constants.OBJ_COMMIT;
  312.                             cached = commits;
  313.                         } else if (trees.hasNext()) {
  314.                             type = Constants.OBJ_TREE;
  315.                             cached = trees;
  316.                         } else if (blobs.hasNext()) {
  317.                             type = Constants.OBJ_BLOB;
  318.                             cached = blobs;
  319.                         } else if (tags.hasNext()) {
  320.                             type = Constants.OBJ_TAG;
  321.                             cached = tags;
  322.                         } else {
  323.                             return false;
  324.                         }
  325.                     }
  326.                     return true;
  327.                 }

  328.                 @Override
  329.                 public BitmapObject next() {
  330.                     if (!hasNext())
  331.                         throw new NoSuchElementException();

  332.                     int position = cached.next();
  333.                     if (position < bitmapIndex.indexObjectCount) {
  334.                         out.type = type;
  335.                         out.objectId = bitmapIndex.packIndex.getObject(position);
  336.                     } else {
  337.                         position -= bitmapIndex.indexObjectCount;
  338.                         MutableEntry entry = bitmapIndex.mutableIndex.getObject(position);
  339.                         out.type = entry.type;
  340.                         out.objectId = entry;
  341.                     }
  342.                     return out;
  343.                 }

  344.                 @Override
  345.                 public void remove() {
  346.                     throw new UnsupportedOperationException();
  347.                 }
  348.             };
  349.         }

  350.         EWAHCompressedBitmap getEwahCompressedBitmap() {
  351.             return bitmap;
  352.         }

  353.         private EWAHCompressedBitmap ewahBitmap(Bitmap other) {
  354.             if (other instanceof CompressedBitmap) {
  355.                 CompressedBitmap b = (CompressedBitmap) other;
  356.                 if (b.bitmapIndex != bitmapIndex) {
  357.                     throw new IllegalArgumentException();
  358.                 }
  359.                 return b.bitmap;
  360.             }
  361.             if (other instanceof CompressedBitmapBuilder) {
  362.                 CompressedBitmapBuilder b = (CompressedBitmapBuilder) other;
  363.                 if (b.bitmapIndex != bitmapIndex) {
  364.                     throw new IllegalArgumentException();
  365.                 }
  366.                 return b.bitset.combine();
  367.             }
  368.             throw new IllegalArgumentException();
  369.         }
  370.     }

  371.     private static final class MutableBitmapIndex {
  372.         private final ObjectIdOwnerMap<MutableEntry>
  373.                 revMap = new ObjectIdOwnerMap<>();

  374.         private final BlockList<MutableEntry>
  375.                 revList = new BlockList<>();

  376.         int findPosition(AnyObjectId objectId) {
  377.             MutableEntry entry = revMap.get(objectId);
  378.             if (entry == null)
  379.                 return -1;
  380.             return entry.position;
  381.         }

  382.         MutableEntry getObject(int position) {
  383.             try {
  384.                 MutableEntry entry = revList.get(position);
  385.                 if (entry == null)
  386.                     throw new IllegalArgumentException(MessageFormat.format(
  387.                             JGitText.get().objectNotFound,
  388.                             String.valueOf(position)));
  389.                 return entry;
  390.             } catch (IndexOutOfBoundsException ex) {
  391.                 throw new IllegalArgumentException(ex);
  392.             }
  393.         }

  394.         int findOrInsert(AnyObjectId objectId, int type) {
  395.             MutableEntry entry = new MutableEntry(
  396.                     objectId, type, revList.size());
  397.             revList.add(entry);
  398.             revMap.add(entry);
  399.             return entry.position;
  400.         }
  401.     }

  402.     private static final class MutableEntry extends ObjectIdOwnerMap.Entry {
  403.         final int type;

  404.         final int position;

  405.         MutableEntry(AnyObjectId objectId, int type, int position) {
  406.             super(objectId);
  407.             this.type = type;
  408.             this.position = position;
  409.         }
  410.     }

  411.     private static final class BitmapObjectImpl extends BitmapObject {
  412.         private ObjectId objectId;

  413.         private int type;

  414.         @Override
  415.         public ObjectId getObjectId() {
  416.             return objectId;
  417.         }

  418.         @Override
  419.         public int getType() {
  420.             return type;
  421.         }
  422.     }

  423.     static final EWAHCompressedBitmap ones(int sizeInBits) {
  424.         EWAHCompressedBitmap mask = new EWAHCompressedBitmap();
  425.         mask.addStreamOfEmptyWords(
  426.                 true, sizeInBits / EWAHCompressedBitmap.WORD_IN_BITS);
  427.         int remaining = sizeInBits % EWAHCompressedBitmap.WORD_IN_BITS;
  428.         if (remaining > 0)
  429.             mask.addWord((1L << remaining) - 1, remaining);
  430.         return mask;
  431.     }
  432. }