/*
 * 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.internal.storage.file;

import java.text.MessageFormat;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;

import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.internal.storage.file.BitmapIndexImpl.CompressedBitmap;
import org.eclipse.jgit.internal.storage.pack.ObjectToPack;
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.util.BlockList;

import com.googlecode.javaewah.EWAHCompressedBitmap;

Helper for constructing PackBitmapIndexes.
/** * Helper for constructing * {@link org.eclipse.jgit.internal.storage.file.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 BlockList<PositionEntry> byOffset; final BlockList<StoredBitmap> byAddOrder = new BlockList<>(); final ObjectIdOwnerMap<PositionEntry> positionEntries = new ObjectIdOwnerMap<>();
Creates a PackBitmapIndex used for building the contents of an index file.
Params:
  • objects – objects sorted by name. The list must be initially sorted by ObjectId (name); it will be resorted in place.
/** * Creates a PackBitmapIndex used for building the contents of an index * file. * * @param objects * objects sorted by name. The list must be initially sorted by * ObjectId (name); it will be resorted in place. */
public PackBitmapIndexBuilder(List<ObjectToPack> objects) { super(new ObjectIdOwnerMap<StoredBitmap>()); byOffset = new BlockList<>(objects.size()); sortByOffsetAndIndex(byOffset, positionEntries, objects); // 64 objects fit in a single long word (64 bits). // On average a repository is 30% commits, 30% trees, 30% blobs. // Initialize bitmap capacity for worst case to minimize growing. int sizeInWords = Math.max(4, byOffset.size() / 64 / 3); commits = new EWAHCompressedBitmap(sizeInWords); trees = new EWAHCompressedBitmap(sizeInWords); blobs = new EWAHCompressedBitmap(sizeInWords); tags = new EWAHCompressedBitmap(sizeInWords); for (int i = 0; i < objects.size(); i++) { int type = objects.get(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))); } } commits.trim(); trees.trim(); blobs.trim(); tags.trim(); } private static void sortByOffsetAndIndex(BlockList<PositionEntry> byOffset, ObjectIdOwnerMap<PositionEntry> positionEntries, List<ObjectToPack> entries) { for (int i = 0; i < entries.size(); i++) { positionEntries.add(new PositionEntry(entries.get(i), i)); } Collections.sort(entries, (ObjectToPack a, ObjectToPack b) -> Long .signum(a.getOffset() - b.getOffset())); for (int i = 0; i < entries.size(); i++) { PositionEntry e = positionEntries.get(entries.get(i)); e.offsetPosition = i; byOffset.add(e); } }
Get set of objects included in the pack.
Returns:set of objects included in the pack.
/** * Get set of objects included in the pack. * * @return set of objects included in the pack. */
public ObjectIdOwnerMap<ObjectIdOwnerMap.Entry> getObjectSet() { ObjectIdOwnerMap<ObjectIdOwnerMap.Entry> r = new ObjectIdOwnerMap<>(); for (PositionEntry e : byOffset) { r.add(new ObjectIdOwnerMap.Entry(e) { // A new entry that copies the ObjectId }); } return r; }
Stores the bitmap for the objectId.
Params:
  • objectId – the object id key for the bitmap.
  • bitmap – the bitmap
  • flags – the flags to be stored with the bitmap
/** * 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.
Params:
  • objectId – the object id key for the bitmap.
  • bitmap – the bitmap
  • flags – the flags to be stored with the bitmap
/** * 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) { bitmap.trim(); StoredBitmap result = new StoredBitmap(objectId, bitmap, null, flags); getBitmaps().add(result); byAddOrder.add(result); }
{@inheritDoc}
/** {@inheritDoc} */
@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(); }
{@inheritDoc}
/** {@inheritDoc} */
@Override public int findPosition(AnyObjectId objectId) { PositionEntry entry = positionEntries.get(objectId); if (entry == null) return -1; return entry.offsetPosition; }
{@inheritDoc}
/** {@inheritDoc} */
@Override public ObjectId getObject(int position) throws IllegalArgumentException { ObjectId objectId = byOffset.get(position); if (objectId == null) throw new IllegalArgumentException(); return objectId; }
Get the commit object bitmap.
Returns:the commit object bitmap.
/** * Get the commit object bitmap. * * @return the commit object bitmap. */
public EWAHCompressedBitmap getCommits() { return commits; }
Get the tree object bitmap.
Returns:the tree object bitmap.
/** * Get the tree object bitmap. * * @return the tree object bitmap. */
public EWAHCompressedBitmap getTrees() { return trees; }
Get the blob object bitmap.
Returns:the blob object bitmap.
/** * Get the blob object bitmap. * * @return the blob object bitmap. */
public EWAHCompressedBitmap getBlobs() { return blobs; }
Get the tag object bitmap.
Returns:the tag object bitmap.
/** * Get the tag object bitmap. * * @return the tag object bitmap. */
public EWAHCompressedBitmap getTags() { return tags; }
Get the index storage options.
Returns:the index storage options.
/** * Get the index storage options. * * @return the index storage options. */
public int getOptions() { return PackBitmapIndexV1.OPT_FULL; }
{@inheritDoc}
/** {@inheritDoc} */
@Override public int getBitmapCount() { return getBitmaps().size(); }
Remove all the bitmaps entries added.
/** * Remove all the bitmaps entries added. */
public void clearBitmaps() { byAddOrder.clear(); getBitmaps().clear(); }
{@inheritDoc}
/** {@inheritDoc} */
@Override public int getObjectCount() { return byOffset.size(); }
Get an iterator over the xor compressed entries.
Returns:an iterator over the xor compressed entries.
/** * Get an iterator over the xor compressed entries. * * @return an iterator over the xor compressed entries. */
public Iterable<StoredEntry> getCompressedBitmaps() { // Add order is from oldest to newest. The reverse add order is the // output order. return () -> new Iterator<StoredEntry>() { private int index = byAddOrder.size() - 1; @Override public boolean hasNext() { return index >= 0; } @Override 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(); } bestBitmap.trim(); return new StoredEntry(entry.namePosition, bestBitmap, bestXorOffset, item.getFlags()); } @Override public void remove() { throw new UnsupportedOperationException(); } }; }
Data object for the on disk representation of a bitmap entry.
/** 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; StoredEntry(long objectId, EWAHCompressedBitmap bitmap, int xorOffset, int flags) { this.objectId = objectId; this.bitmap = bitmap; this.xorOffset = xorOffset; this.flags = flags; }
Returns:the bitmap
/** @return the bitmap */
public EWAHCompressedBitmap getBitmap() { return bitmap; }
Returns:the xorOffset
/** @return the xorOffset */
public int getXorOffset() { return xorOffset; }
Returns:the flags
/** @return the flags */
public int getFlags() { return flags; }
Returns:the ObjectId
/** @return the ObjectId */
public long getObjectId() { return objectId; } } private static final class PositionEntry extends ObjectIdOwnerMap.Entry { final int namePosition; int offsetPosition; PositionEntry(AnyObjectId objectId, int namePosition) { super(objectId); this.namePosition = namePosition; } } }