You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
483 lines
13 KiB
483 lines
13 KiB
/* |
|
* 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 com.fr.third.eclipse.jgit.internal.storage.file; |
|
|
|
import com.fr.third.eclipse.jgit.internal.JGitText; |
|
import com.fr.third.eclipse.jgit.lib.AnyObjectId; |
|
import com.fr.third.eclipse.jgit.lib.BitmapIndex; |
|
import com.fr.third.eclipse.jgit.lib.BitmapObject; |
|
import com.fr.third.eclipse.jgit.lib.Constants; |
|
import com.fr.third.eclipse.jgit.lib.ObjectId; |
|
import com.fr.third.eclipse.jgit.lib.ObjectIdOwnerMap; |
|
import com.fr.third.eclipse.jgit.util.BlockList; |
|
import com.fr.third.googlecode.javaewah.EWAHCompressedBitmap; |
|
import com.fr.third.googlecode.javaewah.IntIterator; |
|
|
|
import java.text.MessageFormat; |
|
import java.util.Iterator; |
|
import java.util.NoSuchElementException; |
|
|
|
/** 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<BitmapObject> 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<BitmapObject> 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<BitmapObject>() { |
|
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<MutableEntry> |
|
revMap = new ObjectIdOwnerMap<MutableEntry>(); |
|
|
|
private final BlockList<MutableEntry> |
|
revList = new BlockList<MutableEntry>(); |
|
|
|
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; |
|
} |
|
}
|
|
|