PackBitmapIndexBuilder.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.ArrayList;
  13. import java.util.Collections;
  14. import java.util.LinkedList;
  15. import java.util.List;

  16. import org.eclipse.jgit.internal.JGitText;
  17. import org.eclipse.jgit.internal.storage.pack.BitmapCommit;
  18. import org.eclipse.jgit.internal.storage.pack.ObjectToPack;
  19. import org.eclipse.jgit.lib.AnyObjectId;
  20. import org.eclipse.jgit.lib.BitmapIndex.Bitmap;
  21. import org.eclipse.jgit.lib.Constants;
  22. import org.eclipse.jgit.lib.ObjectId;
  23. import org.eclipse.jgit.lib.ObjectIdOwnerMap;
  24. import org.eclipse.jgit.util.BlockList;

  25. import com.googlecode.javaewah.EWAHCompressedBitmap;

  26. /**
  27.  * Helper for constructing
  28.  * {@link org.eclipse.jgit.internal.storage.file.PackBitmapIndex}es.
  29.  */
  30. public class PackBitmapIndexBuilder extends BasePackBitmapIndex {
  31.     private static final int MAX_XOR_OFFSET_SEARCH = 10;

  32.     private final EWAHCompressedBitmap commits;
  33.     private final EWAHCompressedBitmap trees;
  34.     private final EWAHCompressedBitmap blobs;
  35.     private final EWAHCompressedBitmap tags;
  36.     private final BlockList<PositionEntry> byOffset;

  37.     private final LinkedList<StoredBitmap>
  38.             bitmapsToWriteXorBuffer = new LinkedList<>();

  39.     private List<StoredEntry> bitmapsToWrite = new ArrayList<>();

  40.     final ObjectIdOwnerMap<PositionEntry>
  41.             positionEntries = new ObjectIdOwnerMap<>();

  42.     /**
  43.      * Creates a PackBitmapIndex used for building the contents of an index
  44.      * file.
  45.      *
  46.      * @param objects
  47.      *            objects sorted by name. The list must be initially sorted by
  48.      *            ObjectId (name); it will be resorted in place.
  49.      */
  50.     public PackBitmapIndexBuilder(List<ObjectToPack> objects) {
  51.         super(new ObjectIdOwnerMap<StoredBitmap>());
  52.         byOffset = new BlockList<>(objects.size());
  53.         sortByOffsetAndIndex(byOffset, positionEntries, objects);

  54.         // 64 objects fit in a single long word (64 bits).
  55.         // On average a repository is 30% commits, 30% trees, 30% blobs.
  56.         // Initialize bitmap capacity for worst case to minimize growing.
  57.         int sizeInWords = Math.max(4, byOffset.size() / 64 / 3);
  58.         commits = new EWAHCompressedBitmap(sizeInWords);
  59.         trees = new EWAHCompressedBitmap(sizeInWords);
  60.         blobs = new EWAHCompressedBitmap(sizeInWords);
  61.         tags = new EWAHCompressedBitmap(sizeInWords);
  62.         for (int i = 0; i < objects.size(); i++) {
  63.             int type = objects.get(i).getType();
  64.             switch (type) {
  65.             case Constants.OBJ_COMMIT:
  66.                 commits.set(i);
  67.                 break;
  68.             case Constants.OBJ_TREE:
  69.                 trees.set(i);
  70.                 break;
  71.             case Constants.OBJ_BLOB:
  72.                 blobs.set(i);
  73.                 break;
  74.             case Constants.OBJ_TAG:
  75.                 tags.set(i);
  76.                 break;
  77.             default:
  78.                 throw new IllegalArgumentException(MessageFormat.format(
  79.                         JGitText.get().badObjectType, String.valueOf(type)));
  80.             }
  81.         }
  82.         commits.trim();
  83.         trees.trim();
  84.         blobs.trim();
  85.         tags.trim();
  86.     }

  87.     private static void sortByOffsetAndIndex(BlockList<PositionEntry> byOffset,
  88.             ObjectIdOwnerMap<PositionEntry> positionEntries,
  89.             List<ObjectToPack> entries) {
  90.         for (int i = 0; i < entries.size(); i++) {
  91.             positionEntries.add(new PositionEntry(entries.get(i), i));
  92.         }
  93.         Collections.sort(entries, (ObjectToPack a, ObjectToPack b) -> Long
  94.                 .signum(a.getOffset() - b.getOffset()));
  95.         for (int i = 0; i < entries.size(); i++) {
  96.             PositionEntry e = positionEntries.get(entries.get(i));
  97.             e.offsetPosition = i;
  98.             byOffset.add(e);
  99.         }
  100.     }

  101.     /**
  102.      * Get set of objects included in the pack.
  103.      *
  104.      * @return set of objects included in the pack.
  105.      */
  106.     public ObjectIdOwnerMap<ObjectIdOwnerMap.Entry> getObjectSet() {
  107.         ObjectIdOwnerMap<ObjectIdOwnerMap.Entry> r = new ObjectIdOwnerMap<>();
  108.         for (PositionEntry e : byOffset) {
  109.             r.add(new ObjectIdOwnerMap.Entry(e) {
  110.                 // A new entry that copies the ObjectId
  111.             });
  112.         }
  113.         return r;
  114.     }

  115.     /**
  116.      * Stores the bitmap for the objectId.
  117.      *
  118.      * @param objectId
  119.      *            the object id key for the bitmap.
  120.      * @param bitmap
  121.      *            the bitmap
  122.      * @param flags
  123.      *            the flags to be stored with the bitmap
  124.      */
  125.     public void addBitmap(AnyObjectId objectId, Bitmap bitmap, int flags) {
  126.         addBitmap(objectId, bitmap.retrieveCompressed(), flags);
  127.     }

  128.     /**
  129.      * Processes a commit and prepares its bitmap to write to the bitmap index
  130.      * file.
  131.      *
  132.      * @param c
  133.      *            the commit corresponds to the bitmap.
  134.      * @param bitmap
  135.      *            the bitmap to be written.
  136.      * @param flags
  137.      *            the flags of the commit.
  138.      */
  139.     public void processBitmapForWrite(BitmapCommit c, Bitmap bitmap,
  140.             int flags) {
  141.         EWAHCompressedBitmap compressed = bitmap.retrieveCompressed();
  142.         compressed.trim();
  143.         StoredBitmap newest = new StoredBitmap(c, compressed, null, flags);

  144.         bitmapsToWriteXorBuffer.add(newest);
  145.         if (bitmapsToWriteXorBuffer.size() > MAX_XOR_OFFSET_SEARCH) {
  146.             bitmapsToWrite.add(
  147.                     generateStoredEntry(bitmapsToWriteXorBuffer.pollFirst()));
  148.         }

  149.         if (c.isAddToIndex()) {
  150.             // The Bitmap map in the base class is used to make revwalks
  151.             // efficient, so only add bitmaps that keep it efficient without
  152.             // bloating memory.
  153.             addBitmap(c, bitmap, flags);
  154.         }
  155.     }

  156.     private StoredEntry generateStoredEntry(StoredBitmap bitmapToWrite) {
  157.         int bestXorOffset = 0;
  158.         EWAHCompressedBitmap bestBitmap = bitmapToWrite.getBitmap();

  159.         int offset = 1;
  160.         for (StoredBitmap curr : bitmapsToWriteXorBuffer) {
  161.             EWAHCompressedBitmap bitmap = curr.getBitmap()
  162.                     .xor(bitmapToWrite.getBitmap());
  163.             if (bitmap.sizeInBytes() < bestBitmap.sizeInBytes()) {
  164.                 bestBitmap = bitmap;
  165.                 bestXorOffset = offset;
  166.             }
  167.             offset++;
  168.         }

  169.         PositionEntry entry = positionEntries.get(bitmapToWrite);
  170.         if (entry == null) {
  171.             throw new IllegalStateException();
  172.         }
  173.         bestBitmap.trim();
  174.         StoredEntry result = new StoredEntry(entry.namePosition, bestBitmap,
  175.                 bestXorOffset, bitmapToWrite.getFlags());

  176.         return result;
  177.     }

  178.     /**
  179.      * Stores the bitmap for the objectId.
  180.      *
  181.      * @param objectId
  182.      *            the object id key for the bitmap.
  183.      * @param bitmap
  184.      *            the bitmap
  185.      * @param flags
  186.      *            the flags to be stored with the bitmap
  187.      */
  188.     public void addBitmap(
  189.             AnyObjectId objectId, EWAHCompressedBitmap bitmap, int flags) {
  190.         bitmap.trim();
  191.         StoredBitmap result = new StoredBitmap(objectId, bitmap, null, flags);
  192.         getBitmaps().add(result);
  193.     }

  194.     /** {@inheritDoc} */
  195.     @Override
  196.     public EWAHCompressedBitmap ofObjectType(
  197.             EWAHCompressedBitmap bitmap, int type) {
  198.         switch (type) {
  199.         case Constants.OBJ_BLOB:
  200.             return getBlobs().and(bitmap);
  201.         case Constants.OBJ_TREE:
  202.             return getTrees().and(bitmap);
  203.         case Constants.OBJ_COMMIT:
  204.             return getCommits().and(bitmap);
  205.         case Constants.OBJ_TAG:
  206.             return getTags().and(bitmap);
  207.         }
  208.         throw new IllegalArgumentException();
  209.     }

  210.     /** {@inheritDoc} */
  211.     @Override
  212.     public int findPosition(AnyObjectId objectId) {
  213.         PositionEntry entry = positionEntries.get(objectId);
  214.         if (entry == null)
  215.             return -1;
  216.         return entry.offsetPosition;
  217.     }

  218.     /** {@inheritDoc} */
  219.     @Override
  220.     public ObjectId getObject(int position) throws IllegalArgumentException {
  221.         ObjectId objectId = byOffset.get(position);
  222.         if (objectId == null)
  223.             throw new IllegalArgumentException();
  224.         return objectId;
  225.     }

  226.     /**
  227.      * Get the commit object bitmap.
  228.      *
  229.      * @return the commit object bitmap.
  230.      */
  231.     public EWAHCompressedBitmap getCommits() {
  232.         return commits;
  233.     }

  234.     /**
  235.      * Get the tree object bitmap.
  236.      *
  237.      * @return the tree object bitmap.
  238.      */
  239.     public EWAHCompressedBitmap getTrees() {
  240.         return trees;
  241.     }

  242.     /**
  243.      * Get the blob object bitmap.
  244.      *
  245.      * @return the blob object bitmap.
  246.      */
  247.     public EWAHCompressedBitmap getBlobs() {
  248.         return blobs;
  249.     }

  250.     /**
  251.      * Get the tag object bitmap.
  252.      *
  253.      * @return the tag object bitmap.
  254.      */
  255.     public EWAHCompressedBitmap getTags() {
  256.         return tags;
  257.     }

  258.     /**
  259.      * Get the index storage options.
  260.      *
  261.      * @return the index storage options.
  262.      */
  263.     public int getOptions() {
  264.         return PackBitmapIndexV1.OPT_FULL;
  265.     }

  266.     /** {@inheritDoc} */
  267.     @Override
  268.     public int getBitmapCount() {
  269.         return bitmapsToWriteXorBuffer.size() + bitmapsToWrite.size();
  270.     }

  271.     /**
  272.      * Remove all the bitmaps entries added.
  273.      *
  274.      * @param size
  275.      *            the expected number of bitmap entries to be written.
  276.      */
  277.     public void resetBitmaps(int size) {
  278.         getBitmaps().clear();
  279.         bitmapsToWrite = new ArrayList<>(size);
  280.     }

  281.     /** {@inheritDoc} */
  282.     @Override
  283.     public int getObjectCount() {
  284.         return byOffset.size();
  285.     }

  286.     /**
  287.      * Get list of xor compressed entries that need to be written.
  288.      *
  289.      * @return a list of the xor compressed entries.
  290.      */
  291.     public List<StoredEntry> getCompressedBitmaps() {
  292.         while (!bitmapsToWriteXorBuffer.isEmpty()) {
  293.             bitmapsToWrite.add(
  294.                     generateStoredEntry(bitmapsToWriteXorBuffer.pollFirst()));
  295.         }

  296.         Collections.reverse(bitmapsToWrite);
  297.         return bitmapsToWrite;
  298.     }

  299.     /** Data object for the on disk representation of a bitmap entry. */
  300.     public static final class StoredEntry {
  301.         private final long objectId;
  302.         private final EWAHCompressedBitmap bitmap;
  303.         private final int xorOffset;
  304.         private final int flags;

  305.         StoredEntry(long objectId, EWAHCompressedBitmap bitmap,
  306.                 int xorOffset, int flags) {
  307.             this.objectId = objectId;
  308.             this.bitmap = bitmap;
  309.             this.xorOffset = xorOffset;
  310.             this.flags = flags;
  311.         }

  312.         /** @return the bitmap */
  313.         public EWAHCompressedBitmap getBitmap() {
  314.             return bitmap;
  315.         }

  316.         /** @return the xorOffset */
  317.         public int getXorOffset() {
  318.             return xorOffset;
  319.         }

  320.         /** @return the flags */
  321.         public int getFlags() {
  322.             return flags;
  323.         }

  324.         /** @return the ObjectId */
  325.         public long getObjectId() {
  326.             return objectId;
  327.         }
  328.     }

  329.     private static final class PositionEntry extends ObjectIdOwnerMap.Entry {
  330.         final int namePosition;

  331.         int offsetPosition;

  332.         PositionEntry(AnyObjectId objectId, int namePosition) {
  333.             super(objectId);
  334.             this.namePosition = namePosition;
  335.         }
  336.     }
  337. }