BitmapIndexImpl.java

  1. /*
  2.  * Copyright (C) 2012, Google Inc. and others
  3.  *
  4.  * This program and the accompanying materials are made available under the
  5.  * terms of the Eclipse Distribution License v. 1.0 which is available at
  6.  * https://www.eclipse.org/org/documents/edl-v10.php.
  7.  *
  8.  * SPDX-License-Identifier: BSD-3-Clause
  9.  */

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

  11. import java.text.MessageFormat;
  12. import java.util.Iterator;
  13. import java.util.NoSuchElementException;

  14. import org.eclipse.jgit.internal.JGitText;
  15. import org.eclipse.jgit.lib.AnyObjectId;
  16. import org.eclipse.jgit.lib.BitmapIndex;
  17. import org.eclipse.jgit.lib.BitmapObject;
  18. import org.eclipse.jgit.lib.Constants;
  19. import org.eclipse.jgit.lib.ObjectId;
  20. import org.eclipse.jgit.lib.ObjectIdOwnerMap;
  21. import org.eclipse.jgit.util.BlockList;

  22. import com.googlecode.javaewah.EWAHCompressedBitmap;
  23. import com.googlecode.javaewah.IntIterator;

  24. /**
  25.  * A compressed bitmap representation of the entire object graph.
  26.  */
  27. public class BitmapIndexImpl implements BitmapIndex {
  28.     private static final int EXTRA_BITS = 10 * 1024;

  29.     final PackBitmapIndex packIndex;

  30.     final MutableBitmapIndex mutableIndex;

  31.     final int indexObjectCount;

  32.     /**
  33.      * Creates a BitmapIndex that is back by Compressed bitmaps.
  34.      *
  35.      * @param packIndex
  36.      *            the bitmap index for the pack.
  37.      */
  38.     public BitmapIndexImpl(PackBitmapIndex packIndex) {
  39.         this.packIndex = packIndex;
  40.         mutableIndex = new MutableBitmapIndex();
  41.         indexObjectCount = packIndex.getObjectCount();
  42.     }

  43.     PackBitmapIndex getPackBitmapIndex() {
  44.         return packIndex;
  45.     }

  46.     /** {@inheritDoc} */
  47.     @Override
  48.     public CompressedBitmap getBitmap(AnyObjectId objectId) {
  49.         EWAHCompressedBitmap compressed = packIndex.getBitmap(objectId);
  50.         if (compressed == null)
  51.             return null;
  52.         return new CompressedBitmap(compressed, this);
  53.     }

  54.     /** {@inheritDoc} */
  55.     @Override
  56.     public CompressedBitmapBuilder newBitmapBuilder() {
  57.         return new CompressedBitmapBuilder(this);
  58.     }

  59.     int findPosition(AnyObjectId objectId) {
  60.         int position = packIndex.findPosition(objectId);
  61.         if (position < 0) {
  62.             position = mutableIndex.findPosition(objectId);
  63.             if (position >= 0)
  64.                 position += indexObjectCount;
  65.         }
  66.         return position;
  67.     }

  68.     int findOrInsert(AnyObjectId objectId, int type) {
  69.         int position = findPosition(objectId);
  70.         if (position < 0) {
  71.             position = mutableIndex.findOrInsert(objectId, type);
  72.             position += indexObjectCount;
  73.         }
  74.         return position;
  75.     }

  76.     private static final class ComboBitset {
  77.         private InflatingBitSet inflatingBitmap;

  78.         private BitSet toAdd;

  79.         private BitSet toRemove;

  80.         ComboBitset() {
  81.             this(new EWAHCompressedBitmap());
  82.         }

  83.         ComboBitset(EWAHCompressedBitmap bitmap) {
  84.             this.inflatingBitmap = new InflatingBitSet(bitmap);
  85.         }

  86.         EWAHCompressedBitmap combine() {
  87.             EWAHCompressedBitmap toAddCompressed = null;
  88.             if (toAdd != null) {
  89.                 toAddCompressed = toAdd.toEWAHCompressedBitmap();
  90.                 toAdd = null;
  91.             }

  92.             EWAHCompressedBitmap toRemoveCompressed = null;
  93.             if (toRemove != null) {
  94.                 toRemoveCompressed = toRemove.toEWAHCompressedBitmap();
  95.                 toRemove = null;
  96.             }

  97.             if (toAddCompressed != null)
  98.                 or(toAddCompressed);
  99.             if (toRemoveCompressed != null)
  100.                 andNot(toRemoveCompressed);
  101.             return inflatingBitmap.getBitmap();
  102.         }

  103.         void or(EWAHCompressedBitmap inbits) {
  104.             if (toRemove != null)
  105.                 combine();
  106.             inflatingBitmap = inflatingBitmap.or(inbits);
  107.         }

  108.         void andNot(EWAHCompressedBitmap inbits) {
  109.             if (toAdd != null || toRemove != null)
  110.                 combine();
  111.             inflatingBitmap = inflatingBitmap.andNot(inbits);
  112.         }

  113.         void xor(EWAHCompressedBitmap inbits) {
  114.             if (toAdd != null || toRemove != null)
  115.                 combine();
  116.             inflatingBitmap = inflatingBitmap.xor(inbits);
  117.         }

  118.         boolean contains(int position) {
  119.             if (toRemove != null && toRemove.get(position))
  120.                 return false;
  121.             if (toAdd != null && toAdd.get(position))
  122.                 return true;
  123.             return inflatingBitmap.contains(position);
  124.         }

  125.         void remove(int position) {
  126.             if (toAdd != null)
  127.                 toAdd.clear(position);

  128.             if (inflatingBitmap.maybeContains(position)) {
  129.                 if (toRemove == null)
  130.                     toRemove = new BitSet(position + EXTRA_BITS);
  131.                 toRemove.set(position);
  132.             }
  133.         }

  134.         void set(int position) {
  135.             if (toRemove != null)
  136.                 toRemove.clear(position);

  137.             if (toAdd == null)
  138.                 toAdd = new BitSet(position + EXTRA_BITS);
  139.             toAdd.set(position);
  140.         }
  141.     }

  142.     private static final class CompressedBitmapBuilder implements BitmapBuilder {
  143.         private ComboBitset bitset;
  144.         private final BitmapIndexImpl bitmapIndex;

  145.         CompressedBitmapBuilder(BitmapIndexImpl bitmapIndex) {
  146.             this.bitset = new ComboBitset();
  147.             this.bitmapIndex = bitmapIndex;
  148.         }

  149.         @Override
  150.         public boolean contains(AnyObjectId objectId) {
  151.             int position = bitmapIndex.findPosition(objectId);
  152.             return 0 <= position && bitset.contains(position);
  153.         }

  154.         @Override
  155.         public BitmapBuilder addObject(AnyObjectId objectId, int type) {
  156.             bitset.set(bitmapIndex.findOrInsert(objectId, type));
  157.             return this;
  158.         }

  159.         @Override
  160.         public void remove(AnyObjectId objectId) {
  161.             int position = bitmapIndex.findPosition(objectId);
  162.             if (0 <= position)
  163.                 bitset.remove(position);
  164.         }

  165.         @Override
  166.         public CompressedBitmapBuilder or(Bitmap other) {
  167.             bitset.or(ewahBitmap(other));
  168.             return this;
  169.         }

  170.         @Override
  171.         public CompressedBitmapBuilder andNot(Bitmap other) {
  172.             bitset.andNot(ewahBitmap(other));
  173.             return this;
  174.         }

  175.         @Override
  176.         public CompressedBitmapBuilder xor(Bitmap other) {
  177.             bitset.xor(ewahBitmap(other));
  178.             return this;
  179.         }

  180.         /** @return the fully built immutable bitmap */
  181.         @Override
  182.         public CompressedBitmap build() {
  183.             return new CompressedBitmap(bitset.combine(), bitmapIndex);
  184.         }

  185.         @Override
  186.         public Iterator<BitmapObject> iterator() {
  187.             return build().iterator();
  188.         }

  189.         @Override
  190.         public int cardinality() {
  191.             return bitset.combine().cardinality();
  192.         }

  193.         @Override
  194.         public boolean removeAllOrNone(PackBitmapIndex index) {
  195.             if (!bitmapIndex.packIndex.equals(index))
  196.                 return false;

  197.             EWAHCompressedBitmap curr = bitset.combine()
  198.                     .xor(ones(bitmapIndex.indexObjectCount));

  199.             IntIterator ii = curr.intIterator();
  200.             if (ii.hasNext() && ii.next() < bitmapIndex.indexObjectCount)
  201.                 return false;
  202.             bitset = new ComboBitset(curr);
  203.             return true;
  204.         }

  205.         @Override
  206.         public BitmapIndexImpl getBitmapIndex() {
  207.             return bitmapIndex;
  208.         }

  209.         @Override
  210.         public EWAHCompressedBitmap retrieveCompressed() {
  211.             return build().retrieveCompressed();
  212.         }

  213.         private EWAHCompressedBitmap ewahBitmap(Bitmap other) {
  214.             if (other instanceof CompressedBitmap) {
  215.                 CompressedBitmap b = (CompressedBitmap) other;
  216.                 if (b.bitmapIndex != bitmapIndex) {
  217.                     throw new IllegalArgumentException();
  218.                 }
  219.                 return b.bitmap;
  220.             }
  221.             if (other instanceof CompressedBitmapBuilder) {
  222.                 CompressedBitmapBuilder b = (CompressedBitmapBuilder) other;
  223.                 if (b.bitmapIndex != bitmapIndex) {
  224.                     throw new IllegalArgumentException();
  225.                 }
  226.                 return b.bitset.combine();
  227.             }
  228.             throw new IllegalArgumentException();
  229.         }
  230.     }

  231.     /**
  232.      * Wrapper for a {@link EWAHCompressedBitmap} and {@link PackBitmapIndex}.
  233.      * <p>
  234.      * For a EWAHCompressedBitmap {@code bitmap} representing a vector of
  235.      * bits, {@code new CompressedBitmap(bitmap, bitmapIndex)} represents the
  236.      * objects at those positions in {@code bitmapIndex.packIndex}.
  237.      */
  238.     public static final class CompressedBitmap implements Bitmap {
  239.         final EWAHCompressedBitmap bitmap;
  240.         final BitmapIndexImpl bitmapIndex;

  241.         /**
  242.          * Construct compressed bitmap for given bitmap and bitmap index
  243.          *
  244.          * @param bitmap
  245.          * @param bitmapIndex
  246.          */
  247.         public CompressedBitmap(EWAHCompressedBitmap bitmap, BitmapIndexImpl bitmapIndex) {
  248.             this.bitmap = bitmap;
  249.             this.bitmapIndex = bitmapIndex;
  250.         }

  251.         @Override
  252.         public CompressedBitmap or(Bitmap other) {
  253.             return new CompressedBitmap(bitmap.or(ewahBitmap(other)), bitmapIndex);
  254.         }

  255.         @Override
  256.         public CompressedBitmap andNot(Bitmap other) {
  257.             return new CompressedBitmap(bitmap.andNot(ewahBitmap(other)), bitmapIndex);
  258.         }

  259.         @Override
  260.         public CompressedBitmap xor(Bitmap other) {
  261.             return new CompressedBitmap(bitmap.xor(ewahBitmap(other)), bitmapIndex);
  262.         }

  263.         private final IntIterator ofObjectType(int type) {
  264.             return bitmapIndex.packIndex.ofObjectType(bitmap, type).intIterator();
  265.         }

  266.         @Override
  267.         public Iterator<BitmapObject> iterator() {
  268.             final IntIterator dynamic = bitmap.andNot(ones(bitmapIndex.indexObjectCount))
  269.                     .intIterator();
  270.             final IntIterator commits = ofObjectType(Constants.OBJ_COMMIT);
  271.             final IntIterator trees = ofObjectType(Constants.OBJ_TREE);
  272.             final IntIterator blobs = ofObjectType(Constants.OBJ_BLOB);
  273.             final IntIterator tags = ofObjectType(Constants.OBJ_TAG);
  274.             return new Iterator<BitmapObject>() {
  275.                 private final BitmapObjectImpl out = new BitmapObjectImpl();
  276.                 private int type;
  277.                 private IntIterator cached = dynamic;

  278.                 @Override
  279.                 public boolean hasNext() {
  280.                     if (!cached.hasNext()) {
  281.                         if (commits.hasNext()) {
  282.                             type = Constants.OBJ_COMMIT;
  283.                             cached = commits;
  284.                         } else if (trees.hasNext()) {
  285.                             type = Constants.OBJ_TREE;
  286.                             cached = trees;
  287.                         } else if (blobs.hasNext()) {
  288.                             type = Constants.OBJ_BLOB;
  289.                             cached = blobs;
  290.                         } else if (tags.hasNext()) {
  291.                             type = Constants.OBJ_TAG;
  292.                             cached = tags;
  293.                         } else {
  294.                             return false;
  295.                         }
  296.                     }
  297.                     return true;
  298.                 }

  299.                 @Override
  300.                 public BitmapObject next() {
  301.                     if (!hasNext())
  302.                         throw new NoSuchElementException();

  303.                     int position = cached.next();
  304.                     if (position < bitmapIndex.indexObjectCount) {
  305.                         out.type = type;
  306.                         out.objectId = bitmapIndex.packIndex.getObject(position);
  307.                     } else {
  308.                         position -= bitmapIndex.indexObjectCount;
  309.                         MutableEntry entry = bitmapIndex.mutableIndex.getObject(position);
  310.                         out.type = entry.type;
  311.                         out.objectId = entry;
  312.                     }
  313.                     return out;
  314.                 }

  315.                 @Override
  316.                 public void remove() {
  317.                     throw new UnsupportedOperationException();
  318.                 }
  319.             };
  320.         }

  321.         @Override
  322.         public EWAHCompressedBitmap retrieveCompressed() {
  323.             return bitmap;
  324.         }

  325.         private EWAHCompressedBitmap ewahBitmap(Bitmap other) {
  326.             if (other instanceof CompressedBitmap) {
  327.                 CompressedBitmap b = (CompressedBitmap) other;
  328.                 if (b.bitmapIndex != bitmapIndex) {
  329.                     throw new IllegalArgumentException();
  330.                 }
  331.                 return b.bitmap;
  332.             }
  333.             if (other instanceof CompressedBitmapBuilder) {
  334.                 CompressedBitmapBuilder b = (CompressedBitmapBuilder) other;
  335.                 if (b.bitmapIndex != bitmapIndex) {
  336.                     throw new IllegalArgumentException();
  337.                 }
  338.                 return b.bitset.combine();
  339.             }
  340.             throw new IllegalArgumentException();
  341.         }
  342.     }

  343.     private static final class MutableBitmapIndex {
  344.         private final ObjectIdOwnerMap<MutableEntry>
  345.                 revMap = new ObjectIdOwnerMap<>();

  346.         private final BlockList<MutableEntry>
  347.                 revList = new BlockList<>();

  348.         int findPosition(AnyObjectId objectId) {
  349.             MutableEntry entry = revMap.get(objectId);
  350.             if (entry == null)
  351.                 return -1;
  352.             return entry.position;
  353.         }

  354.         MutableEntry getObject(int position) {
  355.             try {
  356.                 MutableEntry entry = revList.get(position);
  357.                 if (entry == null)
  358.                     throw new IllegalArgumentException(MessageFormat.format(
  359.                             JGitText.get().objectNotFound,
  360.                             String.valueOf(position)));
  361.                 return entry;
  362.             } catch (IndexOutOfBoundsException ex) {
  363.                 throw new IllegalArgumentException(ex);
  364.             }
  365.         }

  366.         int findOrInsert(AnyObjectId objectId, int type) {
  367.             MutableEntry entry = new MutableEntry(
  368.                     objectId, type, revList.size());
  369.             revList.add(entry);
  370.             revMap.add(entry);
  371.             return entry.position;
  372.         }
  373.     }

  374.     private static final class MutableEntry extends ObjectIdOwnerMap.Entry {
  375.         final int type;

  376.         final int position;

  377.         MutableEntry(AnyObjectId objectId, int type, int position) {
  378.             super(objectId);
  379.             this.type = type;
  380.             this.position = position;
  381.         }
  382.     }

  383.     private static final class BitmapObjectImpl extends BitmapObject {
  384.         private ObjectId objectId;

  385.         private int type;

  386.         @Override
  387.         public ObjectId getObjectId() {
  388.             return objectId;
  389.         }

  390.         @Override
  391.         public int getType() {
  392.             return type;
  393.         }
  394.     }

  395.     static final EWAHCompressedBitmap ones(int sizeInBits) {
  396.         EWAHCompressedBitmap mask = new EWAHCompressedBitmap();
  397.         mask.addStreamOfEmptyWords(
  398.                 true, sizeInBits / EWAHCompressedBitmap.WORD_IN_BITS);
  399.         int remaining = sizeInBits % EWAHCompressedBitmap.WORD_IN_BITS;
  400.         if (remaining > 0)
  401.             mask.addWord((1L << remaining) - 1, remaining);
  402.         return mask;
  403.     }
  404. }